From ac409e38b820ebf07a5677a3b393933dd3cf668d Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Mon, 17 Jun 2019 18:24:02 -0300 Subject: [PATCH 01/49] clutter/stage-view: Move unexported functions to private header Next commits will expose ClutterStageView as a public class, so move the functions private to Clutter to a private header. https://gitlab.gnome.org/GNOME/mutter/merge_requests/623 --- clutter/clutter/clutter-stage-view-private.h | 37 ++++++++++++++++++++ clutter/clutter/clutter-stage-view.c | 1 + clutter/clutter/clutter-stage-view.h | 13 ------- clutter/clutter/clutter-stage.c | 1 + clutter/clutter/cogl/clutter-stage-cogl.c | 1 + clutter/clutter/meson.build | 1 + 6 files changed, 41 insertions(+), 13 deletions(-) create mode 100644 clutter/clutter/clutter-stage-view-private.h diff --git a/clutter/clutter/clutter-stage-view-private.h b/clutter/clutter/clutter-stage-view-private.h new file mode 100644 index 000000000..89c42599f --- /dev/null +++ b/clutter/clutter/clutter-stage-view-private.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2019 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#ifndef __CLUTTER_STAGE_VIEW_PRIVATE_H__ +#define __CLUTTER_STAGE_VIEW_PRIVATE_H__ + +#include "clutter/clutter-stage-view.h" + +void clutter_stage_view_blit_offscreen (ClutterStageView *view, + const cairo_rectangle_int_t *clip); + +gboolean clutter_stage_view_is_dirty_viewport (ClutterStageView *view); + +void clutter_stage_view_set_dirty_viewport (ClutterStageView *view, + gboolean dirty); + +gboolean clutter_stage_view_is_dirty_projection (ClutterStageView *view); + +void clutter_stage_view_set_dirty_projection (ClutterStageView *view, + gboolean dirty); + + +#endif /* __CLUTTER_STAGE_VIEW_PRIVATE_H__ */ diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c index c536ac720..e26e2fc07 100644 --- a/clutter/clutter/clutter-stage-view.c +++ b/clutter/clutter/clutter-stage-view.c @@ -18,6 +18,7 @@ #include "clutter-build-config.h" #include "clutter/clutter-stage-view.h" +#include "clutter/clutter-stage-view-private.h" #include #include diff --git a/clutter/clutter/clutter-stage-view.h b/clutter/clutter/clutter-stage-view.h index 126498625..0c3448511 100644 --- a/clutter/clutter/clutter-stage-view.h +++ b/clutter/clutter/clutter-stage-view.h @@ -57,22 +57,9 @@ void clutter_stage_view_transform_to_onscreen (ClutterStageView *vie gfloat *x, gfloat *y); -void clutter_stage_view_blit_offscreen (ClutterStageView *view, - const cairo_rectangle_int_t *clip); - CLUTTER_EXPORT float clutter_stage_view_get_scale (ClutterStageView *view); -gboolean clutter_stage_view_is_dirty_viewport (ClutterStageView *view); - -void clutter_stage_view_set_dirty_viewport (ClutterStageView *view, - gboolean dirty); - -gboolean clutter_stage_view_is_dirty_projection (ClutterStageView *view); - -void clutter_stage_view_set_dirty_projection (ClutterStageView *view, - gboolean dirty); - CLUTTER_EXPORT void clutter_stage_view_get_offscreen_transformation_matrix (ClutterStageView *view, CoglMatrix *matrix); diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c index 1eea5b305..f254b5d49 100644 --- a/clutter/clutter/clutter-stage.c +++ b/clutter/clutter/clutter-stage.c @@ -72,6 +72,7 @@ #include "clutter-private.h" #include "clutter-stage-manager-private.h" #include "clutter-stage-private.h" +#include "clutter-stage-view-private.h" #include "clutter-private.h" #include "cogl/cogl.h" diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c index eab76e52f..d942d9d41 100644 --- a/clutter/clutter/cogl/clutter-stage-cogl.c +++ b/clutter/clutter/cogl/clutter-stage-cogl.c @@ -45,6 +45,7 @@ #include "clutter-main.h" #include "clutter-private.h" #include "clutter-stage-private.h" +#include "clutter-stage-view-private.h" typedef struct _ClutterStageViewCoglPrivate { diff --git a/clutter/clutter/meson.build b/clutter/clutter/meson.build index 671d790df..7feed24aa 100644 --- a/clutter/clutter/meson.build +++ b/clutter/clutter/meson.build @@ -206,6 +206,7 @@ clutter_private_headers = [ 'clutter-stage-manager-private.h', 'clutter-stage-private.h', 'clutter-stage-view.h', + 'clutter-stage-view-private.h', 'clutter-stage-window.h', ] -- 2.26.2 From cc2cf250f91d4b4c939aa1c6f8dd9dad4d77d975 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Mon, 17 Jun 2019 18:32:12 -0300 Subject: [PATCH 02/49] clutter/stage-view: Annotate some functions The GIR parser cannot figure out the ownership model of ClutterStageView.get_framebuffer() and .get_offscreen() without them, and throws us a couple of warnings. https://gitlab.gnome.org/GNOME/mutter/merge_requests/623 --- clutter/clutter/clutter-stage-view.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c index e26e2fc07..0fad6fc44 100644 --- a/clutter/clutter/clutter-stage-view.c +++ b/clutter/clutter/clutter-stage-view.c @@ -66,6 +66,14 @@ clutter_stage_view_get_layout (ClutterStageView *view, *rect = priv->layout; } +/** + * clutter_stage_view_get_framebuffer: + * @view: a #ClutterStageView + * + * Retrieves the framebuffer of @view to draw to. + * + * Returns: (transfer none): a #CoglFramebuffer + */ CoglFramebuffer * clutter_stage_view_get_framebuffer (ClutterStageView *view) { @@ -80,6 +88,14 @@ clutter_stage_view_get_framebuffer (ClutterStageView *view) return priv->framebuffer; } +/** + * clutter_stage_view_get_onscreen: + * @view: a #ClutterStageView + * + * Retrieves the onscreen framebuffer of @view if available. + * + * Returns: (transfer none): a #CoglFramebuffer + */ CoglFramebuffer * clutter_stage_view_get_onscreen (ClutterStageView *view) { -- 2.26.2 From c21e563398edccbd210defe367d89920b2d3f3d1 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Mon, 17 Jun 2019 18:33:17 -0300 Subject: [PATCH 03/49] clutter: Make ClutterStageView a public class As a compositor toolkit, it makes sense to allow consumers of Clutter interact with the stage views themselves. As such, ClutterStageView should be a public class. As such, it is now included in clutter.h and should not be included directly. https://gitlab.gnome.org/GNOME/mutter/merge_requests/623 --- clutter/clutter/clutter-stage-view.h | 4 ++++ clutter/clutter/clutter.h | 1 + clutter/clutter/meson.build | 4 ++-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/clutter/clutter/clutter-stage-view.h b/clutter/clutter/clutter-stage-view.h index 0c3448511..26bf10e79 100644 --- a/clutter/clutter/clutter-stage-view.h +++ b/clutter/clutter/clutter-stage-view.h @@ -18,6 +18,10 @@ #ifndef __CLUTTER_STAGE_VIEW_H__ #define __CLUTTER_STAGE_VIEW_H__ +#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) +#error "Only can be included directly." +#endif + #include #include #include diff --git a/clutter/clutter/clutter.h b/clutter/clutter/clutter.h index 231d8cd1b..ec846910f 100644 --- a/clutter/clutter/clutter.h +++ b/clutter/clutter/clutter.h @@ -101,6 +101,7 @@ #include "clutter-snap-constraint.h" #include "clutter-stage.h" #include "clutter-stage-manager.h" +#include "clutter-stage-view.h" #include "clutter-tap-action.h" #include "clutter-test-utils.h" #include "clutter-texture.h" diff --git a/clutter/clutter/meson.build b/clutter/clutter/meson.build index 7feed24aa..8e0484453 100644 --- a/clutter/clutter/meson.build +++ b/clutter/clutter/meson.build @@ -75,6 +75,7 @@ clutter_headers = [ 'clutter-snap-constraint.h', 'clutter-stage.h', 'clutter-stage-manager.h', + 'clutter-stage-view.h', 'clutter-tap-action.h', 'clutter-test-utils.h', 'clutter-texture.h', @@ -163,6 +164,7 @@ clutter_sources = [ 'clutter-snap-constraint.c', 'clutter-stage.c', 'clutter-stage-manager.c', + 'clutter-stage-view.c', 'clutter-stage-window.c', 'clutter-tap-action.c', 'clutter-test-utils.c', @@ -205,7 +207,6 @@ clutter_private_headers = [ 'clutter-settings-private.h', 'clutter-stage-manager-private.h', 'clutter-stage-private.h', - 'clutter-stage-view.h', 'clutter-stage-view-private.h', 'clutter-stage-window.h', ] @@ -214,7 +215,6 @@ clutter_nonintrospected_sources = [ 'clutter-easing.c', 'clutter-event-translator.c', 'clutter-id-pool.c', - 'clutter-stage-view.c', ] clutter_deprecated_headers = [ -- 2.26.2 From dfb1b9a61f2909be14be5a152f5eacb93cddafa2 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Mon, 17 Jun 2019 18:39:34 -0300 Subject: [PATCH 04/49] clutter/stage: Own clutter_stage_get_view_at() This function is exported as a Mutter-specific function, but now that ClutterStageView is part of the public API, ClutterStage can own this function. https://gitlab.gnome.org/GNOME/mutter/merge_requests/623 --- clutter/clutter/clutter-stage.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/clutter/clutter/clutter-stage.h b/clutter/clutter/clutter-stage.h index 5730af7bd..9d0fdd362 100644 --- a/clutter/clutter/clutter-stage.h +++ b/clutter/clutter/clutter-stage.h @@ -30,6 +30,7 @@ #include #include +#include G_BEGIN_DECLS @@ -274,6 +275,10 @@ gboolean clutter_stage_capture (ClutterStage *stage, cairo_rectangle_int_t *rect, ClutterCapture **captures, int *n_captures); +CLUTTER_EXPORT +ClutterStageView * clutter_stage_get_view_at (ClutterStage *stage, + float x, + float y); G_END_DECLS -- 2.26.2 From 29cd64c50a054384bf737ff12ee62770ae20b305 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Mon, 17 Jun 2019 19:16:47 -0300 Subject: [PATCH 05/49] clutter/stage: Emit after-paint after painting ClutterStage:after-paint is supposed to be emitted after all painting is done, but before the frame is finished. However, as it is right now, it is being emitted after each view is painted -- on multi-monitor setups, after-frame is being emitted multiple times. Send after-paint only once, after all views are painted and before finishing the frame. https://gitlab.gnome.org/GNOME/mutter/merge_requests/623 --- clutter/clutter/clutter-stage-private.h | 1 + clutter/clutter/clutter-stage.c | 5 +++++ clutter/clutter/cogl/clutter-stage-cogl.c | 2 ++ 3 files changed, 8 insertions(+) diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h index 4799c29e1..df0bf642b 100644 --- a/clutter/clutter/clutter-stage-private.h +++ b/clutter/clutter/clutter-stage-private.h @@ -40,6 +40,7 @@ void _clutter_stage_paint_view (ClutterStage ClutterStageView *view, const cairo_rectangle_int_t *clip); +void _clutter_stage_emit_after_paint (ClutterStage *stage); void _clutter_stage_set_window (ClutterStage *stage, ClutterStageWindow *stage_window); ClutterStageWindow *_clutter_stage_get_window (ClutterStage *stage); diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c index f254b5d49..2b437e1f6 100644 --- a/clutter/clutter/clutter-stage.c +++ b/clutter/clutter/clutter-stage.c @@ -690,6 +690,11 @@ _clutter_stage_paint_view (ClutterStage *stage, return; clutter_stage_do_paint_view (stage, view, clip); +} + +void +_clutter_stage_emit_after_paint (ClutterStage *stage) +{ g_signal_emit (stage, stage_signals[AFTER_PAINT], 0); } diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c index d942d9d41..005c6f692 100644 --- a/clutter/clutter/cogl/clutter-stage-cogl.c +++ b/clutter/clutter/cogl/clutter-stage-cogl.c @@ -936,6 +936,8 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window) clutter_stage_cogl_redraw_view (stage_window, view) || swap_event; } + _clutter_stage_emit_after_paint (stage_cogl->wrapper); + _clutter_stage_window_finish_frame (stage_window); if (swap_event) -- 2.26.2 From 152945e1061d37d8e5899d2df9a24c25f46096ed Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Mon, 17 Jun 2019 21:33:42 -0300 Subject: [PATCH 06/49] clutter/stage: Add ClutterStage:paint-view Now that ClutterStageView is embraced as part of the public set of Clutter classes, is it possible to give consumers of this API more information and control over the drawing routines of ClutterStage. Introduce ClutterStage:paint-view, a signal that is emitted for painting a specific view. It's defined as a RUN_LAST signal to give anyone connecting to it the ability to run before the view is actually painted, or after (using the G_CONNECT_AFTER flag, or g_signal_connect_after). This signal has a corresponding class handler, which allows Mutter to have much finer control over the painting routines. In fact, this will allow us to implement a "paint phase watcher" mechanism in the following patches. https://gitlab.gnome.org/GNOME/mutter/merge_requests/623 --- clutter/clutter/clutter-stage.c | 47 ++++++++++++++++++++++++++++++++- clutter/clutter/clutter-stage.h | 5 +++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c index 2b437e1f6..34c4e0119 100644 --- a/clutter/clutter/clutter-stage.c +++ b/clutter/clutter/clutter-stage.c @@ -148,6 +148,8 @@ struct _ClutterStagePrivate gpointer paint_data; GDestroyNotify paint_notify; + cairo_rectangle_int_t view_clip; + int update_freeze_count; guint relayout_pending : 1; @@ -192,6 +194,7 @@ enum DEACTIVATE, DELETE_EVENT, AFTER_PAINT, + PAINT_VIEW, PRESENTED, LAST_SIGNAL @@ -689,7 +692,15 @@ _clutter_stage_paint_view (ClutterStage *stage, if (!priv->impl) return; - clutter_stage_do_paint_view (stage, view, clip); + priv->view_clip = *clip; + + if (g_signal_has_handler_pending (stage, stage_signals[PAINT_VIEW], + 0, TRUE)) + g_signal_emit (stage, stage_signals[PAINT_VIEW], 0, view); + else + CLUTTER_STAGE_GET_CLASS (stage)->paint_view (stage, view); + + priv->view_clip = (cairo_rectangle_int_t) { 0 }; } void @@ -1901,6 +1912,16 @@ clutter_stage_finalize (GObject *object) G_OBJECT_CLASS (clutter_stage_parent_class)->finalize (object); } +static void +clutter_stage_real_paint_view (ClutterStage *stage, + ClutterStageView *view) +{ + ClutterStagePrivate *priv = stage->priv; + const cairo_rectangle_int_t *clip = &priv->view_clip; + + clutter_stage_do_paint_view (stage, view, clip); +} + static void clutter_stage_class_init (ClutterStageClass *klass) { @@ -1930,6 +1951,8 @@ clutter_stage_class_init (ClutterStageClass *klass) actor_class->queue_redraw = clutter_stage_real_queue_redraw; actor_class->apply_transform = clutter_stage_real_apply_transform; + klass->paint_view = clutter_stage_real_paint_view; + /** * ClutterStage:fullscreen: * @@ -2257,6 +2280,28 @@ clutter_stage_class_init (ClutterStageClass *klass) NULL, NULL, NULL, G_TYPE_NONE, 0); + /** + * ClutterStage::paint-view: + * @stage: the stage that received the event + * @view: a #ClutterStageView + * + * The ::paint-view signal is emitted before a #ClutterStageView is being + * painted. + * + * The view is painted in the default handler. Hence, if you want to perform + * some action after the view is painted, like reading the contents of the + * framebuffer, use g_signal_connect_after() or pass %G_CONNECT_AFTER. + */ + stage_signals[PAINT_VIEW] = + g_signal_new (I_("paint-view"), + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterStageClass, paint_view), + NULL, NULL, + _clutter_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + CLUTTER_TYPE_STAGE_VIEW); + /** * ClutterStage::presented: (skip) * @stage: the stage that received the event diff --git a/clutter/clutter/clutter-stage.h b/clutter/clutter/clutter-stage.h index 9d0fdd362..9da63d211 100644 --- a/clutter/clutter/clutter-stage.h +++ b/clutter/clutter/clutter-stage.h @@ -88,9 +88,12 @@ struct _ClutterStageClass gboolean (* delete_event) (ClutterStage *stage, ClutterEvent *event); + void (* paint_view) (ClutterStage *stage, + ClutterStageView *view); + /*< private >*/ /* padding for future expansion */ - gpointer _padding_dummy[31]; + gpointer _padding_dummy[30]; }; /** -- 2.26.2 From 2da9db1546f025aa02e23136b40e96a8ba99d1c5 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Mon, 17 Jun 2019 23:32:00 -0300 Subject: [PATCH 07/49] clutter/tests: Connect to ClutterStage:paint-view ClutterStage:after-paint now does not guarantee a valid implicit framebuffer pushed to the stack. Instead, use the new 'paint-view' signal, that is emitted at a point in the drawing routine where a framebuffer is pushed. In addition to that, stop using the implicit framebuffer API and port the actor-shader-effect test to read from the view's framebuffer directly. https://gitlab.gnome.org/GNOME/mutter/merge_requests/623 --- clutter/tests/conform/actor-shader-effect.c | 32 ++++++++++++--------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/clutter/tests/conform/actor-shader-effect.c b/clutter/tests/conform/actor-shader-effect.c index 93a43ea8b..ac99c5b40 100644 --- a/clutter/tests/conform/actor-shader-effect.c +++ b/clutter/tests/conform/actor-shader-effect.c @@ -209,14 +209,16 @@ make_actor (GType shader_type) } static guint32 -get_pixel (int x, int y) +get_pixel (CoglFramebuffer *fb, + int x, + int y) { guint8 data[4]; - cogl_read_pixels (x, y, 1, 1, - COGL_READ_PIXELS_COLOR_BUFFER, - COGL_PIXEL_FORMAT_RGBA_8888_PRE, - data); + cogl_framebuffer_read_pixels (fb, + x, y, 1, 1, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + data); return (((guint32) data[0] << 16) | ((guint32) data[1] << 8) | @@ -224,19 +226,21 @@ get_pixel (int x, int y) } static void -paint_cb (ClutterStage *stage, - gpointer data) +view_painted_cb (ClutterStage *stage, + ClutterStageView *view, + gpointer data) { + CoglFramebuffer *fb = clutter_stage_view_get_framebuffer (view); gboolean *was_painted = data; /* old shader effect */ - g_assert_cmpint (get_pixel (0, 25), ==, 0xff0000); + g_assert_cmpint (get_pixel (fb, 0, 25), ==, 0xff0000); /* new shader effect */ - g_assert_cmpint (get_pixel (100, 25), ==, 0x00ffff); + g_assert_cmpint (get_pixel (fb, 100, 25), ==, 0x00ffff); /* another new shader effect */ - g_assert_cmpint (get_pixel (200, 25), ==, 0xff00ff); + g_assert_cmpint (get_pixel (fb, 200, 25), ==, 0xff00ff); /* new shader effect */ - g_assert_cmpint (get_pixel (300, 25), ==, 0x00ffff); + g_assert_cmpint (get_pixel (fb, 300, 25), ==, 0x00ffff); *was_painted = TRUE; } @@ -271,9 +275,9 @@ actor_shader_effect (void) clutter_actor_show (stage); was_painted = FALSE; - g_signal_connect (stage, "after-paint", - G_CALLBACK (paint_cb), - &was_painted); + g_signal_connect_after (stage, "paint-view", + G_CALLBACK (view_painted_cb), + &was_painted); while (!was_painted) g_main_context_iteration (NULL, FALSE); -- 2.26.2 From 50271dfbdbb8926474a5cf3019c8552afd40f0da Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Mon, 17 Jun 2019 21:43:05 -0300 Subject: [PATCH 08/49] stage: Introduce MetaStageWatch and family MetaStageWatch, watch modes and the watch function are part of the new stage view watching API. It's design does not rely on signals on purpose; the number of signals that would be emitted would be too high, and would impact performance. MetaStageWatch is an opaque structure outside of MetaStage. This will be used by the screencast code to monitor a single view, which has a one-to-one relatioship to logical monitors. https://gitlab.gnome.org/GNOME/mutter/merge_requests/623 --- src/backends/meta-stage-private.h | 22 +++++++ src/backends/meta-stage.c | 104 ++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) diff --git a/src/backends/meta-stage-private.h b/src/backends/meta-stage-private.h index 639d2372c..963017688 100644 --- a/src/backends/meta-stage-private.h +++ b/src/backends/meta-stage-private.h @@ -27,8 +27,21 @@ G_BEGIN_DECLS +typedef struct _MetaStageWatch MetaStageWatch; typedef struct _MetaOverlay MetaOverlay; +typedef enum +{ + META_STAGE_WATCH_BEFORE_PAINT, + META_STAGE_WATCH_AFTER_ACTOR_PAINT, + META_STAGE_WATCH_AFTER_OVERLAY_PAINT, + META_STAGE_WATCH_AFTER_PAINT, +} MetaStageWatchPhase; + +typedef void (* MetaStageWatchFunc) (MetaStage *stage, + ClutterStageView *view, + gpointer user_data); + ClutterActor *meta_stage_new (MetaBackend *backend); MetaOverlay *meta_stage_create_cursor_overlay (MetaStage *stage); @@ -43,6 +56,15 @@ void meta_stage_update_cursor_overlay (MetaStage *stage, void meta_stage_set_active (MetaStage *stage, gboolean is_active); +MetaStageWatch * meta_stage_watch_view (MetaStage *stage, + ClutterStageView *view, + MetaStageWatchPhase watch_mode, + MetaStageWatchFunc callback, + gpointer user_data); + +void meta_stage_remove_watch (MetaStage *stage, + MetaStageWatch *watch); + G_END_DECLS #endif /* META_STAGE_PRIVATE_H */ diff --git a/src/backends/meta-stage.c b/src/backends/meta-stage.c index 47a00e51a..8809035d1 100644 --- a/src/backends/meta-stage.c +++ b/src/backends/meta-stage.c @@ -30,6 +30,8 @@ #include "meta/meta-monitor-manager.h" #include "meta/util.h" +#define N_WATCH_MODES 4 + enum { ACTORS_PAINTED, @@ -39,6 +41,13 @@ enum static guint signals[N_SIGNALS]; +struct _MetaStageWatch +{ + ClutterStageView *view; + MetaStageWatchFunc callback; + gpointer user_data; +}; + struct _MetaOverlay { gboolean enabled; @@ -55,6 +64,9 @@ struct _MetaStage { ClutterStage parent; + GPtrArray *watchers[N_WATCH_MODES]; + ClutterStageView *current_view; + GList *overlays; gboolean is_active; }; @@ -135,6 +147,7 @@ meta_stage_finalize (GObject *object) { MetaStage *stage = META_STAGE (object); GList *l; + int i; l = stage->overlays; while (l) @@ -143,9 +156,33 @@ meta_stage_finalize (GObject *object) l = g_list_delete_link (l, l); } + for (i = 0; i < N_WATCH_MODES; i++) + g_clear_pointer (&stage->watchers[i], g_ptr_array_unref); + G_OBJECT_CLASS (meta_stage_parent_class)->finalize (object); } +static void +notify_watchers_for_mode (MetaStage *stage, + ClutterStageView *view, + MetaStageWatchPhase watch_phase) +{ + GPtrArray *watchers; + int i; + + watchers = stage->watchers[watch_phase]; + + for (i = 0; i < watchers->len; i++) + { + MetaStageWatch *watch = g_ptr_array_index (watchers, i); + + if (watch->view && view != watch->view) + continue; + + watch->callback (stage, view, watch->user_data); + } +} + static void meta_stage_paint (ClutterActor *actor) { @@ -154,10 +191,30 @@ meta_stage_paint (ClutterActor *actor) CLUTTER_ACTOR_CLASS (meta_stage_parent_class)->paint (actor); + notify_watchers_for_mode (stage, stage->current_view, + META_STAGE_WATCH_AFTER_ACTOR_PAINT); + g_signal_emit (stage, signals[ACTORS_PAINTED], 0); for (l = stage->overlays; l; l = l->next) meta_overlay_paint (l->data); + + notify_watchers_for_mode (stage, stage->current_view, + META_STAGE_WATCH_AFTER_OVERLAY_PAINT); +} + +static void +meta_stage_paint_view (ClutterStage *stage, + ClutterStageView *view) +{ + MetaStage *meta_stage = META_STAGE (stage); + + notify_watchers_for_mode (meta_stage, view, META_STAGE_WATCH_BEFORE_PAINT); + + meta_stage->current_view = view; + CLUTTER_STAGE_CLASS (meta_stage_parent_class)->paint_view (stage, view); + + notify_watchers_for_mode (meta_stage, view, META_STAGE_WATCH_AFTER_PAINT); } static void @@ -202,6 +259,7 @@ meta_stage_class_init (MetaStageClass *klass) stage_class->activate = meta_stage_activate; stage_class->deactivate = meta_stage_deactivate; + stage_class->paint_view = meta_stage_paint_view; signals[ACTORS_PAINTED] = g_signal_new ("actors-painted", G_TYPE_FROM_CLASS (klass), @@ -214,7 +272,12 @@ meta_stage_class_init (MetaStageClass *klass) static void meta_stage_init (MetaStage *stage) { + int i; + clutter_stage_set_user_resizable (CLUTTER_STAGE (stage), FALSE); + + for (i = 0; i < N_WATCH_MODES; i++) + stage->watchers[i] = g_ptr_array_new_with_free_func (g_free); } ClutterActor * @@ -346,3 +409,44 @@ meta_stage_set_active (MetaStage *stage, */ clutter_stage_event (CLUTTER_STAGE (stage), &event); } + +MetaStageWatch * +meta_stage_watch_view (MetaStage *stage, + ClutterStageView *view, + MetaStageWatchPhase watch_phase, + MetaStageWatchFunc callback, + gpointer user_data) +{ + MetaStageWatch *watch; + GPtrArray *watchers; + + watch = g_new0 (MetaStageWatch, 1); + watch->view = view; + watch->callback = callback; + watch->user_data = user_data; + + watchers = stage->watchers[watch_phase]; + g_ptr_array_add (watchers, watch); + + return watch; +} + +void +meta_stage_remove_watch (MetaStage *stage, + MetaStageWatch *watch) +{ + GPtrArray *watchers; + gboolean removed = FALSE; + int i; + + for (i = 0; i < N_WATCH_MODES; i++) + { + watchers = stage->watchers[i]; + removed = g_ptr_array_remove_fast (watchers, watch); + + if (removed) + break; + } + + g_assert (removed); +} -- 2.26.2 From 8c509454f193945f09ef7afb5a397df266d2fffd Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Mon, 17 Jun 2019 21:49:45 -0300 Subject: [PATCH 09/49] screen-cast-monitor-stream-src: Watch monitors using MetaStageWatch This uses the API introduced by the previous commit. By watching specific monitors directly, and not whole stage views, we avoid showing artifacts on multi-monitor setups. Fixes https://gitlab.gnome.org/GNOME/mutter/issues/424 https://gitlab.gnome.org/GNOME/mutter/merge_requests/623 --- .../meta-screen-cast-monitor-stream-src.c | 65 +++++++++++++------ 1 file changed, 44 insertions(+), 21 deletions(-) diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c b/src/backends/meta-screen-cast-monitor-stream-src.c index cb9823148..f582217e5 100644 --- a/src/backends/meta-screen-cast-monitor-stream-src.c +++ b/src/backends/meta-screen-cast-monitor-stream-src.c @@ -32,6 +32,7 @@ #include "backends/meta-monitor.h" #include "backends/meta-screen-cast-monitor-stream.h" #include "backends/meta-screen-cast-session.h" +#include "backends/meta-stage-private.h" #include "clutter/clutter.h" #include "clutter/clutter-mutter.h" #include "core/boxes-private.h" @@ -42,8 +43,9 @@ struct _MetaScreenCastMonitorStreamSrc gboolean cursor_bitmap_invalid; - gulong actors_painted_handler_id; - gulong paint_handler_id; + MetaStageWatch *paint_watch; + MetaStageWatch *after_paint_watch; + gulong cursor_moved_handler_id; gulong cursor_changed_handler_id; }; @@ -113,10 +115,11 @@ meta_screen_cast_monitor_stream_src_get_specs (MetaScreenCastStreamSrc *src, } static void -stage_painted (ClutterActor *actor, - MetaScreenCastMonitorStreamSrc *monitor_src) +stage_painted (MetaStage *stage, + ClutterStageView *view, + gpointer user_data) { - MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (monitor_src); + MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (user_data); meta_screen_cast_stream_src_maybe_record_frame (src); } @@ -245,12 +248,28 @@ meta_screen_cast_monitor_stream_src_enable (MetaScreenCastStreamSrc *src) MetaScreenCastMonitorStreamSrc *monitor_src = META_SCREEN_CAST_MONITOR_STREAM_SRC (src); MetaBackend *backend = get_backend (monitor_src); + MetaRenderer *renderer = meta_backend_get_renderer (backend); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); + MetaRendererView *view; + MetaMonitor *monitor; + MetaLogicalMonitor *logical_monitor; + MetaStage *meta_stage; + ClutterStageView *stage_view; ClutterStage *stage; MetaScreenCastStream *stream; stream = meta_screen_cast_stream_src_get_stream (src); stage = get_stage (monitor_src); + meta_stage = META_STAGE (stage); + monitor = get_monitor (monitor_src); + logical_monitor = meta_monitor_get_logical_monitor (monitor); + view = meta_renderer_get_view_from_logical_monitor (renderer, + logical_monitor); + + if (view) + stage_view = CLUTTER_STAGE_VIEW (view); + else + stage_view = NULL; switch (meta_screen_cast_stream_get_cursor_mode (stream)) { @@ -265,17 +284,21 @@ meta_screen_cast_monitor_stream_src_enable (MetaScreenCastStreamSrc *src) monitor_src); /* Intentional fall-through */ case META_SCREEN_CAST_CURSOR_MODE_HIDDEN: - monitor_src->actors_painted_handler_id = - g_signal_connect (stage, "actors-painted", - G_CALLBACK (stage_painted), - monitor_src); + monitor_src->paint_watch = + meta_stage_watch_view (meta_stage, + stage_view, + META_STAGE_WATCH_AFTER_ACTOR_PAINT, + stage_painted, + monitor_src); break; case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED: inhibit_hw_cursor (monitor_src); - monitor_src->paint_handler_id = - g_signal_connect_after (stage, "paint", - G_CALLBACK (stage_painted), - monitor_src); + monitor_src->after_paint_watch = + meta_stage_watch_view (meta_stage, + stage_view, + META_STAGE_WATCH_AFTER_PAINT, + stage_painted, + monitor_src); break; } @@ -290,21 +313,21 @@ meta_screen_cast_monitor_stream_src_disable (MetaScreenCastStreamSrc *src) MetaBackend *backend = get_backend (monitor_src); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); ClutterStage *stage; + MetaStage *meta_stage; stage = get_stage (monitor_src); + meta_stage = META_STAGE (stage); - if (monitor_src->actors_painted_handler_id) + if (monitor_src->paint_watch) { - g_signal_handler_disconnect (stage, - monitor_src->actors_painted_handler_id); - monitor_src->actors_painted_handler_id = 0; + meta_stage_remove_watch (meta_stage, monitor_src->paint_watch); + monitor_src->paint_watch = NULL; } - if (monitor_src->paint_handler_id) + if (monitor_src->after_paint_watch) { - g_signal_handler_disconnect (stage, - monitor_src->paint_handler_id); - monitor_src->paint_handler_id = 0; + meta_stage_remove_watch (meta_stage, monitor_src->after_paint_watch); + monitor_src->after_paint_watch = NULL; uninhibit_hw_cursor (monitor_src); } -- 2.26.2 From 547e3f004f9d9235efc5dfd816f4a1214cf44616 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 26 Aug 2019 16:09:53 +0300 Subject: [PATCH 10/49] window-actor: Add 'damaged' signal Make it possible to listen for damage on a window actor. For X11, the signal is emitted when damage is reported; for Wayland, it is emitted when any of the surfaces associated with the window is damaged. https://gitlab.gnome.org/GNOME/mutter/merge_requests/752 --- src/compositor/meta-window-actor-private.h | 2 ++ src/compositor/meta-window-actor.c | 23 ++++++++++++++++++ src/wayland/meta-wayland-surface.c | 27 +++++++++++++++++++--- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/compositor/meta-window-actor-private.h b/src/compositor/meta-window-actor-private.h index 9c1c12d09..3df520ac5 100644 --- a/src/compositor/meta-window-actor-private.h +++ b/src/compositor/meta-window-actor-private.h @@ -81,4 +81,6 @@ void meta_window_actor_stereo_notify (MetaWindowActor *actor, gboolean meta_window_actor_is_stereo (MetaWindowActor *actor); +void meta_window_actor_notify_damaged (MetaWindowActor *window_actor); + #endif /* META_WINDOW_ACTOR_PRIVATE_H */ diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c index 11686d00b..f4eba6d42 100644 --- a/src/compositor/meta-window-actor.c +++ b/src/compositor/meta-window-actor.c @@ -116,6 +116,7 @@ enum { FIRST_FRAME, EFFECTS_COMPLETED, + DAMAGED, LAST_SIGNAL }; @@ -226,6 +227,20 @@ meta_window_actor_class_init (MetaWindowActorClass *klass) NULL, NULL, NULL, G_TYPE_NONE, 0); + /** + * MetaWindowActor::damaged: + * @actor: the #MetaWindowActor instance + * + * Notify that one or more of the surfaces of the window have been damaged. + */ + signals[DAMAGED] = + g_signal_new ("damaged", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); + pspec = g_param_spec_object ("meta-window", "MetaWindow", "The displayed MetaWindow", @@ -1445,6 +1460,8 @@ meta_window_actor_process_x11_damage (MetaWindowActor *self, event->area.y, event->area.width, event->area.height); + + meta_window_actor_notify_damaged (self); } void @@ -2053,3 +2070,9 @@ meta_window_actor_is_stereo (MetaWindowActor *self) else return FALSE; } + +void +meta_window_actor_notify_damaged (MetaWindowActor *window_actor) +{ + g_signal_emit (window_actor, signals[DAMAGED], 0); +} diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c index ddad1a45c..6ffcd6a7f 100644 --- a/src/wayland/meta-wayland-surface.c +++ b/src/wayland/meta-wayland-surface.c @@ -669,6 +669,8 @@ void meta_wayland_surface_apply_pending_state (MetaWaylandSurface *surface, MetaWaylandPendingState *pending) { + gboolean had_damage = FALSE; + if (surface->role) { meta_wayland_surface_role_pre_commit (surface->role, pending); @@ -771,9 +773,12 @@ meta_wayland_surface_apply_pending_state (MetaWaylandSurface *surface, if (!cairo_region_is_empty (pending->surface_damage) || !cairo_region_is_empty (pending->buffer_damage)) - surface_process_damage (surface, - pending->surface_damage, - pending->buffer_damage); + { + surface_process_damage (surface, + pending->surface_damage, + pending->buffer_damage); + had_damage = TRUE; + } surface->offset_x += pending->dx; surface->offset_y += pending->dy; @@ -834,6 +839,22 @@ cleanup: pending_state_reset (pending); g_list_foreach (surface->subsurfaces, parent_surface_state_applied, NULL); + + if (had_damage) + { + MetaWindow *toplevel_window; + + toplevel_window = meta_wayland_surface_get_toplevel_window (surface); + if (toplevel_window) + { + MetaWindowActor *toplevel_window_actor; + + toplevel_window_actor = + meta_window_actor_from_window (toplevel_window); + if (toplevel_window_actor) + meta_window_actor_notify_damaged (toplevel_window_actor); + } + } } static void -- 2.26.2 From 27ada1ee57ac85d77cea9a1aee71b77f4939439f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 26 Aug 2019 16:12:30 +0300 Subject: [PATCH 11/49] screen-cast/window: Use window actor damaged signal instead of paint We are really more interested in when a window is damaged, rather than when it's painted, for screen casting windows. This also has the benefit of not listening on the "paint" signal of the actor, meaning it'll open doors for hacks currently necessary for taking a screenshot of a window consisting of multiple surfaces. https://gitlab.gnome.org/GNOME/mutter/merge_requests/752 --- .../meta-screen-cast-window-stream-src.c | 45 +++++-------------- 1 file changed, 11 insertions(+), 34 deletions(-) diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c index dbf330420..c31830b03 100644 --- a/src/backends/meta-screen-cast-window-stream-src.c +++ b/src/backends/meta-screen-cast-window-stream-src.c @@ -34,13 +34,11 @@ struct _MetaScreenCastWindowStreamSrc MetaScreenCastWindow *screen_cast_window; - unsigned long screen_cast_window_before_paint_handler_id; - unsigned long screen_cast_window_after_paint_handler_id; + unsigned long screen_cast_window_damaged_handler_id; unsigned long screen_cast_window_destroyed_handler_id; unsigned long cursor_moved_handler_id; unsigned long cursor_changed_handler_id; - gboolean actor_was_dirty; gboolean cursor_bitmap_invalid; }; @@ -255,15 +253,10 @@ meta_screen_cast_window_stream_src_stop (MetaScreenCastWindowStreamSrc *window_s if (!window_src->screen_cast_window) return; - if (window_src->screen_cast_window_before_paint_handler_id) + if (window_src->screen_cast_window_damaged_handler_id) g_signal_handler_disconnect (window_src->screen_cast_window, - window_src->screen_cast_window_before_paint_handler_id); - window_src->screen_cast_window_before_paint_handler_id = 0; - - if (window_src->screen_cast_window_after_paint_handler_id) - g_signal_handler_disconnect (window_src->screen_cast_window, - window_src->screen_cast_window_after_paint_handler_id); - window_src->screen_cast_window_after_paint_handler_id = 0; + window_src->screen_cast_window_damaged_handler_id); + window_src->screen_cast_window_damaged_handler_id = 0; if (window_src->screen_cast_window_destroyed_handler_id) g_signal_handler_disconnect (window_src->screen_cast_window, @@ -282,23 +275,12 @@ meta_screen_cast_window_stream_src_stop (MetaScreenCastWindowStreamSrc *window_s } static void -screen_cast_window_before_paint (MetaScreenCastWindow *screen_cast_window, - MetaScreenCastWindowStreamSrc *window_src) -{ - window_src->actor_was_dirty = - meta_screen_cast_window_has_damage (screen_cast_window); -} - -static void -screen_cast_window_after_paint (MetaWindowActor *actor, - MetaScreenCastWindowStreamSrc *window_src) +screen_cast_window_damaged (MetaWindowActor *actor, + MetaScreenCastWindowStreamSrc *window_src) { - if (window_src->actor_was_dirty) - { - MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (window_src); + MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (window_src); - meta_screen_cast_stream_src_maybe_record_frame (src); - } + meta_screen_cast_stream_src_maybe_record_frame (src); } static void @@ -378,16 +360,11 @@ meta_screen_cast_window_stream_src_enable (MetaScreenCastStreamSrc *src) window_src->screen_cast_window = META_SCREEN_CAST_WINDOW (window_actor); - window_src->screen_cast_window_before_paint_handler_id = + window_src->screen_cast_window_damaged_handler_id = g_signal_connect (window_src->screen_cast_window, - "paint", - G_CALLBACK (screen_cast_window_before_paint), + "damaged", + G_CALLBACK (screen_cast_window_damaged), window_src); - window_src->screen_cast_window_after_paint_handler_id = - g_signal_connect_after (window_src->screen_cast_window, - "paint", - G_CALLBACK (screen_cast_window_after_paint), - window_src); window_src->screen_cast_window_destroyed_handler_id = g_signal_connect (window_src->screen_cast_window, -- 2.26.2 From 2bf9b015901aa58826ebf96d941f9dbf803ec6b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 26 Aug 2019 16:29:33 +0300 Subject: [PATCH 12/49] window-actor: Add API to get a cairo surface of the window This currently uses a hack where it pushes a CoglFramebuffer backed by a texture to the framebuffer stack, then calls clutter_actor_paint() on the window actor causing it to render into the framebuffer. This has the effect that all subsurfaces of a window will be drawn as part of the window. https://gitlab.gnome.org/GNOME/mutter/merge_requests/752 --- src/compositor/meta-window-actor.c | 113 +++++++++++++++++++++++++++++ src/meta/meta-window-actor.h | 4 + 2 files changed, 117 insertions(+) diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c index f4eba6d42..9d215c745 100644 --- a/src/compositor/meta-window-actor.c +++ b/src/compositor/meta-window-actor.c @@ -2076,3 +2076,116 @@ meta_window_actor_notify_damaged (MetaWindowActor *window_actor) { g_signal_emit (window_actor, signals[DAMAGED], 0); } + +cairo_surface_t * +meta_window_actor_get_image (MetaWindowActor *self, + MetaRectangle *clip) +{ + MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); + ClutterActor *actor = CLUTTER_ACTOR (self); + MetaBackend *backend = meta_get_backend (); + ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); + CoglContext *cogl_context = + clutter_backend_get_cogl_context (clutter_backend); + float resource_scale; + float width, height; + CoglTexture2D *texture; + g_autoptr (GError) error = NULL; + CoglOffscreen *offscreen; + CoglFramebuffer *framebuffer; + CoglColor clear_color; + float x, y; + MetaRectangle scaled_clip; + cairo_surface_t *surface; + + if (!priv->surface) + return NULL; + + if (clutter_actor_get_n_children (actor) == 1) + { + MetaShapedTexture *stex; + + stex = meta_surface_actor_get_texture (priv->surface); + return meta_shaped_texture_get_image (stex, clip); + } + + clutter_actor_get_size (actor, &width, &height); + + if (width == 0 || height == 0) + return NULL; + + if (!clutter_actor_get_resource_scale (actor, &resource_scale)) + return NULL; + + width = ceilf (width * resource_scale); + height = ceilf (height * resource_scale); + + texture = cogl_texture_2d_new_with_size (cogl_context, width, height); + if (!texture) + return NULL; + + cogl_primitive_texture_set_auto_mipmap (COGL_PRIMITIVE_TEXTURE (texture), + FALSE); + + offscreen = cogl_offscreen_new_with_texture (COGL_TEXTURE (texture)); + framebuffer = COGL_FRAMEBUFFER (offscreen); + + cogl_object_unref (texture); + + if (!cogl_framebuffer_allocate (framebuffer, &error)) + { + g_warning ("Failed to allocate framebuffer for screenshot: %s", + error->message); + cogl_object_unref (framebuffer); + cogl_object_unref (texture); + return NULL; + } + + cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 0); + clutter_actor_get_position (actor, &x, &y); + + cogl_push_framebuffer (framebuffer); + + cogl_framebuffer_clear (framebuffer, COGL_BUFFER_BIT_COLOR, &clear_color); + cogl_framebuffer_orthographic (framebuffer, 0, 0, width, height, 0, 1.0); + cogl_framebuffer_scale (framebuffer, resource_scale, resource_scale, 1); + cogl_framebuffer_translate (framebuffer, -x, -y, 0); + + clutter_actor_paint (actor); + + cogl_pop_framebuffer (); + + if (clip) + { + meta_rectangle_scale_double (clip, resource_scale, + META_ROUNDING_STRATEGY_GROW, + &scaled_clip); + meta_rectangle_intersect (&scaled_clip, + &(MetaRectangle) { + .width = width, + .height = height, + }, + &scaled_clip); + } + else + { + scaled_clip = (MetaRectangle) { + .width = width, + .height = height, + }; + } + + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + scaled_clip.width, scaled_clip.height); + cogl_framebuffer_read_pixels (framebuffer, + scaled_clip.x, scaled_clip.y, + scaled_clip.width, scaled_clip.height, + CLUTTER_CAIRO_FORMAT_ARGB32, + cairo_image_surface_get_data (surface)); + + cogl_object_unref (framebuffer); + + cairo_surface_mark_dirty (surface); + + return surface; +} diff --git a/src/meta/meta-window-actor.h b/src/meta/meta-window-actor.h index 9ba164910..30a17c56b 100644 --- a/src/meta/meta-window-actor.h +++ b/src/meta/meta-window-actor.h @@ -46,6 +46,10 @@ void meta_window_actor_sync_visibility (MetaWindowActor *self META_EXPORT gboolean meta_window_actor_is_destroyed (MetaWindowActor *self); +META_EXPORT +cairo_surface_t * meta_window_actor_get_image (MetaWindowActor *self, + cairo_rectangle_int_t *clip); + typedef enum { META_SHADOW_MODE_AUTO, -- 2.26.2 From 2aea9fb5c0b37f764911654e90d7d1917bf3aa0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 26 Aug 2019 16:31:06 +0300 Subject: [PATCH 13/49] window-actor: Use new get_image() API to screen casting window content This fixes screen casting of windows consisting of multiple surfaces to work. https://gitlab.gnome.org/GNOME/mutter/merge_requests/752 --- src/compositor/meta-window-actor.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c index 9d215c745..b82326600 100644 --- a/src/compositor/meta-window-actor.c +++ b/src/compositor/meta-window-actor.c @@ -1985,8 +1985,6 @@ meta_window_actor_capture_into (MetaScreenCastWindow *screen_cast_window, uint8_t *data) { MetaWindowActor *window_actor = META_WINDOW_ACTOR (screen_cast_window); - MetaWindowActorPrivate *priv = - meta_window_actor_get_instance_private (window_actor); cairo_surface_t *image; uint8_t *cr_data; int cr_stride; @@ -1997,7 +1995,7 @@ meta_window_actor_capture_into (MetaScreenCastWindow *screen_cast_window, if (meta_window_actor_is_destroyed (window_actor)) return; - image = meta_surface_actor_get_image (priv->surface, bounds); + image = meta_window_actor_get_image (window_actor, bounds); cr_data = cairo_image_surface_get_data (image); cr_width = cairo_image_surface_get_width (image); cr_height = cairo_image_surface_get_height (image); -- 2.26.2 From 6edebf1a7beed7162f9a69270338010a77088b75 Mon Sep 17 00:00:00 2001 From: Pascal Nowack Date: Mon, 16 Dec 2019 19:00:19 +0100 Subject: [PATCH 14/49] screen-cast: Fix window recording on HiDPI Using the same scale for the window as the logical monitor only works correctly when having the experimental 'scale-monitor-framebuffer' feature enabled. Without this experimental feature, the stream will contain a black screen, where the actual window only takes a small part of it. Therefore, use a scale of 1 for the non- experimental case. Patch is based on commit 3fa6a92cc5dda6ab3939c3e982185f6caf453360. https://gitlab.gnome.org/GNOME/mutter/merge_requests/976 --- src/backends/meta-screen-cast-window-stream.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/backends/meta-screen-cast-window-stream.c b/src/backends/meta-screen-cast-window-stream.c index 50d1806cd..b9a732cba 100644 --- a/src/backends/meta-screen-cast-window-stream.c +++ b/src/backends/meta-screen-cast-window-stream.c @@ -226,11 +226,15 @@ meta_screen_cast_window_stream_initable_init (GInitable *initable, G_CALLBACK (on_window_unmanaged), window_stream); + if (meta_is_stage_views_scaled ()) + scale = (int) ceilf (meta_logical_monitor_get_scale (logical_monitor)); + else + scale = 1; + /* We cannot set the stream size to the exact size of the window, because * windows can be resized, whereas streams cannot. * So we set a size equals to the size of the logical monitor for the window. */ - scale = (int) ceil (meta_logical_monitor_get_scale (logical_monitor)); window_stream->logical_width = logical_monitor->rect.width; window_stream->logical_height = logical_monitor->rect.height; window_stream->stream_width = logical_monitor->rect.width * scale; -- 2.26.2 From 077217b337f18619d3b3b878c5faae488cbafd15 Mon Sep 17 00:00:00 2001 From: Olivier Fourdan Date: Tue, 4 Feb 2020 14:58:30 +0100 Subject: [PATCH 15/49] window-actor: Ensure clipping in `capture_into()` The clip bounds passed in `meta_window_actor_capture_into()` represent the actual allocated buffer size where the window actor image will be eventually copied. As such, it is completely agnostic to the scaling factors that might affect the different surface actors which compose the window actor. So instead of trying to compute the scale factor by which the given clipping bounds need to be adjusted, simply clip the resulting image based on the given bounds to make sure we never overflow the destination buffer. https://gitlab.gnome.org/GNOME/mutter/merge_requests/1022 --- src/compositor/meta-window-actor.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c index b82326600..6d4aa6c1b 100644 --- a/src/compositor/meta-window-actor.c +++ b/src/compositor/meta-window-actor.c @@ -2001,32 +2001,36 @@ meta_window_actor_capture_into (MetaScreenCastWindow *screen_cast_window, cr_height = cairo_image_surface_get_height (image); cr_stride = cairo_image_surface_get_stride (image); - if (cr_width < bounds->width || cr_height < bounds->height) + if (cr_width == bounds->width && cr_height == bounds->height) { + memcpy (data, cr_data, cr_height * cr_stride); + } + else + { + int width = MIN (bounds->width, cr_width); + int height = MIN (bounds->height, cr_height); + int stride = width * bpp; uint8_t *src, *dst; + src = cr_data; dst = data; - for (int i = 0; i < cr_height; i++) + for (int i = 0; i < height; i++) { - memcpy (dst, src, cr_stride); - if (cr_width < bounds->width) - memset (dst + cr_stride, 0, (bounds->width * bpp) - cr_stride); + memcpy (dst, src, stride); + if (width < bounds->width) + memset (dst + stride, 0, (bounds->width * bpp) - stride); src += cr_stride; dst += bounds->width * bpp; } - for (int i = cr_height; i < bounds->height; i++) + for (int i = height; i < bounds->height; i++) { memset (dst, 0, bounds->width * bpp); dst += bounds->width * bpp; } } - else - { - memcpy (data, cr_data, cr_height * cr_stride); - } cairo_surface_destroy (image); } -- 2.26.2 From 06dcbe104c58e1efd1d99fc7d4761698f9d32824 Mon Sep 17 00:00:00 2001 From: Olivier Fourdan Date: Mon, 3 Feb 2020 14:18:57 +0100 Subject: [PATCH 16/49] shaped-texture: Add `get_width()`/`get_height()` API Add an API to retrieve the content size of a shaped texture. https://gitlab.gnome.org/GNOME/mutter/merge_requests/1022 --- src/compositor/meta-shaped-texture-private.h | 3 +++ src/compositor/meta-shaped-texture.c | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/compositor/meta-shaped-texture-private.h b/src/compositor/meta-shaped-texture-private.h index d0efdd4dc..362bbb93f 100644 --- a/src/compositor/meta-shaped-texture-private.h +++ b/src/compositor/meta-shaped-texture-private.h @@ -53,4 +53,7 @@ void meta_shaped_texture_set_viewport_dst_size (MetaShapedTexture *stex, int dst_height); void meta_shaped_texture_reset_viewport_dst_size (MetaShapedTexture *stex); +int meta_shaped_texture_get_width (MetaShapedTexture *stex); +int meta_shaped_texture_get_height (MetaShapedTexture *stex); + #endif diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c index e77a32109..6ef2125c3 100644 --- a/src/compositor/meta-shaped-texture.c +++ b/src/compositor/meta-shaped-texture.c @@ -1585,3 +1585,23 @@ meta_shaped_texture_new (void) { return g_object_new (META_TYPE_SHAPED_TEXTURE, NULL); } + +int +meta_shaped_texture_get_width (MetaShapedTexture *stex) +{ + g_return_val_if_fail (META_IS_SHAPED_TEXTURE (stex), 0); + + ensure_size_valid (stex); + + return stex->dst_width; +} + +int +meta_shaped_texture_get_height (MetaShapedTexture *stex) +{ + g_return_val_if_fail (META_IS_SHAPED_TEXTURE (stex), 0); + + ensure_size_valid (stex); + + return stex->dst_height; +} -- 2.26.2 From 9a9a4466e79d61996304ca0c3e9b327c9b3abc25 Mon Sep 17 00:00:00 2001 From: Olivier Fourdan Date: Tue, 28 Jan 2020 11:13:41 +0100 Subject: [PATCH 17/49] screen-cast-window: Use buffer bounds in place of frame bounds The frame bounds as returned by `meta_window_actor_get_frame_bounds()` would be used as cropping values when streaming a window content. But, as its name implies, it returns the actual frame bounds, whereas we may want to include the whole buffer, to include client side shadows for example. Rename the `get_frame_bounds()` API to `get_buffer_bounds()` (which was previously partly removed with commit 11bd84789) and return the actual buffer bounds to use as the cropping area when streaming a window. Fixes: 931934511 - "Implement MetaScreenCastWindow interface" https://gitlab.gnome.org/GNOME/mutter/merge_requests/1022 Closes: https://gitlab.gnome.org/GNOME/mutter/issues/1018 --- .../meta-screen-cast-window-stream-src.c | 4 +-- src/backends/meta-screen-cast-window.c | 8 +++--- src/backends/meta-screen-cast-window.h | 8 +++--- src/compositor/meta-window-actor.c | 26 ++++++------------- 4 files changed, 18 insertions(+), 28 deletions(-) diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c index c31830b03..210ea0807 100644 --- a/src/backends/meta-screen-cast-window-stream-src.c +++ b/src/backends/meta-screen-cast-window-stream-src.c @@ -230,8 +230,8 @@ meta_screen_cast_window_stream_src_get_videocrop (MetaScreenCastStreamSrc *src, META_SCREEN_CAST_WINDOW_STREAM_SRC (src); MetaRectangle stream_rect; - meta_screen_cast_window_get_frame_bounds (window_src->screen_cast_window, - crop_rect); + meta_screen_cast_window_get_buffer_bounds (window_src->screen_cast_window, + crop_rect); stream_rect.x = 0; stream_rect.y = 0; diff --git a/src/backends/meta-screen-cast-window.c b/src/backends/meta-screen-cast-window.c index ce2bf82c9..91515ded8 100644 --- a/src/backends/meta-screen-cast-window.c +++ b/src/backends/meta-screen-cast-window.c @@ -30,11 +30,11 @@ meta_screen_cast_window_default_init (MetaScreenCastWindowInterface *iface) } void -meta_screen_cast_window_get_frame_bounds (MetaScreenCastWindow *screen_cast_window, - MetaRectangle *bounds) +meta_screen_cast_window_get_buffer_bounds (MetaScreenCastWindow *screen_cast_window, + MetaRectangle *bounds) { - META_SCREEN_CAST_WINDOW_GET_IFACE (screen_cast_window)->get_frame_bounds (screen_cast_window, - bounds); + META_SCREEN_CAST_WINDOW_GET_IFACE (screen_cast_window)->get_buffer_bounds (screen_cast_window, + bounds); } void diff --git a/src/backends/meta-screen-cast-window.h b/src/backends/meta-screen-cast-window.h index badd88224..69e5a34dc 100644 --- a/src/backends/meta-screen-cast-window.h +++ b/src/backends/meta-screen-cast-window.h @@ -37,8 +37,8 @@ struct _MetaScreenCastWindowInterface { GTypeInterface parent_iface; - void (*get_frame_bounds) (MetaScreenCastWindow *screen_cast_window, - MetaRectangle *bounds); + void (*get_buffer_bounds) (MetaScreenCastWindow *screen_cast_window, + MetaRectangle *bounds); void (*transform_relative_position) (MetaScreenCastWindow *screen_cast_window, double x, @@ -59,8 +59,8 @@ struct _MetaScreenCastWindowInterface gboolean (*has_damage) (MetaScreenCastWindow *screen_cast_window); }; -void meta_screen_cast_window_get_frame_bounds (MetaScreenCastWindow *screen_cast_window, - MetaRectangle *bounds); +void meta_screen_cast_window_get_buffer_bounds (MetaScreenCastWindow *screen_cast_window, + MetaRectangle *bounds); void meta_screen_cast_window_transform_relative_position (MetaScreenCastWindow *screen_cast_window, double x, diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c index 6d4aa6c1b..81eb04c84 100644 --- a/src/compositor/meta-window-actor.c +++ b/src/compositor/meta-window-actor.c @@ -1877,29 +1877,19 @@ meta_window_actor_from_window (MetaWindow *window) } static void -meta_window_actor_get_frame_bounds (MetaScreenCastWindow *screen_cast_window, - MetaRectangle *bounds) +meta_window_actor_get_buffer_bounds (MetaScreenCastWindow *screen_cast_window, + MetaRectangle *bounds) { MetaWindowActor *window_actor = META_WINDOW_ACTOR (screen_cast_window); MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (window_actor); - MetaWindow *window; MetaShapedTexture *stex; - MetaRectangle buffer_rect; - MetaRectangle frame_rect; - double scale_x, scale_y; stex = meta_surface_actor_get_texture (priv->surface); - clutter_actor_get_scale (CLUTTER_ACTOR (stex), &scale_x, &scale_y); - - window = priv->window; - meta_window_get_buffer_rect (window, &buffer_rect); - meta_window_get_frame_rect (window, &frame_rect); - - bounds->x = (int) floor ((frame_rect.x - buffer_rect.x) / scale_x); - bounds->y = (int) floor ((frame_rect.y - buffer_rect.y) / scale_y); - bounds->width = (int) ceil (frame_rect.width / scale_x); - bounds->height = (int) ceil (frame_rect.height / scale_y); + *bounds = (MetaRectangle) { + .width = meta_shaped_texture_get_width (stex), + .height = meta_shaped_texture_get_height (stex) + }; } static void @@ -1917,7 +1907,7 @@ meta_window_actor_transform_relative_position (MetaScreenCastWindow *screen_cast MetaRectangle bounds; ClutterVertex v1 = { 0.f, }, v2 = { 0.f, }; - meta_window_actor_get_frame_bounds (screen_cast_window, &bounds); + meta_window_actor_get_buffer_bounds (screen_cast_window, &bounds); v1.x = CLAMP ((float) x, bounds.x, @@ -2044,7 +2034,7 @@ meta_window_actor_has_damage (MetaScreenCastWindow *screen_cast_window) static void screen_cast_window_iface_init (MetaScreenCastWindowInterface *iface) { - iface->get_frame_bounds = meta_window_actor_get_frame_bounds; + iface->get_buffer_bounds = meta_window_actor_get_buffer_bounds; iface->transform_relative_position = meta_window_actor_transform_relative_position; iface->transform_cursor_position = meta_window_actor_transform_cursor_position; iface->capture_into = meta_window_actor_capture_into; -- 2.26.2 From c6f68fe763e736c5fd84913407427254c5dc7dea Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 14 Jan 2020 09:44:45 +0100 Subject: [PATCH 18/49] screen-cast: Update to PipeWire 0.3 API Update to 0.3 API [jadahl: update Dockerfile to include new enough pipewire] [jadahl: dropped Dockerfile changes for backport] Fixes: https://gitlab.gnome.org/GNOME/mutter/issues/1051 https://gitlab.gnome.org/GNOME/mutter/merge_requests/1062 --- meson.build | 4 +- src/backends/meta-screen-cast-stream-src.c | 255 ++++++++------------- 2 files changed, 101 insertions(+), 158 deletions(-) diff --git a/meson.build b/meson.build index b2239ed81..8ef592bc5 100644 --- a/meson.build +++ b/meson.build @@ -43,7 +43,7 @@ libinput_req = '>= 1.4' gbm_req = '>= 10.3' # screen cast version requirements -libpipewire_req = '>= 0.2.5' +libpipewire_req = '>= 0.3.0' gnome = import('gnome') pkg = import('pkgconfig') @@ -233,7 +233,7 @@ endif have_remote_desktop = get_option('remote_desktop') if have_remote_desktop - libpipewire_dep = dependency('libpipewire-0.2', version: libpipewire_req) + libpipewire_dep = dependency('libpipewire-0.3', version: libpipewire_req) endif have_introspection = get_option('introspection') diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c index 82c5cba43..ba1ce94a7 100644 --- a/src/backends/meta-screen-cast-stream-src.c +++ b/src/backends/meta-screen-cast-stream-src.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -62,15 +63,6 @@ enum static guint signals[N_SIGNALS]; -typedef struct _MetaSpaType -{ - struct spa_type_media_type media_type; - struct spa_type_media_subtype media_subtype; - struct spa_type_format_video format_video; - struct spa_type_video_format video_format; - uint32_t meta_cursor; -} MetaSpaType; - typedef struct _MetaPipeWireSource { GSource base; @@ -82,19 +74,19 @@ typedef struct _MetaScreenCastStreamSrcPrivate { MetaScreenCastStream *stream; + struct pw_context *pipewire_context; struct pw_core *pipewire_core; - struct pw_remote *pipewire_remote; - struct pw_type *pipewire_type; MetaPipeWireSource *pipewire_source; - struct spa_hook pipewire_remote_listener; + struct spa_hook pipewire_core_listener; gboolean is_enabled; struct pw_stream *pipewire_stream; struct spa_hook pipewire_stream_listener; + uint32_t node_id; - MetaSpaType spa_type; struct spa_video_info_raw video_format; + int video_stride; uint64_t last_frame_timestamp_us; @@ -112,8 +104,6 @@ G_DEFINE_TYPE_WITH_CODE (MetaScreenCastStreamSrc, meta_screen_cast_stream_src_init_initable_iface) G_ADD_PRIVATE (MetaScreenCastStreamSrc)) -#define PROP_RANGE(min, max) 2, (min), (max) - static void meta_screen_cast_stream_src_get_specs (MetaScreenCastStreamSrc *src, int *width, @@ -286,9 +276,6 @@ meta_screen_cast_stream_src_set_empty_cursor_sprite_metadata (MetaScreenCastStre int x, int y) { - MetaScreenCastStreamSrcPrivate *priv = - meta_screen_cast_stream_src_get_instance_private (src); - MetaSpaType *spa_type = &priv->spa_type; struct spa_meta_bitmap *spa_meta_bitmap; spa_meta_cursor->id = 1; @@ -300,7 +287,7 @@ meta_screen_cast_stream_src_set_empty_cursor_sprite_metadata (MetaScreenCastStre spa_meta_bitmap = SPA_MEMBER (spa_meta_cursor, spa_meta_cursor->bitmap_offset, struct spa_meta_bitmap); - spa_meta_bitmap->format = spa_type->video_format.RGBA; + spa_meta_bitmap->format = SPA_VIDEO_FORMAT_RGBA; spa_meta_bitmap->offset = sizeof (struct spa_meta_bitmap); spa_meta_cursor->hotspot.x = 0; @@ -317,9 +304,6 @@ meta_screen_cast_stream_src_set_cursor_sprite_metadata (MetaScreenCastStreamSrc int y, float scale) { - MetaScreenCastStreamSrcPrivate *priv = - meta_screen_cast_stream_src_get_instance_private (src); - MetaSpaType *spa_type = &priv->spa_type; CoglTexture *cursor_texture; struct spa_meta_bitmap *spa_meta_bitmap; int hotspot_x, hotspot_y; @@ -346,7 +330,7 @@ meta_screen_cast_stream_src_set_cursor_sprite_metadata (MetaScreenCastStreamSrc spa_meta_bitmap = SPA_MEMBER (spa_meta_cursor, spa_meta_cursor->bitmap_offset, struct spa_meta_bitmap); - spa_meta_bitmap->format = spa_type->video_format.RGBA; + spa_meta_bitmap->format = SPA_VIDEO_FORMAT_RGBA; spa_meta_bitmap->offset = sizeof (struct spa_meta_bitmap); meta_cursor_sprite_get_hotspot (cursor_sprite, &hotspot_x, &hotspot_y); @@ -382,12 +366,10 @@ static void add_cursor_metadata (MetaScreenCastStreamSrc *src, struct spa_buffer *spa_buffer) { - MetaScreenCastStreamSrcPrivate *priv = - meta_screen_cast_stream_src_get_instance_private (src); - MetaSpaType *spa_type = &priv->spa_type; struct spa_meta_cursor *spa_meta_cursor; - spa_meta_cursor = spa_buffer_find_meta (spa_buffer, spa_type->meta_cursor); + spa_meta_cursor = spa_buffer_find_meta_data (spa_buffer, SPA_META_Cursor, + sizeof (*spa_meta_cursor)); if (spa_meta_cursor) meta_screen_cast_stream_src_set_cursor_metadata (src, spa_meta_cursor); } @@ -447,14 +429,14 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src) { data = spa_buffer->datas[0].data; } - else if (spa_buffer->datas[0].type == priv->pipewire_type->data.MemFd) + else if (spa_buffer->datas[0].type == SPA_DATA_MemFd) { map = mmap (NULL, spa_buffer->datas[0].maxsize + spa_buffer->datas[0].mapoffset, PROT_READ | PROT_WRITE, MAP_SHARED, spa_buffer->datas[0].fd, 0); if (map == MAP_FAILED) { - g_warning ("Failed to mmap pipewire stream buffer: %s\n", + g_warning ("Failed to mmap pipewire stream buffer: %s", strerror (errno)); return; } @@ -469,28 +451,30 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src) if (meta_screen_cast_stream_src_record_frame (src, data)) { - struct spa_meta_video_crop *spa_meta_video_crop; + struct spa_meta_region *spa_meta_video_crop; spa_buffer->datas[0].chunk->size = spa_buffer->datas[0].maxsize; + spa_buffer->datas[0].chunk->stride = priv->video_stride; /* Update VideoCrop if needed */ spa_meta_video_crop = - spa_buffer_find_meta (spa_buffer, priv->pipewire_type->meta.VideoCrop); + spa_buffer_find_meta_data (spa_buffer, SPA_META_VideoCrop, + sizeof (*spa_meta_video_crop)); if (spa_meta_video_crop) { if (meta_screen_cast_stream_src_get_videocrop (src, &crop_rect)) { - spa_meta_video_crop->x = crop_rect.x; - spa_meta_video_crop->y = crop_rect.y; - spa_meta_video_crop->width = crop_rect.width; - spa_meta_video_crop->height = crop_rect.height; + spa_meta_video_crop->region.position.x = crop_rect.x; + spa_meta_video_crop->region.position.y = crop_rect.y; + spa_meta_video_crop->region.size.width = crop_rect.width; + spa_meta_video_crop->region.size.height = crop_rect.height; } else { - spa_meta_video_crop->x = 0; - spa_meta_video_crop->y = 0; - spa_meta_video_crop->width = priv->stream_width; - spa_meta_video_crop->height = priv->stream_height; + spa_meta_video_crop->region.position.x = 0; + spa_meta_video_crop->region.position.y = 0; + spa_meta_video_crop->region.size.width = priv->stream_width; + spa_meta_video_crop->region.size.height = priv->stream_height; } } } @@ -555,7 +539,6 @@ on_stream_state_changed (void *data, MetaScreenCastStreamSrc *src = data; MetaScreenCastStreamSrcPrivate *priv = meta_screen_cast_stream_src_get_instance_private (src); - uint32_t node_id; switch (state) { @@ -563,14 +546,12 @@ on_stream_state_changed (void *data, g_warning ("pipewire stream error: %s", error_message); meta_screen_cast_stream_src_notify_closed (src); break; - case PW_STREAM_STATE_CONFIGURE: - node_id = pw_stream_get_node_id (priv->pipewire_stream); - g_signal_emit (src, signals[READY], 0, (unsigned int) node_id); - break; - case PW_STREAM_STATE_UNCONNECTED: - case PW_STREAM_STATE_CONNECTING: - case PW_STREAM_STATE_READY: case PW_STREAM_STATE_PAUSED: + if (priv->node_id == SPA_ID_INVALID && priv->pipewire_stream) + { + priv->node_id = pw_stream_get_node_id (priv->pipewire_stream); + g_signal_emit (src, signals[READY], 0, (unsigned int) priv->node_id); + } if (meta_screen_cast_stream_src_is_enabled (src)) meta_screen_cast_stream_src_disable (src); break; @@ -578,68 +559,69 @@ on_stream_state_changed (void *data, if (!meta_screen_cast_stream_src_is_enabled (src)) meta_screen_cast_stream_src_enable (src); break; + case PW_STREAM_STATE_UNCONNECTED: + case PW_STREAM_STATE_CONNECTING: + break; } } static void -on_stream_format_changed (void *data, - const struct spa_pod *format) +on_stream_param_changed (void *data, + uint32_t id, + const struct spa_pod *format) { MetaScreenCastStreamSrc *src = data; MetaScreenCastStreamSrcPrivate *priv = meta_screen_cast_stream_src_get_instance_private (src); - struct pw_type *pipewire_type = priv->pipewire_type; uint8_t params_buffer[1024]; int32_t width, height, stride, size; struct spa_pod_builder pod_builder; const struct spa_pod *params[3]; const int bpp = 4; - if (!format) - { - pw_stream_finish_format (priv->pipewire_stream, 0, NULL, 0); - return; - } + if (!format || id != SPA_PARAM_Format) + return; spa_format_video_raw_parse (format, - &priv->video_format, - &priv->spa_type.format_video); + &priv->video_format); width = priv->video_format.size.width; height = priv->video_format.size.height; stride = SPA_ROUND_UP_N (width * bpp, 4); size = height * stride; + priv->video_stride = stride; + pod_builder = SPA_POD_BUILDER_INIT (params_buffer, sizeof (params_buffer)); - params[0] = spa_pod_builder_object ( + params[0] = spa_pod_builder_add_object ( &pod_builder, - pipewire_type->param.idBuffers, pipewire_type->param_buffers.Buffers, - ":", pipewire_type->param_buffers.size, "i", size, - ":", pipewire_type->param_buffers.stride, "i", stride, - ":", pipewire_type->param_buffers.buffers, "iru", 16, PROP_RANGE (2, 16), - ":", pipewire_type->param_buffers.align, "i", 16); - - params[1] = spa_pod_builder_object ( + SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, + SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int (16, 2, 16), + SPA_PARAM_BUFFERS_blocks, SPA_POD_Int (1), + SPA_PARAM_BUFFERS_size, SPA_POD_Int (size), + SPA_PARAM_BUFFERS_stride, SPA_POD_Int (stride), + SPA_PARAM_BUFFERS_align, SPA_POD_Int (16)); + + params[1] = spa_pod_builder_add_object ( &pod_builder, - pipewire_type->param.idMeta, pipewire_type->param_meta.Meta, - ":", pipewire_type->param_meta.type, "I", pipewire_type->meta.VideoCrop, - ":", pipewire_type->param_meta.size, "i", sizeof (struct spa_meta_video_crop)); + SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, + SPA_PARAM_META_type, SPA_POD_Id (SPA_META_VideoCrop), + SPA_PARAM_META_size, SPA_POD_Int (sizeof (struct spa_meta_region))); - params[2] = spa_pod_builder_object ( + params[2] = spa_pod_builder_add_object ( &pod_builder, - pipewire_type->param.idMeta, pipewire_type->param_meta.Meta, - ":", pipewire_type->param_meta.type, "I", priv->spa_type.meta_cursor, - ":", pipewire_type->param_meta.size, "i", CURSOR_META_SIZE (64, 64)); + SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, + SPA_PARAM_META_type, SPA_POD_Id (SPA_META_Cursor), + SPA_PARAM_META_size, SPA_POD_Int (CURSOR_META_SIZE (64, 64))); - pw_stream_finish_format (priv->pipewire_stream, 0, - params, G_N_ELEMENTS (params)); + pw_stream_update_params (priv->pipewire_stream, params, G_N_ELEMENTS (params)); } static const struct pw_stream_events stream_events = { PW_VERSION_STREAM_EVENTS, .state_changed = on_stream_state_changed, - .format_changed = on_stream_format_changed, + .param_changed = on_stream_param_changed, }; static struct pw_stream * @@ -652,8 +634,6 @@ create_pipewire_stream (MetaScreenCastStreamSrc *src, uint8_t buffer[1024]; struct spa_pod_builder pod_builder = SPA_POD_BUILDER_INIT (buffer, sizeof (buffer)); - MetaSpaType *spa_type = &priv->spa_type; - struct pw_type *pipewire_type = priv->pipewire_type; float frame_rate; MetaFraction frame_rate_fraction; struct spa_fraction max_framerate; @@ -661,7 +641,9 @@ create_pipewire_stream (MetaScreenCastStreamSrc *src, const struct spa_pod *params[1]; int result; - pipewire_stream = pw_stream_new (priv->pipewire_remote, + priv->node_id = SPA_ID_INVALID; + + pipewire_stream = pw_stream_new (priv->pipewire_core, "meta-screen-cast-src", NULL); if (!pipewire_stream) @@ -682,17 +664,17 @@ create_pipewire_stream (MetaScreenCastStreamSrc *src, max_framerate = SPA_FRACTION (frame_rate_fraction.num, frame_rate_fraction.denom); - params[0] = spa_pod_builder_object ( + params[0] = spa_pod_builder_add_object ( &pod_builder, - pipewire_type->param.idEnumFormat, pipewire_type->spa_format, - "I", spa_type->media_type.video, - "I", spa_type->media_subtype.raw, - ":", spa_type->format_video.format, "I", spa_type->video_format.BGRx, - ":", spa_type->format_video.size, "R", &SPA_RECTANGLE (priv->stream_width, - priv->stream_height), - ":", spa_type->format_video.framerate, "F", &SPA_FRACTION (0, 1), - ":", spa_type->format_video.max_framerate, "Fru", &max_framerate, - PROP_RANGE (&min_framerate, + SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, + SPA_FORMAT_mediaType, SPA_POD_Id (SPA_MEDIA_TYPE_video), + SPA_FORMAT_mediaSubtype, SPA_POD_Id (SPA_MEDIA_SUBTYPE_raw), + SPA_FORMAT_VIDEO_format, SPA_POD_Id (SPA_VIDEO_FORMAT_BGRx), + SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle (&SPA_RECTANGLE (priv->stream_width, + priv->stream_height)), + SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction (&SPA_FRACTION (0, 1)), + SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_CHOICE_RANGE_Fraction (&max_framerate, + &min_framerate, &max_framerate)); pw_stream_add_listener (pipewire_stream, @@ -702,7 +684,7 @@ create_pipewire_stream (MetaScreenCastStreamSrc *src, result = pw_stream_connect (pipewire_stream, PW_DIRECTION_OUTPUT, - NULL, + SPA_ID_INVALID, (PW_STREAM_FLAG_DRIVER | PW_STREAM_FLAG_MAP_BUFFERS), params, G_N_ELEMENTS (params)); @@ -717,40 +699,18 @@ create_pipewire_stream (MetaScreenCastStreamSrc *src, } static void -on_state_changed (void *data, - enum pw_remote_state old, - enum pw_remote_state state, - const char *error_message) +on_core_error (void *data, + uint32_t id, + int seq, + int res, + const char *message) { MetaScreenCastStreamSrc *src = data; - MetaScreenCastStreamSrcPrivate *priv = - meta_screen_cast_stream_src_get_instance_private (src); - struct pw_stream *pipewire_stream; - GError *error = NULL; - switch (state) - { - case PW_REMOTE_STATE_ERROR: - g_warning ("pipewire remote error: %s\n", error_message); - meta_screen_cast_stream_src_notify_closed (src); - break; - case PW_REMOTE_STATE_CONNECTED: - pipewire_stream = create_pipewire_stream (src, &error); - if (!pipewire_stream) - { - g_warning ("Could not create pipewire stream: %s", error->message); - g_error_free (error); - meta_screen_cast_stream_src_notify_closed (src); - } - else - { - priv->pipewire_stream = pipewire_stream; - } - break; - case PW_REMOTE_STATE_UNCONNECTED: - case PW_REMOTE_STATE_CONNECTING: - break; - } + g_warning ("pipewire remote error: id:%u %s", id, message); + + if (id == PW_ID_CORE && res == -EPIPE) + meta_screen_cast_stream_src_notify_closed (src); } static gboolean @@ -793,17 +753,6 @@ static GSourceFuncs pipewire_source_funcs = pipewire_loop_source_finalize }; -static void -init_spa_type (MetaSpaType *type, - struct spa_type_map *map) -{ - spa_type_media_type_map (map, &type->media_type); - spa_type_media_subtype_map (map, &type->media_subtype); - spa_type_format_video_map (map, &type->format_video); - spa_type_video_format_map (map, &type->video_format); - type->meta_cursor = spa_type_map_get_id(map, SPA_TYPE_META__Cursor); -} - static MetaPipeWireSource * create_pipewire_source (void) { @@ -829,9 +778,9 @@ create_pipewire_source (void) return pipewire_source; } -static const struct pw_remote_events remote_events = { - PW_VERSION_REMOTE_EVENTS, - .state_changed = on_state_changed, +static const struct pw_core_events core_events = { + PW_VERSION_CORE_EVENTS, + .error = on_core_error, }; static gboolean @@ -851,37 +800,31 @@ meta_screen_cast_stream_src_initable_init (GInitable *initable, return FALSE; } - priv->pipewire_core = pw_core_new (priv->pipewire_source->pipewire_loop, - NULL); - if (!priv->pipewire_core) + priv->pipewire_context = pw_context_new (priv->pipewire_source->pipewire_loop, + NULL, 0); + if (!priv->pipewire_context) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Failed to create pipewire core"); + "Failed to create pipewire context"); return FALSE; } - priv->pipewire_remote = pw_remote_new (priv->pipewire_core, NULL, 0); - if (!priv->pipewire_remote) + priv->pipewire_core = pw_context_connect (priv->pipewire_context, NULL, 0); + if (!priv->pipewire_core) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Couldn't creat pipewire remote"); + "Couldn't connect pipewire context"); return FALSE; } - pw_remote_add_listener (priv->pipewire_remote, - &priv->pipewire_remote_listener, - &remote_events, - src); + pw_core_add_listener (priv->pipewire_core, + &priv->pipewire_core_listener, + &core_events, + src); - priv->pipewire_type = pw_core_get_type (priv->pipewire_core); - init_spa_type (&priv->spa_type, priv->pipewire_type->map); - - if (pw_remote_connect (priv->pipewire_remote) != 0) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Couldn't connect pipewire remote"); - return FALSE; - } + priv->pipewire_stream = create_pipewire_stream (src, error); + if (!priv->pipewire_stream) + return FALSE; return TRUE; } @@ -912,8 +855,8 @@ meta_screen_cast_stream_src_finalize (GObject *object) meta_screen_cast_stream_src_disable (src); g_clear_pointer (&priv->pipewire_stream, pw_stream_destroy); - g_clear_pointer (&priv->pipewire_remote, pw_remote_destroy); - g_clear_pointer (&priv->pipewire_core, pw_core_destroy); + g_clear_pointer (&priv->pipewire_core, pw_core_disconnect); + g_clear_pointer (&priv->pipewire_context, pw_context_destroy); g_source_destroy (&priv->pipewire_source->base); G_OBJECT_CLASS (meta_screen_cast_stream_src_parent_class)->finalize (object); -- 2.26.2 From b29e2f833357a0b29bb42999b4d6be725be067d1 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 10 Jun 2019 17:07:55 +0300 Subject: [PATCH 19/49] egl: Introduce meta_egl_create_dmabuf_image This bit of code was more or less duplicated in meta-renderer-native-gles3.c and meta-wayland-dma-buf.c. Start consolidating the two implementations by moving the *-gles3.c function into meta-egl.c and generalizing it so it could also accommodate the meta-wayland-dma-buf.c usage. The workaround in the *-gles3.c implementation is moved to the caller. It is the caller's responsibility to check for the existence of the appropriate EGL extensions. Commit 6f59e4858e24c828e3ab0e611d36dfaaded1b272 worked around the lack of EGL_EXT_image_dma_buf_import_modifiers with the assumption that if the modifier is linear, there is no need to pass it into EGL. The problem is that not passing a modifier explicitly to EGL invokes implementation-defined behaviour, so we should not have that workaround in meta-egl.c. This patch intends to be pure refactoring, no behavioral changes. The one change is the addition of g_assert to catch overwriting arbitrary memory. https://gitlab.gnome.org/GNOME/mutter/merge_requests/615 --- src/backends/meta-egl.c | 94 ++++++++++++- src/backends/meta-egl.h | 13 ++ .../native/meta-renderer-native-gles3.c | 125 +++--------------- 3 files changed, 127 insertions(+), 105 deletions(-) diff --git a/src/backends/meta-egl.c b/src/backends/meta-egl.c index a28eef4ca..fdeff4f77 100644 --- a/src/backends/meta-egl.c +++ b/src/backends/meta-egl.c @@ -1,7 +1,8 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* - * Copyright (C) 2016 Red Hat Inc. + * Copyright (C) 2016, 2017 Red Hat Inc. + * Copyright (C) 2018, 2019 DisplayLink (UK) Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -575,6 +576,97 @@ meta_egl_destroy_image (MetaEgl *egl, return TRUE; } +EGLImageKHR +meta_egl_create_dmabuf_image (MetaEgl *egl, + EGLDisplay egl_display, + unsigned int width, + unsigned int height, + uint32_t drm_format, + uint32_t n_planes, + const int *fds, + const uint32_t *strides, + const uint32_t *offsets, + const uint64_t *modifiers, + GError **error) +{ + EGLint attribs[37]; + int atti = 0; + + /* This requires the Mesa commit in + * Mesa 10.3 (08264e5dad4df448e7718e782ad9077902089a07) or + * Mesa 10.2.7 (55d28925e6109a4afd61f109e845a8a51bd17652). + * Otherwise Mesa closes the fd behind our back and re-importing + * will fail. + * https://bugs.freedesktop.org/show_bug.cgi?id=76188 + */ + + attribs[atti++] = EGL_WIDTH; + attribs[atti++] = width; + attribs[atti++] = EGL_HEIGHT; + attribs[atti++] = height; + attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT; + attribs[atti++] = drm_format; + + if (n_planes > 0) + { + attribs[atti++] = EGL_DMA_BUF_PLANE0_FD_EXT; + attribs[atti++] = fds[0]; + attribs[atti++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT; + attribs[atti++] = offsets[0]; + attribs[atti++] = EGL_DMA_BUF_PLANE0_PITCH_EXT; + attribs[atti++] = strides[0]; + if (modifiers) + { + attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT; + attribs[atti++] = modifiers[0] & 0xFFFFFFFF; + attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT; + attribs[atti++] = modifiers[0] >> 32; + } + } + + if (n_planes > 1) + { + attribs[atti++] = EGL_DMA_BUF_PLANE1_FD_EXT; + attribs[atti++] = fds[1]; + attribs[atti++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT; + attribs[atti++] = offsets[1]; + attribs[atti++] = EGL_DMA_BUF_PLANE1_PITCH_EXT; + attribs[atti++] = strides[1]; + if (modifiers) + { + attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT; + attribs[atti++] = modifiers[1] & 0xFFFFFFFF; + attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT; + attribs[atti++] = modifiers[1] >> 32; + } + } + + if (n_planes > 2) + { + attribs[atti++] = EGL_DMA_BUF_PLANE2_FD_EXT; + attribs[atti++] = fds[2]; + attribs[atti++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT; + attribs[atti++] = offsets[2]; + attribs[atti++] = EGL_DMA_BUF_PLANE2_PITCH_EXT; + attribs[atti++] = strides[2]; + if (modifiers) + { + attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT; + attribs[atti++] = modifiers[2] & 0xFFFFFFFF; + attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT; + attribs[atti++] = modifiers[2] >> 32; + } + } + + attribs[atti++] = EGL_NONE; + g_assert (atti <= G_N_ELEMENTS (attribs)); + + return meta_egl_create_image (egl, egl_display, EGL_NO_CONTEXT, + EGL_LINUX_DMA_BUF_EXT, NULL, + attribs, + error); +} + gboolean meta_egl_make_current (MetaEgl *egl, EGLDisplay display, diff --git a/src/backends/meta-egl.h b/src/backends/meta-egl.h index 81b53b32d..4591e7d85 100644 --- a/src/backends/meta-egl.h +++ b/src/backends/meta-egl.h @@ -2,6 +2,7 @@ /* * Copyright (C) 2016 Red Hat Inc. + * Copyright (C) 2019 DisplayLink (UK) Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -101,6 +102,18 @@ gboolean meta_egl_destroy_image (MetaEgl *egl, EGLImageKHR image, GError **error); +EGLImageKHR meta_egl_create_dmabuf_image (MetaEgl *egl, + EGLDisplay egl_display, + unsigned int width, + unsigned int height, + uint32_t drm_format, + uint32_t n_planes, + const int *fds, + const uint32_t *strides, + const uint32_t *offsets, + const uint64_t *modifiers, + GError **error); + EGLSurface meta_egl_create_window_surface (MetaEgl *egl, EGLDisplay display, EGLConfig config, diff --git a/src/backends/native/meta-renderer-native-gles3.c b/src/backends/native/meta-renderer-native-gles3.c index 7afea8648..740b52ef6 100644 --- a/src/backends/native/meta-renderer-native-gles3.c +++ b/src/backends/native/meta-renderer-native-gles3.c @@ -45,101 +45,6 @@ #error "Somehow included OpenGL headers when we shouldn't have" #endif -static EGLImageKHR -create_egl_image (MetaEgl *egl, - EGLDisplay egl_display, - EGLContext egl_context, - unsigned int width, - unsigned int height, - uint32_t n_planes, - uint32_t *strides, - uint32_t *offsets, - uint64_t *modifiers, - uint32_t format, - int fd, - GError **error) -{ - EGLint attribs[37]; - int atti = 0; - gboolean has_modifier; - - /* This requires the Mesa commit in - * Mesa 10.3 (08264e5dad4df448e7718e782ad9077902089a07) or - * Mesa 10.2.7 (55d28925e6109a4afd61f109e845a8a51bd17652). - * Otherwise Mesa closes the fd behind our back and re-importing - * will fail. - * https://bugs.freedesktop.org/show_bug.cgi?id=76188 - */ - - attribs[atti++] = EGL_WIDTH; - attribs[atti++] = width; - attribs[atti++] = EGL_HEIGHT; - attribs[atti++] = height; - attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT; - attribs[atti++] = format; - - has_modifier = (modifiers[0] != DRM_FORMAT_MOD_INVALID && - modifiers[0] != DRM_FORMAT_MOD_LINEAR); - - if (n_planes > 0) - { - attribs[atti++] = EGL_DMA_BUF_PLANE0_FD_EXT; - attribs[atti++] = fd; - attribs[atti++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT; - attribs[atti++] = offsets[0]; - attribs[atti++] = EGL_DMA_BUF_PLANE0_PITCH_EXT; - attribs[atti++] = strides[0]; - if (has_modifier) - { - attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT; - attribs[atti++] = modifiers[0] & 0xFFFFFFFF; - attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT; - attribs[atti++] = modifiers[0] >> 32; - } - } - - if (n_planes > 1) - { - attribs[atti++] = EGL_DMA_BUF_PLANE1_FD_EXT; - attribs[atti++] = fd; - attribs[atti++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT; - attribs[atti++] = offsets[1]; - attribs[atti++] = EGL_DMA_BUF_PLANE1_PITCH_EXT; - attribs[atti++] = strides[1]; - if (has_modifier) - { - attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT; - attribs[atti++] = modifiers[1] & 0xFFFFFFFF; - attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT; - attribs[atti++] = modifiers[1] >> 32; - } - } - - if (n_planes > 2) - { - attribs[atti++] = EGL_DMA_BUF_PLANE2_FD_EXT; - attribs[atti++] = fd; - attribs[atti++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT; - attribs[atti++] = offsets[2]; - attribs[atti++] = EGL_DMA_BUF_PLANE2_PITCH_EXT; - attribs[atti++] = strides[2]; - if (has_modifier) - { - attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT; - attribs[atti++] = modifiers[2] & 0xFFFFFFFF; - attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT; - attribs[atti++] = modifiers[2] >> 32; - } - } - - attribs[atti++] = EGL_NONE; - - return meta_egl_create_image (egl, egl_display, EGL_NO_CONTEXT, - EGL_LINUX_DMA_BUF_EXT, NULL, - attribs, - error); -} - static void paint_egl_image (MetaGles3 *gles3, EGLImageKHR egl_image, @@ -195,8 +100,10 @@ meta_renderer_native_gles3_blit_shared_bo (MetaEgl *egl, uint32_t strides[4] = { 0 }; uint32_t offsets[4] = { 0 }; uint64_t modifiers[4] = { 0 }; + int fds[4] = { -1, -1, -1, -1 }; uint32_t format; EGLImageKHR egl_image; + gboolean use_modifiers; shared_bo_fd = gbm_bo_get_fd (shared_bo); if (shared_bo_fd < 0) @@ -216,17 +123,27 @@ meta_renderer_native_gles3_blit_shared_bo (MetaEgl *egl, strides[i] = gbm_bo_get_stride_for_plane (shared_bo, i); offsets[i] = gbm_bo_get_offset (shared_bo, i); modifiers[i] = gbm_bo_get_modifier (shared_bo); + fds[i] = shared_bo_fd; } - egl_image = create_egl_image (egl, - egl_display, - egl_context, - width, height, - n_planes, - strides, offsets, - modifiers, format, - shared_bo_fd, - error); + /* Workaround for https://gitlab.gnome.org/GNOME/mutter/issues/18 */ + if (modifiers[0] == DRM_FORMAT_MOD_LINEAR || + modifiers[0] == DRM_FORMAT_MOD_INVALID) + use_modifiers = FALSE; + else + use_modifiers = TRUE; + + egl_image = meta_egl_create_dmabuf_image (egl, + egl_display, + width, + height, + format, + n_planes, + fds, + strides, + offsets, + use_modifiers ? modifiers : NULL, + error); close (shared_bo_fd); if (!egl_image) -- 2.26.2 From 5af71a6c4a723951b26597ee710462b18867d113 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 29 Nov 2018 13:37:16 +0200 Subject: [PATCH 20/49] renderer/native: Add meta_dumb_buffer_ensure_dmabuf_fd Follow-up work will use this in an attempt to use the primary GPU to copy into secondary GPU dumb buffers. https://gitlab.gnome.org/GNOME/mutter/merge_requests/615 --- src/backends/native/meta-renderer-native.c | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c index ffb64a6bd..76cc79e23 100644 --- a/src/backends/native/meta-renderer-native.c +++ b/src/backends/native/meta-renderer-native.c @@ -133,6 +133,7 @@ typedef struct _MetaDumbBuffer int height; int stride_bytes; uint32_t drm_format; + int dmabuf_fd; } MetaDumbBuffer; typedef struct _MetaOnscreenNativeSecondaryGpuState @@ -242,6 +243,10 @@ init_dumb_fb (MetaDumbBuffer *dumb_fb, uint32_t format, GError **error); +static int +meta_dumb_buffer_ensure_dmabuf_fd (MetaDumbBuffer *dumb_fb, + MetaGpuKms *gpu_kms); + static MetaEgl * meta_renderer_native_get_egl (MetaRendererNative *renderer_native); @@ -2892,6 +2897,7 @@ init_dumb_fb (MetaDumbBuffer *dumb_fb, dumb_fb->height = height; dumb_fb->stride_bytes = create_arg.pitch; dumb_fb->drm_format = format; + dumb_fb->dmabuf_fd = -1; return TRUE; @@ -2909,6 +2915,33 @@ err_ioctl: return FALSE; } +static int +meta_dumb_buffer_ensure_dmabuf_fd (MetaDumbBuffer *dumb_fb, + MetaGpuKms *gpu_kms) +{ + int ret; + int kms_fd; + int dmabuf_fd; + + if (dumb_fb->dmabuf_fd != -1) + return dumb_fb->dmabuf_fd; + + kms_fd = meta_gpu_kms_get_fd (gpu_kms); + + ret = drmPrimeHandleToFD (kms_fd, dumb_fb->handle, DRM_CLOEXEC, + &dmabuf_fd); + if (ret) + { + g_debug ("Failed to export dumb drm buffer: %s", + g_strerror (errno)); + return -1; + } + + dumb_fb->dmabuf_fd = dmabuf_fd; + + return dumb_fb->dmabuf_fd; +} + static void release_dumb_fb (MetaDumbBuffer *dumb_fb, MetaGpuKms *gpu_kms) @@ -2919,6 +2952,9 @@ release_dumb_fb (MetaDumbBuffer *dumb_fb, if (!dumb_fb->map) return; + if (dumb_fb->dmabuf_fd != -1) + close (dumb_fb->dmabuf_fd); + munmap (dumb_fb->map, dumb_fb->map_size); dumb_fb->map = NULL; -- 2.26.2 From a6e2e1cb5fd25e152bbc3347078b0e9261a94ceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Tue, 16 Jul 2019 18:27:38 +0200 Subject: [PATCH 21/49] cogl/texture: Make is_get_data_supported() a bool on the texture Comparing the gl target is not enough. More on that later. https://gitlab.gnome.org/GNOME/mutter/merge_requests/687 --- cogl/cogl/cogl-texture-2d-private.h | 1 + cogl/cogl/cogl-texture-2d.c | 1 + cogl/cogl/driver/gl/cogl-texture-2d-gl.c | 6 ++---- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cogl/cogl/cogl-texture-2d-private.h b/cogl/cogl/cogl-texture-2d-private.h index 450d156f1..c1e6dff50 100644 --- a/cogl/cogl/cogl-texture-2d-private.h +++ b/cogl/cogl/cogl-texture-2d-private.h @@ -47,6 +47,7 @@ struct _CoglTexture2D gboolean auto_mipmap; gboolean mipmaps_dirty; gboolean is_foreign; + gboolean is_get_data_supported; /* TODO: factor out these OpenGL specific members into some form * of driver private state. */ diff --git a/cogl/cogl/cogl-texture-2d.c b/cogl/cogl/cogl-texture-2d.c index 76f0e3a87..76cb75a5c 100644 --- a/cogl/cogl/cogl-texture-2d.c +++ b/cogl/cogl/cogl-texture-2d.c @@ -107,6 +107,7 @@ _cogl_texture_2d_create_base (CoglContext *ctx, tex_2d->mipmaps_dirty = TRUE; tex_2d->auto_mipmap = TRUE; + tex_2d->is_get_data_supported = TRUE; tex_2d->gl_target = GL_TEXTURE_2D; diff --git a/cogl/cogl/driver/gl/cogl-texture-2d-gl.c b/cogl/cogl/driver/gl/cogl-texture-2d-gl.c index e36c3523e..a9329ad50 100644 --- a/cogl/cogl/driver/gl/cogl-texture-2d-gl.c +++ b/cogl/cogl/driver/gl/cogl-texture-2d-gl.c @@ -514,6 +514,7 @@ allocate_custom_egl_image_external (CoglTexture2D *tex_2d, tex_2d->internal_format = internal_format; tex_2d->gl_target = GL_TEXTURE_EXTERNAL_OES; + tex_2d->is_get_data_supported = FALSE; return TRUE; } @@ -840,10 +841,7 @@ _cogl_texture_2d_gl_copy_from_bitmap (CoglTexture2D *tex_2d, gboolean _cogl_texture_2d_gl_is_get_data_supported (CoglTexture2D *tex_2d) { - if (tex_2d->gl_target == GL_TEXTURE_EXTERNAL_OES) - return FALSE; - else - return TRUE; + return tex_2d->is_get_data_supported; } void -- 2.26.2 From 50108eca1503c8883370f0f877cab8d25f89ad2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Tue, 16 Jul 2019 18:29:29 +0200 Subject: [PATCH 22/49] cogl/texture: Add EGLImage texture import flags The flags are 'none', and 'no-get-data' meaning get_data() is not supported. https://gitlab.gnome.org/GNOME/mutter/merge_requests/687 --- cogl/cogl/cogl-texture-2d.c | 3 +++ cogl/cogl/cogl-texture-2d.h | 7 +++++++ cogl/cogl/cogl-texture-private.h | 1 + cogl/cogl/driver/gl/cogl-texture-2d-gl.c | 2 ++ cogl/cogl/winsys/cogl-winsys-egl-x11.c | 1 + src/wayland/meta-wayland-buffer.c | 3 +++ src/wayland/meta-wayland-dma-buf.c | 3 +++ 7 files changed, 20 insertions(+) diff --git a/cogl/cogl/cogl-texture-2d.c b/cogl/cogl/cogl-texture-2d.c index 76cb75a5c..6544f8a62 100644 --- a/cogl/cogl/cogl-texture-2d.c +++ b/cogl/cogl/cogl-texture-2d.c @@ -242,6 +242,7 @@ cogl_egl_texture_2d_new_from_image (CoglContext *ctx, int height, CoglPixelFormat format, EGLImageKHR image, + CoglEglImageFlags flags, CoglError **error) { CoglTextureLoader *loader; @@ -262,6 +263,7 @@ cogl_egl_texture_2d_new_from_image (CoglContext *ctx, loader->src.egl_image.width = width; loader->src.egl_image.height = height; loader->src.egl_image.format = format; + loader->src.egl_image.flags = flags; tex = _cogl_texture_2d_create_base (ctx, width, height, format, loader); @@ -436,6 +438,7 @@ cogl_wayland_texture_2d_new_from_buffer (CoglContext *ctx, width, height, internal_format, image, + COGL_EGL_IMAGE_FLAG_NONE, error); _cogl_egl_destroy_image (ctx, image); return tex; diff --git a/cogl/cogl/cogl-texture-2d.h b/cogl/cogl/cogl-texture-2d.h index 29b8b2d6b..44b9d4636 100644 --- a/cogl/cogl/cogl-texture-2d.h +++ b/cogl/cogl/cogl-texture-2d.h @@ -65,6 +65,12 @@ G_BEGIN_DECLS typedef struct _CoglTexture2D CoglTexture2D; #define COGL_TEXTURE_2D(X) ((CoglTexture2D *)X) +typedef enum _CoglEglImageFlags +{ + COGL_EGL_IMAGE_FLAG_NONE = 0, + COGL_EGL_IMAGE_FLAG_NO_GET_DATA = 1 << 0, +} CoglEglImageFlags; + /** * cogl_texture_2d_get_gtype: * @@ -244,6 +250,7 @@ cogl_egl_texture_2d_new_from_image (CoglContext *ctx, int height, CoglPixelFormat format, EGLImageKHR image, + CoglEglImageFlags flags, CoglError **error); typedef gboolean (*CoglTexture2DEGLImageExternalAlloc) (CoglTexture2D *tex_2d, diff --git a/cogl/cogl/cogl-texture-private.h b/cogl/cogl/cogl-texture-private.h index 05a0045f8..794d0d7e7 100644 --- a/cogl/cogl/cogl-texture-private.h +++ b/cogl/cogl/cogl-texture-private.h @@ -184,6 +184,7 @@ typedef struct _CoglTextureLoader int width; int height; CoglPixelFormat format; + CoglEglImageFlags flags; } egl_image; #endif #if defined (COGL_HAS_EGL_SUPPORT) diff --git a/cogl/cogl/driver/gl/cogl-texture-2d-gl.c b/cogl/cogl/driver/gl/cogl-texture-2d-gl.c index a9329ad50..34432ccba 100644 --- a/cogl/cogl/driver/gl/cogl-texture-2d-gl.c +++ b/cogl/cogl/driver/gl/cogl-texture-2d-gl.c @@ -328,6 +328,8 @@ allocate_from_egl_image (CoglTexture2D *tex_2d, } tex_2d->internal_format = internal_format; + tex_2d->is_get_data_supported = + !(loader->src.egl_image.flags & COGL_EGL_IMAGE_FLAG_NO_GET_DATA); _cogl_texture_set_allocated (tex, internal_format, diff --git a/cogl/cogl/winsys/cogl-winsys-egl-x11.c b/cogl/cogl/winsys/cogl-winsys-egl-x11.c index 4f0a12543..6a89206a1 100644 --- a/cogl/cogl/winsys/cogl-winsys-egl-x11.c +++ b/cogl/cogl/winsys/cogl-winsys-egl-x11.c @@ -802,6 +802,7 @@ _cogl_winsys_texture_pixmap_x11_create (CoglTexturePixmapX11 *tex_pixmap) tex->height, texture_format, egl_tex_pixmap->image, + COGL_EGL_IMAGE_FLAG_NONE, NULL)); tex_pixmap->winsys = egl_tex_pixmap; diff --git a/src/wayland/meta-wayland-buffer.c b/src/wayland/meta-wayland-buffer.c index f45679d3a..cdaad26eb 100644 --- a/src/wayland/meta-wayland-buffer.c +++ b/src/wayland/meta-wayland-buffer.c @@ -289,6 +289,7 @@ egl_image_buffer_attach (MetaWaylandBuffer *buffer, int format, width, height, y_inverted; CoglPixelFormat cogl_format; EGLImageKHR egl_image; + CoglEglImageFlags flags; CoglTexture2D *texture_2d; if (buffer->egl_image.texture) @@ -343,10 +344,12 @@ egl_image_buffer_attach (MetaWaylandBuffer *buffer, if (egl_image == EGL_NO_IMAGE_KHR) return FALSE; + flags = COGL_EGL_IMAGE_FLAG_NONE; texture_2d = cogl_egl_texture_2d_new_from_image (cogl_context, width, height, cogl_format, egl_image, + flags, error); meta_egl_destroy_image (egl, egl_display, egl_image, NULL); diff --git a/src/wayland/meta-wayland-dma-buf.c b/src/wayland/meta-wayland-dma-buf.c index e49fba9cf..786b88304 100644 --- a/src/wayland/meta-wayland-dma-buf.c +++ b/src/wayland/meta-wayland-dma-buf.c @@ -77,6 +77,7 @@ meta_wayland_dma_buf_realize_texture (MetaWaylandBuffer *buffer, MetaWaylandDmaBufBuffer *dma_buf = buffer->dma_buf.dma_buf; CoglPixelFormat cogl_format; EGLImageKHR egl_image; + CoglEglImageFlags flags; CoglTexture2D *texture; EGLint attribs[64]; int attr_idx = 0; @@ -184,11 +185,13 @@ meta_wayland_dma_buf_realize_texture (MetaWaylandBuffer *buffer, if (egl_image == EGL_NO_IMAGE_KHR) return FALSE; + flags = COGL_EGL_IMAGE_FLAG_NONE; texture = cogl_egl_texture_2d_new_from_image (cogl_context, dma_buf->width, dma_buf->height, cogl_format, egl_image, + flags, error); meta_egl_destroy_image (egl, egl_display, egl_image, NULL); -- 2.26.2 From cca085b00c029debaed80c070e316a3a7ff7e44d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Tue, 16 Jul 2019 18:30:12 +0200 Subject: [PATCH 23/49] dma-buf: Mark DMA-BUF textures as paint-only Reading pixels directly from a texture imported from a DMA-BUF EGLImage may result compressed textures to be transferred into non-compressed texture. This may have side effects causing it to be rendered incorrectly in subsequent paints. Avoid this by passing the no-get-data flag to the texture creator function, eventually causing mutter to use an intermediate offscreen framebuffer when reading pixels from such textures. https://bugs.freedesktop.org/show_bug.cgi?id=111140 https://gitlab.freedesktop.org/xorg/xserver/issues/545 https://gitlab.gnome.org/GNOME/mutter/merge_requests/687 --- src/wayland/meta-wayland-dma-buf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wayland/meta-wayland-dma-buf.c b/src/wayland/meta-wayland-dma-buf.c index 786b88304..5661fb6ff 100644 --- a/src/wayland/meta-wayland-dma-buf.c +++ b/src/wayland/meta-wayland-dma-buf.c @@ -185,7 +185,7 @@ meta_wayland_dma_buf_realize_texture (MetaWaylandBuffer *buffer, if (egl_image == EGL_NO_IMAGE_KHR) return FALSE; - flags = COGL_EGL_IMAGE_FLAG_NONE; + flags = COGL_EGL_IMAGE_FLAG_NO_GET_DATA; texture = cogl_egl_texture_2d_new_from_image (cogl_context, dma_buf->width, dma_buf->height, -- 2.26.2 From 8e25a750e9084cbc4f7e7048453ef33dfa9ea314 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Mon, 24 Feb 2020 12:44:52 -0300 Subject: [PATCH 24/49] cogl/framebuffer: Add cogl_framebuffer_flush In future patches, we'll create additional CoglFramebuffers that will be shared via DMA-Buf with PipeWire. When recording frames, we'll blit the current onscreen framebuffer into the shared one. However, that presents a problem: cogl_framebuffer_blit() mimics glBlitFramebuffer() semantics, and doesn't do an implicit flush of the GPU command stream. As a consequence, clients may receive unblitted or incomplete framebuffers. We could use cogl_framebuffer_finish() to ensure the commands were submitted to the GPU, but it is too harsh -- it blocks the CPU completely until the commands are finished! Add cogl_framebuffer_flush(), which ensures the command stream is submitted to the GPU without blocking the CPU. Even though we don't use the framebuffer specifically, it may be useful in the future for e.g. a potential Vulkan backend to have access to the framebuffer. https://gitlab.gnome.org/GNOME/mutter/merge_requests/1086 --- cogl/cogl/cogl-driver.h | 3 +++ cogl/cogl/cogl-framebuffer.c | 11 +++++++++++ cogl/cogl/cogl-framebuffer.h | 12 ++++++++++++ cogl/cogl/driver/gl/cogl-framebuffer-gl-private.h | 3 +++ cogl/cogl/driver/gl/cogl-framebuffer-gl.c | 6 ++++++ cogl/cogl/driver/gl/gl/cogl-driver-gl.c | 1 + cogl/cogl/driver/gl/gles/cogl-driver-gles.c | 1 + cogl/cogl/driver/nop/cogl-driver-nop.c | 1 + cogl/cogl/driver/nop/cogl-framebuffer-nop-private.h | 3 +++ cogl/cogl/driver/nop/cogl-framebuffer-nop.c | 5 +++++ 10 files changed, 46 insertions(+) diff --git a/cogl/cogl/cogl-driver.h b/cogl/cogl/cogl-driver.h index 86682d8a7..af92146d0 100644 --- a/cogl/cogl/cogl-driver.h +++ b/cogl/cogl/cogl-driver.h @@ -87,6 +87,9 @@ struct _CoglDriverVtable void (* framebuffer_finish) (CoglFramebuffer *framebuffer); + void + (* framebuffer_flush) (CoglFramebuffer *framebuffer); + void (* framebuffer_discard_buffers) (CoglFramebuffer *framebuffer, unsigned long buffers); diff --git a/cogl/cogl/cogl-framebuffer.c b/cogl/cogl/cogl-framebuffer.c index 948cd112d..d64fc89fb 100644 --- a/cogl/cogl/cogl-framebuffer.c +++ b/cogl/cogl/cogl-framebuffer.c @@ -1568,6 +1568,17 @@ cogl_framebuffer_finish (CoglFramebuffer *framebuffer) ctx->driver_vtable->framebuffer_finish (framebuffer); } +void +cogl_framebuffer_flush (CoglFramebuffer *framebuffer) +{ + + CoglContext *ctx = framebuffer->context; + + _cogl_framebuffer_flush_journal (framebuffer); + + ctx->driver_vtable->framebuffer_flush (framebuffer); +} + void cogl_framebuffer_push_matrix (CoglFramebuffer *framebuffer) { diff --git a/cogl/cogl/cogl-framebuffer.h b/cogl/cogl/cogl-framebuffer.h index 230a78627..38ada9feb 100644 --- a/cogl/cogl/cogl-framebuffer.h +++ b/cogl/cogl/cogl-framebuffer.h @@ -1910,6 +1910,18 @@ cogl_blit_framebuffer (CoglFramebuffer *src, int height, GError **error); +/** + * cogl_framebuffer_flush: + * @framebuffer: A #CoglFramebuffer pointer + * + * Flushes @framebuffer to ensure the current batch of commands is + * submitted to the GPU. + * + * Unlike cogl_framebuffer_finish(), this does not block the CPU. + */ +void +cogl_framebuffer_flush (CoglFramebuffer *framebuffer); + G_END_DECLS #endif /* __COGL_FRAMEBUFFER_H */ diff --git a/cogl/cogl/driver/gl/cogl-framebuffer-gl-private.h b/cogl/cogl/driver/gl/cogl-framebuffer-gl-private.h index 214f45f0f..bbd7b0e99 100644 --- a/cogl/cogl/driver/gl/cogl-framebuffer-gl-private.h +++ b/cogl/cogl/driver/gl/cogl-framebuffer-gl-private.h @@ -61,6 +61,9 @@ _cogl_framebuffer_gl_query_bits (CoglFramebuffer *framebuffer, void _cogl_framebuffer_gl_finish (CoglFramebuffer *framebuffer); +void +_cogl_framebuffer_gl_flush (CoglFramebuffer *framebuffer); + void _cogl_framebuffer_gl_discard_buffers (CoglFramebuffer *framebuffer, unsigned long buffers); diff --git a/cogl/cogl/driver/gl/cogl-framebuffer-gl.c b/cogl/cogl/driver/gl/cogl-framebuffer-gl.c index 90d08954d..6466fd6bc 100644 --- a/cogl/cogl/driver/gl/cogl-framebuffer-gl.c +++ b/cogl/cogl/driver/gl/cogl-framebuffer-gl.c @@ -1115,6 +1115,12 @@ _cogl_framebuffer_gl_finish (CoglFramebuffer *framebuffer) GE (framebuffer->context, glFinish ()); } +void +_cogl_framebuffer_gl_flush (CoglFramebuffer *framebuffer) +{ + GE (framebuffer->context, glFlush ()); +} + void _cogl_framebuffer_gl_discard_buffers (CoglFramebuffer *framebuffer, unsigned long buffers) diff --git a/cogl/cogl/driver/gl/gl/cogl-driver-gl.c b/cogl/cogl/driver/gl/gl/cogl-driver-gl.c index e06e27961..716617b54 100644 --- a/cogl/cogl/driver/gl/gl/cogl-driver-gl.c +++ b/cogl/cogl/driver/gl/gl/cogl-driver-gl.c @@ -668,6 +668,7 @@ _cogl_driver_gl = _cogl_framebuffer_gl_clear, _cogl_framebuffer_gl_query_bits, _cogl_framebuffer_gl_finish, + _cogl_framebuffer_gl_flush, _cogl_framebuffer_gl_discard_buffers, _cogl_framebuffer_gl_draw_attributes, _cogl_framebuffer_gl_draw_indexed_attributes, diff --git a/cogl/cogl/driver/gl/gles/cogl-driver-gles.c b/cogl/cogl/driver/gl/gles/cogl-driver-gles.c index bcb0bdf07..902bd0bd3 100644 --- a/cogl/cogl/driver/gl/gles/cogl-driver-gles.c +++ b/cogl/cogl/driver/gl/gles/cogl-driver-gles.c @@ -447,6 +447,7 @@ _cogl_driver_gles = _cogl_framebuffer_gl_clear, _cogl_framebuffer_gl_query_bits, _cogl_framebuffer_gl_finish, + _cogl_framebuffer_gl_flush, _cogl_framebuffer_gl_discard_buffers, _cogl_framebuffer_gl_draw_attributes, _cogl_framebuffer_gl_draw_indexed_attributes, diff --git a/cogl/cogl/driver/nop/cogl-driver-nop.c b/cogl/cogl/driver/nop/cogl-driver-nop.c index b41a2bcc5..8cab69afa 100644 --- a/cogl/cogl/driver/nop/cogl-driver-nop.c +++ b/cogl/cogl/driver/nop/cogl-driver-nop.c @@ -66,6 +66,7 @@ _cogl_driver_nop = _cogl_framebuffer_nop_clear, _cogl_framebuffer_nop_query_bits, _cogl_framebuffer_nop_finish, + _cogl_framebuffer_nop_flush, _cogl_framebuffer_nop_discard_buffers, _cogl_framebuffer_nop_draw_attributes, _cogl_framebuffer_nop_draw_indexed_attributes, diff --git a/cogl/cogl/driver/nop/cogl-framebuffer-nop-private.h b/cogl/cogl/driver/nop/cogl-framebuffer-nop-private.h index 05bbde6b4..810b98413 100644 --- a/cogl/cogl/driver/nop/cogl-framebuffer-nop-private.h +++ b/cogl/cogl/driver/nop/cogl-framebuffer-nop-private.h @@ -64,6 +64,9 @@ _cogl_framebuffer_nop_query_bits (CoglFramebuffer *framebuffer, void _cogl_framebuffer_nop_finish (CoglFramebuffer *framebuffer); +void +_cogl_framebuffer_nop_flush (CoglFramebuffer *framebuffer); + void _cogl_framebuffer_nop_discard_buffers (CoglFramebuffer *framebuffer, unsigned long buffers); diff --git a/cogl/cogl/driver/nop/cogl-framebuffer-nop.c b/cogl/cogl/driver/nop/cogl-framebuffer-nop.c index 643e304ea..db9819524 100644 --- a/cogl/cogl/driver/nop/cogl-framebuffer-nop.c +++ b/cogl/cogl/driver/nop/cogl-framebuffer-nop.c @@ -76,6 +76,11 @@ _cogl_framebuffer_nop_finish (CoglFramebuffer *framebuffer) { } +void +_cogl_framebuffer_nop_flush (CoglFramebuffer *framebuffer) +{ +} + void _cogl_framebuffer_nop_discard_buffers (CoglFramebuffer *framebuffer, unsigned long buffers) -- 2.26.2 From 80affcf2cd04a0a05dd6cb45ea7475b12d9f6841 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Mon, 9 Dec 2019 10:03:07 -0300 Subject: [PATCH 25/49] cogl/context: Add cogl_renderer_create_dma_buf() and family This is a winsys-specific API that allows exporting a DMA buffer fd. The CoglDmaBufHandle structure allows passing the ownership of the DMA buffer to whoever is using it, so the winsys doesn't need to manually track it. https://gitlab.gnome.org/GNOME/mutter/merge_requests/1086 --- cogl/cogl/cogl-dma-buf-handle.c | 94 ++++++++++++++++++++++++++ cogl/cogl/cogl-dma-buf-handle.h | 83 +++++++++++++++++++++++ cogl/cogl/cogl-renderer.c | 14 ++++ cogl/cogl/cogl-renderer.h | 21 ++++++ cogl/cogl/cogl-types.h | 8 +++ cogl/cogl/cogl.h | 1 + cogl/cogl/meson.build | 2 + cogl/cogl/winsys/cogl-winsys-private.h | 6 ++ 8 files changed, 229 insertions(+) create mode 100644 cogl/cogl/cogl-dma-buf-handle.c create mode 100644 cogl/cogl/cogl-dma-buf-handle.h diff --git a/cogl/cogl/cogl-dma-buf-handle.c b/cogl/cogl/cogl-dma-buf-handle.c new file mode 100644 index 000000000..4a8f709f2 --- /dev/null +++ b/cogl/cogl/cogl-dma-buf-handle.c @@ -0,0 +1,94 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2020 Endless, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Georges Basile Stavracas Neto + */ + +#include "cogl-config.h" + +#include "cogl-dma-buf-handle.h" +#include "cogl-object.h" + +#include + +struct _CoglDmaBufHandle +{ + CoglFramebuffer *framebuffer; + int dmabuf_fd; + gpointer user_data; + GDestroyNotify destroy_func; +}; + +CoglDmaBufHandle * +cogl_dma_buf_handle_new (CoglFramebuffer *framebuffer, + int dmabuf_fd, + gpointer user_data, + GDestroyNotify destroy_func) +{ + CoglDmaBufHandle *dmabuf_handle; + + g_assert (framebuffer); + g_assert (dmabuf_fd != -1); + + dmabuf_handle = g_new0 (CoglDmaBufHandle, 1); + dmabuf_handle->framebuffer = cogl_object_ref (framebuffer); + dmabuf_handle->dmabuf_fd = dmabuf_fd; + dmabuf_handle->user_data = user_data; + dmabuf_handle->destroy_func = destroy_func; + + return dmabuf_handle; +} + +void +cogl_dma_buf_handle_free (CoglDmaBufHandle *dmabuf_handle) +{ + g_return_if_fail (dmabuf_handle != NULL); + + g_clear_pointer (&dmabuf_handle->framebuffer, cogl_object_unref); + + if (dmabuf_handle->destroy_func) + g_clear_pointer (&dmabuf_handle->user_data, dmabuf_handle->destroy_func); + + if (dmabuf_handle->dmabuf_fd != -1) + close (dmabuf_handle->dmabuf_fd); + + g_free (dmabuf_handle); +} + +CoglFramebuffer * +cogl_dma_buf_handle_get_framebuffer (CoglDmaBufHandle *dmabuf_handle) +{ + return dmabuf_handle->framebuffer; +} + +int +cogl_dma_buf_handle_get_fd (CoglDmaBufHandle *dmabuf_handle) +{ + return dmabuf_handle->dmabuf_fd; +} + diff --git a/cogl/cogl/cogl-dma-buf-handle.h b/cogl/cogl/cogl-dma-buf-handle.h new file mode 100644 index 000000000..25b9b0ccb --- /dev/null +++ b/cogl/cogl/cogl-dma-buf-handle.h @@ -0,0 +1,83 @@ +/* + * Cogl + * + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2020 Endless, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Georges Basile Stavracas Neto + */ + + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_DMA_BUF_HANDLE_H__ +#define __COGL_DMA_BUF_HANDLE_H__ + +#include +#include + +/** + * cogl_dma_buf_handle_new: (skip) + */ +CoglDmaBufHandle * +cogl_dma_buf_handle_new (CoglFramebuffer *framebuffer, + int dmabuf_fd, + gpointer data, + GDestroyNotify destroy_func); + +/** + * cogl_dma_buf_handle_free: (skip) + * + * Releases @dmabuf_handle; it is a programming error to release + * an already released handle. + */ +void +cogl_dma_buf_handle_free (CoglDmaBufHandle *dmabuf_handle); + +/** + * cogl_dma_buf_handle_get_framebuffer: (skip) + * + * Retrieves the #CoglFramebuffer, backed by an exported DMABuf buffer, + * of @dmabuf_handle. + * + * Returns: (transfer none): a #CoglFramebuffer + */ +CoglFramebuffer * +cogl_dma_buf_handle_get_framebuffer (CoglDmaBufHandle *dmabuf_handle); + +/** + * cogl_dma_buf_handle_get_fd: (skip) + * + * Retrieves the file descriptor of @dmabuf_handle. + * + * Returns: a valid file descriptor + */ +int +cogl_dma_buf_handle_get_fd (CoglDmaBufHandle *dmabuf_handle); + + +#endif /* __COGL_DMA_BUF_HANDLE_H__ */ diff --git a/cogl/cogl/cogl-renderer.c b/cogl/cogl/cogl-renderer.c index 854aa1fdd..8c2ff0702 100644 --- a/cogl/cogl/cogl-renderer.c +++ b/cogl/cogl/cogl-renderer.c @@ -838,3 +838,17 @@ cogl_renderer_foreach_output (CoglRenderer *renderer, for (l = renderer->outputs; l; l = l->next) callback (l->data, user_data); } + +CoglDmaBufHandle * +cogl_renderer_create_dma_buf (CoglRenderer *renderer, + int width, + int height, + GError **error) +{ + const CoglWinsysVtable *winsys = _cogl_renderer_get_winsys (renderer); + + if (winsys->renderer_create_dma_buf) + return winsys->renderer_create_dma_buf (renderer, width, height, error); + + return NULL; +} diff --git a/cogl/cogl/cogl-renderer.h b/cogl/cogl/cogl-renderer.h index 0b3ddb8f2..c5e904106 100644 --- a/cogl/cogl/cogl-renderer.h +++ b/cogl/cogl/cogl-renderer.h @@ -418,6 +418,27 @@ cogl_renderer_foreach_output (CoglRenderer *renderer, CoglOutputCallback callback, void *user_data); +/** + * cogl_renderer_create_dma_buf: (skip) + * @renderer: A #CoglRenderer + * @width: width of the new + * @height: height of the new + * @error: (nullable): return location for a #GError + * + * Creates a new #CoglFramebuffer with @width x @height, and format + * hardcoded to XRGB, and exports the new framebuffer's DMA buffer + * handle. + * + * Returns: (nullable)(transfer full): a #CoglDmaBufHandle. The + * return result must be released with cogl_dma_buf_handle_free() + * after use. + */ +CoglDmaBufHandle * +cogl_renderer_create_dma_buf (CoglRenderer *renderer, + int width, + int height, + GError **error); + G_END_DECLS #endif /* __COGL_RENDERER_H__ */ diff --git a/cogl/cogl/cogl-types.h b/cogl/cogl/cogl-types.h index 69d304cf0..1d420dc2c 100644 --- a/cogl/cogl/cogl-types.h +++ b/cogl/cogl/cogl-types.h @@ -138,6 +138,14 @@ typedef int32_t CoglAngle; typedef struct _CoglColor CoglColor; typedef struct _CoglTextureVertex CoglTextureVertex; +/** + * CoglDmaBufHandle: (skip) + * + * An opaque type that tracks the lifetime of a DMA buffer fd. Release + * with cogl_dma_buf_handle_free(). + */ +typedef struct _CoglDmaBufHandle CoglDmaBufHandle; + /* Enum declarations */ #define COGL_A_BIT (1 << 4) diff --git a/cogl/cogl/cogl.h b/cogl/cogl/cogl.h index 565ea289a..065278d36 100644 --- a/cogl/cogl/cogl.h +++ b/cogl/cogl/cogl.h @@ -61,6 +61,7 @@ #include #include #include +#include #include #include #include diff --git a/cogl/cogl/meson.build b/cogl/cogl/meson.build index 8032669e4..1934e7574 100644 --- a/cogl/cogl/meson.build +++ b/cogl/cogl/meson.build @@ -102,6 +102,7 @@ cogl_nonintrospected_headers = [ 'cogl-renderer.h', 'cogl-swap-chain.h', 'cogl-onscreen-template.h', + 'cogl-dma-buf-handle.h', 'cogl-display.h', 'cogl-context.h', 'cogl-snippet.h', @@ -217,6 +218,7 @@ cogl_sources = [ 'cogl-i18n-private.h', 'cogl-debug.h', 'cogl-debug-options.h', + 'cogl-dma-buf-handle.c', 'cogl-gpu-info.c', 'cogl-gpu-info-private.h', 'cogl-context-private.h', diff --git a/cogl/cogl/winsys/cogl-winsys-private.h b/cogl/cogl/winsys/cogl-winsys-private.h index c6b2f3579..59c8945ce 100644 --- a/cogl/cogl/winsys/cogl-winsys-private.h +++ b/cogl/cogl/winsys/cogl-winsys-private.h @@ -100,6 +100,12 @@ typedef struct _CoglWinsysVtable void (*display_destroy) (CoglDisplay *display); + CoglDmaBufHandle * + (*renderer_create_dma_buf) (CoglRenderer *renderer, + int width, + int height, + GError **error); + gboolean (*context_init) (CoglContext *context, CoglError **error); -- 2.26.2 From aee3b6d1933375b9b8fb144c523a6227cd935142 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Mon, 9 Dec 2019 10:04:56 -0300 Subject: [PATCH 26/49] renderer-native: Move DMA buffer creation to an auxiliary function This will be reused by the DMA buffer exporting function. https://gitlab.gnome.org/GNOME/mutter/merge_requests/1086 --- src/backends/native/meta-renderer-native.c | 92 +++++++++++++++++++--- 1 file changed, 83 insertions(+), 9 deletions(-) diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c index 76cc79e23..75d6ec4c2 100644 --- a/src/backends/native/meta-renderer-native.c +++ b/src/backends/native/meta-renderer-native.c @@ -2214,6 +2214,89 @@ wait_for_pending_flips (CoglOnscreen *onscreen) meta_gpu_kms_wait_for_flip (onscreen_native->render_gpu, NULL); } +static CoglContext * +cogl_context_from_renderer_native (MetaRendererNative *renderer_native) +{ + MetaBackend *backend = backend_from_renderer_native (renderer_native); + ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); + + return clutter_backend_get_cogl_context (clutter_backend); +} + +static CoglFramebuffer * +create_dma_buf_framebuffer (MetaRendererNative *renderer_native, + int dmabuf_fd, + uint32_t width, + uint32_t height, + uint32_t stride, + uint32_t offset, + uint64_t modifier, + uint32_t drm_format, + GError **error) +{ + CoglContext *cogl_context = + cogl_context_from_renderer_native (renderer_native); + CoglDisplay *cogl_display = cogl_context->display; + CoglRenderer *cogl_renderer = cogl_display->renderer; + CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; + EGLDisplay egl_display = cogl_renderer_egl->edpy; + MetaEgl *egl = meta_renderer_native_get_egl (renderer_native); + EGLImageKHR egl_image; + uint32_t strides[1]; + uint32_t offsets[1]; + uint64_t modifiers[1]; + CoglPixelFormat cogl_format; + CoglEglImageFlags flags; + CoglTexture2D *cogl_tex; + CoglOffscreen *cogl_fbo; + int ret; + + ret = cogl_pixel_format_from_drm_format (drm_format, &cogl_format, NULL); + g_assert (ret); + + strides[0] = stride; + offsets[0] = offset; + modifiers[0] = modifier; + egl_image = meta_egl_create_dmabuf_image (egl, + egl_display, + width, + height, + drm_format, + 1 /* n_planes */, + &dmabuf_fd, + strides, + offsets, + modifiers, + error); + if (egl_image == EGL_NO_IMAGE_KHR) + return NULL; + + flags = COGL_EGL_IMAGE_FLAG_NO_GET_DATA; + cogl_tex = cogl_egl_texture_2d_new_from_image (cogl_context, + width, + height, + cogl_format, + egl_image, + flags, + error); + + meta_egl_destroy_image (egl, egl_display, egl_image, NULL); + + if (!cogl_tex) + return NULL; + + cogl_fbo = cogl_offscreen_new_with_texture (COGL_TEXTURE (cogl_tex)); + cogl_object_unref (cogl_tex); + + if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (cogl_fbo), error)) + { + cogl_object_unref (cogl_fbo); + return NULL; + } + + return COGL_FRAMEBUFFER (cogl_fbo); +} + static void copy_shared_framebuffer_gpu (CoglOnscreen *onscreen, MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state, @@ -3457,15 +3540,6 @@ calculate_view_transform (MetaMonitorManager *monitor_manager, return crtc_transform; } -static CoglContext * -cogl_context_from_renderer_native (MetaRendererNative *renderer_native) -{ - MetaBackend *backend = backend_from_renderer_native (renderer_native); - ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); - - return clutter_backend_get_cogl_context (clutter_backend); -} - static gboolean should_force_shadow_fb (MetaRendererNative *renderer_native, MetaGpuKms *primary_gpu) -- 2.26.2 From 1eab96d6e3813ec5745e882d9a195adf4a226ff6 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Mon, 9 Dec 2019 10:05:37 -0300 Subject: [PATCH 27/49] renderer-native: Implement DMA buffer creation Create a new gbm_bo using the same given geometry, and export the new bo's DMA buffer fd. The new bo lives as long as necessary to be used, and reused, by PipeWire. Unfortunately, PipeWire doesn't support modifiers properly, so use the linear format for now. For now, a hardcoded format of DRM_FORMAT_XRGB8888 is set, so we don't need to negotiate the format with PipeWire early. https://gitlab.gnome.org/GNOME/mutter/merge_requests/1086 --- src/backends/native/meta-renderer-native.c | 68 ++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c index 75d6ec4c2..ba98de650 100644 --- a/src/backends/native/meta-renderer-native.c +++ b/src/backends/native/meta-renderer-native.c @@ -2618,6 +2618,73 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, _cogl_winsys_egl_ensure_current (cogl_display); } +static CoglDmaBufHandle * +meta_renderer_native_create_dma_buf (CoglRenderer *cogl_renderer, + int width, + int height, + GError **error) +{ + CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; + MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform; + MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native; + + switch (renderer_gpu_data->mode) + { + case META_RENDERER_NATIVE_MODE_GBM: + { + CoglFramebuffer *dmabuf_fb; + struct gbm_bo *new_bo; + int dmabuf_fd = -1; + + new_bo = gbm_bo_create (renderer_gpu_data->gbm.device, + width, height, DRM_FORMAT_XRGB8888, + GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR); + + if (!new_bo) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Failed to allocate buffer"); + return NULL; + } + + dmabuf_fd = gbm_bo_get_fd (new_bo); + + if (dmabuf_fd == -1) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS, + "Failed to export buffer's DMA fd: %s", + g_strerror (errno)); + return NULL; + } + + dmabuf_fb = create_dma_buf_framebuffer (renderer_native, + dmabuf_fd, + width, height, + gbm_bo_get_stride (new_bo), + gbm_bo_get_offset (new_bo, 0), + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_XRGB8888, + error); + + if (!dmabuf_fb) + return NULL; + + return cogl_dma_buf_handle_new (dmabuf_fb, dmabuf_fd, new_bo, + (GDestroyNotify) gbm_bo_destroy); + } + break; +#ifdef HAVE_EGL_DEVICE + case META_RENDERER_NATIVE_MODE_EGL_DEVICE: + break; +#endif + } + + g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN, + "Current mode does not support exporting DMA buffers"); + + return NULL; +} + static gboolean meta_renderer_native_init_egl_context (CoglContext *cogl_context, GError **error) @@ -3461,6 +3528,7 @@ get_native_cogl_winsys_vtable (CoglRenderer *cogl_renderer) vtable.renderer_connect = meta_renderer_native_connect; vtable.renderer_disconnect = meta_renderer_native_disconnect; + vtable.renderer_create_dma_buf = meta_renderer_native_create_dma_buf; vtable.onscreen_init = meta_renderer_native_init_onscreen; vtable.onscreen_deinit = meta_renderer_native_release_onscreen; -- 2.26.2 From a8503b2e8b5573f9decf97264a26877adf65c4c0 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Mon, 9 Dec 2019 10:07:29 -0300 Subject: [PATCH 28/49] screen-cast-stream-src: Support DMA buffer sharing Implement PipeWire's add_buffer and remove buffer, try and export a DMA buffer first and, on failure, fallback to memfd. When DMA buffers are successfully created and shared, blit the framebuffer contents when drawing instead of downloading the pixels. https://gitlab.gnome.org/GNOME/mutter/merge_requests/1086 --- src/backends/meta-screen-cast-stream-src.c | 189 ++++++++++++++++++--- src/backends/meta-screen-cast-stream-src.h | 2 + 2 files changed, 164 insertions(+), 27 deletions(-) diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c index ba1ce94a7..0eb9f4d8c 100644 --- a/src/backends/meta-screen-cast-stream-src.c +++ b/src/backends/meta-screen-cast-stream-src.c @@ -25,6 +25,7 @@ #include "backends/meta-screen-cast-stream-src.h" #include +#include #include #include #include @@ -90,6 +91,8 @@ typedef struct _MetaScreenCastStreamSrcPrivate uint64_t last_frame_timestamp_us; + GHashTable *dmabuf_handles; + int stream_width; int stream_height; } MetaScreenCastStreamSrcPrivate; @@ -139,6 +142,19 @@ meta_screen_cast_stream_src_record_frame (MetaScreenCastStreamSrc *src, return klass->record_frame (src, data); } +static gboolean +meta_screen_cast_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc *src, + CoglFramebuffer *framebuffer) +{ + MetaScreenCastStreamSrcClass *klass = + META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src); + + if (klass->blit_to_framebuffer) + return klass->blit_to_framebuffer (src, framebuffer); + + return FALSE; +} + static void meta_screen_cast_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src, struct spa_meta_cursor *spa_meta_cursor) @@ -394,6 +410,33 @@ maybe_record_cursor (MetaScreenCastStreamSrc *src, g_assert_not_reached (); } +static gboolean +do_record_frame (MetaScreenCastStreamSrc *src, + struct spa_buffer *spa_buffer, + uint8_t *data) +{ + MetaScreenCastStreamSrcPrivate *priv = + meta_screen_cast_stream_src_get_instance_private (src); + + if (spa_buffer->datas[0].data || + spa_buffer->datas[0].type == SPA_DATA_MemFd) + { + return meta_screen_cast_stream_src_record_frame (src, data); + } + else if (spa_buffer->datas[0].type == SPA_DATA_DmaBuf) + { + CoglDmaBufHandle *dmabuf_handle = + g_hash_table_lookup (priv->dmabuf_handles, + GINT_TO_POINTER (spa_buffer->datas[0].fd)); + CoglFramebuffer *dmabuf_fbo = + cogl_dma_buf_handle_get_framebuffer (dmabuf_handle); + + return meta_screen_cast_stream_src_blit_to_framebuffer (src, dmabuf_fbo); + } + + return FALSE; +} + void meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src) { @@ -402,8 +445,7 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src) MetaRectangle crop_rect; struct pw_buffer *buffer; struct spa_buffer *spa_buffer; - uint8_t *map = NULL; - uint8_t *data; + uint8_t *data = NULL; uint64_t now_us; now_us = g_get_monotonic_time (); @@ -424,32 +466,15 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src) } spa_buffer = buffer->buffer; + data = spa_buffer->datas[0].data; - if (spa_buffer->datas[0].data) - { - data = spa_buffer->datas[0].data; - } - else if (spa_buffer->datas[0].type == SPA_DATA_MemFd) + if (spa_buffer->datas[0].type != SPA_DATA_DmaBuf && !data) { - map = mmap (NULL, spa_buffer->datas[0].maxsize + spa_buffer->datas[0].mapoffset, - PROT_READ | PROT_WRITE, MAP_SHARED, - spa_buffer->datas[0].fd, 0); - if (map == MAP_FAILED) - { - g_warning ("Failed to mmap pipewire stream buffer: %s", - strerror (errno)); - return; - } - - data = SPA_MEMBER (map, spa_buffer->datas[0].mapoffset, uint8_t); - } - else - { - g_warning ("Unhandled spa buffer type: %d", spa_buffer->datas[0].type); + g_critical ("Invalid buffer data"); return; } - if (meta_screen_cast_stream_src_record_frame (src, data)) + if (do_record_frame (src, spa_buffer, data)) { struct spa_meta_region *spa_meta_video_crop; @@ -487,9 +512,6 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src) priv->last_frame_timestamp_us = now_us; - if (map) - munmap (map, spa_buffer->datas[0].maxsize + spa_buffer->datas[0].mapoffset); - pw_stream_queue_buffer (priv->pipewire_stream, buffer); } @@ -618,10 +640,116 @@ on_stream_param_changed (void *data, pw_stream_update_params (priv->pipewire_stream, params, G_N_ELEMENTS (params)); } +static void +on_stream_add_buffer (void *data, + struct pw_buffer *buffer) +{ + MetaScreenCastStreamSrc *src = data; + MetaScreenCastStreamSrcPrivate *priv = + meta_screen_cast_stream_src_get_instance_private (src); + CoglContext *context = + clutter_backend_get_cogl_context (clutter_get_default_backend ()); + CoglRenderer *renderer = cogl_context_get_renderer (context); + g_autoptr (GError) error = NULL; + CoglDmaBufHandle *dmabuf_handle; + struct spa_buffer *spa_buffer = buffer->buffer; + struct spa_data *spa_data = spa_buffer->datas; + const int bpp = 4; + int stride; + + stride = SPA_ROUND_UP_N (priv->video_format.size.width * bpp, 4); + + spa_data[0].mapoffset = 0; + spa_data[0].maxsize = stride * priv->video_format.size.height; + + dmabuf_handle = cogl_renderer_create_dma_buf (renderer, + priv->stream_width, + priv->stream_height, + &error); + + if (error) + g_debug ("Error exporting DMA buffer handle: %s", error->message); + + if (dmabuf_handle) + { + spa_data[0].type = SPA_DATA_DmaBuf; + spa_data[0].flags = SPA_DATA_FLAG_READWRITE; + spa_data[0].fd = cogl_dma_buf_handle_get_fd (dmabuf_handle); + spa_data[0].data = NULL; + + g_hash_table_insert (priv->dmabuf_handles, + GINT_TO_POINTER (spa_data[0].fd), + dmabuf_handle); + } + else + { + unsigned int seals; + + /* Fallback to a memfd buffer */ + spa_data[0].type = SPA_DATA_MemFd; + spa_data[0].flags = SPA_DATA_FLAG_READWRITE; + spa_data[0].fd = memfd_create ("mutter-screen-cast-memfd", + MFD_CLOEXEC | MFD_ALLOW_SEALING); + if (spa_data[0].fd == -1) + { + g_critical ("Can't create memfd: %m"); + return; + } + spa_data[0].mapoffset = 0; + spa_data[0].maxsize = stride * priv->video_format.size.height; + + if (ftruncate (spa_data[0].fd, spa_data[0].maxsize) < 0) + { + g_critical ("Can't truncate to %d: %m", spa_data[0].maxsize); + return; + } + + seals = F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL; + if (fcntl (spa_data[0].fd, F_ADD_SEALS, seals) == -1) + g_warning ("Failed to add seals: %m"); + + spa_data[0].data = mmap (NULL, + spa_data[0].maxsize, + PROT_READ | PROT_WRITE, + MAP_SHARED, + spa_data[0].fd, + spa_data[0].mapoffset); + if (spa_data[0].data == MAP_FAILED) + { + g_critical ("Failed to mmap memory: %m"); + return; + } + } +} + +static void +on_stream_remove_buffer (void *data, + struct pw_buffer *buffer) +{ + MetaScreenCastStreamSrc *src = data; + MetaScreenCastStreamSrcPrivate *priv = + meta_screen_cast_stream_src_get_instance_private (src); + struct spa_buffer *spa_buffer = buffer->buffer; + struct spa_data *spa_data = spa_buffer->datas; + + if (spa_data[0].type == SPA_DATA_DmaBuf) + { + if (!g_hash_table_remove (priv->dmabuf_handles, GINT_TO_POINTER (spa_data[0].fd))) + g_critical ("Failed to remove non-exported DMA buffer"); + } + else if (spa_data[0].type == SPA_DATA_MemFd) + { + munmap (spa_data[0].data, spa_data[0].maxsize); + close (spa_data[0].fd); + } +} + static const struct pw_stream_events stream_events = { PW_VERSION_STREAM_EVENTS, .state_changed = on_stream_state_changed, .param_changed = on_stream_param_changed, + .add_buffer = on_stream_add_buffer, + .remove_buffer = on_stream_remove_buffer, }; static struct pw_stream * @@ -686,7 +814,7 @@ create_pipewire_stream (MetaScreenCastStreamSrc *src, PW_DIRECTION_OUTPUT, SPA_ID_INVALID, (PW_STREAM_FLAG_DRIVER | - PW_STREAM_FLAG_MAP_BUFFERS), + PW_STREAM_FLAG_ALLOC_BUFFERS), params, G_N_ELEMENTS (params)); if (result != 0) { @@ -854,6 +982,7 @@ meta_screen_cast_stream_src_finalize (GObject *object) if (meta_screen_cast_stream_src_is_enabled (src)) meta_screen_cast_stream_src_disable (src); + g_clear_pointer (&priv->dmabuf_handles, g_hash_table_destroy); g_clear_pointer (&priv->pipewire_stream, pw_stream_destroy); g_clear_pointer (&priv->pipewire_core, pw_core_disconnect); g_clear_pointer (&priv->pipewire_context, pw_context_destroy); @@ -905,6 +1034,12 @@ meta_screen_cast_stream_src_get_property (GObject *object, static void meta_screen_cast_stream_src_init (MetaScreenCastStreamSrc *src) { + MetaScreenCastStreamSrcPrivate *priv = + meta_screen_cast_stream_src_get_instance_private (src); + + priv->dmabuf_handles = + g_hash_table_new_full (NULL, NULL, NULL, + (GDestroyNotify) cogl_dma_buf_handle_free); } static void diff --git a/src/backends/meta-screen-cast-stream-src.h b/src/backends/meta-screen-cast-stream-src.h index fc0e5bc77..3f6a1af2b 100644 --- a/src/backends/meta-screen-cast-stream-src.h +++ b/src/backends/meta-screen-cast-stream-src.h @@ -55,6 +55,8 @@ struct _MetaScreenCastStreamSrcClass void (* disable) (MetaScreenCastStreamSrc *src); gboolean (* record_frame) (MetaScreenCastStreamSrc *src, uint8_t *data); + gboolean (* blit_to_framebuffer) (MetaScreenCastStreamSrc *src, + CoglFramebuffer *framebuffer); gboolean (* get_videocrop) (MetaScreenCastStreamSrc *src, MetaRectangle *crop_rect); void (* set_cursor_metadata) (MetaScreenCastStreamSrc *src, -- 2.26.2 From b25135cdeda1ee1aa26bb645ba981478c823d413 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Mon, 9 Dec 2019 10:11:51 -0300 Subject: [PATCH 29/49] monitor-stream-src: Implement blitting view framebuffers Add the vfunc override that actually consume the new Cogl API. Every view that fits into the logical monitor is rendered. Fixes https://gitlab.gnome.org/GNOME/mutter/issues/639 https://gitlab.gnome.org/GNOME/mutter/merge_requests/1086 --- .../meta-screen-cast-monitor-stream-src.c | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c b/src/backends/meta-screen-cast-monitor-stream-src.c index f582217e5..0c7d8ec17 100644 --- a/src/backends/meta-screen-cast-monitor-stream-src.c +++ b/src/backends/meta-screen-cast-monitor-stream-src.c @@ -367,6 +367,66 @@ meta_screen_cast_monitor_stream_src_record_frame (MetaScreenCastStreamSrc *src, return TRUE; } +static gboolean +meta_screen_cast_monitor_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc *src, + CoglFramebuffer *framebuffer) +{ + MetaScreenCastMonitorStreamSrc *monitor_src = + META_SCREEN_CAST_MONITOR_STREAM_SRC (src); + MetaBackend *backend = get_backend (monitor_src); + MetaRenderer *renderer = meta_backend_get_renderer (backend); + MetaMonitor *monitor; + MetaLogicalMonitor *logical_monitor; + MetaRectangle logical_monitor_layout; + GList *l; + float view_scale; + + monitor = get_monitor (monitor_src); + logical_monitor = meta_monitor_get_logical_monitor (monitor); + logical_monitor_layout = meta_logical_monitor_get_layout (logical_monitor); + + if (meta_is_stage_views_scaled ()) + view_scale = meta_logical_monitor_get_scale (logical_monitor); + else + view_scale = 1.0; + + for (l = meta_renderer_get_views (renderer); l; l = l->next) + { + ClutterStageView *view = CLUTTER_STAGE_VIEW (l->data); + g_autoptr (GError) error = NULL; + CoglFramebuffer *view_framebuffer; + MetaRectangle view_layout; + int x, y; + + clutter_stage_view_get_layout (view, &view_layout); + + if (!meta_rectangle_overlap (&logical_monitor_layout, &view_layout)) + continue; + + view_framebuffer = clutter_stage_view_get_framebuffer (view); + + x = (int) roundf ((view_layout.x - logical_monitor_layout.x) * view_scale); + y = (int) roundf ((view_layout.y - logical_monitor_layout.y) * view_scale); + + if (!cogl_blit_framebuffer (view_framebuffer, + framebuffer, + 0, 0, + x, y, + cogl_framebuffer_get_width (view_framebuffer), + cogl_framebuffer_get_height (view_framebuffer), + &error)) + { + g_warning ("Error blitting view into DMABuf framebuffer: %s", + error->message); + return FALSE; + } + } + + cogl_framebuffer_flush (framebuffer); + + return TRUE; +} + static void meta_screen_cast_monitor_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src, struct spa_meta_cursor *spa_meta_cursor) @@ -492,6 +552,8 @@ meta_screen_cast_monitor_stream_src_class_init (MetaScreenCastMonitorStreamSrcCl src_class->enable = meta_screen_cast_monitor_stream_src_enable; src_class->disable = meta_screen_cast_monitor_stream_src_disable; src_class->record_frame = meta_screen_cast_monitor_stream_src_record_frame; + src_class->blit_to_framebuffer = + meta_screen_cast_monitor_stream_src_blit_to_framebuffer; src_class->set_cursor_metadata = meta_screen_cast_monitor_stream_src_set_cursor_metadata; } -- 2.26.2 From 7806ab62bbf51e2ca884b17a314c74b38f3ae9c5 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Fri, 28 Feb 2020 12:23:15 -0300 Subject: [PATCH 30/49] window-stream-source: Draw into DMA buffer image Much like monitor streaming, implement window streaming by making the window actor draw itself with a paint context that used the passed framebuffer. Now that all MetaScreenCastStreamSrc subclasses implement blit_to_framebuffer, remove the conditional check from meta_screen_cast_stream_src_blit_to_framebuffer(). https://gitlab.gnome.org/GNOME/mutter/merge_requests/1086 --- src/backends/meta-screen-cast-stream-src.c | 5 +- .../meta-screen-cast-window-stream-src.c | 21 +++++++ src/backends/meta-screen-cast-window.c | 11 ++++ src/backends/meta-screen-cast-window.h | 8 +++ src/compositor/meta-window-actor.c | 63 +++++++++++++++++++ 5 files changed, 104 insertions(+), 4 deletions(-) diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c index 0eb9f4d8c..6f7551ad4 100644 --- a/src/backends/meta-screen-cast-stream-src.c +++ b/src/backends/meta-screen-cast-stream-src.c @@ -149,10 +149,7 @@ meta_screen_cast_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc *src, MetaScreenCastStreamSrcClass *klass = META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src); - if (klass->blit_to_framebuffer) - return klass->blit_to_framebuffer (src, framebuffer); - - return FALSE; + return klass->blit_to_framebuffer (src, framebuffer); } static void diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c index 210ea0807..3f9cab694 100644 --- a/src/backends/meta-screen-cast-window-stream-src.c +++ b/src/backends/meta-screen-cast-window-stream-src.c @@ -412,6 +412,25 @@ meta_screen_cast_window_stream_src_record_frame (MetaScreenCastStreamSrc *src, return TRUE; } +static gboolean +meta_screen_cast_window_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc *src, + CoglFramebuffer *framebuffer) +{ + MetaScreenCastWindowStreamSrc *window_src = + META_SCREEN_CAST_WINDOW_STREAM_SRC (src); + MetaRectangle stream_rect; + + stream_rect.x = 0; + stream_rect.y = 0; + stream_rect.width = get_stream_width (window_src); + stream_rect.height = get_stream_height (window_src); + + return + meta_screen_cast_window_blit_to_framebuffer (window_src->screen_cast_window, + &stream_rect, + framebuffer); +} + static void meta_screen_cast_window_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src, struct spa_meta_cursor *spa_meta_cursor) @@ -496,6 +515,8 @@ meta_screen_cast_window_stream_src_class_init (MetaScreenCastWindowStreamSrcClas src_class->enable = meta_screen_cast_window_stream_src_enable; src_class->disable = meta_screen_cast_window_stream_src_disable; src_class->record_frame = meta_screen_cast_window_stream_src_record_frame; + src_class->blit_to_framebuffer = + meta_screen_cast_window_stream_src_blit_to_framebuffer; src_class->get_videocrop = meta_screen_cast_window_stream_src_get_videocrop; src_class->set_cursor_metadata = meta_screen_cast_window_stream_src_set_cursor_metadata; } diff --git a/src/backends/meta-screen-cast-window.c b/src/backends/meta-screen-cast-window.c index 91515ded8..50b65a5df 100644 --- a/src/backends/meta-screen-cast-window.c +++ b/src/backends/meta-screen-cast-window.c @@ -78,6 +78,17 @@ meta_screen_cast_window_capture_into (MetaScreenCastWindow *screen_cast_window, data); } +gboolean +meta_screen_cast_window_blit_to_framebuffer (MetaScreenCastWindow *screen_cast_window, + MetaRectangle *bounds, + CoglFramebuffer *framebuffer) +{ + MetaScreenCastWindowInterface *iface = + META_SCREEN_CAST_WINDOW_GET_IFACE (screen_cast_window); + + return iface->blit_to_framebuffer (screen_cast_window, bounds, framebuffer); +} + gboolean meta_screen_cast_window_has_damage (MetaScreenCastWindow *screen_cast_window) { diff --git a/src/backends/meta-screen-cast-window.h b/src/backends/meta-screen-cast-window.h index 69e5a34dc..45b681292 100644 --- a/src/backends/meta-screen-cast-window.h +++ b/src/backends/meta-screen-cast-window.h @@ -56,6 +56,10 @@ struct _MetaScreenCastWindowInterface MetaRectangle *bounds, uint8_t *data); + gboolean (*blit_to_framebuffer) (MetaScreenCastWindow *screen_cast_window, + MetaRectangle *bounds, + CoglFramebuffer *framebuffer); + gboolean (*has_damage) (MetaScreenCastWindow *screen_cast_window); }; @@ -78,6 +82,10 @@ void meta_screen_cast_window_capture_into (MetaScreenCastWindow *screen_cast_win MetaRectangle *bounds, uint8_t *data); +gboolean meta_screen_cast_window_blit_to_framebuffer (MetaScreenCastWindow *screen_cast_window, + MetaRectangle *bounds, + CoglFramebuffer *framebuffer); + gboolean meta_screen_cast_window_has_damage (MetaScreenCastWindow *screen_cast_window); G_END_DECLS diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c index 81eb04c84..a7dc25004 100644 --- a/src/compositor/meta-window-actor.c +++ b/src/compositor/meta-window-actor.c @@ -2025,6 +2025,68 @@ meta_window_actor_capture_into (MetaScreenCastWindow *screen_cast_window, cairo_surface_destroy (image); } +static gboolean +meta_window_actor_blit_to_framebuffer (MetaScreenCastWindow *screen_cast_window, + MetaRectangle *bounds, + CoglFramebuffer *framebuffer) +{ + MetaWindowActor *window_actor = META_WINDOW_ACTOR (screen_cast_window); + ClutterActor *actor = CLUTTER_ACTOR (window_actor); + MetaRectangle scaled_clip; + CoglColor clear_color; + float resource_scale; + float width, height; + float x, y; + + if (meta_window_actor_is_destroyed (window_actor)) + return FALSE; + + clutter_actor_get_size (actor, &width, &height); + + if (width == 0 || height == 0) + return FALSE; + + if (!clutter_actor_get_resource_scale (actor, &resource_scale)) + return FALSE; + + width = ceilf (width * resource_scale); + height = ceilf (height * resource_scale); + + cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 0); + clutter_actor_get_position (actor, &x, &y); + + cogl_framebuffer_push_matrix (framebuffer); + + cogl_framebuffer_clear (framebuffer, COGL_BUFFER_BIT_COLOR, &clear_color); + cogl_framebuffer_orthographic (framebuffer, 0, 0, width, height, 0, 1.0); + cogl_framebuffer_scale (framebuffer, resource_scale, resource_scale, 1); + cogl_framebuffer_translate (framebuffer, -x, -y, 0); + + meta_rectangle_scale_double (bounds, resource_scale, + META_ROUNDING_STRATEGY_GROW, + &scaled_clip); + meta_rectangle_intersect (&scaled_clip, + &(MetaRectangle) { + .width = width, + .height = height, + }, + &scaled_clip); + + cogl_framebuffer_push_rectangle_clip (framebuffer, + scaled_clip.x, scaled_clip.y, + scaled_clip.x + scaled_clip.width, + scaled_clip.y + scaled_clip.height); + + cogl_push_framebuffer (framebuffer); + clutter_actor_paint (actor); + cogl_pop_framebuffer (); + + cogl_framebuffer_pop_clip (framebuffer); + cogl_framebuffer_pop_matrix (framebuffer); + + return TRUE; +} + static gboolean meta_window_actor_has_damage (MetaScreenCastWindow *screen_cast_window) { @@ -2038,6 +2100,7 @@ screen_cast_window_iface_init (MetaScreenCastWindowInterface *iface) iface->transform_relative_position = meta_window_actor_transform_relative_position; iface->transform_cursor_position = meta_window_actor_transform_cursor_position; iface->capture_into = meta_window_actor_capture_into; + iface->blit_to_framebuffer = meta_window_actor_blit_to_framebuffer; iface->has_damage = meta_window_actor_has_damage; } -- 2.26.2 From d005b89e4846aeaca01568865dd7d440f036c783 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Mon, 24 Feb 2020 14:14:07 -0300 Subject: [PATCH 31/49] screen-cast-stream-src: Remove unused parameter The 'data' parameter is not used in maybe_record_cursor(), so remove it. https://gitlab.gnome.org/GNOME/mutter/merge_requests/1086 --- src/backends/meta-screen-cast-stream-src.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c index 6f7551ad4..9c3657c75 100644 --- a/src/backends/meta-screen-cast-stream-src.c +++ b/src/backends/meta-screen-cast-stream-src.c @@ -389,8 +389,7 @@ add_cursor_metadata (MetaScreenCastStreamSrc *src, static void maybe_record_cursor (MetaScreenCastStreamSrc *src, - struct spa_buffer *spa_buffer, - uint8_t *data) + struct spa_buffer *spa_buffer) { MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src); @@ -505,7 +504,7 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src) spa_buffer->datas[0].chunk->size = 0; } - maybe_record_cursor (src, spa_buffer, data); + maybe_record_cursor (src, spa_buffer); priv->last_frame_timestamp_us = now_us; -- 2.26.2 From 0b8f27b4adf72ade1c8f5e958d9fcf31a9063d94 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Fri, 28 Feb 2020 15:47:23 -0300 Subject: [PATCH 32/49] monitor-stream-src: Use cogl_framebuffer_finish() Even though cogl_framebuffer_flush() was supposed to be enough, it ends up creating streams with odd visual glitches that look very much like unfinished frames. Switch back to cogl_framebuffer_finish(), which is admittedly an overkill, but it's what works for now. There is anedoctal evidence showing it doesn't incur in worse performance. https://gitlab.gnome.org/GNOME/mutter/merge_requests/1086 --- src/backends/meta-screen-cast-monitor-stream-src.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c b/src/backends/meta-screen-cast-monitor-stream-src.c index 0c7d8ec17..af763dc09 100644 --- a/src/backends/meta-screen-cast-monitor-stream-src.c +++ b/src/backends/meta-screen-cast-monitor-stream-src.c @@ -422,7 +422,7 @@ meta_screen_cast_monitor_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc } } - cogl_framebuffer_flush (framebuffer); + cogl_framebuffer_finish (framebuffer); return TRUE; } -- 2.26.2 From 1b9704405568f1a52168ce8c5267fe9a5ab1f789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 9 Mar 2020 17:43:54 +0100 Subject: [PATCH 33/49] screen-cast-stream-src: Don't complain when we can't dequeue buffer PipeWire will be unable to dequeue a buffer if all are already busy. This can happen for valid reasons, e.g. the stream consumer not being fast enough, so don't complain in the journal if it happens. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1115 --- src/backends/meta-screen-cast-stream-src.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c index 9c3657c75..170f34043 100644 --- a/src/backends/meta-screen-cast-stream-src.c +++ b/src/backends/meta-screen-cast-stream-src.c @@ -456,10 +456,7 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src) buffer = pw_stream_dequeue_buffer (priv->pipewire_stream); if (!buffer) - { - g_warning ("Failed to dequeue at PipeWire buffer"); - return; - } + return; spa_buffer = buffer->buffer; data = spa_buffer->datas[0].data; -- 2.26.2 From 99a671e71cedcb3ef3093d0afcc823bfdd8eaf79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 5 Mar 2020 23:29:26 +0100 Subject: [PATCH 34/49] screen-cast-stream-src: Don't leak GSource For every stream src, we created and attached a GSource. Upon stream src destruction, we g_source_destroy():ed the GSource. What g_source_destroy() does, hawever, is not really "destroy" it but only detaches it from the main context removing the reference the context had added for it via g_source_attach(). This caused the GSource to leak, although in a detached state, as the reference taken on creation was still held. Fix this by also removing our own reference to it when finalizing. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1106 --- src/backends/meta-screen-cast-stream-src.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c index 170f34043..4f3d821ef 100644 --- a/src/backends/meta-screen-cast-stream-src.c +++ b/src/backends/meta-screen-cast-stream-src.c @@ -885,7 +885,7 @@ create_pipewire_source (void) pipewire_source->pipewire_loop = pw_loop_new (NULL); if (!pipewire_source->pipewire_loop) { - g_source_destroy ((GSource *) pipewire_source); + g_source_unref ((GSource *) pipewire_source); return NULL; } @@ -980,6 +980,7 @@ meta_screen_cast_stream_src_finalize (GObject *object) g_clear_pointer (&priv->pipewire_core, pw_core_disconnect); g_clear_pointer (&priv->pipewire_context, pw_context_destroy); g_source_destroy (&priv->pipewire_source->base); + g_source_unref (&priv->pipewire_source->base); G_OBJECT_CLASS (meta_screen_cast_stream_src_parent_class)->finalize (object); } -- 2.26.2 From 6af35cad8bc09c291c29fffb08fe8ba3b76b779e Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Mon, 16 Mar 2020 19:47:30 -0300 Subject: [PATCH 35/49] window-actor: Shuffle some lines around Move the CoglColor assignment right above the cogl_framebuffer_clear() call, and let these wonderful partners together to delight us with an easier to read code. https://gitlab.gnome.org/GNOME/mutter/merge_requests/1129 --- src/compositor/meta-window-actor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c index a7dc25004..f90d2b0af 100644 --- a/src/compositor/meta-window-actor.c +++ b/src/compositor/meta-window-actor.c @@ -2052,11 +2052,11 @@ meta_window_actor_blit_to_framebuffer (MetaScreenCastWindow *screen_cast_window, width = ceilf (width * resource_scale); height = ceilf (height * resource_scale); - cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 0); clutter_actor_get_position (actor, &x, &y); cogl_framebuffer_push_matrix (framebuffer); + cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 0); cogl_framebuffer_clear (framebuffer, COGL_BUFFER_BIT_COLOR, &clear_color); cogl_framebuffer_orthographic (framebuffer, 0, 0, width, height, 0, 1.0); cogl_framebuffer_scale (framebuffer, resource_scale, resource_scale, 1); -- 2.26.2 From 4ecb19fffd37786d6b8efc3b05e7239bee49e763 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Mon, 16 Mar 2020 19:55:16 -0300 Subject: [PATCH 36/49] window-actor: Clip before translate when blitting cogl_framebuffer_push_rectangle_clip() acts on the current modelview matrix. That means the result of clipping then translating will be different of the result of translating then clipping. What we want for window screencasting is the former, not the latter. Move the translation code (and associated) to after clipping. Fixes: https://gitlab.gnome.org/GNOME/mutter/issues/1097 https://gitlab.gnome.org/GNOME/mutter/merge_requests/1129 --- src/compositor/meta-window-actor.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c index f90d2b0af..c9ef5846a 100644 --- a/src/compositor/meta-window-actor.c +++ b/src/compositor/meta-window-actor.c @@ -2054,13 +2054,9 @@ meta_window_actor_blit_to_framebuffer (MetaScreenCastWindow *screen_cast_window, clutter_actor_get_position (actor, &x, &y); - cogl_framebuffer_push_matrix (framebuffer); - cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 0); cogl_framebuffer_clear (framebuffer, COGL_BUFFER_BIT_COLOR, &clear_color); cogl_framebuffer_orthographic (framebuffer, 0, 0, width, height, 0, 1.0); - cogl_framebuffer_scale (framebuffer, resource_scale, resource_scale, 1); - cogl_framebuffer_translate (framebuffer, -x, -y, 0); meta_rectangle_scale_double (bounds, resource_scale, META_ROUNDING_STRATEGY_GROW, @@ -2077,12 +2073,16 @@ meta_window_actor_blit_to_framebuffer (MetaScreenCastWindow *screen_cast_window, scaled_clip.x + scaled_clip.width, scaled_clip.y + scaled_clip.height); + cogl_framebuffer_push_matrix (framebuffer); + cogl_framebuffer_scale (framebuffer, resource_scale, resource_scale, 1); + cogl_framebuffer_translate (framebuffer, -x, -y, 0); + cogl_push_framebuffer (framebuffer); clutter_actor_paint (actor); cogl_pop_framebuffer (); - cogl_framebuffer_pop_clip (framebuffer); cogl_framebuffer_pop_matrix (framebuffer); + cogl_framebuffer_pop_clip (framebuffer); return TRUE; } -- 2.26.2 From 14f76043aa912b186ecbce0e5dae46f521fec10f Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Tue, 17 Mar 2020 16:55:39 -0300 Subject: [PATCH 37/49] window-stream-src: Ensure initial frame is recorded MetaScreenCastWindowStreamSrc connects to the "damaged" signal of MetaWindowActor. This signal is not exactly tied to the paint cycle of the stage, and a damage may take quite a while to arrive when a client doesn't want to draw anything. For that reason, the window screencast can start empty, waiting for a damage to arrive. Ensure at least one frame is recorded when enabling the window stream. Fixes: https://gitlab.gnome.org/GNOME/mutter/issues/1097 https://gitlab.gnome.org/GNOME/mutter/merge_requests/1129 --- src/backends/meta-screen-cast-window-stream-src.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c index 3f9cab694..0e0907e82 100644 --- a/src/backends/meta-screen-cast-window-stream-src.c +++ b/src/backends/meta-screen-cast-window-stream-src.c @@ -389,6 +389,8 @@ meta_screen_cast_window_stream_src_enable (MetaScreenCastStreamSrc *src) case META_SCREEN_CAST_CURSOR_MODE_HIDDEN: break; } + + meta_screen_cast_stream_src_maybe_record_frame (src); } static void -- 2.26.2 From 4c4324e21cc3739c6d0a384519f8b8a47d9d8216 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Tue, 17 Mar 2020 18:22:29 -0300 Subject: [PATCH 38/49] window-stream-src: Implement cursor blitting A regression compared to the old code, we're not drawing the cursor when on EMBEDDED mode. Blit the cursor to the screencast framebuffer when on EMBEDDED mode. https://gitlab.gnome.org/GNOME/mutter/merge_requests/1129 --- .../meta-screen-cast-window-stream-src.c | 81 ++++++++++++++++++- 1 file changed, 77 insertions(+), 4 deletions(-) diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c index 0e0907e82..87505b793 100644 --- a/src/backends/meta-screen-cast-window-stream-src.c +++ b/src/backends/meta-screen-cast-window-stream-src.c @@ -178,6 +178,65 @@ maybe_draw_cursor_sprite (MetaScreenCastWindowStreamSrc *window_src, cairo_surface_destroy (cursor_surface); } +static void +maybe_blit_cursor_sprite (MetaScreenCastWindowStreamSrc *window_src, + CoglFramebuffer *framebuffer, + MetaRectangle *stream_rect) +{ + MetaBackend *backend = get_backend (window_src); + CoglContext *cogl_context = + clutter_backend_get_cogl_context (clutter_get_default_backend ()); + MetaCursorRenderer *cursor_renderer = + meta_backend_get_cursor_renderer (backend); + MetaScreenCastWindow *screen_cast_window; + MetaCursorSprite *cursor_sprite; + ClutterPoint relative_cursor_position; + ClutterPoint cursor_position; + CoglTexture *cursor_texture; + CoglPipeline *pipeline; + int width, height; + float scale; + int hotspot_x, hotspot_y; + float x, y; + + cursor_sprite = meta_cursor_renderer_get_cursor (cursor_renderer); + if (!cursor_sprite) + return; + + cursor_texture = meta_cursor_sprite_get_cogl_texture (cursor_sprite); + if (!cursor_texture) + return; + + screen_cast_window = window_src->screen_cast_window; + cursor_position = meta_cursor_renderer_get_position (cursor_renderer); + if (!meta_screen_cast_window_transform_cursor_position (screen_cast_window, + cursor_sprite, + &cursor_position, + &scale, + &relative_cursor_position)) + return; + + meta_cursor_sprite_get_hotspot (cursor_sprite, &hotspot_x, &hotspot_y); + + x = (relative_cursor_position.x - hotspot_x) * scale; + y = (relative_cursor_position.y - hotspot_y) * scale; + width = cogl_texture_get_width (cursor_texture); + height = cogl_texture_get_height (cursor_texture); + + pipeline = cogl_pipeline_new (cogl_context); + cogl_pipeline_set_layer_texture (pipeline, 0, cursor_texture); + cogl_pipeline_set_layer_filters (pipeline, 0, + COGL_PIPELINE_FILTER_LINEAR, + COGL_PIPELINE_FILTER_LINEAR); + + cogl_framebuffer_draw_rectangle (framebuffer, + pipeline, + x, y, + x + width, y + height); + + cogl_object_unref (pipeline); +} + static gboolean capture_into (MetaScreenCastWindowStreamSrc *window_src, uint8_t *data) @@ -420,6 +479,7 @@ meta_screen_cast_window_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc { MetaScreenCastWindowStreamSrc *window_src = META_SCREEN_CAST_WINDOW_STREAM_SRC (src); + MetaScreenCastStream *stream; MetaRectangle stream_rect; stream_rect.x = 0; @@ -427,10 +487,23 @@ meta_screen_cast_window_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc stream_rect.width = get_stream_width (window_src); stream_rect.height = get_stream_height (window_src); - return - meta_screen_cast_window_blit_to_framebuffer (window_src->screen_cast_window, - &stream_rect, - framebuffer); + if (!meta_screen_cast_window_blit_to_framebuffer (window_src->screen_cast_window, + &stream_rect, + framebuffer)) + return FALSE; + + stream = meta_screen_cast_stream_src_get_stream (src); + switch (meta_screen_cast_stream_get_cursor_mode (stream)) + { + case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED: + maybe_blit_cursor_sprite (window_src, framebuffer, &stream_rect); + break; + case META_SCREEN_CAST_CURSOR_MODE_METADATA: + case META_SCREEN_CAST_CURSOR_MODE_HIDDEN: + break; + } + + return TRUE; } static void -- 2.26.2 From f454e95162dd44feb1581b52521c637b378aa3bd Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Mon, 16 Mar 2020 19:31:25 -0300 Subject: [PATCH 39/49] window-stream-src: Finish framebuffer after blitting Just like what's done for monitor screencasting. Unfortunately, there's no mechanism to share fences with PipeWire clients yet, which forces us to guarantee that a frame is completed after blitting. https://gitlab.gnome.org/GNOME/mutter/merge_requests/1129 --- src/backends/meta-screen-cast-window-stream-src.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c index 87505b793..c252b4356 100644 --- a/src/backends/meta-screen-cast-window-stream-src.c +++ b/src/backends/meta-screen-cast-window-stream-src.c @@ -503,6 +503,8 @@ meta_screen_cast_window_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc break; } + cogl_framebuffer_finish (framebuffer); + return TRUE; } -- 2.26.2 From 3658b9f61921b6568913966be5bd28ceb111c23b Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Wed, 18 Mar 2020 21:12:26 -0300 Subject: [PATCH 40/49] clutter/actor: Add culling inhibiting API This will allow us to continue painting actors that are outside the visible boundaries of the stage view. https://gitlab.gnome.org/GNOME/mutter/merge_requests/1129 --- clutter/clutter/clutter-actor.c | 64 ++++++++++++++++++++++++++++++++- clutter/clutter/clutter-actor.h | 5 +++ 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c index 803f76aae..77a61ae1a 100644 --- a/clutter/clutter/clutter-actor.c +++ b/clutter/clutter/clutter-actor.c @@ -703,6 +703,7 @@ struct _ClutterActorPrivate guint8 opacity; gint opacity_override; + unsigned int inhibit_culling_counter; ClutterOffscreenRedirect offscreen_redirect; @@ -3824,6 +3825,7 @@ clutter_actor_paint (ClutterActor *self) { ClutterActorPrivate *priv; ClutterPickMode pick_mode; + gboolean culling_inhibited; gboolean clip_set = FALSE; ClutterStage *stage; @@ -3973,7 +3975,10 @@ clutter_actor_paint (ClutterActor *self) * paint then the last-paint-volume would likely represent the new * actor position not the old. */ - if (!in_clone_paint () && pick_mode == CLUTTER_PICK_NONE) + culling_inhibited = priv->inhibit_culling_counter > 0; + if (!culling_inhibited && + !in_clone_paint () && + pick_mode == CLUTTER_PICK_NONE) { gboolean success; /* annoyingly gcc warns if uninitialized even though @@ -16014,6 +16019,63 @@ clutter_actor_get_opacity_override (ClutterActor *self) return self->priv->opacity_override; } +/** + * clutter_actor_inhibit_culling: + * @actor: a #ClutterActor + * + * Increases the culling inhibitor counter. Inhibiting culling + * forces the actor to be painted even when outside the visible + * bounds of the stage view. + * + * This is usually necessary when an actor is being painted on + * another paint context. + * + * Pair with clutter_actor_uninhibit_culling() when the actor doesn't + * need to be painted anymore. + */ +void +clutter_actor_inhibit_culling (ClutterActor *actor) +{ + ClutterActorPrivate *priv; + + g_return_if_fail (CLUTTER_IS_ACTOR (actor)); + + priv = actor->priv; + + priv->inhibit_culling_counter++; + _clutter_actor_set_enable_paint_unmapped (actor, TRUE); +} + +/** + * clutter_actor_uninhibit_culling: + * @actor: a #ClutterActor + * + * Decreases the culling inhibitor counter. See clutter_actor_inhibit_culling() + * for when inhibit culling is necessary. + * + * Calling this function without a matching call to + * clutter_actor_inhibit_culling() is a programming error. + */ +void +clutter_actor_uninhibit_culling (ClutterActor *actor) +{ + ClutterActorPrivate *priv; + + g_return_if_fail (CLUTTER_IS_ACTOR (actor)); + + priv = actor->priv; + + if (priv->inhibit_culling_counter == 0) + { + g_critical ("Unpaired call to clutter_actor_uninhibit_culling"); + return; + } + + priv->inhibit_culling_counter--; + if (priv->inhibit_culling_counter == 0) + _clutter_actor_set_enable_paint_unmapped (actor, FALSE); +} + /* Allows you to disable applying the actors model view transform during * a paint. Used by ClutterClone. */ void diff --git a/clutter/clutter/clutter-actor.h b/clutter/clutter/clutter-actor.h index 7d2168af1..f6e0acbb2 100644 --- a/clutter/clutter/clutter-actor.h +++ b/clutter/clutter/clutter-actor.h @@ -870,6 +870,11 @@ void clutter_actor_set_opacity_override CLUTTER_EXPORT gint clutter_actor_get_opacity_override (ClutterActor *self); +CLUTTER_EXPORT +void clutter_actor_inhibit_culling (ClutterActor *actor); +CLUTTER_EXPORT +void clutter_actor_uninhibit_culling (ClutterActor *actor); + /** * ClutterActorCreateChildFunc: * @item: (type GObject): the item in the model -- 2.26.2 From 471c6d372bd9e1a79cf10e46fb8cbc4c110d1da6 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Wed, 18 Mar 2020 21:14:58 -0300 Subject: [PATCH 41/49] window-actor: Inhibit culling when blitting to screencast This allows us to screencast any window continuously, even without it being visible. Because it's still being painted, clients continue to receive frame callbacks, and people are happy again. https://gitlab.gnome.org/GNOME/mutter/merge_requests/1129 --- src/compositor/meta-window-actor.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c index c9ef5846a..08d1b0a87 100644 --- a/src/compositor/meta-window-actor.c +++ b/src/compositor/meta-window-actor.c @@ -2049,6 +2049,8 @@ meta_window_actor_blit_to_framebuffer (MetaScreenCastWindow *screen_cast_window, if (!clutter_actor_get_resource_scale (actor, &resource_scale)) return FALSE; + clutter_actor_inhibit_culling (actor); + width = ceilf (width * resource_scale); height = ceilf (height * resource_scale); @@ -2084,6 +2086,8 @@ meta_window_actor_blit_to_framebuffer (MetaScreenCastWindow *screen_cast_window, cogl_framebuffer_pop_matrix (framebuffer); cogl_framebuffer_pop_clip (framebuffer); + clutter_actor_uninhibit_culling (actor); + return TRUE; } @@ -2151,33 +2155,36 @@ meta_window_actor_get_image (MetaWindowActor *self, CoglColor clear_color; float x, y; MetaRectangle scaled_clip; - cairo_surface_t *surface; + cairo_surface_t *surface = NULL; if (!priv->surface) return NULL; + clutter_actor_inhibit_culling (actor); + if (clutter_actor_get_n_children (actor) == 1) { MetaShapedTexture *stex; stex = meta_surface_actor_get_texture (priv->surface); - return meta_shaped_texture_get_image (stex, clip); + surface = meta_shaped_texture_get_image (stex, clip); + goto out; } clutter_actor_get_size (actor, &width, &height); if (width == 0 || height == 0) - return NULL; + goto out; if (!clutter_actor_get_resource_scale (actor, &resource_scale)) - return NULL; + goto out; width = ceilf (width * resource_scale); height = ceilf (height * resource_scale); texture = cogl_texture_2d_new_with_size (cogl_context, width, height); if (!texture) - return NULL; + goto out; cogl_primitive_texture_set_auto_mipmap (COGL_PRIMITIVE_TEXTURE (texture), FALSE); @@ -2193,7 +2200,7 @@ meta_window_actor_get_image (MetaWindowActor *self, error->message); cogl_object_unref (framebuffer); cogl_object_unref (texture); - return NULL; + goto out; } cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 0); @@ -2242,5 +2249,7 @@ meta_window_actor_get_image (MetaWindowActor *self, cairo_surface_mark_dirty (surface); +out: + clutter_actor_uninhibit_culling (actor); return surface; } -- 2.26.2 From f29763e93ddf7933bd5ea3d2cd5769683b9e312b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 18 May 2020 17:52:09 +0200 Subject: [PATCH 42/49] screen-cast/monitor-src: Get views from the stage Otherwise we don't get the views when in the X11 session. --- clutter/clutter/clutter-mutter.h | 3 +++ clutter/clutter/clutter-stage-private.h | 2 -- src/backends/meta-screen-cast-monitor-stream-src.c | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/clutter/clutter/clutter-mutter.h b/clutter/clutter/clutter-mutter.h index a53080457..6137605d4 100644 --- a/clutter/clutter/clutter-mutter.h +++ b/clutter/clutter/clutter-mutter.h @@ -55,6 +55,9 @@ void clutter_stage_update_resource_scales (ClutterStage *stage); CLUTTER_EXPORT gboolean clutter_actor_has_damage (ClutterActor *actor); +CLUTTER_EXPORT +GList *_clutter_stage_peek_stage_views (ClutterStage *stage); + #undef __CLUTTER_H_INSIDE__ #endif /* __CLUTTER_MUTTER_H__ */ diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h index df0bf642b..42474687a 100644 --- a/clutter/clutter/clutter-stage-private.h +++ b/clutter/clutter/clutter-stage-private.h @@ -133,8 +133,6 @@ void _clutter_stage_presented (ClutterStage *stag CoglFrameEvent frame_event, ClutterFrameInfo *frame_info); -GList * _clutter_stage_peek_stage_views (ClutterStage *stage); - G_END_DECLS #endif /* __CLUTTER_STAGE_PRIVATE_H__ */ diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c b/src/backends/meta-screen-cast-monitor-stream-src.c index af763dc09..655b68261 100644 --- a/src/backends/meta-screen-cast-monitor-stream-src.c +++ b/src/backends/meta-screen-cast-monitor-stream-src.c @@ -374,7 +374,7 @@ meta_screen_cast_monitor_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc MetaScreenCastMonitorStreamSrc *monitor_src = META_SCREEN_CAST_MONITOR_STREAM_SRC (src); MetaBackend *backend = get_backend (monitor_src); - MetaRenderer *renderer = meta_backend_get_renderer (backend); + ClutterStage *stage = CLUTTER_STAGE (meta_backend_get_stage (backend)); MetaMonitor *monitor; MetaLogicalMonitor *logical_monitor; MetaRectangle logical_monitor_layout; @@ -390,7 +390,7 @@ meta_screen_cast_monitor_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc else view_scale = 1.0; - for (l = meta_renderer_get_views (renderer); l; l = l->next) + for (l = _clutter_stage_peek_stage_views (stage); l; l = l->next) { ClutterStageView *view = CLUTTER_STAGE_VIEW (l->data); g_autoptr (GError) error = NULL; -- 2.26.2 From d6d401f74452e7eb2f2ac0272b30603782ea95c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Tue, 12 May 2020 08:52:01 +0200 Subject: [PATCH 43/49] screen-cast-stream-src: Don't throttle if max framerate is 1/0 The max framerate 1/0 means variable without any particular max, so don't throttle if that was set. Not doing this would end up with a floating point exception. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1251 --- src/backends/meta-screen-cast-stream-src.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c index 4f3d821ef..ad0d9ed79 100644 --- a/src/backends/meta-screen-cast-stream-src.c +++ b/src/backends/meta-screen-cast-stream-src.c @@ -445,7 +445,8 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src) uint64_t now_us; now_us = g_get_monotonic_time (); - if (priv->last_frame_timestamp_us != 0 && + if (priv->video_format.max_framerate.num > 0 && + priv->last_frame_timestamp_us != 0 && (now_us - priv->last_frame_timestamp_us < ((1000000 * priv->video_format.max_framerate.denom) / priv->video_format.max_framerate.num))) -- 2.26.2 From 0e00b9b3516362009c671cfd62dd32dc9691e647 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Tue, 12 May 2020 16:14:00 +0200 Subject: [PATCH 44/49] screen-cast-src: Notify that about the stream being closed on idle We're iterating inside the PipeWire loop when detecting PipeWire errors, and shouldn't destroy the PipeWire objects mid-iteration. Avoid this by first disabling the stream src (effectively stopping the recording), then notifying about it being closed in an idle callback. The notification eventually makes the rest of the screen cast code clean up the objects, including the src and the associated PipeWire objects, but will do so outside the PipeWire loop iteration. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1251 --- src/backends/meta-screen-cast-stream-src.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c index ad0d9ed79..0500bfec5 100644 --- a/src/backends/meta-screen-cast-stream-src.c +++ b/src/backends/meta-screen-cast-stream-src.c @@ -540,10 +540,16 @@ meta_screen_cast_stream_src_disable (MetaScreenCastStreamSrc *src) priv->is_enabled = FALSE; } -static void -meta_screen_cast_stream_src_notify_closed (MetaScreenCastStreamSrc *src) +static gboolean +notify_stream_src_closed_idle (gpointer user_data) { + MetaScreenCastStreamSrc *src = user_data; + g_signal_emit (src, signals[CLOSED], 0); + + g_object_unref (src); + + return G_SOURCE_REMOVE; } static void @@ -560,7 +566,9 @@ on_stream_state_changed (void *data, { case PW_STREAM_STATE_ERROR: g_warning ("pipewire stream error: %s", error_message); - meta_screen_cast_stream_src_notify_closed (src); + if (meta_screen_cast_stream_src_is_enabled (src)) + meta_screen_cast_stream_src_disable (src); + g_idle_add (notify_stream_src_closed_idle, g_object_ref (src)); break; case PW_STREAM_STATE_PAUSED: if (priv->node_id == SPA_ID_INVALID && priv->pipewire_stream) @@ -832,7 +840,11 @@ on_core_error (void *data, g_warning ("pipewire remote error: id:%u %s", id, message); if (id == PW_ID_CORE && res == -EPIPE) - meta_screen_cast_stream_src_notify_closed (src); + { + if (meta_screen_cast_stream_src_is_enabled (src)) + meta_screen_cast_stream_src_disable (src); + g_idle_add (notify_stream_src_closed_idle, g_object_ref (src)); + } } static gboolean -- 2.26.2 From 788a6f4adea0f976598014744149fb78feac145d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 18 May 2020 19:10:49 +0200 Subject: [PATCH 45/49] compositor: Only check for stereo when using GLX If EGL Xlib is used, we'll get bogus return value and crash. --- src/compositor/compositor.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c index 6c08c8fe4..a6ae55abb 100644 --- a/src/compositor/compositor.c +++ b/src/compositor/compositor.c @@ -534,9 +534,17 @@ typedef struct { static gboolean display_has_stereo_tree_ext (MetaX11Display *x11_display) { + MetaBackend *backend = meta_get_backend (); + ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); + CoglContext *cogl_context = + clutter_backend_get_cogl_context (clutter_backend); + CoglRenderer *cogl_renderer = cogl_context_get_renderer (cogl_context); Display *xdisplay = x11_display->xdisplay; const char *extensions_string; + if (cogl_renderer_get_winsys_id (cogl_renderer) != COGL_WINSYS_ID_GLX) + return FALSE; + static const char * (*query_extensions_string) (Display *display, int screen); -- 2.26.2 From 1aa9d081b69dc8ce7948a2189bd6ca0dd4c9611b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Fri, 3 Apr 2020 17:12:58 +0200 Subject: [PATCH 46/49] window-actor: Set viewport when blitting to screencast fb This fixes an issue where a non-maximized screen casted window would be stretched to fill the whole screen cast stream, instead of just the crop that corresponds to the current window size. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1174 --- src/compositor/meta-window-actor.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c index 08d1b0a87..1167acfa5 100644 --- a/src/compositor/meta-window-actor.c +++ b/src/compositor/meta-window-actor.c @@ -2059,6 +2059,7 @@ meta_window_actor_blit_to_framebuffer (MetaScreenCastWindow *screen_cast_window, cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 0); cogl_framebuffer_clear (framebuffer, COGL_BUFFER_BIT_COLOR, &clear_color); cogl_framebuffer_orthographic (framebuffer, 0, 0, width, height, 0, 1.0); + cogl_framebuffer_set_viewport (framebuffer, 0, 0, width, height); meta_rectangle_scale_double (bounds, resource_scale, META_ROUNDING_STRATEGY_GROW, -- 2.26.2 From 289cad91ac820b7280485603865d3060b5f84cf8 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Sat, 16 May 2020 10:44:04 +0200 Subject: [PATCH 47/49] backends: Ensure remote desktop dbus interface state Ensure that it does receive Start and Stop orderly, and error out otherwise. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1258 --- src/backends/meta-remote-desktop-session.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/backends/meta-remote-desktop-session.c b/src/backends/meta-remote-desktop-session.c index f069cba2f..8d53dc41c 100644 --- a/src/backends/meta-remote-desktop-session.c +++ b/src/backends/meta-remote-desktop-session.c @@ -56,6 +56,7 @@ struct _MetaRemoteDesktopSession MetaScreenCastSession *screen_cast_session; gulong screen_cast_session_closed_handler_id; + guint started : 1; ClutterVirtualInputDevice *virtual_pointer; ClutterVirtualInputDevice *virtual_keyboard; @@ -120,7 +121,7 @@ meta_remote_desktop_session_start (MetaRemoteDesktopSession *session, ClutterDeviceManager *device_manager = clutter_device_manager_get_default (); - g_assert (!session->virtual_pointer && !session->virtual_keyboard); + g_assert (!session->started); if (session->screen_cast_session) { @@ -140,6 +141,7 @@ meta_remote_desktop_session_start (MetaRemoteDesktopSession *session, CLUTTER_TOUCHSCREEN_DEVICE); init_remote_access_handle (session); + session->started = TRUE; return TRUE; } @@ -150,6 +152,8 @@ meta_remote_desktop_session_close (MetaRemoteDesktopSession *session) MetaDBusRemoteDesktopSession *skeleton = META_DBUS_REMOTE_DESKTOP_SESSION (session); + session->started = FALSE; + if (session->screen_cast_session) { g_signal_handler_disconnect (session->screen_cast_session, @@ -261,6 +265,14 @@ handle_start (MetaDBusRemoteDesktopSession *skeleton, MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton); GError *error = NULL; + if (session->started) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, + "Already started"); + return TRUE; + } + if (!check_permission (session, invocation)) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, @@ -293,6 +305,14 @@ handle_stop (MetaDBusRemoteDesktopSession *skeleton, { MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton); + if (!session->started) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, + "Session not started"); + return TRUE; + } + if (!check_permission (session, invocation)) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, -- 2.26.2 From ccbfc529a11e52a27e8d6751753e82e55319e3c8 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Sat, 16 May 2020 10:46:57 +0200 Subject: [PATCH 48/49] backends: Make uniform checks on remote desktop input dbus methods They all checked that the remote session service talked with the correct peer, and some of them did check that there is an associated screencast session. Add a new check for the session being started (as it's state is decoupled with screencast session availability) and move all checks to a function that is called from all input-oriented DBus methods. Fixes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1254 https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1258 --- src/backends/meta-remote-desktop-session.c | 145 ++++++++------------- 1 file changed, 51 insertions(+), 94 deletions(-) diff --git a/src/backends/meta-remote-desktop-session.c b/src/backends/meta-remote-desktop-session.c index 8d53dc41c..59057fc70 100644 --- a/src/backends/meta-remote-desktop-session.c +++ b/src/backends/meta-remote-desktop-session.c @@ -258,6 +258,37 @@ check_permission (MetaRemoteDesktopSession *session, g_dbus_method_invocation_get_sender (invocation)) == 0; } +static gboolean +meta_remote_desktop_session_check_can_notify (MetaRemoteDesktopSession *session, + GDBusMethodInvocation *invocation) +{ + if (!session->started) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, + "Session not started"); + return FALSE; + } + + if (!check_permission (session, invocation)) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_ACCESS_DENIED, + "Permission denied"); + return FALSE; + } + + if (!session->screen_cast_session) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, + "No screen cast active"); + return FALSE; + } + + return TRUE; +} + static gboolean handle_start (MetaDBusRemoteDesktopSession *skeleton, GDBusMethodInvocation *invocation) @@ -337,13 +368,8 @@ handle_notify_keyboard_keycode (MetaDBusRemoteDesktopSession *skeleton, MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton); ClutterKeyState state; - if (!check_permission (session, invocation)) - { - g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, - G_DBUS_ERROR_ACCESS_DENIED, - "Permission denied"); - return TRUE; - } + if (!meta_remote_desktop_session_check_can_notify (session, invocation)) + return TRUE; if (pressed) state = CLUTTER_KEY_STATE_PRESSED; @@ -369,13 +395,8 @@ handle_notify_keyboard_keysym (MetaDBusRemoteDesktopSession *skeleton, MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton); ClutterKeyState state; - if (!check_permission (session, invocation)) - { - g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, - G_DBUS_ERROR_ACCESS_DENIED, - "Permission denied"); - return TRUE; - } + if (!meta_remote_desktop_session_check_can_notify (session, invocation)) + return TRUE; if (pressed) state = CLUTTER_KEY_STATE_PRESSED; @@ -423,13 +444,8 @@ handle_notify_pointer_button (MetaDBusRemoteDesktopSession *skeleton, uint32_t button; ClutterButtonState state; - if (!check_permission (session, invocation)) - { - g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, - G_DBUS_ERROR_ACCESS_DENIED, - "Permission denied"); - return TRUE; - } + if (!meta_remote_desktop_session_check_can_notify (session, invocation)) + return TRUE; button = translate_to_clutter_button (button_code); @@ -459,13 +475,8 @@ handle_notify_pointer_axis (MetaDBusRemoteDesktopSession *skeleton, MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton); ClutterScrollFinishFlags finish_flags = CLUTTER_SCROLL_FINISHED_NONE; - if (!check_permission (session, invocation)) - { - g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, - G_DBUS_ERROR_ACCESS_DENIED, - "Permission denied"); - return TRUE; - } + if (!meta_remote_desktop_session_check_can_notify (session, invocation)) + return TRUE; if (flags & META_REMOTE_DESKTOP_NOTIFY_AXIS_FLAGS_FINISH) { @@ -512,13 +523,8 @@ handle_notify_pointer_axis_discrete (MetaDBusRemoteDesktopSession *skeleton, ClutterScrollDirection direction; int step_count; - if (!check_permission (session, invocation)) - { - g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, - G_DBUS_ERROR_ACCESS_DENIED, - "Permission denied"); - return TRUE; - } + if (!meta_remote_desktop_session_check_can_notify (session, invocation)) + return TRUE; if (axis > 1) { @@ -563,13 +569,8 @@ handle_notify_pointer_motion_relative (MetaDBusRemoteDesktopSession *skeleton, { MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton); - if (!check_permission (session, invocation)) - { - g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, - G_DBUS_ERROR_ACCESS_DENIED, - "Permission denied"); - return TRUE; - } + if (!meta_remote_desktop_session_check_can_notify (session, invocation)) + return TRUE; clutter_virtual_input_device_notify_relative_motion (session->virtual_pointer, CLUTTER_CURRENT_TIME, @@ -592,21 +593,8 @@ handle_notify_pointer_motion_absolute (MetaDBusRemoteDesktopSession *skeleton, MetaScreenCastStream *stream; double abs_x, abs_y; - if (!check_permission (session, invocation)) - { - g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, - G_DBUS_ERROR_ACCESS_DENIED, - "Permission denied"); - return TRUE; - } - - if (!session->screen_cast_session) - { - g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, - G_DBUS_ERROR_FAILED, - "No screen cast active"); - return TRUE; - } + if (!meta_remote_desktop_session_check_can_notify (session, invocation)) + return TRUE; stream = meta_screen_cast_session_get_stream (session->screen_cast_session, stream_path); @@ -642,21 +630,8 @@ handle_notify_touch_down (MetaDBusRemoteDesktopSession *skeleton, MetaScreenCastStream *stream; double abs_x, abs_y; - if (!check_permission (session, invocation)) - { - g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, - G_DBUS_ERROR_ACCESS_DENIED, - "Permission denied"); - return TRUE; - } - - if (!session->screen_cast_session) - { - g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, - G_DBUS_ERROR_FAILED, - "No screen cast active"); - return TRUE; - } + if (!meta_remote_desktop_session_check_can_notify (session, invocation)) + return TRUE; stream = meta_screen_cast_session_get_stream (session->screen_cast_session, stream_path); @@ -693,21 +668,8 @@ handle_notify_touch_motion (MetaDBusRemoteDesktopSession *skeleton, MetaScreenCastStream *stream; double abs_x, abs_y; - if (!check_permission (session, invocation)) - { - g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, - G_DBUS_ERROR_ACCESS_DENIED, - "Permission denied"); - return TRUE; - } - - if (!session->screen_cast_session) - { - g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, - G_DBUS_ERROR_FAILED, - "No screen cast active"); - return TRUE; - } + if (!meta_remote_desktop_session_check_can_notify (session, invocation)) + return TRUE; stream = meta_screen_cast_session_get_stream (session->screen_cast_session, stream_path); @@ -739,13 +701,8 @@ handle_notify_touch_up (MetaDBusRemoteDesktopSession *skeleton, { MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton); - if (!check_permission (session, invocation)) - { - g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, - G_DBUS_ERROR_ACCESS_DENIED, - "Permission denied"); - return TRUE; - } + if (!meta_remote_desktop_session_check_can_notify (session, invocation)) + return TRUE; clutter_virtual_input_device_notify_touch_up (session->virtual_touchscreen, CLUTTER_CURRENT_TIME, -- 2.26.2 From 519c86b833f57286e51f2a1514003e9e3461bd7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Tue, 21 Apr 2020 15:44:32 +0200 Subject: [PATCH 49/49] remote-access-controller: Allow inhibiting remote access Inhibiting remote access means any current remote access session is terminated, and no new ones can be created, until remote access is uninhibited. The inhibitation is ref counted, meaning there can be more than one inhibitor. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1212 (cherry picked from commit 4300f1f91d726051893146d7b294d8852782f137) --- src/backends/meta-backend-types.h | 1 + src/backends/meta-backend.c | 4 +- .../meta-remote-access-controller-private.h | 4 ++ src/backends/meta-remote-access-controller.c | 49 +++++++++++++++++++ src/backends/meta-remote-desktop.c | 39 +++++++++++++++ src/backends/meta-remote-desktop.h | 4 ++ src/backends/meta-screen-cast.c | 34 +++++++++++++ src/backends/meta-screen-cast.h | 4 ++ src/meta/meta-remote-access-controller.h | 6 +++ 9 files changed, 143 insertions(+), 2 deletions(-) diff --git a/src/backends/meta-backend-types.h b/src/backends/meta-backend-types.h index eb982d73e..98cac8b9e 100644 --- a/src/backends/meta-backend-types.h +++ b/src/backends/meta-backend-types.h @@ -49,6 +49,7 @@ typedef struct _MetaTileInfo MetaTileInfo; typedef struct _MetaRenderer MetaRenderer; typedef struct _MetaRendererView MetaRendererView; +typedef struct _MetaRemoteDesktop MetaRemoteDesktop; typedef struct _MetaScreenCast MetaScreenCast; typedef struct _MetaScreenCastSession MetaScreenCastSession; typedef struct _MetaScreenCastStream MetaScreenCastStream; diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c index 72cfbdaf3..750a9248a 100644 --- a/src/backends/meta-backend.c +++ b/src/backends/meta-backend.c @@ -501,12 +501,12 @@ meta_backend_real_post_init (MetaBackend *backend) priv->input_settings = meta_backend_create_input_settings (backend); #ifdef HAVE_REMOTE_DESKTOP - priv->remote_access_controller = - g_object_new (META_TYPE_REMOTE_ACCESS_CONTROLLER, NULL); priv->dbus_session_watcher = g_object_new (META_TYPE_DBUS_SESSION_WATCHER, NULL); priv->screen_cast = meta_screen_cast_new (backend, priv->dbus_session_watcher); priv->remote_desktop = meta_remote_desktop_new (priv->dbus_session_watcher); + priv->remote_access_controller = + meta_remote_access_controller_new (priv->remote_desktop, priv->screen_cast); #endif /* HAVE_REMOTE_DESKTOP */ if (!meta_monitor_manager_is_headless (priv->monitor_manager)) diff --git a/src/backends/meta-remote-access-controller-private.h b/src/backends/meta-remote-access-controller-private.h index fce2082bf..444b71a77 100644 --- a/src/backends/meta-remote-access-controller-private.h +++ b/src/backends/meta-remote-access-controller-private.h @@ -21,8 +21,12 @@ #ifndef META_REMOTE_ACCESS_CONTROLLER_PRIVATE_H #define META_REMOTE_ACCESS_CONTROLLER_PRIVATE_H +#include "backends/meta-backend-types.h" #include "meta/meta-remote-access-controller.h" +MetaRemoteAccessController * meta_remote_access_controller_new (MetaRemoteDesktop *remote_desktop, + MetaScreenCast *screen_cast); + void meta_remote_access_controller_notify_new_handle (MetaRemoteAccessController *controller, MetaRemoteAccessHandle *handle); diff --git a/src/backends/meta-remote-access-controller.c b/src/backends/meta-remote-access-controller.c index 0e0ebe2bd..e5ae0b5bd 100644 --- a/src/backends/meta-remote-access-controller.c +++ b/src/backends/meta-remote-access-controller.c @@ -22,6 +22,9 @@ #include "backends/meta-remote-access-controller-private.h" +#include "backends/meta-remote-desktop.h" +#include "backends/meta-screen-cast.h" + enum { HANDLE_STOPPED, @@ -52,6 +55,9 @@ G_DEFINE_TYPE_WITH_PRIVATE (MetaRemoteAccessHandle, struct _MetaRemoteAccessController { GObject parent; + + MetaRemoteDesktop *remote_desktop; + MetaScreenCast *screen_cast; }; G_DEFINE_TYPE (MetaRemoteAccessController, @@ -94,6 +100,49 @@ meta_remote_access_controller_notify_new_handle (MetaRemoteAccessController *con handle); } +/** + * meta_remote_access_controller_inhibit_remote_access: + * @controller: a #MetaRemoteAccessController + * + * Inhibits remote access sessions from being created and running. Any active + * remote access session will be terminated. + */ +void +meta_remote_access_controller_inhibit_remote_access (MetaRemoteAccessController *controller) +{ + meta_remote_desktop_inhibit (controller->remote_desktop); + meta_screen_cast_inhibit (controller->screen_cast); +} + +/** + * meta_remote_access_controller_uninhibit_remote_access: + * @controller: a #MetaRemoteAccessController + * + * Uninhibits remote access sessions from being created and running. If this was + * the last inhibitation that was inhibited, new remote access sessions can now + * be created. + */ +void +meta_remote_access_controller_uninhibit_remote_access (MetaRemoteAccessController *controller) +{ + meta_screen_cast_uninhibit (controller->screen_cast); + meta_remote_desktop_uninhibit (controller->remote_desktop); +} + +MetaRemoteAccessController * +meta_remote_access_controller_new (MetaRemoteDesktop *remote_desktop, + MetaScreenCast *screen_cast) +{ + MetaRemoteAccessController *remote_access_controller; + + remote_access_controller = g_object_new (META_TYPE_REMOTE_ACCESS_CONTROLLER, + NULL); + remote_access_controller->remote_desktop = remote_desktop; + remote_access_controller->screen_cast = screen_cast; + + return remote_access_controller; +} + static void meta_remote_access_handle_init (MetaRemoteAccessHandle *handle) { diff --git a/src/backends/meta-remote-desktop.c b/src/backends/meta-remote-desktop.c index d741dccd8..6d87d755b 100644 --- a/src/backends/meta-remote-desktop.c +++ b/src/backends/meta-remote-desktop.c @@ -56,6 +56,8 @@ struct _MetaRemoteDesktop int dbus_name_id; + int inhibit_count; + GHashTable *sessions; MetaDbusSessionWatcher *session_watcher; @@ -70,6 +72,34 @@ G_DEFINE_TYPE_WITH_CODE (MetaRemoteDesktop, G_IMPLEMENT_INTERFACE (META_DBUS_TYPE_REMOTE_DESKTOP, meta_remote_desktop_init_iface)); +void +meta_remote_desktop_inhibit (MetaRemoteDesktop *remote_desktop) +{ + remote_desktop->inhibit_count++; + if (remote_desktop->inhibit_count == 1) + { + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init (&iter, remote_desktop->sessions); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + MetaRemoteDesktopSession *session = value; + + g_hash_table_iter_steal (&iter); + meta_remote_desktop_session_close (session); + } + } +} + +void +meta_remote_desktop_uninhibit (MetaRemoteDesktop *remote_desktop) +{ + g_return_if_fail (remote_desktop->inhibit_count > 0); + + remote_desktop->inhibit_count--; +} + GDBusConnection * meta_remote_desktop_get_connection (MetaRemoteDesktop *remote_desktop) { @@ -108,6 +138,15 @@ handle_create_session (MetaDBusRemoteDesktop *skeleton, char *session_path; const char *client_dbus_name; + if (remote_desktop->inhibit_count > 0) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_ACCESS_DENIED, + "Session creation inhibited"); + + return TRUE; + } + peer_name = g_dbus_method_invocation_get_sender (invocation); session = meta_remote_desktop_session_new (remote_desktop, peer_name, diff --git a/src/backends/meta-remote-desktop.h b/src/backends/meta-remote-desktop.h index 3eebc13d5..210a84a04 100644 --- a/src/backends/meta-remote-desktop.h +++ b/src/backends/meta-remote-desktop.h @@ -36,6 +36,10 @@ G_DECLARE_FINAL_TYPE (MetaRemoteDesktop, meta_remote_desktop, META, REMOTE_DESKTOP, MetaDBusRemoteDesktopSkeleton) +void meta_remote_desktop_inhibit (MetaRemoteDesktop *remote_desktop); + +void meta_remote_desktop_uninhibit (MetaRemoteDesktop *remote_desktop); + MetaRemoteDesktopSession * meta_remote_desktop_get_session (MetaRemoteDesktop *remote_desktop, const char *session_id); diff --git a/src/backends/meta-screen-cast.c b/src/backends/meta-screen-cast.c index 063fffd8e..46bc26838 100644 --- a/src/backends/meta-screen-cast.c +++ b/src/backends/meta-screen-cast.c @@ -40,6 +40,8 @@ struct _MetaScreenCast int dbus_name_id; + int inhibit_count; + GList *sessions; MetaDbusSessionWatcher *session_watcher; @@ -54,6 +56,29 @@ G_DEFINE_TYPE_WITH_CODE (MetaScreenCast, meta_screen_cast, G_IMPLEMENT_INTERFACE (META_DBUS_TYPE_SCREEN_CAST, meta_screen_cast_init_iface)) +void +meta_screen_cast_inhibit (MetaScreenCast *screen_cast) +{ + screen_cast->inhibit_count++; + if (screen_cast->inhibit_count == 1) + { + while (screen_cast->sessions) + { + MetaScreenCastSession *session = screen_cast->sessions->data; + + meta_screen_cast_session_close (session); + } + } +} + +void +meta_screen_cast_uninhibit (MetaScreenCast *screen_cast) +{ + g_return_if_fail (screen_cast->inhibit_count > 0); + + screen_cast->inhibit_count--; +} + GDBusConnection * meta_screen_cast_get_connection (MetaScreenCast *screen_cast) { @@ -118,6 +143,15 @@ handle_create_session (MetaDBusScreenCast *skeleton, char *remote_desktop_session_id = NULL; MetaScreenCastSessionType session_type; + if (screen_cast->inhibit_count > 0) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_ACCESS_DENIED, + "Session creation inhibited"); + + return TRUE; + } + g_variant_lookup (properties, "remote-desktop-session-id", "s", &remote_desktop_session_id); diff --git a/src/backends/meta-screen-cast.h b/src/backends/meta-screen-cast.h index 994c40c53..a3b650cd8 100644 --- a/src/backends/meta-screen-cast.h +++ b/src/backends/meta-screen-cast.h @@ -42,6 +42,10 @@ G_DECLARE_FINAL_TYPE (MetaScreenCast, meta_screen_cast, META, SCREEN_CAST, MetaDBusScreenCastSkeleton) +void meta_screen_cast_inhibit (MetaScreenCast *screen_cast); + +void meta_screen_cast_uninhibit (MetaScreenCast *screen_cast); + GDBusConnection * meta_screen_cast_get_connection (MetaScreenCast *screen_cast); MetaBackend * meta_screen_cast_get_backend (MetaScreenCast *screen_cast); diff --git a/src/meta/meta-remote-access-controller.h b/src/meta/meta-remote-access-controller.h index e7c707bbc..56f0dcbe2 100644 --- a/src/meta/meta-remote-access-controller.h +++ b/src/meta/meta-remote-access-controller.h @@ -51,4 +51,10 @@ G_DECLARE_FINAL_TYPE (MetaRemoteAccessController, META, REMOTE_ACCESS_CONTROLLER, GObject) +META_EXPORT +void meta_remote_access_controller_inhibit_remote_access (MetaRemoteAccessController *controller); + +META_EXPORT +void meta_remote_access_controller_uninhibit_remote_access (MetaRemoteAccessController *controller); + #endif /* META_REMOTE_ACCESS_CONTROLLER_H */ -- 2.26.2