diff --git a/SOURCES/0001-Add-support-for-quad-buffer-stereo.patch b/SOURCES/0001-Add-support-for-quad-buffer-stereo.patch
index 38a1213..7d150d2 100644
--- a/SOURCES/0001-Add-support-for-quad-buffer-stereo.patch
+++ b/SOURCES/0001-Add-support-for-quad-buffer-stereo.patch
@@ -1,4 +1,4 @@
-From 8d7356fd7439f94f163438d55f2b2d3d918de96d Mon Sep 17 00:00:00 2001
+From 6108e0175932f74733c46ebe13db06998f26d4ba Mon Sep 17 00:00:00 2001
 From: "Owen W. Taylor" <otaylor@fishsoup.net>
 Date: Thu, 8 May 2014 18:44:15 -0400
 Subject: [PATCH] Add support for quad-buffer stereo
@@ -21,7 +21,7 @@ texture_from_pixmap.
  src/compositor/compositor-private.h          |   9 ++
  src/compositor/compositor.c                  | 125 +++++++++++++++
  src/compositor/meta-shaped-texture-private.h |   5 +-
- src/compositor/meta-shaped-texture.c         |  84 +++++++++-
+ src/compositor/meta-shaped-texture.c         |  85 +++++++++-
  src/compositor/meta-surface-actor-wayland.c  |   2 +-
  src/compositor/meta-surface-actor-x11.c      |  54 ++++++-
  src/compositor/meta-surface-actor-x11.h      |   5 +
@@ -32,7 +32,7 @@ texture_from_pixmap.
  src/core/stereo.h                            |  28 ++++
  src/meson.build                              |   2 +
  src/wayland/meta-wayland-surface.c           |   2 +-
- 14 files changed, 481 insertions(+), 20 deletions(-)
+ 14 files changed, 482 insertions(+), 20 deletions(-)
  create mode 100644 src/core/stereo.c
  create mode 100644 src/core/stereo.h
 
@@ -265,7 +265,7 @@ index a86a2bff0..d0efdd4dc 100644
                                              gboolean           is_y_inverted);
  void meta_shaped_texture_set_snippet (MetaShapedTexture *stex,
 diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c
-index d64e214e5..e77a32109 100644
+index d64e214e5..332b4c814 100644
 --- a/src/compositor/meta-shaped-texture.c
 +++ b/src/compositor/meta-shaped-texture.c
 @@ -88,8 +88,10 @@ struct _MetaShapedTexture
@@ -287,7 +287,7 @@ index d64e214e5..e77a32109 100644
  
    stex->texture = NULL;
    stex->mask_texture = NULL;
-@@ -297,6 +300,9 @@ meta_shaped_texture_dispose (GObject *object)
+@@ -297,7 +300,11 @@ meta_shaped_texture_dispose (GObject *object)
      meta_texture_tower_free (stex->paint_tower);
    stex->paint_tower = NULL;
  
@@ -295,9 +295,11 @@ index d64e214e5..e77a32109 100644
 +  g_clear_pointer (&stex->paint_tower_right, meta_texture_tower_free);
 +
    g_clear_pointer (&stex->texture, cogl_object_unref);
++  g_clear_pointer (&stex->texture_right, cogl_object_unref);
    g_clear_pointer (&stex->opaque_region, cairo_region_destroy);
  
-@@ -507,8 +513,9 @@ paint_clipped_rectangle (MetaShapedTexture     *stex,
+   meta_shaped_texture_set_mask_texture (stex, NULL);
+@@ -507,8 +514,9 @@ paint_clipped_rectangle (MetaShapedTexture     *stex,
  }
  
  static void
@@ -309,7 +311,7 @@ index d64e214e5..e77a32109 100644
  {
    int width, height;
  
-@@ -516,8 +523,11 @@ set_cogl_texture (MetaShapedTexture *stex,
+@@ -516,8 +524,11 @@ set_cogl_texture (MetaShapedTexture *stex,
  
    if (stex->texture)
      cogl_object_unref (stex->texture);
@@ -321,7 +323,7 @@ index d64e214e5..e77a32109 100644
  
    if (cogl_tex != NULL)
      {
-@@ -531,6 +541,9 @@ set_cogl_texture (MetaShapedTexture *stex,
+@@ -531,6 +542,9 @@ set_cogl_texture (MetaShapedTexture *stex,
        height = 0;
      }
  
@@ -331,7 +333,7 @@ index d64e214e5..e77a32109 100644
    if (stex->tex_width != width ||
        stex->tex_height != height)
      {
-@@ -544,8 +557,23 @@ set_cogl_texture (MetaShapedTexture *stex,
+@@ -544,8 +558,23 @@ set_cogl_texture (MetaShapedTexture *stex,
     * previous buffer. We only queue a redraw in response to surface
     * damage. */
  
@@ -356,7 +358,7 @@ index d64e214e5..e77a32109 100644
  }
  
  static gboolean
-@@ -779,7 +807,9 @@ meta_shaped_texture_paint (ClutterActor *actor)
+@@ -779,7 +808,9 @@ meta_shaped_texture_paint (ClutterActor *actor)
  {
    MetaShapedTexture *stex = META_SHAPED_TEXTURE (actor);
    CoglTexture *paint_tex;
@@ -366,7 +368,7 @@ index d64e214e5..e77a32109 100644
  
    if (!stex->texture)
      return;
-@@ -841,7 +871,32 @@ meta_shaped_texture_paint (ClutterActor *actor)
+@@ -841,7 +872,32 @@ meta_shaped_texture_paint (ClutterActor *actor)
      return;
  
    fb = cogl_get_draw_framebuffer ();
@@ -400,7 +402,7 @@ index d64e214e5..e77a32109 100644
  }
  
  static void
-@@ -915,6 +970,12 @@ meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex,
+@@ -915,6 +971,12 @@ meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex,
        stex->create_mipmaps = create_mipmaps;
        base_texture = create_mipmaps ? stex->texture : NULL;
        meta_texture_tower_set_base_texture (stex->paint_tower, base_texture);
@@ -413,7 +415,7 @@ index d64e214e5..e77a32109 100644
      }
  }
  
-@@ -1046,6 +1107,12 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex,
+@@ -1046,6 +1108,12 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex,
                                    clip.y,
                                    clip.width,
                                    clip.height);
@@ -426,7 +428,7 @@ index d64e214e5..e77a32109 100644
  
    stex->prev_invalidation = stex->last_invalidation;
    stex->last_invalidation = g_get_monotonic_time ();
-@@ -1092,17 +1159,18 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex,
+@@ -1092,17 +1160,18 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex,
  }
  
  /**
@@ -901,5 +903,5 @@ index da0acfcbb..ddad1a45c 100644
                meta_shaped_texture_set_is_y_inverted (stex, is_y_inverted);
                g_clear_pointer (&snippet, cogl_object_unref);
 -- 
-2.25.1
+2.28.0
 
diff --git a/SOURCES/0001-backend-Add-getter-for-MetaScreenCast.patch b/SOURCES/0001-backend-Add-getter-for-MetaScreenCast.patch
new file mode 100644
index 0000000..d50b11b
--- /dev/null
+++ b/SOURCES/0001-backend-Add-getter-for-MetaScreenCast.patch
@@ -0,0 +1,49 @@
+From 967d8236d81c8689f2fe60621ec7e66d88b43dea Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Wed, 17 Jun 2020 17:46:25 +0200
+Subject: [PATCH 1/4] backend: Add getter for MetaScreenCast
+
+https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1318
+---
+ src/backends/meta-backend-private.h |  2 ++
+ src/backends/meta-backend.c         | 11 +++++++++++
+ 2 files changed, 13 insertions(+)
+
+diff --git a/src/backends/meta-backend-private.h b/src/backends/meta-backend-private.h
+index 81ec81e5f1..77f4da77c4 100644
+--- a/src/backends/meta-backend-private.h
++++ b/src/backends/meta-backend-private.h
+@@ -138,6 +138,8 @@ MetaEgl * meta_backend_get_egl (MetaBackend *backend);
+ 
+ #ifdef HAVE_REMOTE_DESKTOP
+ MetaRemoteDesktop * meta_backend_get_remote_desktop (MetaBackend *backend);
++
++MetaScreenCast * meta_backend_get_screen_cast (MetaBackend *backend);
+ #endif
+ 
+ gboolean meta_backend_grab_device (MetaBackend *backend,
+diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c
+index 750a9248a8..b498b7aa44 100644
+--- a/src/backends/meta-backend.c
++++ b/src/backends/meta-backend.c
+@@ -965,6 +965,17 @@ meta_backend_get_remote_desktop (MetaBackend *backend)
+ 
+   return priv->remote_desktop;
+ }
++
++/**
++ * meta_backend_get_screen_cast: (skip)
++ */
++MetaScreenCast *
++meta_backend_get_screen_cast (MetaBackend *backend)
++{
++  MetaBackendPrivate *priv = meta_backend_get_instance_private (backend);
++
++  return priv->screen_cast;
++}
+ #endif /* HAVE_REMOTE_DESKTOP */
+ 
+ /**
+-- 
+2.26.2
+
diff --git a/SOURCES/0001-clutter-avoid-redundant-_clutter_paint_node_init_typ.patch b/SOURCES/0001-clutter-avoid-redundant-_clutter_paint_node_init_typ.patch
new file mode 100644
index 0000000..028c8bd
--- /dev/null
+++ b/SOURCES/0001-clutter-avoid-redundant-_clutter_paint_node_init_typ.patch
@@ -0,0 +1,53 @@
+From 4c1c3541efa37acf3a03822289a8ab8705cbbc4e Mon Sep 17 00:00:00 2001
+From: Christian Hergert <chergert@redhat.com>
+Date: Sun, 23 Feb 2020 17:27:08 -0800
+Subject: [PATCH 1/3] clutter: avoid redundant _clutter_paint_node_init_types()
+
+This only needs to be initialized once but is in the hot path of creating
+new paint nodes (for which we create many). Instead, do this as part of
+the clutter_init() workflow to keep it out of the hot path.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/1087
+---
+ clutter/clutter/clutter-main.c       | 4 ++++
+ clutter/clutter/clutter-paint-node.c | 2 --
+ 2 files changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/clutter/clutter/clutter-main.c b/clutter/clutter/clutter-main.c
+index 71ec0d80c..645c8bceb 100644
+--- a/clutter/clutter/clutter-main.c
++++ b/clutter/clutter/clutter-main.c
+@@ -61,6 +61,7 @@
+ #include "clutter-main.h"
+ #include "clutter-master-clock.h"
+ #include "clutter-mutter.h"
++#include "clutter-paint-node-private.h"
+ #include "clutter-private.h"
+ #include "clutter-settings-private.h"
+ #include "clutter-stage-manager.h"
+@@ -1366,6 +1367,9 @@ clutter_init_real (GError **error)
+   if (clutter_enable_accessibility)
+     cally_accessibility_init ();
+ 
++  /* Initialize types required for paint nodes */
++  _clutter_paint_node_init_types ();
++
+   return CLUTTER_INIT_SUCCESS;
+ }
+ 
+diff --git a/clutter/clutter/clutter-paint-node.c b/clutter/clutter/clutter-paint-node.c
+index e731ca60a..73765a4e9 100644
+--- a/clutter/clutter/clutter-paint-node.c
++++ b/clutter/clutter/clutter-paint-node.c
+@@ -1177,8 +1177,6 @@ _clutter_paint_node_create (GType gtype)
+ {
+   g_return_val_if_fail (g_type_is_a (gtype, CLUTTER_TYPE_PAINT_NODE), NULL);
+ 
+-  _clutter_paint_node_init_types ();
+-
+   return (gpointer) g_type_create_instance (gtype);
+ }
+ 
+-- 
+2.26.0
+
diff --git a/SOURCES/0001-monitor-manager-kms-Trigger-hotplug-processing-on-gp.patch b/SOURCES/0001-monitor-manager-kms-Trigger-hotplug-processing-on-gp.patch
new file mode 100644
index 0000000..427bb17
--- /dev/null
+++ b/SOURCES/0001-monitor-manager-kms-Trigger-hotplug-processing-on-gp.patch
@@ -0,0 +1,41 @@
+From 9f8564ce066aeb704341d6f926daec0045243b70 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Thu, 25 Jun 2020 10:06:38 +0200
+Subject: [PATCH 1/2] monitor-manager-kms: Trigger hotplug processing on gpu
+ removal
+
+---
+ src/backends/native/meta-monitor-manager-kms.c | 16 +++++++++++-----
+ 1 file changed, 11 insertions(+), 5 deletions(-)
+
+diff --git a/src/backends/native/meta-monitor-manager-kms.c b/src/backends/native/meta-monitor-manager-kms.c
+index 9a0364441a..2819881576 100644
+--- a/src/backends/native/meta-monitor-manager-kms.c
++++ b/src/backends/native/meta-monitor-manager-kms.c
+@@ -470,12 +470,18 @@ on_uevent (GUdevClient *client,
+ 
+       if (!g_strcmp0 (seat_id, device_seat))
+         handle_gpu_hotplug (manager_kms, device);
+-    }
+-
+-  if (!g_udev_device_get_property_as_boolean (device, "HOTPLUG"))
+-    return;
+ 
+-  handle_hotplug_event (manager);
++      handle_hotplug_event (manager);
++    }
++  else if (g_str_equal (action, "remove") &&
++           g_udev_device_get_device_file (device) != NULL)
++    {
++      handle_hotplug_event (manager);
++    }
++  else if (g_udev_device_get_property_as_boolean (device, "HOTPLUG"))
++    {
++      handle_hotplug_event (manager);
++    }
+ }
+ 
+ static void
+-- 
+2.26.2
+
diff --git a/SOURCES/0001-renderer-Add-API-to-check-whether-renderer-is-hardwa.patch b/SOURCES/0001-renderer-Add-API-to-check-whether-renderer-is-hardwa.patch
new file mode 100644
index 0000000..2be8b13
--- /dev/null
+++ b/SOURCES/0001-renderer-Add-API-to-check-whether-renderer-is-hardwa.patch
@@ -0,0 +1,108 @@
+From d107b52939ca0acb1f8dacf1275278edba64eebe Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Tue, 1 Oct 2019 11:53:57 +0200
+Subject: [PATCH] renderer: Add API to check whether renderer is hardware
+ accelerated
+
+Also expose an introspected variant via the MetaBackend.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/838
+---
+ src/backends/meta-backend.c  | 15 +++++++++++++++
+ src/backends/meta-renderer.c | 27 +++++++++++++++++++++++++++
+ src/backends/meta-renderer.h |  2 ++
+ src/meta/meta-backend.h      |  3 +++
+ 4 files changed, 47 insertions(+)
+
+diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c
+index 72cfbdaf3..e61181f9a 100644
+--- a/src/backends/meta-backend.c
++++ b/src/backends/meta-backend.c
+@@ -985,6 +985,21 @@ meta_backend_get_remote_access_controller (MetaBackend *backend)
+ #endif
+ }
+ 
++/**
++ * meta_backend_is_rendering_hardware_accelerated:
++ * @backend: A #MetaBackend
++ *
++ * Returns: %TRUE if the rendering is hardware accelerated, otherwise
++ * %FALSE.
++ */
++gboolean
++meta_backend_is_rendering_hardware_accelerated (MetaBackend *backend)
++{
++  MetaRenderer *renderer = meta_backend_get_renderer (backend);
++
++  return meta_renderer_is_hardware_accelerated (renderer);
++}
++
+ /**
+  * meta_backend_grab_device: (skip)
+  */
+diff --git a/src/backends/meta-renderer.c b/src/backends/meta-renderer.c
+index 87ba9f9f0..470220fc8 100644
+--- a/src/backends/meta-renderer.c
++++ b/src/backends/meta-renderer.c
+@@ -166,6 +166,33 @@ meta_renderer_get_view_from_logical_monitor (MetaRenderer       *renderer,
+   return NULL;
+ }
+ 
++gboolean
++meta_renderer_is_hardware_accelerated (MetaRenderer *renderer)
++{
++  MetaRendererPrivate *priv = meta_renderer_get_instance_private (renderer);
++  MetaBackend *backend = meta_get_backend ();
++  ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
++  CoglContext *cogl_context =
++    clutter_backend_get_cogl_context (clutter_backend);
++  CoglGpuInfo *info = &cogl_context->gpu;
++
++  switch (info->architecture)
++    {
++    case COGL_GPU_INFO_ARCHITECTURE_UNKNOWN:
++    case COGL_GPU_INFO_ARCHITECTURE_SANDYBRIDGE:
++    case COGL_GPU_INFO_ARCHITECTURE_SGX:
++    case COGL_GPU_INFO_ARCHITECTURE_MALI:
++      return TRUE;
++    case COGL_GPU_INFO_ARCHITECTURE_LLVMPIPE:
++    case COGL_GPU_INFO_ARCHITECTURE_SOFTPIPE:
++    case COGL_GPU_INFO_ARCHITECTURE_SWRAST:
++      return FALSE;
++    }
++
++  g_assert_not_reached ();
++  return FALSE;
++}
++
+ static void
+ meta_renderer_finalize (GObject *object)
+ {
+diff --git a/src/backends/meta-renderer.h b/src/backends/meta-renderer.h
+index 478baee91..97bf36860 100644
+--- a/src/backends/meta-renderer.h
++++ b/src/backends/meta-renderer.h
+@@ -59,4 +59,6 @@ GList * meta_renderer_get_views (MetaRenderer *renderer);
+ MetaRendererView * meta_renderer_get_view_from_logical_monitor (MetaRenderer       *renderer,
+                                                                 MetaLogicalMonitor *logical_monitor);
+ 
++gboolean meta_renderer_is_hardware_accelerated (MetaRenderer *renderer);
++
+ #endif /* META_RENDERER_H */
+diff --git a/src/meta/meta-backend.h b/src/meta/meta-backend.h
+index aaa6aae97..8edc0bf2c 100644
+--- a/src/meta/meta-backend.h
++++ b/src/meta/meta-backend.h
+@@ -64,6 +64,9 @@ MetaSettings *meta_backend_get_settings (MetaBackend *backend);
+ META_EXPORT
+ MetaRemoteAccessController * meta_backend_get_remote_access_controller (MetaBackend *backend);
+ 
++META_EXPORT
++gboolean meta_backend_is_rendering_hardware_accelerated (MetaBackend *backend);
++
+ META_EXPORT
+ void meta_clutter_init (void);
+ 
+-- 
+2.26.2
+
diff --git a/SOURCES/0001-screen-cast-src-Destroy-hash-dmabuf-table-after-stre.patch b/SOURCES/0001-screen-cast-src-Destroy-hash-dmabuf-table-after-stre.patch
new file mode 100644
index 0000000..a0f76ff
--- /dev/null
+++ b/SOURCES/0001-screen-cast-src-Destroy-hash-dmabuf-table-after-stre.patch
@@ -0,0 +1,40 @@
+From b32ae04c122f4f76ffad296c15ba00a13800db57 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Tue, 2 Jun 2020 16:33:05 +0000
+Subject: [PATCH 1/2] screen-cast-src: Destroy hash dmabuf table after stream
+
+The stream will clean up the buffers, so let it do that before we
+destroy them under its feet. Note that it'll only do this after the
+following PipeWire commit:
+
+    commit fbaa4ddedd84afdffca16f090dcc4b0db8ccfc29
+    Author: Wim Taymans <wtaymans@redhat.com>
+    Date:   Mon Jun 1 15:36:09 2020 +0200
+
+        stream: allow NULL param and 0 buffers in disconnect
+
+https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1283
+
+
+(cherry picked from commit 97175f8fa14171606ecb95d0bf107ef8b2d71b74)
+---
+ src/backends/meta-screen-cast-stream-src.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c
+index 0500bfec5..ff4af440c 100644
+--- a/src/backends/meta-screen-cast-stream-src.c
++++ b/src/backends/meta-screen-cast-stream-src.c
+@@ -988,8 +988,8 @@ 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->dmabuf_handles, g_hash_table_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);
+-- 
+2.26.2
+
diff --git a/SOURCES/0001-stage-x11-Check-that-message-is-WM_PROTOCOLS-before-.patch b/SOURCES/0001-stage-x11-Check-that-message-is-WM_PROTOCOLS-before-.patch
new file mode 100644
index 0000000..095e0d2
--- /dev/null
+++ b/SOURCES/0001-stage-x11-Check-that-message-is-WM_PROTOCOLS-before-.patch
@@ -0,0 +1,101 @@
+From 639b7ba7f2729a95593c0b85d4789f76152e6099 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Thu, 18 Jun 2020 21:17:29 +0200
+Subject: [PATCH] stage/x11: Check that message is WM_PROTOCOLS before assuming
+ so
+
+When a touch sequence was rejected, we'd update the event timestamps of
+incoming touch events to help with implementing grabs. This was done by
+sending a ClientMessage with a counter, and comparing the counter to
+decide whether we're seing a replayed event or not.
+
+This had the unforseen consequence that we would potentially end up
+destroying all actors including the stage, since, when mutter receives a
+ClientMessage event, it would assume that it's a WM_PROTOCOLS event, and
+handle it as such. The problem with this approach is that it would
+ignore fact that there might be other ClientMessage types sent to it,
+for example the touch synchronization one. What could happen is that the
+touch count value would match up with the value of the WM_DELETE_WINDOW
+atom, clutter would treat this as WM_PROTOCOLS:WM_DELETE_WINDOW, which
+it'd translate to clutter_actor_destroy(stage).
+
+Destroying the stage in such a way is not expected, and caused wierd
+crashes in different places depending on what was going on.
+
+This commit make sure we only treat WM_PROTOCOLS client messages as
+WM_PROTOCOLS client messages effectively avoiding the issue.
+
+This fixes crashes such as:
+
+ #0  meta_window_get_buffer_rect (window=0x0, rect=rect@entry=0x7ffd7fc62e40) at core/window.c:4396
+ #1  0x00007f1e2634837f in get_top_visible_window_actor (compositor=0x297d700, compositor=0x297d700) at compositor/compositor.c:1059
+ #2  meta_compositor_sync_stack (compositor=0x297d700, stack=<optimized out>, stack@entry=0x26e3140) at compositor/compositor.c:1176
+ #3  0x00007f1e263757ac in meta_stack_tracker_sync_stack (tracker=0x297dbc0) at core/stack-tracker.c:871
+ #4  0x00007f1e26375899 in stack_tracker_sync_stack_later (data=<optimized out>) at core/stack-tracker.c:881
+ #5  0x00007f1e26376914 in run_repaint_laters (laters_list=0x7f1e2663b7d8 <laters+24>) at core/util.c:809
+ #6  run_all_repaint_laters (data=<optimized out>) at core/util.c:826
+ #7  0x00007f1e26b18325 in _clutter_run_repaint_functions (flags=flags@entry=CLUTTER_REPAINT_FLAGS_PRE_PAINT) at clutter-main.c:3448
+ #8  0x00007f1e26b18fc5 in master_clock_update_stages (master_clock=0x32d6a80, stages=0x4e5a740) at clutter-master-clock-default.c:437
+ #9  clutter_clock_dispatch (source=<optimized out>, callback=<optimized out>, user_data=<optimized out>) at clutter-master-clock-default.c:567
+ #10 0x00007f1e27e48049 in g_main_dispatch (context=0x225b8d0) at gmain.c:3175
+ #11 g_main_context_dispatch (context=context@entry=0x225b8d0) at gmain.c:3828
+ #12 0x00007f1e27e483a8 in g_main_context_iterate (context=0x225b8d0, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at gmain.c:3901
+ #13 0x00007f1e27e4867a in g_main_loop_run (loop=0x24e29f0) at gmain.c:4097
+ #14 0x00007f1e2636a3dc in meta_run () at core/main.c:666
+ #15 0x000000000040219c in main (argc=1, argv=0x7ffd7fc63238) at ../src/main.c:534
+
+and
+
+ #0  0x00007f93943c1f25 in raise () at /usr/lib/libc.so.6
+ #1  0x00007f93943ab897 in abort () at /usr/lib/libc.so.6
+ #2  0x00007f9393e1e062 in g_assertion_message (domain=<optimized out>, file=<optimized out>, line=<optimized out>, func=0x7f93933e6860 <__func__.116322> "meta_x11_get_stage_window",
+ #3  0x00007f9393e4ab1d in g_assertion_message_expr ()
+ #4  0x00007f939338ecd7 in meta_x11_get_stage_window (stage=<optimized out>) at ../mutter/src/backends/x11/meta-stage-x11.c:923
+ #5  0x00007f939339e599 in meta_backend_x11_cm_translate_device_event (x11=<optimized out>, device_event=0x55bc8bcfd6b0) at ../mutter/src/backends/x11/cm/meta-backend-x11-cm.c:381
+ #6  0x00007f939339f2e2 in meta_backend_x11_translate_device_event (device_event=0x55bc8bcfd6b0, x11=0x55bc89dd5220) at ../mutter/src/backends/x11/meta-backend-x11.c:179
+ #7  0x00007f939339f2e2 in translate_device_event (device_event=0x55bc8bcfd6b0, x11=0x55bc89dd5220) at ../mutter/src/backends/x11/meta-backend-x11.c:208
+ #8  0x00007f939339f2e2 in maybe_spoof_event_as_stage_event (input_event=0x55bc8bcfd6b0, x11=0x55bc89dd5220) at ../mutter/src/backends/x11/meta-backend-x11.c:284
+ #9  0x00007f939339f2e2 in handle_input_event (event=0x7fff62d60490, x11=0x55bc89dd5220) at ../mutter/src/backends/x11/meta-backend-x11.c:309
+ #10 0x00007f939339f2e2 in handle_host_xevent (event=0x7fff62d60490, backend=0x55bc89dd5220) at ../mutter/src/backends/x11/meta-backend-x11.c:413
+ #11 0x00007f939339f2e2 in x_event_source_dispatch (source=<optimized out>, callback=<optimized out>, user_data=<optimized out>) at ../mutter/src/backends/x11/meta-backend-x11.c:467
+ #12 0x00007f9393e6c39e in g_main_dispatch (context=0x55bc89dd03e0) at ../glib/glib/gmain.c:3179
+ #13 0x00007f9393e6c39e in g_main_context_dispatch (context=context@entry=0x55bc89dd03e0) at ../glib/glib/gmain.c:3844
+ #14 0x00007f9393e6e1b1 in g_main_context_iterate (context=0x55bc89dd03e0, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at ../glib/glib/gmain.c:3917
+ #15 0x00007f9393e6f0c3 in g_main_loop_run (loop=0x55bc8a042640) at ../glib/glib/gmain.c:4111
+ #16 0x00007f9393369a0c in meta_run () at ../mutter/src/core/main.c:676
+ #17 0x000055bc880f2426 in main (argc=<optimized out>, argv=<optimized out>) at ../gnome-shell/src/main.c:552
+
+Related: https://gitlab.gnome.org/GNOME/mutter/-/issues/338
+Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/951
+
+https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1317
+---
+ clutter/clutter/x11/clutter-stage-x11.c | 11 +++++++----
+ 1 file changed, 7 insertions(+), 4 deletions(-)
+
+diff --git a/clutter/clutter/x11/clutter-stage-x11.c b/clutter/clutter/x11/clutter-stage-x11.c
+index d043bcf31d..123078fc22 100644
+--- a/clutter/clutter/x11/clutter-stage-x11.c
++++ b/clutter/clutter/x11/clutter-stage-x11.c
+@@ -1306,11 +1306,14 @@ clutter_stage_x11_translate_event (ClutterEventTranslator *translator,
+                     _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage)),
+                     stage,
+                     (unsigned int) stage_xwindow);
+-      if (handle_wm_protocols_event (backend_x11, stage_x11, xevent))
++      if (xevent->xclient.message_type == backend_x11->atom_WM_PROTOCOLS)
+         {
+-          event->any.type = CLUTTER_DELETE;
+-          event->any.stage = stage;
+-          res = CLUTTER_TRANSLATE_QUEUE;
++          if (handle_wm_protocols_event (backend_x11, stage_x11, xevent))
++            {
++              event->any.type = CLUTTER_DELETE;
++              event->any.stage = stage;
++              res = CLUTTER_TRANSLATE_QUEUE;
++            }
+         }
+       break;
+ 
+-- 
+2.26.2
+
diff --git a/SOURCES/0001-window-actor-Don-t-show-actor-until-meta_window_acto.patch b/SOURCES/0001-window-actor-Don-t-show-actor-until-meta_window_acto.patch
new file mode 100644
index 0000000..df533eb
--- /dev/null
+++ b/SOURCES/0001-window-actor-Don-t-show-actor-until-meta_window_acto.patch
@@ -0,0 +1,35 @@
+From 24ddf60768412fd3f5f7b432449b9ed2ea0d18b3 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Tue, 18 Feb 2020 23:01:28 +0100
+Subject: [PATCH] window-actor: Don't show actor until meta_window_actor_show()
+
+By default clutter will show an actor as it is added to a parent. This
+means that after we create the window actor, when it's added to the
+window group, we implicitly show it. What we really want is to not show
+it until the window is supposed to be shown, which happens when
+meta_window_actor_show() is called, as showing prior to that, could
+cause issues.
+
+Avoid the implicit show by setting the "show-on-set-parent" property on
+the window actor to `FALSE` on window actor construction.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/1066
+---
+ src/compositor/compositor.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c
+index a6ae55abb9..ce2c1b8a3b 100644
+--- a/src/compositor/compositor.c
++++ b/src/compositor/compositor.c
+@@ -810,6 +810,7 @@ meta_compositor_add_window (MetaCompositor    *compositor,
+ 
+   window_actor = g_object_new (window_actor_type,
+                                "meta-window", window,
++                               "show-on-set-parent", FALSE,
+                                NULL);
+ 
+   if (window->layer == META_LAYER_OVERRIDE_REDIRECT)
+-- 
+2.26.2
+
diff --git a/SOURCES/0002-clutter-avoid-g_signal_emit_by_name-from-ClutterActo.patch b/SOURCES/0002-clutter-avoid-g_signal_emit_by_name-from-ClutterActo.patch
new file mode 100644
index 0000000..d281b8b
--- /dev/null
+++ b/SOURCES/0002-clutter-avoid-g_signal_emit_by_name-from-ClutterActo.patch
@@ -0,0 +1,192 @@
+From 6881aa5ca235ee0737c2a409ffab966a10e5971e Mon Sep 17 00:00:00 2001
+From: Christian Hergert <christian@hergert.me>
+Date: Mon, 24 Feb 2020 22:36:27 +0000
+Subject: [PATCH 2/3] clutter: avoid g_signal_emit_by_name() from ClutterActor
+
+g_signal_emit_by_name() is used to emit signals on ClutterContainer when
+actors are removed or added. It happens to do various interface lookups
+which are a bit unneccessary and can allocate memory.
+
+Simply using emission wrappers makes all of that go away.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/1083
+---
+ clutter/clutter/cally/cally-actor.c         |  5 +--
+ clutter/clutter/clutter-actor.c             | 17 ++++++++--
+ clutter/clutter/clutter-actor.h             |  5 ++-
+ clutter/clutter/clutter-container-private.h | 36 +++++++++++++++++++++
+ clutter/clutter/clutter-container.c         | 21 ++++++++++++
+ 5 files changed, 78 insertions(+), 6 deletions(-)
+ create mode 100644 clutter/clutter/clutter-container-private.h
+
+diff --git a/clutter/clutter/cally/cally-actor.c b/clutter/clutter/cally/cally-actor.c
+index 548615f48..517969625 100644
+--- a/clutter/clutter/cally/cally-actor.c
++++ b/clutter/clutter/cally/cally-actor.c
+@@ -604,10 +604,11 @@ cally_actor_real_remove_actor (ClutterActor *container,
+   g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), 0);
+ 
+   atk_parent = ATK_OBJECT (data);
+-  atk_child = clutter_actor_get_accessible (actor);
+ 
+-  if (atk_child)
++  if (clutter_actor_has_accessible (actor))
+     {
++      atk_child = clutter_actor_get_accessible (actor);
++
+       g_value_init (&values.old_value, G_TYPE_POINTER);
+       g_value_set_pointer (&values.old_value, atk_parent);
+ 
+diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c
+index 803f76aae..93d0a93ef 100644
+--- a/clutter/clutter/clutter-actor.c
++++ b/clutter/clutter/clutter-actor.c
+@@ -624,7 +624,7 @@
+ #include "clutter-color-static.h"
+ #include "clutter-color.h"
+ #include "clutter-constraint-private.h"
+-#include "clutter-container.h"
++#include "clutter-container-private.h"
+ #include "clutter-content-private.h"
+ #include "clutter-debug.h"
+ #include "clutter-easing.h"
+@@ -4372,7 +4372,7 @@ clutter_actor_remove_child_internal (ClutterActor                 *self,
+ 
+   /* we need to emit the signal before dropping the reference */
+   if (emit_actor_removed)
+-    g_signal_emit_by_name (self, "actor-removed", child);
++    _clutter_container_emit_actor_removed (CLUTTER_CONTAINER (self), child);
+ 
+   if (notify_first_last)
+     {
+@@ -13060,7 +13060,7 @@ clutter_actor_add_child_internal (ClutterActor              *self,
+     }
+ 
+   if (emit_actor_added)
+-    g_signal_emit_by_name (self, "actor-added", child);
++    _clutter_container_emit_actor_added (CLUTTER_CONTAINER (self), child);
+ 
+   if (notify_first_last)
+     {
+@@ -21448,3 +21448,14 @@ clutter_actor_create_texture_paint_node (ClutterActor *self,
+ 
+   return node;
+ }
++
++gboolean
++clutter_actor_has_accessible (ClutterActor *actor)
++{
++  g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE);
++
++  if (CLUTTER_ACTOR_GET_CLASS (actor)->has_accessible)
++    return CLUTTER_ACTOR_GET_CLASS (actor)->has_accessible (actor);
++
++  return TRUE;
++}
+diff --git a/clutter/clutter/clutter-actor.h b/clutter/clutter/clutter-actor.h
+index 7d2168af1..d286f2843 100644
+--- a/clutter/clutter/clutter-actor.h
++++ b/clutter/clutter/clutter-actor.h
+@@ -296,10 +296,11 @@ struct _ClutterActorClass
+ 
+   gboolean (* touch_event)          (ClutterActor         *self,
+                                      ClutterTouchEvent    *event);
++  gboolean (* has_accessible)       (ClutterActor         *self);
+ 
+   /*< private >*/
+   /* padding for future expansion */
+-  gpointer _padding_dummy[26];
++  gpointer _padding_dummy[25];
+ };
+ 
+ /**
+@@ -369,6 +370,8 @@ CLUTTER_EXPORT
+ const gchar *                   clutter_actor_get_name                          (ClutterActor                *self);
+ CLUTTER_EXPORT
+ AtkObject *                     clutter_actor_get_accessible                    (ClutterActor                *self);
++CLUTTER_EXPORT
++gboolean                        clutter_actor_has_accessible                    (ClutterActor                *self);
+ 
+ CLUTTER_EXPORT
+ gboolean                        clutter_actor_is_visible                        (ClutterActor                *self);
+diff --git a/clutter/clutter/clutter-container-private.h b/clutter/clutter/clutter-container-private.h
+new file mode 100644
+index 000000000..d619a6531
+--- /dev/null
++++ b/clutter/clutter/clutter-container-private.h
+@@ -0,0 +1,36 @@
++/*
++ * Clutter.
++ *
++ * An OpenGL based 'interactive canvas' library.
++ *
++ * Copyright 2020 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 <http://www.gnu.org/licenses/>.
++ */
++
++#ifndef __CLUTTER_CONTAINER_PRIVATE_H__
++#define __CLUTTER_CONTAINER_PRIVATE_H__
++
++#include <clutter/clutter-container.h>
++
++G_BEGIN_DECLS
++
++void _clutter_container_emit_actor_added   (ClutterContainer *container,
++                                            ClutterActor     *actor);
++void _clutter_container_emit_actor_removed (ClutterContainer *container,
++                                            ClutterActor     *actor);
++
++G_END_DECLS
++
++#endif /* __CLUTTER_CONTAINER_PRIVATE_H__ */
+diff --git a/clutter/clutter/clutter-container.c b/clutter/clutter/clutter-container.c
+index 0f611ae55..79abb5b4f 100644
+--- a/clutter/clutter/clutter-container.c
++++ b/clutter/clutter/clutter-container.c
+@@ -37,6 +37,7 @@
+ 
+ #include "clutter-actor-private.h"
+ #include "clutter-child-meta.h"
++#include "clutter-container-private.h"
+ #include "clutter-debug.h"
+ #include "clutter-main.h"
+ #include "clutter-marshal.h"
+@@ -1446,3 +1447,23 @@ clutter_container_child_notify (ClutterContainer *container,
+                                                          child,
+                                                          pspec);
+ }
++
++void
++_clutter_container_emit_actor_added (ClutterContainer *container,
++                                     ClutterActor     *actor)
++{
++  g_return_if_fail (CLUTTER_IS_CONTAINER (container));
++  g_return_if_fail (CLUTTER_IS_ACTOR (actor));
++
++  g_signal_emit (container, container_signals[ACTOR_ADDED], 0, actor);
++}
++
++void
++_clutter_container_emit_actor_removed (ClutterContainer *container,
++                                       ClutterActor     *actor)
++{
++  g_return_if_fail (CLUTTER_IS_CONTAINER (container));
++  g_return_if_fail (CLUTTER_IS_ACTOR (actor));
++
++  g_signal_emit (container, container_signals[ACTOR_REMOVED], 0, actor);
++}
+-- 
+2.26.0
+
diff --git a/SOURCES/0002-gpu-kms-Reset-CRTC-mode-and-output-list-if-no-resour.patch b/SOURCES/0002-gpu-kms-Reset-CRTC-mode-and-output-list-if-no-resour.patch
new file mode 100644
index 0000000..5a1c1ff
--- /dev/null
+++ b/SOURCES/0002-gpu-kms-Reset-CRTC-mode-and-output-list-if-no-resour.patch
@@ -0,0 +1,28 @@
+From a192b9abd77aa14ae794850e41d210472f86b9b0 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Thu, 25 Jun 2020 10:09:48 +0200
+Subject: [PATCH 2/2] gpu-kms: Reset CRTC, mode and output list if no resources
+
+On device removal, the next resource retrieval will fail; handle this by
+just clearing the CRTC, mode and outputs.
+---
+ src/backends/native/meta-gpu-kms.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/src/backends/native/meta-gpu-kms.c b/src/backends/native/meta-gpu-kms.c
+index 93e509def5..dc93abb7b1 100644
+--- a/src/backends/native/meta-gpu-kms.c
++++ b/src/backends/native/meta-gpu-kms.c
+@@ -871,6 +871,9 @@ meta_gpu_kms_read_current (MetaGpu  *gpu,
+                      local_error->message);
+           gpu_kms->resources_init_failed_before = TRUE;
+         }
++      meta_gpu_take_outputs (gpu, NULL);
++      meta_gpu_take_modes (gpu, NULL);
++      meta_gpu_take_crtcs (gpu, NULL);
+       return TRUE;
+     }
+ 
+-- 
+2.26.2
+
diff --git a/SOURCES/0002-renderer-native-Add-API-to-get-primary-GPU.patch b/SOURCES/0002-renderer-native-Add-API-to-get-primary-GPU.patch
new file mode 100644
index 0000000..bd9f592
--- /dev/null
+++ b/SOURCES/0002-renderer-native-Add-API-to-get-primary-GPU.patch
@@ -0,0 +1,46 @@
+From 63455680096e015eaf023760466593b6f42f9cf5 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Thu, 12 Sep 2019 11:50:34 +0200
+Subject: [PATCH 2/4] renderer/native: Add API to get primary GPU
+
+Will be used when acquiring scanouts from Wayland buffers.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/798
+---
+ src/backends/native/meta-renderer-native.c | 6 ++++++
+ src/backends/native/meta-renderer-native.h | 2 ++
+ 2 files changed, 8 insertions(+)
+
+diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c
+index dbb88edb8e..25833b6cf6 100644
+--- a/src/backends/native/meta-renderer-native.c
++++ b/src/backends/native/meta-renderer-native.c
+@@ -317,6 +317,12 @@ meta_gbm_device_from_gpu (MetaGpuKms *gpu_kms)
+   return renderer_gpu_data->gbm.device;
+ }
+ 
++MetaGpuKms *
++meta_renderer_native_get_primary_gpu (MetaRendererNative *renderer_native)
++{
++  return renderer_native->primary_gpu_kms;
++}
++
+ static MetaRendererNativeGpuData *
+ meta_create_renderer_native_gpu_data (MetaGpuKms *gpu_kms)
+ {
+diff --git a/src/backends/native/meta-renderer-native.h b/src/backends/native/meta-renderer-native.h
+index 9eecdead1c..b59366e267 100644
+--- a/src/backends/native/meta-renderer-native.h
++++ b/src/backends/native/meta-renderer-native.h
+@@ -53,6 +53,8 @@ struct gbm_device * meta_gbm_device_from_gpu (MetaGpuKms *gpu_kms);
+ 
+ gboolean meta_renderer_native_supports_mirroring (MetaRendererNative *renderer_native);
+ 
++MetaGpuKms * meta_renderer_native_get_primary_gpu (MetaRendererNative *renderer_native);
++
+ void meta_renderer_native_finish_frame (MetaRendererNative *renderer_native);
+ 
+ int64_t meta_renderer_native_get_frame_counter (MetaRendererNative *renderer_native);
+-- 
+2.26.2
+
diff --git a/SOURCES/0002-renderer-native-Don-t-leak-DMA-buffer-CoglFramebuffe.patch b/SOURCES/0002-renderer-native-Don-t-leak-DMA-buffer-CoglFramebuffe.patch
new file mode 100644
index 0000000..4e383f2
--- /dev/null
+++ b/SOURCES/0002-renderer-native-Don-t-leak-DMA-buffer-CoglFramebuffe.patch
@@ -0,0 +1,44 @@
+From 2e4d3e22aff7cc8e41fa08d798c55a39a542476c Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Tue, 2 Jun 2020 18:34:57 +0200
+Subject: [PATCH 2/2] renderer-native: Don't leak DMA buffer CoglFramebuffer
+
+When we created the DMA buffer backed CoglFramebuffer, we handed it over
+to CoglDmaBufHandle which took its own reference. What we failed to do
+was to release our own reference to it, effectively leaking it.
+
+https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1283
+(cherry picked from commit c823b5ddba18d30e1fdb74d6764cd40637dc4054)
+---
+ src/backends/native/meta-renderer-native.c | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c
+index ba98de650..dbb88edb8 100644
+--- a/src/backends/native/meta-renderer-native.c
++++ b/src/backends/native/meta-renderer-native.c
+@@ -2633,6 +2633,7 @@ meta_renderer_native_create_dma_buf (CoglRenderer  *cogl_renderer,
+     case META_RENDERER_NATIVE_MODE_GBM:
+       {
+         CoglFramebuffer *dmabuf_fb;
++        CoglDmaBufHandle *dmabuf_handle;
+         struct gbm_bo *new_bo;
+         int dmabuf_fd = -1;
+ 
+@@ -2669,8 +2670,11 @@ meta_renderer_native_create_dma_buf (CoglRenderer  *cogl_renderer,
+         if (!dmabuf_fb)
+           return NULL;
+ 
+-        return cogl_dma_buf_handle_new (dmabuf_fb, dmabuf_fd, new_bo,
+-                                        (GDestroyNotify) gbm_bo_destroy);
++        dmabuf_handle =
++          cogl_dma_buf_handle_new (dmabuf_fb, dmabuf_fd, new_bo,
++                                   (GDestroyNotify) gbm_bo_destroy);
++        cogl_object_unref (dmabuf_fb);
++        return dmabuf_handle;
+       }
+       break;
+ #ifdef HAVE_EGL_DEVICE
+-- 
+2.26.2
+
diff --git a/SOURCES/0003-clutter-fix-hole-in-ClutterPaintNode.patch b/SOURCES/0003-clutter-fix-hole-in-ClutterPaintNode.patch
new file mode 100644
index 0000000..557f1ff
--- /dev/null
+++ b/SOURCES/0003-clutter-fix-hole-in-ClutterPaintNode.patch
@@ -0,0 +1,167 @@
+From df565fcb681a50aac5046981c5aba04073d14856 Mon Sep 17 00:00:00 2001
+From: Christian Hergert <christian@hergert.me>
+Date: Fri, 21 Feb 2020 22:36:31 +0000
+Subject: [PATCH 3/3] clutter: fix hole in ClutterPaintNode
+
+Fixing the missalignment takes the structure from 80 bytes down to 72.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/1081
+---
+ clutter/clutter/clutter-actor.c              |  8 +++----
+ clutter/clutter/clutter-canvas.c             |  2 +-
+ clutter/clutter/clutter-image.c              |  2 +-
+ clutter/clutter/clutter-paint-node-private.h |  6 ++---
+ clutter/clutter/clutter-paint-node.c         | 23 +++++++++++++++-----
+ clutter/clutter/clutter-paint-node.h         |  3 +++
+ 6 files changed, 30 insertions(+), 14 deletions(-)
+
+diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c
+index 93d0a93ef..ff5c4a69d 100644
+--- a/clutter/clutter/clutter-actor.c
++++ b/clutter/clutter/clutter-actor.c
+@@ -3758,7 +3758,7 @@ clutter_actor_paint_node (ClutterActor     *actor,
+         clear_flags |= COGL_BUFFER_BIT_COLOR;
+ 
+       node = clutter_root_node_new (fb, &bg_color, clear_flags);
+-      clutter_paint_node_set_name (node, "stageClear");
++      clutter_paint_node_set_static_name (node, "stageClear");
+       clutter_paint_node_add_rectangle (node, &box);
+       clutter_paint_node_add_child (root, node);
+       clutter_paint_node_unref (node);
+@@ -3773,7 +3773,7 @@ clutter_actor_paint_node (ClutterActor     *actor,
+                      / 255;
+ 
+       node = clutter_color_node_new (&bg_color);
+-      clutter_paint_node_set_name (node, "backgroundColor");
++      clutter_paint_node_set_static_name (node, "backgroundColor");
+       clutter_paint_node_add_rectangle (node, &box);
+       clutter_paint_node_add_child (root, node);
+       clutter_paint_node_unref (node);
+@@ -4069,7 +4069,7 @@ clutter_actor_continue_paint (ClutterActor *self)
+            * virtual function can then be called directly.
+            */
+           dummy = _clutter_dummy_node_new (self);
+-          clutter_paint_node_set_name (dummy, "Root");
++          clutter_paint_node_set_static_name (dummy, "Root");
+ 
+           /* XXX - for 1.12, we use the return value of paint_node() to
+            * decide whether we should emit the ::paint signal.
+@@ -21427,7 +21427,7 @@ clutter_actor_create_texture_paint_node (ClutterActor *self,
+   color.alpha = clutter_actor_get_paint_opacity_internal (self);
+ 
+   node = clutter_texture_node_new (texture, &color, priv->min_filter, priv->mag_filter);
+-  clutter_paint_node_set_name (node, "Texture");
++  clutter_paint_node_set_static_name (node, "Texture");
+ 
+   if (priv->content_repeat == CLUTTER_REPEAT_NONE)
+     clutter_paint_node_add_rectangle (node, &box);
+diff --git a/clutter/clutter/clutter-canvas.c b/clutter/clutter/clutter-canvas.c
+index b0f1f080c..89c031be2 100644
+--- a/clutter/clutter/clutter-canvas.c
++++ b/clutter/clutter/clutter-canvas.c
+@@ -351,7 +351,7 @@ clutter_canvas_paint_content (ClutterContent   *content,
+     return;
+ 
+   node = clutter_actor_create_texture_paint_node (actor, priv->texture);
+-  clutter_paint_node_set_name (node, "Canvas Content");
++  clutter_paint_node_set_static_name (node, "Canvas Content");
+   clutter_paint_node_add_child (root, node);
+   clutter_paint_node_unref (node);
+ 
+diff --git a/clutter/clutter/clutter-image.c b/clutter/clutter/clutter-image.c
+index 266c68799..790e09521 100644
+--- a/clutter/clutter/clutter-image.c
++++ b/clutter/clutter/clutter-image.c
+@@ -129,7 +129,7 @@ clutter_image_paint_content (ClutterContent   *content,
+     return;
+ 
+   node = clutter_actor_create_texture_paint_node (actor, priv->texture);
+-  clutter_paint_node_set_name (node, "Image Content");
++  clutter_paint_node_set_static_name (node, "Image Content");
+   clutter_paint_node_add_child (root, node);
+   clutter_paint_node_unref (node);
+ }
+diff --git a/clutter/clutter/clutter-paint-node-private.h b/clutter/clutter/clutter-paint-node-private.h
+index d61b89951..720df1458 100644
+--- a/clutter/clutter/clutter-paint-node-private.h
++++ b/clutter/clutter/clutter-paint-node-private.h
+@@ -48,11 +48,11 @@ struct _ClutterPaintNode
+   ClutterPaintNode *next_sibling;
+   ClutterPaintNode *last_child;
+ 
+-  guint n_children;
+-
+   GArray *operations;
+ 
+-  gchar *name;
++  const gchar *name;
++
++  guint n_children;
+ 
+   volatile int ref_count;
+ };
+diff --git a/clutter/clutter/clutter-paint-node.c b/clutter/clutter/clutter-paint-node.c
+index 73765a4e9..1f9451a43 100644
+--- a/clutter/clutter/clutter-paint-node.c
++++ b/clutter/clutter/clutter-paint-node.c
+@@ -171,8 +171,6 @@ clutter_paint_node_real_finalize (ClutterPaintNode *node)
+ {
+   ClutterPaintNode *iter;
+ 
+-  g_free (node->name);
+-
+   if (node->operations != NULL)
+     {
+       guint i;
+@@ -294,7 +292,8 @@ clutter_paint_node_get_type (void)
+  *
+  * The @name will be used for debugging purposes.
+  *
+- * The @node will copy the passed string.
++ * The @node will intern @name using g_intern_string(). If you have access to a
++ * static string, use clutter_paint_node_set_static_name() instead.
+  *
+  * Since: 1.10
+  */
+@@ -304,8 +303,22 @@ clutter_paint_node_set_name (ClutterPaintNode *node,
+ {
+   g_return_if_fail (CLUTTER_IS_PAINT_NODE (node));
+ 
+-  g_free (node->name);
+-  node->name = g_strdup (name);
++  node->name = g_intern_string (name);
++}
++
++/**
++ * clutter_paint_node_set_static_name: (skip)
++ *
++ * Like clutter_paint_node_set_name() but uses a static or interned string
++ * containing the name.
++ */
++void
++clutter_paint_node_set_static_name (ClutterPaintNode *node,
++                                    const char       *name)
++{
++  g_return_if_fail (CLUTTER_IS_PAINT_NODE (node));
++
++  node->name = name;
+ }
+ 
+ /**
+diff --git a/clutter/clutter/clutter-paint-node.h b/clutter/clutter/clutter-paint-node.h
+index c42abbc3d..7d25f1681 100644
+--- a/clutter/clutter/clutter-paint-node.h
++++ b/clutter/clutter/clutter-paint-node.h
+@@ -55,6 +55,9 @@ void                    clutter_paint_node_paint                        (Clutter
+ CLUTTER_EXPORT
+ void                    clutter_paint_node_set_name                     (ClutterPaintNode      *node,
+                                                                          const char            *name);
++CLUTTER_EXPORT
++void                    clutter_paint_node_set_static_name              (ClutterPaintNode      *node,
++                                                                         const char            *name);
+ 
+ CLUTTER_EXPORT
+ void                    clutter_paint_node_add_child                    (ClutterPaintNode      *node,
+-- 
+2.26.0
+
diff --git a/SOURCES/0003-screen-cast-Move-DMA-buffer-allocation-to-MetaScreen.patch b/SOURCES/0003-screen-cast-Move-DMA-buffer-allocation-to-MetaScreen.patch
new file mode 100644
index 0000000..8ef52a5
--- /dev/null
+++ b/SOURCES/0003-screen-cast-Move-DMA-buffer-allocation-to-MetaScreen.patch
@@ -0,0 +1,108 @@
+From 914fd2bec65c2e9928b03d5bc94930bc0151f998 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Wed, 17 Jun 2020 17:48:05 +0200
+Subject: [PATCH 3/4] screen-cast: Move DMA buffer allocation to MetaScreenCast
+
+The aim with centralizing it is to be able to apply global policy to DMA
+buffer allocations, e.g. disabling due to various hueristics.
+
+https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1318
+---
+ src/backends/meta-screen-cast-stream-src.c | 18 ++++++--------
+ src/backends/meta-screen-cast.c            | 28 ++++++++++++++++++++++
+ src/backends/meta-screen-cast.h            |  4 ++++
+ 3 files changed, 39 insertions(+), 11 deletions(-)
+
+diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c
+index ff4af440c1..b77186415f 100644
+--- a/src/backends/meta-screen-cast-stream-src.c
++++ b/src/backends/meta-screen-cast-stream-src.c
+@@ -649,10 +649,10 @@ on_stream_add_buffer (void             *data,
+   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;
++  MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src);
++  MetaScreenCastSession *session = meta_screen_cast_stream_get_session (stream);
++  MetaScreenCast *screen_cast =
++    meta_screen_cast_session_get_screen_cast (session);
+   CoglDmaBufHandle *dmabuf_handle;
+   struct spa_buffer *spa_buffer = buffer->buffer;
+   struct spa_data *spa_data = spa_buffer->datas;
+@@ -664,13 +664,9 @@ on_stream_add_buffer (void             *data,
+   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);
++  dmabuf_handle = meta_screen_cast_create_dma_buf_handle (screen_cast,
++                                                          priv->stream_width,
++                                                          priv->stream_height);
+ 
+   if (dmabuf_handle)
+     {
+diff --git a/src/backends/meta-screen-cast.c b/src/backends/meta-screen-cast.c
+index 46bc268389..5f1ca8b5ca 100644
+--- a/src/backends/meta-screen-cast.c
++++ b/src/backends/meta-screen-cast.c
+@@ -94,6 +94,34 @@ meta_screen_cast_get_backend (MetaScreenCast *screen_cast)
+   return screen_cast->backend;
+ }
+ 
++CoglDmaBufHandle *
++meta_screen_cast_create_dma_buf_handle (MetaScreenCast *screen_cast,
++                                        int             width,
++                                        int             height)
++{
++  ClutterBackend *clutter_backend =
++    meta_backend_get_clutter_backend (screen_cast->backend);
++  CoglContext *cogl_context =
++    clutter_backend_get_cogl_context (clutter_backend);
++  CoglRenderer *cogl_renderer = cogl_context_get_renderer (cogl_context);
++  g_autoptr (GError) error = NULL;
++  CoglDmaBufHandle *dmabuf_handle;
++
++  dmabuf_handle = cogl_renderer_create_dma_buf (cogl_renderer,
++                                                width, height,
++                                                &error);
++  if (!dmabuf_handle)
++    {
++      g_warning ("Failed to allocate DMA buffer, "
++                 "disabling DMA buffer based screen casting: %s",
++                 error->message);
++      screen_cast->disable_dma_bufs = TRUE;
++      return NULL;
++    }
++
++  return dmabuf_handle;
++}
++
+ static gboolean
+ register_remote_desktop_screen_cast_session (MetaScreenCastSession  *session,
+                                              const char             *remote_desktop_session_id,
+diff --git a/src/backends/meta-screen-cast.h b/src/backends/meta-screen-cast.h
+index a3b650cd80..fb5a38f34f 100644
+--- a/src/backends/meta-screen-cast.h
++++ b/src/backends/meta-screen-cast.h
+@@ -50,6 +50,10 @@ GDBusConnection * meta_screen_cast_get_connection (MetaScreenCast *screen_cast);
+ 
+ MetaBackend * meta_screen_cast_get_backend (MetaScreenCast *screen_cast);
+ 
++CoglDmaBufHandle * meta_screen_cast_create_dma_buf_handle (MetaScreenCast *screen_cast,
++                                                           int             width,
++                                                           int             height);
++
+ MetaScreenCast * meta_screen_cast_new (MetaBackend            *backend,
+                                        MetaDbusSessionWatcher *session_watcher);
+ 
+-- 
+2.26.2
+
diff --git a/SOURCES/0004-screen-cast-Disable-DMA-buffer-based-screen-casting-.patch b/SOURCES/0004-screen-cast-Disable-DMA-buffer-based-screen-casting-.patch
new file mode 100644
index 0000000..6963888
--- /dev/null
+++ b/SOURCES/0004-screen-cast-Disable-DMA-buffer-based-screen-casting-.patch
@@ -0,0 +1,209 @@
+From a239886e159e6609c3e298effbd0243af8d0e333 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Tue, 16 Jun 2020 11:30:11 +0200
+Subject: [PATCH 4/4] screen-cast: Disable DMA buffer based screen casting for
+ QXL
+
+QXL doesn't support mmap():ing a DMA buffer allocated in mutter inside
+the PipeWire stream consumer process. To make screen casting work again
+on QXL, disable DMA buffer based screen casting for QXL.
+
+Eventually, it should be the client that renegotiates the supported
+buffer types, but until then we need this list.
+
+https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1318
+---
+ src/backends/meta-screen-cast.c           | 11 ++++++
+ src/backends/meta-screen-cast.h           |  2 ++
+ src/backends/native/meta-backend-native.c | 41 +++++++++++++++++++++++
+ src/backends/native/meta-gpu-kms.c        | 26 ++++++++++++++
+ src/backends/native/meta-gpu-kms.h        |  2 ++
+ 5 files changed, 82 insertions(+)
+
+diff --git a/src/backends/meta-screen-cast.c b/src/backends/meta-screen-cast.c
+index 5f1ca8b5ca..268155e7b3 100644
+--- a/src/backends/meta-screen-cast.c
++++ b/src/backends/meta-screen-cast.c
+@@ -46,6 +46,8 @@ struct _MetaScreenCast
+ 
+   MetaDbusSessionWatcher *session_watcher;
+   MetaBackend *backend;
++
++  gboolean disable_dma_bufs;
+ };
+ 
+ static void
+@@ -94,6 +96,12 @@ meta_screen_cast_get_backend (MetaScreenCast *screen_cast)
+   return screen_cast->backend;
+ }
+ 
++void
++meta_screen_cast_disable_dma_bufs (MetaScreenCast *screen_cast)
++{
++  screen_cast->disable_dma_bufs = TRUE;
++}
++
+ CoglDmaBufHandle *
+ meta_screen_cast_create_dma_buf_handle (MetaScreenCast *screen_cast,
+                                         int             width,
+@@ -107,6 +115,9 @@ meta_screen_cast_create_dma_buf_handle (MetaScreenCast *screen_cast,
+   g_autoptr (GError) error = NULL;
+   CoglDmaBufHandle *dmabuf_handle;
+ 
++  if (screen_cast->disable_dma_bufs)
++    return NULL;
++
+   dmabuf_handle = cogl_renderer_create_dma_buf (cogl_renderer,
+                                                 width, height,
+                                                 &error);
+diff --git a/src/backends/meta-screen-cast.h b/src/backends/meta-screen-cast.h
+index fb5a38f34f..fa54be729f 100644
+--- a/src/backends/meta-screen-cast.h
++++ b/src/backends/meta-screen-cast.h
+@@ -50,6 +50,8 @@ GDBusConnection * meta_screen_cast_get_connection (MetaScreenCast *screen_cast);
+ 
+ MetaBackend * meta_screen_cast_get_backend (MetaScreenCast *screen_cast);
+ 
++void meta_screen_cast_disable_dma_bufs (MetaScreenCast *screen_cast);
++
+ CoglDmaBufHandle * meta_screen_cast_create_dma_buf_handle (MetaScreenCast *screen_cast,
+                                                            int             width,
+                                                            int             height);
+diff --git a/src/backends/native/meta-backend-native.c b/src/backends/native/meta-backend-native.c
+index c473681cb0..2bf7f5e7e2 100644
+--- a/src/backends/native/meta-backend-native.c
++++ b/src/backends/native/meta-backend-native.c
+@@ -57,6 +57,10 @@
+ #include "core/meta-border.h"
+ #include "meta/main.h"
+ 
++#ifdef HAVE_REMOTE_DESKTOP
++#include "backends/meta-screen-cast.h"
++#endif
++
+ struct _MetaBackendNative
+ {
+   MetaBackend parent;
+@@ -327,6 +331,39 @@ meta_backend_native_create_clutter_backend (MetaBackend *backend)
+   return g_object_new (META_TYPE_CLUTTER_BACKEND_NATIVE, NULL);
+ }
+ 
++#ifdef HAVE_REMOTE_DESKTOP
++static void
++maybe_disable_screen_cast_dma_bufs (MetaBackendNative *native)
++{
++  MetaBackend *backend = META_BACKEND (native);
++  MetaRenderer *renderer = meta_backend_get_renderer (backend);
++  MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer);
++  MetaGpuKms *primary_gpu;
++  const char *driver_name;
++  int i;
++  static const char *disable_dma_buf_drivers[] = {
++    "qxl",
++  };
++
++  primary_gpu = meta_renderer_native_get_primary_gpu (renderer_native);
++  driver_name = meta_gpu_kms_get_driver_name (primary_gpu);
++
++  for (i = 0; i < G_N_ELEMENTS (disable_dma_buf_drivers); i++)
++    {
++      if (g_strcmp0 (driver_name, disable_dma_buf_drivers[i]) == 0)
++        {
++          MetaScreenCast *screen_cast = meta_backend_get_screen_cast (backend);
++
++          g_message ("The '%s' driver doesn't support DMA buffer screen sharing, disabling.",
++                     driver_name);
++
++          meta_screen_cast_disable_dma_bufs (screen_cast);
++          return;
++        }
++    }
++}
++#endif /* HAVE_REMOTE_DESKTOP */
++
+ static void
+ meta_backend_native_post_init (MetaBackend *backend)
+ {
+@@ -338,6 +375,10 @@ meta_backend_native_post_init (MetaBackend *backend)
+                                                 NULL, NULL);
+   clutter_evdev_set_relative_motion_filter (manager, relative_motion_filter,
+                                             meta_backend_get_monitor_manager (backend));
++
++#ifdef HAVE_REMOTE_DESKTOP
++  maybe_disable_screen_cast_dma_bufs (META_BACKEND_NATIVE (backend));
++#endif
+ }
+ 
+ static MetaMonitorManager *
+diff --git a/src/backends/native/meta-gpu-kms.c b/src/backends/native/meta-gpu-kms.c
+index c569b948ef..93e509def5 100644
+--- a/src/backends/native/meta-gpu-kms.c
++++ b/src/backends/native/meta-gpu-kms.c
+@@ -66,6 +66,8 @@ struct _MetaGpuKms
+   char *file_path;
+   GSource *source;
+ 
++  char *driver_name;
++
+   clockid_t clock_id;
+ 
+   drmModeConnector **connectors;
+@@ -790,6 +792,27 @@ init_outputs (MetaGpuKms       *gpu_kms,
+   setup_output_clones (gpu);
+ }
+ 
++static void
++init_info (MetaGpuKms *gpu_kms)
++{
++  drmVersion *drm_version;
++
++  drm_version = drmGetVersion (gpu_kms->fd);
++  if (!drm_version)
++    return;
++
++  gpu_kms->driver_name = g_strndup (drm_version->name,
++                                    drm_version->name_len);
++
++  drmFreeVersion (drm_version);
++}
++
++const char *
++meta_gpu_kms_get_driver_name (MetaGpuKms *gpu_kms)
++{
++  return gpu_kms->driver_name;
++}
++
+ static gboolean
+ meta_kms_resources_init (MetaKmsResources  *resources,
+                          int                fd,
+@@ -865,6 +888,7 @@ meta_gpu_kms_read_current (MetaGpu  *gpu,
+   init_crtcs (gpu_kms, &resources);
+   init_outputs (gpu_kms, &resources);
+   init_frame_clock (gpu_kms);
++  init_info (gpu_kms);
+ 
+   meta_kms_resources_release (&resources);
+ 
+@@ -940,6 +964,8 @@ meta_gpu_kms_finalize (GObject *object)
+ 
+   free_resources (gpu_kms);
+ 
++  g_free (gpu_kms->driver_name);
++
+   G_OBJECT_CLASS (meta_gpu_kms_parent_class)->finalize (object);
+ }
+ 
+diff --git a/src/backends/native/meta-gpu-kms.h b/src/backends/native/meta-gpu-kms.h
+index 1f7a939e27..6096e58341 100644
+--- a/src/backends/native/meta-gpu-kms.h
++++ b/src/backends/native/meta-gpu-kms.h
+@@ -108,4 +108,6 @@ MetaGpuKmsFlipClosureContainer * meta_gpu_kms_wrap_flip_closure (MetaGpuKms *gpu
+ 
+ void meta_gpu_kms_flip_closure_container_free (MetaGpuKmsFlipClosureContainer *closure_container);
+ 
++const char * meta_gpu_kms_get_driver_name (MetaGpuKms *gpu_kms);
++
+ #endif /* META_GPU_KMS_H */
+-- 
+2.26.2
+
diff --git a/SOURCES/cursor-move-only-screen-cast-fixes.patch b/SOURCES/cursor-move-only-screen-cast-fixes.patch
new file mode 100644
index 0000000..03d9814
--- /dev/null
+++ b/SOURCES/cursor-move-only-screen-cast-fixes.patch
@@ -0,0 +1,1115 @@
+From 30caca0cb389dcbbab3d7ba72b92fce8e243b30b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Fri, 3 Jul 2020 16:42:45 +0200
+Subject: [PATCH 1/9] screen-cast-src: Make the two record vfuncs more
+ similarly named
+
+Both do more or less the same but with different methods - one puts
+pixels into a buffer using the CPU, the other puts pixels into a buffer
+using the GPU.
+
+However, they are behaving slightly different, which they shouldn't.
+Lets first address the misleading disconnect in naming, and later we'll
+make them behave more similarly.
+
+https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1351
+(cherry picked from commit d07335cd4ca094d790eac75e75cff01a28fda827)
+---
+ .../meta-screen-cast-monitor-stream-src.c       | 15 ++++++++-------
+ src/backends/meta-screen-cast-stream-src.c      | 17 +++++++++--------
+ src/backends/meta-screen-cast-stream-src.h      |  8 ++++----
+ .../meta-screen-cast-window-stream-src.c        | 15 ++++++++-------
+ 4 files changed, 29 insertions(+), 26 deletions(-)
+
+diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c b/src/backends/meta-screen-cast-monitor-stream-src.c
+index 655b682610..a1a98eb05b 100644
+--- a/src/backends/meta-screen-cast-monitor-stream-src.c
++++ b/src/backends/meta-screen-cast-monitor-stream-src.c
+@@ -347,8 +347,8 @@ meta_screen_cast_monitor_stream_src_disable (MetaScreenCastStreamSrc *src)
+ }
+ 
+ static gboolean
+-meta_screen_cast_monitor_stream_src_record_frame (MetaScreenCastStreamSrc *src,
+-                                                  uint8_t                 *data)
++meta_screen_cast_monitor_stream_src_record_to_buffer (MetaScreenCastStreamSrc *src,
++                                                      uint8_t                 *data)
+ {
+   MetaScreenCastMonitorStreamSrc *monitor_src =
+     META_SCREEN_CAST_MONITOR_STREAM_SRC (src);
+@@ -368,8 +368,8 @@ meta_screen_cast_monitor_stream_src_record_frame (MetaScreenCastStreamSrc *src,
+ }
+ 
+ static gboolean
+-meta_screen_cast_monitor_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc *src,
+-                                                         CoglFramebuffer         *framebuffer)
++meta_screen_cast_monitor_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src,
++                                                           CoglFramebuffer         *framebuffer)
+ {
+   MetaScreenCastMonitorStreamSrc *monitor_src =
+     META_SCREEN_CAST_MONITOR_STREAM_SRC (src);
+@@ -551,9 +551,10 @@ meta_screen_cast_monitor_stream_src_class_init (MetaScreenCastMonitorStreamSrcCl
+   src_class->get_specs = meta_screen_cast_monitor_stream_src_get_specs;
+   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->record_to_buffer =
++    meta_screen_cast_monitor_stream_src_record_to_buffer;
++  src_class->record_to_framebuffer =
++    meta_screen_cast_monitor_stream_src_record_to_framebuffer;
+   src_class->set_cursor_metadata =
+     meta_screen_cast_monitor_stream_src_set_cursor_metadata;
+ }
+diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c
+index b77186415f..bafb82388d 100644
+--- a/src/backends/meta-screen-cast-stream-src.c
++++ b/src/backends/meta-screen-cast-stream-src.c
+@@ -133,23 +133,23 @@ meta_screen_cast_stream_src_get_videocrop (MetaScreenCastStreamSrc *src,
+ }
+ 
+ static gboolean
+-meta_screen_cast_stream_src_record_frame (MetaScreenCastStreamSrc *src,
+-                                          uint8_t                 *data)
++meta_screen_cast_stream_src_record_to_buffer (MetaScreenCastStreamSrc *src,
++                                              uint8_t                 *data)
+ {
+   MetaScreenCastStreamSrcClass *klass =
+     META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src);
+ 
+-  return klass->record_frame (src, data);
++  return klass->record_to_buffer (src, data);
+ }
+ 
+ static gboolean
+-meta_screen_cast_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc *src,
+-                                                 CoglFramebuffer         *framebuffer)
++meta_screen_cast_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src,
++                                                   CoglFramebuffer         *framebuffer)
+ {
+   MetaScreenCastStreamSrcClass *klass =
+     META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src);
+ 
+-  return klass->blit_to_framebuffer (src, framebuffer);
++  return klass->record_to_framebuffer (src, framebuffer);
+ }
+ 
+ static void
+@@ -417,7 +417,7 @@ do_record_frame (MetaScreenCastStreamSrc *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);
++      return meta_screen_cast_stream_src_record_to_buffer (src, data);
+     }
+   else if (spa_buffer->datas[0].type == SPA_DATA_DmaBuf)
+     {
+@@ -427,7 +427,8 @@ do_record_frame (MetaScreenCastStreamSrc *src,
+       CoglFramebuffer *dmabuf_fbo =
+         cogl_dma_buf_handle_get_framebuffer (dmabuf_handle);
+ 
+-      return meta_screen_cast_stream_src_blit_to_framebuffer (src, dmabuf_fbo);
++      return meta_screen_cast_stream_src_record_to_framebuffer (src,
++                                                                dmabuf_fbo);
+     }
+ 
+   return FALSE;
+diff --git a/src/backends/meta-screen-cast-stream-src.h b/src/backends/meta-screen-cast-stream-src.h
+index 3f6a1af2bb..0eda02f717 100644
+--- a/src/backends/meta-screen-cast-stream-src.h
++++ b/src/backends/meta-screen-cast-stream-src.h
+@@ -53,10 +53,10 @@ struct _MetaScreenCastStreamSrcClass
+                       float                   *frame_rate);
+   void (* enable) (MetaScreenCastStreamSrc *src);
+   void (* disable) (MetaScreenCastStreamSrc *src);
+-  gboolean (* record_frame) (MetaScreenCastStreamSrc *src,
+-                             uint8_t                 *data);
+-  gboolean (* blit_to_framebuffer) (MetaScreenCastStreamSrc *src,
+-                                    CoglFramebuffer         *framebuffer);
++  gboolean (* record_to_buffer) (MetaScreenCastStreamSrc *src,
++                                 uint8_t                 *data);
++  gboolean (* record_to_framebuffer) (MetaScreenCastStreamSrc *src,
++                                      CoglFramebuffer         *framebuffer);
+   gboolean (* get_videocrop) (MetaScreenCastStreamSrc *src,
+                               MetaRectangle           *crop_rect);
+   void (* set_cursor_metadata) (MetaScreenCastStreamSrc *src,
+diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c
+index c252b4356b..281df5e7b2 100644
+--- a/src/backends/meta-screen-cast-window-stream-src.c
++++ b/src/backends/meta-screen-cast-window-stream-src.c
+@@ -462,8 +462,8 @@ meta_screen_cast_window_stream_src_disable (MetaScreenCastStreamSrc *src)
+ }
+ 
+ static gboolean
+-meta_screen_cast_window_stream_src_record_frame (MetaScreenCastStreamSrc *src,
+-                                                 uint8_t                 *data)
++meta_screen_cast_window_stream_src_record_to_buffer (MetaScreenCastStreamSrc *src,
++                                                     uint8_t                 *data)
+ {
+   MetaScreenCastWindowStreamSrc *window_src =
+     META_SCREEN_CAST_WINDOW_STREAM_SRC (src);
+@@ -474,8 +474,8 @@ meta_screen_cast_window_stream_src_record_frame (MetaScreenCastStreamSrc *src,
+ }
+ 
+ static gboolean
+-meta_screen_cast_window_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc *src,
+-                                                        CoglFramebuffer         *framebuffer)
++meta_screen_cast_window_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src,
++                                                          CoglFramebuffer         *framebuffer)
+ {
+   MetaScreenCastWindowStreamSrc *window_src =
+     META_SCREEN_CAST_WINDOW_STREAM_SRC (src);
+@@ -591,9 +591,10 @@ meta_screen_cast_window_stream_src_class_init (MetaScreenCastWindowStreamSrcClas
+   src_class->get_specs = meta_screen_cast_window_stream_src_get_specs;
+   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->record_to_buffer =
++    meta_screen_cast_window_stream_src_record_to_buffer;
++  src_class->record_to_framebuffer =
++    meta_screen_cast_window_stream_src_record_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;
+ }
+-- 
+2.26.2
+
+
+From ddc2094222fb55143922364cd4887cb18f856628 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Fri, 3 Jul 2020 16:46:44 +0200
+Subject: [PATCH 2/9] screen-cast/window-stream-src: Fix indentation
+
+https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1351
+(cherry picked from commit b1d45820efc5c9136f12d8a3b97a573a2eede9e7)
+---
+ src/backends/meta-screen-cast-window-stream-src.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c
+index 281df5e7b2..abdc791575 100644
+--- a/src/backends/meta-screen-cast-window-stream-src.c
++++ b/src/backends/meta-screen-cast-window-stream-src.c
+@@ -488,8 +488,8 @@ meta_screen_cast_window_stream_src_record_to_framebuffer (MetaScreenCastStreamSr
+   stream_rect.height = get_stream_height (window_src);
+ 
+   if (!meta_screen_cast_window_blit_to_framebuffer (window_src->screen_cast_window,
+-                                                     &stream_rect,
+-                                                     framebuffer))
++                                                    &stream_rect,
++                                                    framebuffer))
+     return FALSE;
+ 
+   stream = meta_screen_cast_stream_src_get_stream (src);
+-- 
+2.26.2
+
+
+From 59382848840aeb5c6491412742f474a3fb61639e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Fri, 3 Jul 2020 16:48:52 +0200
+Subject: [PATCH 3/9] screen-cast/src: Add flag to maybe_record()
+
+Will later be used to make recording avoid recording actual pixel
+content if e.g. only the cursor moved.
+
+https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1351
+(cherry picked from commit 92db8902d9c3229a13d104bba71dd74f14d6dfac)
+---
+ src/backends/meta-screen-cast-monitor-stream-src.c |  8 ++++++--
+ src/backends/meta-screen-cast-stream-src.c         |  3 ++-
+ src/backends/meta-screen-cast-stream-src.h         |  8 +++++++-
+ src/backends/meta-screen-cast-window-stream-src.c  | 12 +++++++++---
+ 4 files changed, 24 insertions(+), 7 deletions(-)
+
+diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c b/src/backends/meta-screen-cast-monitor-stream-src.c
+index a1a98eb05b..8d57fafc0f 100644
+--- a/src/backends/meta-screen-cast-monitor-stream-src.c
++++ b/src/backends/meta-screen-cast-monitor-stream-src.c
+@@ -120,8 +120,10 @@ stage_painted (MetaStage        *stage,
+                gpointer          user_data)
+ {
+   MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (user_data);
++  MetaScreenCastRecordFlag flags;
+ 
+-  meta_screen_cast_stream_src_maybe_record_frame (src);
++  flags = META_SCREEN_CAST_RECORD_FLAG_NONE;
++  meta_screen_cast_stream_src_maybe_record_frame (src, flags);
+ }
+ 
+ static MetaBackend *
+@@ -180,6 +182,7 @@ sync_cursor_state (MetaScreenCastMonitorStreamSrc *monitor_src)
+ {
+   MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (monitor_src);
+   ClutterStage *stage = get_stage (monitor_src);
++  MetaScreenCastRecordFlag flags;
+ 
+   if (!is_cursor_in_stream (monitor_src))
+     return;
+@@ -187,7 +190,8 @@ sync_cursor_state (MetaScreenCastMonitorStreamSrc *monitor_src)
+   if (clutter_stage_is_redraw_queued (stage))
+     return;
+ 
+-  meta_screen_cast_stream_src_maybe_record_frame (src);
++  flags = META_SCREEN_CAST_RECORD_FLAG_NONE;
++  meta_screen_cast_stream_src_maybe_record_frame (src, flags);
+ }
+ 
+ static void
+diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c
+index bafb82388d..303c030be7 100644
+--- a/src/backends/meta-screen-cast-stream-src.c
++++ b/src/backends/meta-screen-cast-stream-src.c
+@@ -435,7 +435,8 @@ do_record_frame (MetaScreenCastStreamSrc *src,
+ }
+ 
+ void
+-meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src)
++meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc  *src,
++                                                MetaScreenCastRecordFlag  flags)
+ {
+   MetaScreenCastStreamSrcPrivate *priv =
+     meta_screen_cast_stream_src_get_instance_private (src);
+diff --git a/src/backends/meta-screen-cast-stream-src.h b/src/backends/meta-screen-cast-stream-src.h
+index 0eda02f717..6c73d05c1d 100644
+--- a/src/backends/meta-screen-cast-stream-src.h
++++ b/src/backends/meta-screen-cast-stream-src.h
+@@ -37,6 +37,11 @@
+ 
+ typedef struct _MetaScreenCastStream MetaScreenCastStream;
+ 
++typedef enum _MetaScreenCastRecordFlag
++{
++  META_SCREEN_CAST_RECORD_FLAG_NONE = 0,
++} MetaScreenCastRecordFlag;
++
+ #define META_TYPE_SCREEN_CAST_STREAM_SRC (meta_screen_cast_stream_src_get_type ())
+ G_DECLARE_DERIVABLE_TYPE (MetaScreenCastStreamSrc,
+                           meta_screen_cast_stream_src,
+@@ -63,7 +68,8 @@ struct _MetaScreenCastStreamSrcClass
+                                 struct spa_meta_cursor  *spa_meta_cursor);
+ };
+ 
+-void meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src);
++void meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc  *src,
++                                                     MetaScreenCastRecordFlag  flags);
+ 
+ MetaScreenCastStream * meta_screen_cast_stream_src_get_stream (MetaScreenCastStreamSrc *src);
+ 
+diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c
+index abdc791575..f64d00860a 100644
+--- a/src/backends/meta-screen-cast-window-stream-src.c
++++ b/src/backends/meta-screen-cast-window-stream-src.c
+@@ -338,8 +338,10 @@ screen_cast_window_damaged (MetaWindowActor               *actor,
+                             MetaScreenCastWindowStreamSrc *window_src)
+ {
+   MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (window_src);
++  MetaScreenCastRecordFlag flags;
+ 
+-  meta_screen_cast_stream_src_maybe_record_frame (src);
++  flags = META_SCREEN_CAST_RECORD_FLAG_NONE;
++  meta_screen_cast_stream_src_maybe_record_frame (src, flags);
+ }
+ 
+ static void
+@@ -376,6 +378,7 @@ static void
+ sync_cursor_state (MetaScreenCastWindowStreamSrc *window_src)
+ {
+   MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (window_src);
++  MetaScreenCastRecordFlag flags;
+ 
+   if (!is_cursor_in_stream (window_src))
+     return;
+@@ -383,7 +386,8 @@ sync_cursor_state (MetaScreenCastWindowStreamSrc *window_src)
+   if (meta_screen_cast_window_has_damage (window_src->screen_cast_window))
+     return;
+ 
+-  meta_screen_cast_stream_src_maybe_record_frame (src);
++  flags = META_SCREEN_CAST_RECORD_FLAG_NONE;
++  meta_screen_cast_stream_src_maybe_record_frame (src, flags);
+ }
+ 
+ static void
+@@ -412,6 +416,7 @@ meta_screen_cast_window_stream_src_enable (MetaScreenCastStreamSrc *src)
+   MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend);
+   MetaWindowActor *window_actor;
+   MetaScreenCastStream *stream;
++  MetaScreenCastRecordFlag flags;
+ 
+   window_actor = meta_window_actor_from_window (get_window (window_src));
+   if (!window_actor)
+@@ -449,7 +454,8 @@ meta_screen_cast_window_stream_src_enable (MetaScreenCastStreamSrc *src)
+       break;
+     }
+ 
+-  meta_screen_cast_stream_src_maybe_record_frame (src);
++  flags = META_SCREEN_CAST_RECORD_FLAG_NONE;
++  meta_screen_cast_stream_src_maybe_record_frame (src, flags);
+ }
+ 
+ static void
+-- 
+2.26.2
+
+
+From b8d76f2ded6a0c8b88403d97d4ea2c84993c0263 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Fri, 3 Jul 2020 16:52:43 +0200
+Subject: [PATCH 4/9] screen-cast: Let the reason for recording determine what
+ to record
+
+E.g. we'll have pointer movement that, if no painting is already
+scheduled, should only send new cursor metadata without any new pixel
+buffer. When this happens, tell next step to not record the pixels if
+this was the case, instead of having it rediscover this itself.
+
+Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1323
+https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1351
+(cherry picked from commit cf88d648822eb6b7d412c08d4038c657d415bfff)
+---
+ .../meta-screen-cast-monitor-stream-src.c     |  5 +-
+ src/backends/meta-screen-cast-stream-src.c    | 51 +++++++++++--------
+ src/backends/meta-screen-cast-stream-src.h    |  1 +
+ .../meta-screen-cast-window-stream-src.c      |  4 +-
+ 4 files changed, 33 insertions(+), 28 deletions(-)
+
+diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c b/src/backends/meta-screen-cast-monitor-stream-src.c
+index 8d57fafc0f..2352c3b3d8 100644
+--- a/src/backends/meta-screen-cast-monitor-stream-src.c
++++ b/src/backends/meta-screen-cast-monitor-stream-src.c
+@@ -190,7 +190,7 @@ sync_cursor_state (MetaScreenCastMonitorStreamSrc *monitor_src)
+   if (clutter_stage_is_redraw_queued (stage))
+     return;
+ 
+-  flags = META_SCREEN_CAST_RECORD_FLAG_NONE;
++  flags = META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY;
+   meta_screen_cast_stream_src_maybe_record_frame (src, flags);
+ }
+ 
+@@ -361,9 +361,6 @@ meta_screen_cast_monitor_stream_src_record_to_buffer (MetaScreenCastStreamSrc *s
+   MetaLogicalMonitor *logical_monitor;
+ 
+   stage = get_stage (monitor_src);
+-  if (!clutter_stage_is_redraw_queued (stage))
+-    return FALSE;
+-
+   monitor = get_monitor (monitor_src);
+   logical_monitor = meta_monitor_get_logical_monitor (monitor);
+   clutter_stage_capture_into (stage, FALSE, &logical_monitor->rect, data);
+diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c
+index 303c030be7..aa4b03b180 100644
+--- a/src/backends/meta-screen-cast-stream-src.c
++++ b/src/backends/meta-screen-cast-stream-src.c
+@@ -470,34 +470,41 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc  *src,
+       return;
+     }
+ 
+-  if (do_record_frame (src, spa_buffer, data))
++  if (!(flags & META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY))
+     {
+-      struct spa_meta_region *spa_meta_video_crop;
++      if (do_record_frame (src, spa_buffer, data))
++        {
++          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;
++          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_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->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
++          /* Update VideoCrop if needed */
++          spa_meta_video_crop =
++            spa_buffer_find_meta_data (spa_buffer, SPA_META_VideoCrop,
++                                       sizeof (*spa_meta_video_crop));
++          if (spa_meta_video_crop)
+             {
+-              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;
++              if (meta_screen_cast_stream_src_get_videocrop (src, &crop_rect))
++                {
++                  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->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;
++                }
+             }
+         }
++      else
++        {
++          spa_buffer->datas[0].chunk->size = 0;
++        }
+     }
+   else
+     {
+diff --git a/src/backends/meta-screen-cast-stream-src.h b/src/backends/meta-screen-cast-stream-src.h
+index 6c73d05c1d..87054eedf5 100644
+--- a/src/backends/meta-screen-cast-stream-src.h
++++ b/src/backends/meta-screen-cast-stream-src.h
+@@ -40,6 +40,7 @@ typedef struct _MetaScreenCastStream MetaScreenCastStream;
+ typedef enum _MetaScreenCastRecordFlag
+ {
+   META_SCREEN_CAST_RECORD_FLAG_NONE = 0,
++  META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY = 1 << 0,
+ } MetaScreenCastRecordFlag;
+ 
+ #define META_TYPE_SCREEN_CAST_STREAM_SRC (meta_screen_cast_stream_src_get_type ())
+diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c
+index f64d00860a..63c3429df0 100644
+--- a/src/backends/meta-screen-cast-window-stream-src.c
++++ b/src/backends/meta-screen-cast-window-stream-src.c
+@@ -340,7 +340,7 @@ screen_cast_window_damaged (MetaWindowActor               *actor,
+   MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (window_src);
+   MetaScreenCastRecordFlag flags;
+ 
+-  flags = META_SCREEN_CAST_RECORD_FLAG_NONE;
++  flags = META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY;
+   meta_screen_cast_stream_src_maybe_record_frame (src, flags);
+ }
+ 
+@@ -386,7 +386,7 @@ sync_cursor_state (MetaScreenCastWindowStreamSrc *window_src)
+   if (meta_screen_cast_window_has_damage (window_src->screen_cast_window))
+     return;
+ 
+-  flags = META_SCREEN_CAST_RECORD_FLAG_NONE;
++  flags = META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY;
+   meta_screen_cast_stream_src_maybe_record_frame (src, flags);
+ }
+ 
+-- 
+2.26.2
+
+
+From 5c925cf4de91c9fdd44cb1c13748b2f4d6187dd9 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Fri, 3 Jul 2020 16:57:01 +0200
+Subject: [PATCH 5/9] screen-cast/src: Make record functions return an error
+ when failing
+
+Now that we don't use the record function to early out depending on
+implicit state (don't record pixels if only cursor moved for example),
+let it simply report an error when it fails, as we should no longer ever
+return without pixels if nothing failed.
+
+https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1351
+(cherry picked from commit 2d899596e21d43ab241d0ba37c0a9f90c2e610be)
+---
+ .../meta-screen-cast-monitor-stream-src.c     | 19 +++++------
+ src/backends/meta-screen-cast-stream-src.c    | 32 ++++++++++++-------
+ src/backends/meta-screen-cast-stream-src.h    | 10 +++---
+ .../meta-screen-cast-window-stream-src.c      | 16 +++++++---
+ 4 files changed, 45 insertions(+), 32 deletions(-)
+
+diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c b/src/backends/meta-screen-cast-monitor-stream-src.c
+index 2352c3b3d8..27b3ea37d8 100644
+--- a/src/backends/meta-screen-cast-monitor-stream-src.c
++++ b/src/backends/meta-screen-cast-monitor-stream-src.c
+@@ -351,8 +351,9 @@ meta_screen_cast_monitor_stream_src_disable (MetaScreenCastStreamSrc *src)
+ }
+ 
+ static gboolean
+-meta_screen_cast_monitor_stream_src_record_to_buffer (MetaScreenCastStreamSrc *src,
+-                                                      uint8_t                 *data)
++meta_screen_cast_monitor_stream_src_record_to_buffer (MetaScreenCastStreamSrc  *src,
++                                                      uint8_t                  *data,
++                                                      GError                  **error)
+ {
+   MetaScreenCastMonitorStreamSrc *monitor_src =
+     META_SCREEN_CAST_MONITOR_STREAM_SRC (src);
+@@ -369,8 +370,9 @@ meta_screen_cast_monitor_stream_src_record_to_buffer (MetaScreenCastStreamSrc *s
+ }
+ 
+ static gboolean
+-meta_screen_cast_monitor_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src,
+-                                                           CoglFramebuffer         *framebuffer)
++meta_screen_cast_monitor_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc  *src,
++                                                           CoglFramebuffer          *framebuffer,
++                                                           GError                  **error)
+ {
+   MetaScreenCastMonitorStreamSrc *monitor_src =
+     META_SCREEN_CAST_MONITOR_STREAM_SRC (src);
+@@ -394,7 +396,6 @@ meta_screen_cast_monitor_stream_src_record_to_framebuffer (MetaScreenCastStreamS
+   for (l = _clutter_stage_peek_stage_views (stage); 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;
+@@ -415,12 +416,8 @@ meta_screen_cast_monitor_stream_src_record_to_framebuffer (MetaScreenCastStreamS
+                                   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;
+-        }
++                                  error))
++        return FALSE;
+     }
+ 
+   cogl_framebuffer_finish (framebuffer);
+diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c
+index aa4b03b180..b930d5e7c0 100644
+--- a/src/backends/meta-screen-cast-stream-src.c
++++ b/src/backends/meta-screen-cast-stream-src.c
+@@ -133,23 +133,25 @@ meta_screen_cast_stream_src_get_videocrop (MetaScreenCastStreamSrc *src,
+ }
+ 
+ static gboolean
+-meta_screen_cast_stream_src_record_to_buffer (MetaScreenCastStreamSrc *src,
+-                                              uint8_t                 *data)
++meta_screen_cast_stream_src_record_to_buffer (MetaScreenCastStreamSrc  *src,
++                                              uint8_t                  *data,
++                                              GError                  **error)
+ {
+   MetaScreenCastStreamSrcClass *klass =
+     META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src);
+ 
+-  return klass->record_to_buffer (src, data);
++  return klass->record_to_buffer (src, data, error);
+ }
+ 
+ static gboolean
+-meta_screen_cast_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src,
+-                                                   CoglFramebuffer         *framebuffer)
++meta_screen_cast_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc  *src,
++                                                   CoglFramebuffer          *framebuffer,
++                                                   GError                  **error)
+ {
+   MetaScreenCastStreamSrcClass *klass =
+     META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src);
+ 
+-  return klass->record_to_framebuffer (src, framebuffer);
++  return klass->record_to_framebuffer (src, framebuffer, error);
+ }
+ 
+ static void
+@@ -407,9 +409,10 @@ maybe_record_cursor (MetaScreenCastStreamSrc *src,
+ }
+ 
+ static gboolean
+-do_record_frame (MetaScreenCastStreamSrc *src,
+-                 struct spa_buffer       *spa_buffer,
+-                 uint8_t                 *data)
++do_record_frame (MetaScreenCastStreamSrc  *src,
++                 struct spa_buffer        *spa_buffer,
++                 uint8_t                  *data,
++                 GError                  **error)
+ {
+   MetaScreenCastStreamSrcPrivate *priv =
+     meta_screen_cast_stream_src_get_instance_private (src);
+@@ -417,7 +420,7 @@ do_record_frame (MetaScreenCastStreamSrc *src,
+   if (spa_buffer->datas[0].data ||
+       spa_buffer->datas[0].type == SPA_DATA_MemFd)
+     {
+-      return meta_screen_cast_stream_src_record_to_buffer (src, data);
++      return meta_screen_cast_stream_src_record_to_buffer (src, data, error);
+     }
+   else if (spa_buffer->datas[0].type == SPA_DATA_DmaBuf)
+     {
+@@ -428,9 +431,12 @@ do_record_frame (MetaScreenCastStreamSrc *src,
+         cogl_dma_buf_handle_get_framebuffer (dmabuf_handle);
+ 
+       return meta_screen_cast_stream_src_record_to_framebuffer (src,
+-                                                                dmabuf_fbo);
++                                                                dmabuf_fbo,
++                                                                error);
+     }
+ 
++  g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
++               "Unknown SPA buffer type %u", spa_buffer->datas[0].type);
+   return FALSE;
+ }
+ 
+@@ -445,6 +451,7 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc  *src,
+   struct spa_buffer *spa_buffer;
+   uint8_t *data = NULL;
+   uint64_t now_us;
++  g_autoptr (GError) error = NULL;
+ 
+   now_us = g_get_monotonic_time ();
+   if (priv->video_format.max_framerate.num > 0 &&
+@@ -472,7 +479,7 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc  *src,
+ 
+   if (!(flags & META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY))
+     {
+-      if (do_record_frame (src, spa_buffer, data))
++      if (do_record_frame (src, spa_buffer, data, &error))
+         {
+           struct spa_meta_region *spa_meta_video_crop;
+ 
+@@ -503,6 +510,7 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc  *src,
+         }
+       else
+         {
++          g_warning ("Failed to record screen cast frame: %s", error->message);
+           spa_buffer->datas[0].chunk->size = 0;
+         }
+     }
+diff --git a/src/backends/meta-screen-cast-stream-src.h b/src/backends/meta-screen-cast-stream-src.h
+index 87054eedf5..152790ecfb 100644
+--- a/src/backends/meta-screen-cast-stream-src.h
++++ b/src/backends/meta-screen-cast-stream-src.h
+@@ -59,10 +59,12 @@ struct _MetaScreenCastStreamSrcClass
+                       float                   *frame_rate);
+   void (* enable) (MetaScreenCastStreamSrc *src);
+   void (* disable) (MetaScreenCastStreamSrc *src);
+-  gboolean (* record_to_buffer) (MetaScreenCastStreamSrc *src,
+-                                 uint8_t                 *data);
+-  gboolean (* record_to_framebuffer) (MetaScreenCastStreamSrc *src,
+-                                      CoglFramebuffer         *framebuffer);
++  gboolean (* record_to_buffer) (MetaScreenCastStreamSrc  *src,
++                                 uint8_t                  *data,
++                                 GError                  **error);
++  gboolean (* record_to_framebuffer) (MetaScreenCastStreamSrc  *src,
++                                      CoglFramebuffer          *framebuffer,
++                                      GError                  **error);
+   gboolean (* get_videocrop) (MetaScreenCastStreamSrc *src,
+                               MetaRectangle           *crop_rect);
+   void (* set_cursor_metadata) (MetaScreenCastStreamSrc *src,
+diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c
+index 63c3429df0..70e868997e 100644
+--- a/src/backends/meta-screen-cast-window-stream-src.c
++++ b/src/backends/meta-screen-cast-window-stream-src.c
+@@ -468,8 +468,9 @@ meta_screen_cast_window_stream_src_disable (MetaScreenCastStreamSrc *src)
+ }
+ 
+ static gboolean
+-meta_screen_cast_window_stream_src_record_to_buffer (MetaScreenCastStreamSrc *src,
+-                                                     uint8_t                 *data)
++meta_screen_cast_window_stream_src_record_to_buffer (MetaScreenCastStreamSrc  *src,
++                                                     uint8_t                  *data,
++                                                     GError                  **error)
+ {
+   MetaScreenCastWindowStreamSrc *window_src =
+     META_SCREEN_CAST_WINDOW_STREAM_SRC (src);
+@@ -480,8 +481,9 @@ meta_screen_cast_window_stream_src_record_to_buffer (MetaScreenCastStreamSrc *sr
+ }
+ 
+ static gboolean
+-meta_screen_cast_window_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src,
+-                                                          CoglFramebuffer         *framebuffer)
++meta_screen_cast_window_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc  *src,
++                                                          CoglFramebuffer          *framebuffer,
++                                                          GError                  **error)
+ {
+   MetaScreenCastWindowStreamSrc *window_src =
+     META_SCREEN_CAST_WINDOW_STREAM_SRC (src);
+@@ -496,7 +498,11 @@ meta_screen_cast_window_stream_src_record_to_framebuffer (MetaScreenCastStreamSr
+   if (!meta_screen_cast_window_blit_to_framebuffer (window_src->screen_cast_window,
+                                                     &stream_rect,
+                                                     framebuffer))
+-    return FALSE;
++    {
++      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
++                   "Failed to blit window content to framebuffer");
++      return FALSE;
++    }
+ 
+   stream = meta_screen_cast_stream_src_get_stream (src);
+   switch (meta_screen_cast_stream_get_cursor_mode (stream))
+-- 
+2.26.2
+
+
+From 47b03793413aad449f021f64e66c68ca95be9a0f Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Fri, 3 Jul 2020 23:50:28 +0200
+Subject: [PATCH 6/9] screen-cast/src: Fix signedness of timestamp field
+
+https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1351
+(cherry picked from commit 449fa7bf81fe0bee63f497d896cbeffe84dca82d)
+---
+ src/backends/meta-screen-cast-stream-src.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c
+index b930d5e7c0..f6f66daaa3 100644
+--- a/src/backends/meta-screen-cast-stream-src.c
++++ b/src/backends/meta-screen-cast-stream-src.c
+@@ -89,7 +89,7 @@ typedef struct _MetaScreenCastStreamSrcPrivate
+   struct spa_video_info_raw video_format;
+   int video_stride;
+ 
+-  uint64_t last_frame_timestamp_us;
++  int64_t last_frame_timestamp_us;
+ 
+   GHashTable *dmabuf_handles;
+ 
+-- 
+2.26.2
+
+
+From d8c8ea23e700f85a55a5cc0b79151fbed75ab191 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Fri, 3 Jul 2020 23:57:31 +0200
+Subject: [PATCH 7/9] screen-cast/src: Record follow up frame after timeout
+
+During animation or other things that cause multiple frames in a row
+being painted, we might skip recording frames if the max framerate is
+reached.
+
+Doing so means we might end up skipping the last frame in a series,
+ending with the last frame we sent was not the last one, making things
+appear to get stuck sometimes.
+
+Handle this by creating a timeout if we ever throttle, and at the time
+the timeout callback is triggered, make sure we eventually send an up to
+date frame.
+
+This is handle differently depending on the source type. A monitor
+source type reports 1x1 pixel damage on each view its monitor overlaps,
+while a window source type simply records a frame from the surface
+directly, except without recording a timestamp, so that timestamps
+always refer to when damage actually happened.
+
+https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1351
+(cherry picked from commit e8052f169b957a502bf86ca65071582692039b9c)
+---
+ .../meta-screen-cast-monitor-stream-src.c     | 43 +++++++++++
+ src/backends/meta-screen-cast-stream-src.c    | 77 +++++++++++++++++--
+ src/backends/meta-screen-cast-stream-src.h    |  4 +
+ .../meta-screen-cast-window-stream-src.c      | 11 +++
+ 4 files changed, 130 insertions(+), 5 deletions(-)
+
+diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c b/src/backends/meta-screen-cast-monitor-stream-src.c
+index 27b3ea37d8..3079578d8d 100644
+--- a/src/backends/meta-screen-cast-monitor-stream-src.c
++++ b/src/backends/meta-screen-cast-monitor-stream-src.c
+@@ -190,6 +190,9 @@ sync_cursor_state (MetaScreenCastMonitorStreamSrc *monitor_src)
+   if (clutter_stage_is_redraw_queued (stage))
+     return;
+ 
++  if (meta_screen_cast_stream_src_pending_follow_up_frame (src))
++    return;
++
+   flags = META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY;
+   meta_screen_cast_stream_src_maybe_record_frame (src, flags);
+ }
+@@ -425,6 +428,44 @@ meta_screen_cast_monitor_stream_src_record_to_framebuffer (MetaScreenCastStreamS
+   return TRUE;
+ }
+ 
++static void
++meta_screen_cast_monitor_stream_record_follow_up (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);
++  ClutterStage *stage = get_stage (monitor_src);
++  MetaMonitor *monitor;
++  MetaLogicalMonitor *logical_monitor;
++  MetaRectangle logical_monitor_layout;
++  GList *l;
++
++  monitor = get_monitor (monitor_src);
++  logical_monitor = meta_monitor_get_logical_monitor (monitor);
++  logical_monitor_layout = meta_logical_monitor_get_layout (logical_monitor);
++
++  for (l = meta_renderer_get_views (renderer); l; l = l->next)
++    {
++      MetaRendererView *view = l->data;
++      MetaRectangle view_layout;
++      MetaRectangle damage;
++
++      clutter_stage_view_get_layout (CLUTTER_STAGE_VIEW (view), &view_layout);
++
++      if (!meta_rectangle_overlap (&logical_monitor_layout, &view_layout))
++        continue;
++
++      damage = (cairo_rectangle_int_t) {
++        .x = view_layout.x,
++        .y = view_layout.y,
++        .width = 1,
++        .height = 1,
++      };
++      clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stage), &damage);
++    }
++}
++
+ static void
+ meta_screen_cast_monitor_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src,
+                                                          struct spa_meta_cursor  *spa_meta_cursor)
+@@ -553,6 +594,8 @@ meta_screen_cast_monitor_stream_src_class_init (MetaScreenCastMonitorStreamSrcCl
+     meta_screen_cast_monitor_stream_src_record_to_buffer;
+   src_class->record_to_framebuffer =
+     meta_screen_cast_monitor_stream_src_record_to_framebuffer;
++  src_class->record_follow_up =
++    meta_screen_cast_monitor_stream_record_follow_up;
+   src_class->set_cursor_metadata =
+     meta_screen_cast_monitor_stream_src_set_cursor_metadata;
+ }
+diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c
+index f6f66daaa3..55af56f8b9 100644
+--- a/src/backends/meta-screen-cast-stream-src.c
++++ b/src/backends/meta-screen-cast-stream-src.c
+@@ -90,6 +90,7 @@ typedef struct _MetaScreenCastStreamSrcPrivate
+   int video_stride;
+ 
+   int64_t last_frame_timestamp_us;
++  guint follow_up_frame_source_id;
+ 
+   GHashTable *dmabuf_handles;
+ 
+@@ -107,6 +108,12 @@ G_DEFINE_TYPE_WITH_CODE (MetaScreenCastStreamSrc,
+                                                 meta_screen_cast_stream_src_init_initable_iface)
+                          G_ADD_PRIVATE (MetaScreenCastStreamSrc))
+ 
++static inline uint32_t
++us2ms (uint64_t us)
++{
++  return (uint32_t) (us / 1000);
++}
++
+ static void
+ meta_screen_cast_stream_src_get_specs (MetaScreenCastStreamSrc *src,
+                                        int                     *width,
+@@ -154,6 +161,15 @@ meta_screen_cast_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc  *src
+   return klass->record_to_framebuffer (src, framebuffer, error);
+ }
+ 
++static void
++meta_screen_cast_stream_src_record_follow_up (MetaScreenCastStreamSrc *src)
++{
++  MetaScreenCastStreamSrcClass *klass =
++    META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src);
++
++  klass->record_follow_up (src);
++}
++
+ static void
+ meta_screen_cast_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src,
+                                                  struct spa_meta_cursor  *spa_meta_cursor)
+@@ -440,6 +456,43 @@ do_record_frame (MetaScreenCastStreamSrc  *src,
+   return FALSE;
+ }
+ 
++gboolean
++meta_screen_cast_stream_src_pending_follow_up_frame (MetaScreenCastStreamSrc *src)
++{
++  MetaScreenCastStreamSrcPrivate *priv =
++    meta_screen_cast_stream_src_get_instance_private (src);
++
++  return priv->follow_up_frame_source_id != 0;
++}
++
++static gboolean
++follow_up_frame_cb (gpointer user_data)
++{
++  MetaScreenCastStreamSrc *src = user_data;
++  MetaScreenCastStreamSrcPrivate *priv =
++    meta_screen_cast_stream_src_get_instance_private (src);
++
++  priv->follow_up_frame_source_id = 0;
++  meta_screen_cast_stream_src_record_follow_up (src);
++
++  return G_SOURCE_REMOVE;
++}
++
++static void
++maybe_schedule_follow_up_frame (MetaScreenCastStreamSrc *src,
++                                int64_t                  timeout_us)
++{
++  MetaScreenCastStreamSrcPrivate *priv =
++    meta_screen_cast_stream_src_get_instance_private (src);
++
++  if (priv->follow_up_frame_source_id)
++    return;
++
++  priv->follow_up_frame_source_id = g_timeout_add (us2ms (timeout_us),
++                                                   follow_up_frame_cb,
++                                                   src);
++}
++
+ void
+ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc  *src,
+                                                 MetaScreenCastRecordFlag  flags)
+@@ -455,11 +508,24 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc  *src,
+ 
+   now_us = g_get_monotonic_time ();
+   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)))
+-    return;
++      priv->last_frame_timestamp_us != 0)
++    {
++      int64_t min_interval_us;
++      int64_t time_since_last_frame_us;
++
++      min_interval_us = ((1000000 * priv->video_format.max_framerate.denom) /
++                         priv->video_format.max_framerate.num);
++
++      time_since_last_frame_us = now_us - priv->last_frame_timestamp_us;
++      if (time_since_last_frame_us < min_interval_us)
++        {
++          int64_t timeout_us;
++
++          timeout_us = min_interval_us - time_since_last_frame_us;
++          maybe_schedule_follow_up_frame (src, timeout_us);
++          return;
++        }
++    }
+ 
+   if (!priv->pipewire_stream)
+     return;
+@@ -479,6 +545,7 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc  *src,
+ 
+   if (!(flags & META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY))
+     {
++      g_clear_handle_id (&priv->follow_up_frame_source_id, g_source_remove);
+       if (do_record_frame (src, spa_buffer, data, &error))
+         {
+           struct spa_meta_region *spa_meta_video_crop;
+diff --git a/src/backends/meta-screen-cast-stream-src.h b/src/backends/meta-screen-cast-stream-src.h
+index 152790ecfb..81ea20b173 100644
+--- a/src/backends/meta-screen-cast-stream-src.h
++++ b/src/backends/meta-screen-cast-stream-src.h
+@@ -65,6 +65,8 @@ struct _MetaScreenCastStreamSrcClass
+   gboolean (* record_to_framebuffer) (MetaScreenCastStreamSrc  *src,
+                                       CoglFramebuffer          *framebuffer,
+                                       GError                  **error);
++  void (* record_follow_up) (MetaScreenCastStreamSrc *src);
++
+   gboolean (* get_videocrop) (MetaScreenCastStreamSrc *src,
+                               MetaRectangle           *crop_rect);
+   void (* set_cursor_metadata) (MetaScreenCastStreamSrc *src,
+@@ -74,6 +76,8 @@ struct _MetaScreenCastStreamSrcClass
+ void meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc  *src,
+                                                      MetaScreenCastRecordFlag  flags);
+ 
++gboolean meta_screen_cast_stream_src_pending_follow_up_frame (MetaScreenCastStreamSrc *src);
++
+ MetaScreenCastStream * meta_screen_cast_stream_src_get_stream (MetaScreenCastStreamSrc *src);
+ 
+ gboolean meta_screen_cast_stream_src_draw_cursor_into (MetaScreenCastStreamSrc  *src,
+diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c
+index 70e868997e..7026ec3b4f 100644
+--- a/src/backends/meta-screen-cast-window-stream-src.c
++++ b/src/backends/meta-screen-cast-window-stream-src.c
+@@ -520,6 +520,15 @@ meta_screen_cast_window_stream_src_record_to_framebuffer (MetaScreenCastStreamSr
+   return TRUE;
+ }
+ 
++static void
++meta_screen_cast_window_stream_record_follow_up (MetaScreenCastStreamSrc *src)
++{
++  MetaScreenCastRecordFlag flags;
++
++  flags = META_SCREEN_CAST_RECORD_FLAG_NONE;
++  meta_screen_cast_stream_src_maybe_record_frame (src, flags);
++}
++
+ static void
+ meta_screen_cast_window_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src,
+                                                         struct spa_meta_cursor  *spa_meta_cursor)
+@@ -607,6 +616,8 @@ meta_screen_cast_window_stream_src_class_init (MetaScreenCastWindowStreamSrcClas
+     meta_screen_cast_window_stream_src_record_to_buffer;
+   src_class->record_to_framebuffer =
+     meta_screen_cast_window_stream_src_record_to_framebuffer;
++  src_class->record_follow_up =
++    meta_screen_cast_window_stream_record_follow_up;
+   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;
+ }
+-- 
+2.26.2
+
+
+From f2babf5129df9e948f471e3d464162888a99201d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Wed, 8 Jul 2020 15:08:23 +0200
+Subject: [PATCH 8/9] screen-cast/src: Use G_USEC_PER_SEC instead of 1000000
+
+https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1351
+(cherry picked from commit 0c6ac287e6da91ba76bf3958befef4bec6ed28f6)
+---
+ src/backends/meta-screen-cast-stream-src.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c
+index 55af56f8b9..1d6c2b9d08 100644
+--- a/src/backends/meta-screen-cast-stream-src.c
++++ b/src/backends/meta-screen-cast-stream-src.c
+@@ -513,8 +513,9 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc  *src,
+       int64_t min_interval_us;
+       int64_t time_since_last_frame_us;
+ 
+-      min_interval_us = ((1000000 * priv->video_format.max_framerate.denom) /
+-                         priv->video_format.max_framerate.num);
++      min_interval_us =
++        ((G_USEC_PER_SEC * priv->video_format.max_framerate.denom) /
++         priv->video_format.max_framerate.num);
+ 
+       time_since_last_frame_us = now_us - priv->last_frame_timestamp_us;
+       if (time_since_last_frame_us < min_interval_us)
+-- 
+2.26.2
+
+
+From 950b3ea51391ffcb434f8f5380459174ba4c4853 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Fri, 10 Jul 2020 07:06:33 +0000
+Subject: [PATCH 9/9] screen-cast/src: Remove follow up timeout source on
+ disable
+
+We failed to remove the timeout source when disabling, meaning that if a
+follow up was scheduled, and shortly after we disabled the source, the
+timeout would be invoked after the source was freed causing
+use-after-free bugs.
+
+Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1337
+
+https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1365
+
+(cherry picked from commit d67ba3ea65717ceab3e0c91267191c6ed2aac2c2)
+(cherry picked from commit 1fd53c480f9bb58bd4ac0efc2bbce17dfda8645b)
+---
+ src/backends/meta-screen-cast-stream-src.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c
+index 1d6c2b9d08..f39d348baa 100644
+--- a/src/backends/meta-screen-cast-stream-src.c
++++ b/src/backends/meta-screen-cast-stream-src.c
+@@ -622,6 +622,8 @@ meta_screen_cast_stream_src_disable (MetaScreenCastStreamSrc *src)
+ 
+   META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src)->disable (src);
+ 
++  g_clear_handle_id (&priv->follow_up_frame_source_id, g_source_remove);
++
+   priv->is_enabled = FALSE;
+ }
+ 
+-- 
+2.26.2
+
diff --git a/SOURCES/mutter-bump-screencast-api-version.patch b/SOURCES/mutter-bump-screencast-api-version.patch
new file mode 100644
index 0000000..37be67e
--- /dev/null
+++ b/SOURCES/mutter-bump-screencast-api-version.patch
@@ -0,0 +1,13 @@
+diff --git a/src/backends/meta-screen-cast.c b/src/backends/meta-screen-cast.c
+index 268155e..18fc779 100644
+--- a/src/backends/meta-screen-cast.c
++++ b/src/backends/meta-screen-cast.c
+@@ -32,7 +32,7 @@
+ 
+ #define META_SCREEN_CAST_DBUS_SERVICE "org.gnome.Mutter.ScreenCast"
+ #define META_SCREEN_CAST_DBUS_PATH "/org/gnome/Mutter/ScreenCast"
+-#define META_SCREEN_CAST_API_VERSION 2
++#define META_SCREEN_CAST_API_VERSION 3
+ 
+ struct _MetaScreenCast
+ {
diff --git a/SOURCES/screen-cast-remote-desktop-improvements.patch b/SOURCES/screen-cast-remote-desktop-improvements.patch
new file mode 100644
index 0000000..954602c
--- /dev/null
+++ b/SOURCES/screen-cast-remote-desktop-improvements.patch
@@ -0,0 +1,5977 @@
+From ac409e38b820ebf07a5677a3b393933dd3cf668d Mon Sep 17 00:00:00 2001
+From: Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
+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 <http://www.gnu.org/licenses/>.
++ */
++
++#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 <cairo-gobject.h>
+ #include <math.h>
+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 <georges.stavracas@gmail.com>
+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 <georges.stavracas@gmail.com>
+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 <clutter/clutter.h> can be included directly."
++#endif
++
+ #include <cairo.h>
+ #include <glib-object.h>
+ #include <cogl/cogl.h>
+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 <georges.stavracas@gmail.com>
+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 <clutter/clutter-types.h>
+ #include <clutter/clutter-group.h>
++#include <clutter/clutter-stage-view.h>
+ 
+ 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 <georges.stavracas@gmail.com>
+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 <georges.stavracas@gmail.com>
+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 <georges.stavracas@gmail.com>
+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 <georges.stavracas@gmail.com>
+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 <georges.stavracas@gmail.com>
+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?= <jadahl@gmail.com>
+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?= <jadahl@gmail.com>
+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?= <jadahl@gmail.com>
+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?= <jadahl@gmail.com>
+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 <Pascal.Nowack@gmx.de>
+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 <ofourdan@redhat.com>
+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 <ofourdan@redhat.com>
+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 <ofourdan@redhat.com>
+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 <wtaymans@redhat.com>
+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 <spa/param/props.h>
+ #include <spa/param/format-utils.h>
+ #include <spa/param/video/format-utils.h>
++#include <spa/utils/result.h>
+ #include <stdint.h>
+ #include <sys/mman.h>
+ 
+@@ -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 <pekka.paalanen@collabora.com>
+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 <pekka.paalanen@collabora.com>
+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?= <jadahl@gmail.com>
+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?= <jadahl@gmail.com>
+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?= <jadahl@gmail.com>
+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 <georges.stavracas@gmail.com>
+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 <georges.stavracas@gmail.com>
+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 <georges.stavracas@gmail.com>
++ */
++
++#include "cogl-config.h"
++
++#include "cogl-dma-buf-handle.h"
++#include "cogl-object.h"
++
++#include <unistd.h>
++
++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 <georges.stavracas@gmail.com>
++ */
++
++
++#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
++#error "Only <cogl/cogl.h> can be included directly."
++#endif
++
++#ifndef __COGL_DMA_BUF_HANDLE_H__
++#define __COGL_DMA_BUF_HANDLE_H__
++
++#include <cogl/cogl-types.h>
++#include <cogl/cogl-framebuffer.h>
++
++/**
++ * 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 <cogl/cogl1-context.h>
+ #include <cogl/cogl-bitmap.h>
+ #include <cogl/cogl-color.h>
++#include <cogl/cogl-dma-buf-handle.h>
+ #include <cogl/cogl-matrix.h>
+ #include <cogl/cogl-matrix-stack.h>
+ #include <cogl/cogl-offscreen.h>
+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 <georges.stavracas@gmail.com>
+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 <georges.stavracas@gmail.com>
+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 <georges.stavracas@gmail.com>
+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 <errno.h>
++#include <fcntl.h>
+ #include <pipewire/pipewire.h>
+ #include <spa/param/props.h>
+ #include <spa/param/format-utils.h>
+@@ -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 <georges.stavracas@gmail.com>
+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 <georges.stavracas@gmail.com>
+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 <georges.stavracas@gmail.com>
+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 <georges.stavracas@gmail.com>
+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?= <jadahl@gmail.com>
+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?= <jadahl@gmail.com>
+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 <georges.stavracas@gmail.com>
+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 <georges.stavracas@gmail.com>
+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 <georges.stavracas@gmail.com>
+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 <georges.stavracas@gmail.com>
+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 <georges.stavracas@gmail.com>
+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 <georges.stavracas@gmail.com>
+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 <georges.stavracas@gmail.com>
+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?= <jadahl@gmail.com>
+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?= <jadahl@gmail.com>
+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?= <jadahl@gmail.com>
+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?= <jadahl@gmail.com>
+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?= <jadahl@gmail.com>
+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 <carlosg@gnome.org>
+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 <carlosg@gnome.org>
+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?= <jadahl@gmail.com>
+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
+
diff --git a/SPECS/mutter.spec b/SPECS/mutter.spec
index a0cdf2c..dd96fb4 100644
--- a/SPECS/mutter.spec
+++ b/SPECS/mutter.spec
@@ -3,12 +3,12 @@
 %global gsettings_desktop_schemas_version 3.21.4
 %global json_glib_version 0.12.0
 %global libinput_version 1.4
-%global pipewire_version 0.2.2
+%global pipewire_version 0.3.0
 %global mutter_api_version 4
 
 Name:          mutter
 Version:       3.32.2
-Release:       36%{?dist}
+Release:       48%{?dist}
 Summary:       Window and compositing manager based on Clutter
 
 License:       GPLv2+
@@ -111,8 +111,53 @@ Patch281: 0001-crtc-xrandr-Respect-configured-RANDR-panning.patch
 # gnome-shell core dump after connection to docking station (#1809079)
 Patch282: handle-hotplug-better.patch
 
-# Fix corrupted background after suspend (#1829261)
-Patch283: 0001-background-Reload-when-GPU-memory-is-invalidated.patch
+# Improve performance under load (#1820760)
+Patch290: 0001-clutter-avoid-redundant-_clutter_paint_node_init_typ.patch
+Patch291: 0002-clutter-avoid-g_signal_emit_by_name-from-ClutterActo.patch
+Patch292: 0003-clutter-fix-hole-in-ClutterPaintNode.patch
+
+# Fix corrupted background after suspend (#1828162)
+Patch300: 0001-background-Reload-when-GPU-memory-is-invalidated.patch
+
+# Backport screen cast and remote desktop improvements (#1837381)
+# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/623
+# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/752
+# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/976
+# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1022
+# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1062
+# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/687
+# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1086
+# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1115
+# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1106
+# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1129
+# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1251
+# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1174
+# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1258
+# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1212
+Patch400: screen-cast-remote-desktop-improvements.patch
+# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1283
+Patch401: 0001-screen-cast-src-Destroy-hash-dmabuf-table-after-stre.patch
+Patch402: 0002-renderer-native-Don-t-leak-DMA-buffer-CoglFramebuffe.patch
+Patch403: 0001-renderer-Add-API-to-check-whether-renderer-is-hardwa.patch
+# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1318 (#1847062)
+Patch404: 0001-backend-Add-getter-for-MetaScreenCast.patch
+Patch405: 0002-renderer-native-Add-API-to-get-primary-GPU.patch
+Patch406: 0003-screen-cast-Move-DMA-buffer-allocation-to-MetaScreen.patch
+Patch407: 0004-screen-cast-Disable-DMA-buffer-based-screen-casting-.patch
+# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1351
+# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1365
+Patch408: cursor-move-only-screen-cast-fixes.patch
+Patch409: mutter-bump-screencast-api-version.patch
+
+# Only treat WM_PROTOCOLS messages as WM_PROTOCOL messages (#1847203)
+Patch500: 0001-stage-x11-Check-that-message-is-WM_PROTOCOLS-before-.patch
+
+# Don't show widow actor until explictly shown (#1719937)
+Patch501: 0001-window-actor-Don-t-show-actor-until-meta_window_acto.patch
+
+# Handle GPU unplug gracefully (#1846191)
+Patch502: 0001-monitor-manager-kms-Trigger-hotplug-processing-on-gp.patch
+Patch503: 0002-gpu-kms-Reset-CRTC-mode-and-output-list-if-no-resour.patch
 
 BuildRequires: chrpath
 BuildRequires: pango-devel
@@ -255,13 +300,61 @@ desktop-file-validate %{buildroot}/%{_datadir}/applications/%{name}.desktop
 %{_datadir}/mutter-%{mutter_api_version}/tests
 
 %changelog
-* Wed May 06 2020 Jonas Ådahl <jadahl@redhat.com> - 3.32.2-36
+* Mon Sep 21 2020 Jonas Ådahl <jadahl@redhat.com> - 3.32.2-48
+- Fix GLX stereo buffer rebase error
+  Resolves: #1880339
+
+* Thu Sep 03 2020 Florian Müllner <fmuellner@redhat.com> - 3.32.2-47
+- Fix screen sharing on wayland
+  Resolves: #1873963
+
+* Wed Jul 15 2020 Jonas Ådahl <jadahl@redhat.com> - 3.32.2-46
+- Handle cursor only screen cast frames better
+  Related: #1837381
+
+* Thu Jul 02 2020 Jonas Ådahl <jadahl@redhat.com> - 3.32.2-45
+- Handle GPU unplug gracefully
+  Resolves: #1846191
+
+* Thu Jun 25 2020 Jonas Ådahl <jadahl@redhat.com> - 3.32.2-44
+- Don't show widow actor until explictly shown
+  Resolves: #1719937
+
+* Thu Jun 25 2020 Jonas Ådahl <jadahl@redhat.com> - 3.32.2-43
+- Only treat WM_PROTOCOLS messages as WM_PROTOCOL messages
+  Resolves: #1847203
+
+* Tue Jun 16 2020 Jonas Ådahl <jadahl@redhat.com> - 3.32.2-42
+- Don't pass DMA buffers if they can't be mmap():ed
+  Related: #1847062
+
+* Wed Jun 10 2020 Florian Müllner <fmuellner@redhat.com> - 3.32.2-41
+- Backport is_rendering_hardware_acclerated() API
+  Related: #1837381
+
+* Wed Jun 03 2020 Jonas Ådahl <jadahl@redhat.com> - 3.32.2-40
+- Fix DMA buffer memory leak
+  Related: #1837381
+
+* Mon May 25 2020 Jonas Ådahl <jadahl@redhat.com> - 3.32.2-39
+- Fix incorrect pipewire dependency version
+  Related: #1837381
+
+* Mon May 25 2020 Jonas Ådahl <jadahl@redhat.com> - 3.32.2-38
+- Backport screen cast and remote desktop improvements
+  Resolves: #1837381
+
+* Tue May 19 2020 Jonas Ådahl <jadahl@redhat.com> - 3.32.2-37
 - Fix corrupted background after suspend
-  Resolves: #1829261
+  Resolves: #1828162
+
+* Wed Apr 08 2020 Florian Müllner <fmuellner@redhat.com> - 3.32.2-36
+- Improve performance under IO load
+  Resolves: #1820760
 
 * Mon Mar 23 2020 Jonas Ådahl <jadahl@redhat.com> - 3.32.2-35
 - Drop EGLStream robustness patches
-  Resolves: #1821198
+  Resolves: #1815430
 
 * Thu Mar 05 2020 Jonas Ådahl <jadahl@redhat.com> - 3.32.2-34
 - gnome-shell core dump after connection to docking station
@@ -319,7 +412,7 @@ desktop-file-validate %{buildroot}/%{_datadir}/applications/%{name}.desktop
 - Fix mode switch pad buttons without LEDs
   Resolves: #1666070
 
-* Mon Dec 01 2019 Tomas Pelka <tpelka@redhat.com> - 3.32.2-20
+* Mon Dec 02 2019 Tomas Pelka <tpelka@redhat.com> - 3.32.2-20
 - Need rebuild in correct build target
   Resolves: #1730891