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..7582a55
--- /dev/null
+++ b/SOURCES/0001-clutter-avoid-redundant-_clutter_paint_node_init_typ.patch
@@ -0,0 +1,53 @@
+From a9f9f9b36a03535480b31534547bea7c9f7cf4c1 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 46537f322..8a465fdc2 100644
+--- a/clutter/clutter/clutter-main.c
++++ b/clutter/clutter/clutter-main.c
+@@ -63,6 +63,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"
+@@ -1390,6 +1391,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 391f48398..db68b7766 100644
+--- a/clutter/clutter/clutter-paint-node.c
++++ b/clutter/clutter/clutter-paint-node.c
+@@ -1113,8 +1113,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-screen-Expose-workspace-layout-properties.patch b/SOURCES/0001-screen-Expose-workspace-layout-properties.patch
index bc7ed96..9a0eb8b 100644
--- a/SOURCES/0001-screen-Expose-workspace-layout-properties.patch
+++ b/SOURCES/0001-screen-Expose-workspace-layout-properties.patch
@@ -1,4 +1,4 @@
-From 6b8e2636911c79d77d95aaac62336dd4f4063f63 Mon Sep 17 00:00:00 2001
+From 5efefc0e46963749a53a8d81ff6641fd6f9e6e1f Mon Sep 17 00:00:00 2001
 From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
 Date: Tue, 4 Jun 2019 21:21:37 +0200
 Subject: [PATCH] screen: Expose workspace layout properties
@@ -13,11 +13,11 @@ workspace layouts it supports.
 
 https://gitlab.gnome.org/GNOME/mutter/merge_requests/618
 ---
- src/core/screen.c | 27 +++++++++++++++++++++++++++
- 1 file changed, 27 insertions(+)
+ src/core/screen.c | 30 ++++++++++++++++++++++++++++++
+ 1 file changed, 30 insertions(+)
 
 diff --git a/src/core/screen.c b/src/core/screen.c
-index c14bba0cf..4fac02cdb 100644
+index c14bba0cf..090baecda 100644
 --- a/src/core/screen.c
 +++ b/src/core/screen.c
 @@ -81,6 +81,9 @@ static void on_monitors_changed          (MetaMonitorManager *manager,
@@ -75,6 +75,16 @@ index c14bba0cf..4fac02cdb 100644
  }
  
  /**
+@@ -1785,6 +1812,9 @@ meta_screen_override_workspace_layout (MetaScreen      *screen,
+   screen->rows_of_workspaces = n_rows;
+   screen->columns_of_workspaces = n_columns;
+ 
++  g_object_notify (G_OBJECT (screen), "layout-columns");
++  g_object_notify (G_OBJECT (screen), "layout-rows");
++
+   /* In theory we should remove _NET_DESKTOP_LAYOUT from _NET_SUPPORTED at this
+    * point, but it's unlikely that anybody checks that, and it's unlikely that
+    * anybody who checks that handles changes, so we'd probably just create
 -- 
-2.21.0
+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..f8c8ae7
--- /dev/null
+++ b/SOURCES/0001-stage-x11-Check-that-message-is-WM_PROTOCOLS-before-.patch
@@ -0,0 +1,101 @@
+From 0f9e86c21d35c8e2760a2de6ea22ebd271810483 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 1ee2f40ab0..bd5aabc884 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/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..5746804
--- /dev/null
+++ b/SOURCES/0002-clutter-avoid-g_signal_emit_by_name-from-ClutterActo.patch
@@ -0,0 +1,192 @@
+From 04e5e144ce21c811601e169c13fdef7b8edeae12 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 f341d3616..77195c0b0 100644
+--- a/clutter/clutter/cally/cally-actor.c
++++ b/clutter/clutter/cally/cally-actor.c
+@@ -606,10 +606,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 6954f0396..8da53d3f1 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"
+@@ -4310,7 +4310,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)
+     {
+@@ -12980,7 +12980,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)
+     {
+@@ -21150,3 +21150,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 0c5a08c8c..9b0665c72 100644
+--- a/clutter/clutter/clutter-actor.h
++++ b/clutter/clutter/clutter-actor.h
+@@ -295,10 +295,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];
+ };
+ 
+ /**
+@@ -368,6 +369,8 @@ CLUTTER_AVAILABLE_IN_ALL
+ const gchar *                   clutter_actor_get_name                          (ClutterActor                *self);
+ CLUTTER_AVAILABLE_IN_ALL
+ AtkObject *                     clutter_actor_get_accessible                    (ClutterActor                *self);
++CLUTTER_AVAILABLE_IN_ALL
++gboolean                        clutter_actor_has_accessible                    (ClutterActor                *self);
+ 
+ CLUTTER_AVAILABLE_IN_1_24
+ 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 81fe4dc2e..52942a246 100644
+--- a/clutter/clutter/clutter-container.c
++++ b/clutter/clutter/clutter-container.c
+@@ -39,6 +39,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"
+@@ -1448,3 +1449,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/0003-clutter-fix-hole-in-ClutterPaintNode.patch b/SOURCES/0003-clutter-fix-hole-in-ClutterPaintNode.patch
new file mode 100644
index 0000000..0440f51
--- /dev/null
+++ b/SOURCES/0003-clutter-fix-hole-in-ClutterPaintNode.patch
@@ -0,0 +1,167 @@
+From 6d849de8ff486be21ff931bfa63313240b212852 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 8da53d3f1..995de8c35 100644
+--- a/clutter/clutter/clutter-actor.c
++++ b/clutter/clutter/clutter-actor.c
+@@ -3692,7 +3692,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);
+@@ -3707,7 +3707,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);
+@@ -4014,7 +4014,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.
+@@ -21129,7 +21129,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 42e54ce9d..512e434ed 100644
+--- a/clutter/clutter/clutter-canvas.c
++++ b/clutter/clutter/clutter-canvas.c
+@@ -319,7 +319,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 722b0375d..51e610073 100644
+--- a/clutter/clutter/clutter-image.c
++++ b/clutter/clutter/clutter-image.c
+@@ -108,7 +108,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 2945b78a4..ea1665ada 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 db68b7766..5129e00cf 100644
+--- a/clutter/clutter/clutter-paint-node.c
++++ b/clutter/clutter/clutter-paint-node.c
+@@ -173,8 +173,6 @@ clutter_paint_node_real_finalize (ClutterPaintNode *node)
+ {
+   ClutterPaintNode *iter;
+ 
+-  g_free (node->name);
+-
+   if (node->operations != NULL)
+     {
+       guint i;
+@@ -296,7 +294,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
+  */
+@@ -306,8 +305,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 5f3e962e4..d3e4e7922 100644
+--- a/clutter/clutter/clutter-paint-node.h
++++ b/clutter/clutter/clutter-paint-node.h
+@@ -52,6 +52,9 @@ void                    clutter_paint_node_unref                        (Clutter
+ CLUTTER_AVAILABLE_IN_1_10
+ void                    clutter_paint_node_set_name                     (ClutterPaintNode      *node,
+                                                                          const char            *name);
++CLUTTER_AVAILABLE_IN_ALL
++void                    clutter_paint_node_set_static_name              (ClutterPaintNode      *node,
++                                                                         const char            *name);
+ 
+ CLUTTER_AVAILABLE_IN_1_10
+ void                    clutter_paint_node_add_child                    (ClutterPaintNode      *node,
+-- 
+2.26.0
+
diff --git a/SOURCES/fix-extended-osk-characters.patch b/SOURCES/fix-extended-osk-characters.patch
new file mode 100644
index 0000000..d6a8c6b
--- /dev/null
+++ b/SOURCES/fix-extended-osk-characters.patch
@@ -0,0 +1,272 @@
+From a9c7fb96d9d03547d53c6fdd27a1cf6f4d00b17d Mon Sep 17 00:00:00 2001
+From: Andrea Azzarone <azzaronea@gmail.com>
+Date: Fri, 13 Jul 2018 14:49:38 +0200
+Subject: [PATCH] clutter/x11: Implement keycode remap to keysyms on virtual
+ key devices
+
+Keycode lookup can fail for serveral reasons, e.g. if there is no combination of
+modifiers and keycodes that can produce the target keysym with the current
+keyboard layout.
+
+In case the keycode lookup fails, remap temporarily the keysym to an unused
+keycodes.
+
+Fixes: https://gitlab.gnome.org/GNOME/gnome-shell/issues/109
+---
+ clutter/clutter/x11/clutter-keymap-x11.c      | 154 ++++++++++++++++++
+ clutter/clutter/x11/clutter-keymap-x11.h      |   6 +-
+ .../x11/clutter-virtual-input-device-x11.c    |  19 ++-
+ 3 files changed, 173 insertions(+), 6 deletions(-)
+
+diff --git a/clutter/clutter/x11/clutter-keymap-x11.c b/clutter/clutter/x11/clutter-keymap-x11.c
+index c34e676a4..744fab979 100644
+--- a/clutter/clutter/x11/clutter-keymap-x11.c
++++ b/clutter/clutter/x11/clutter-keymap-x11.c
+@@ -81,6 +81,9 @@ struct _ClutterKeymapX11
+   int current_group;
+ #endif
+ 
++  GHashTable *reserved_keycodes;
++  GQueue *available_keycodes;
++
+   guint caps_lock_state : 1;
+   guint num_lock_state  : 1;
+   guint has_direction   : 1;
+@@ -441,15 +444,98 @@ clutter_keymap_x11_set_property (GObject      *gobject,
+     }
+ }
+ 
++static void
++clutter_keymap_x11_refresh_reserved_keycodes (ClutterKeymapX11 *keymap_x11)
++{
++  Display *dpy = clutter_x11_get_default_display ();
++  GHashTableIter iter;
++  gpointer key, value;
++
++  g_hash_table_iter_init (&iter, keymap_x11->reserved_keycodes);
++  while (g_hash_table_iter_next (&iter, &key, &value))
++    {
++      guint reserved_keycode = GPOINTER_TO_UINT (key);
++      guint reserved_keysym = GPOINTER_TO_UINT (value);
++      guint actual_keysym = XkbKeycodeToKeysym (dpy, reserved_keycode, 0, 0);
++
++      /* If an available keycode is no longer mapped to the stored keysym, then
++       * the keycode should not be considered available anymore and should be
++       * removed both from the list of available and reserved keycodes.
++       */
++      if (reserved_keysym != actual_keysym)
++        {
++          g_hash_table_iter_remove (&iter);
++          g_queue_remove (keymap_x11->available_keycodes, key);
++        }
++    }
++}
++
++static gboolean
++clutter_keymap_x11_replace_keycode (ClutterKeymapX11 *keymap_x11,
++                                    KeyCode           keycode,
++                                    KeySym            keysym)
++{
++  if (CLUTTER_BACKEND_X11 (keymap_x11->backend)->use_xkb)
++    {
++      Display *dpy = clutter_x11_get_default_display ();
++      XkbDescPtr xkb = get_xkb (keymap_x11);
++      XkbMapChangesRec changes;
++
++      XFlush (dpy);
++
++      xkb->device_spec = XkbUseCoreKbd;
++      memset (&changes, 0, sizeof(changes));
++
++      if (keysym != NoSymbol)
++        {
++          int types[XkbNumKbdGroups] = { XkbOneLevelIndex };
++          XkbChangeTypesOfKey (xkb, keycode, 1, XkbGroup1Mask, types, &changes);
++          XkbKeySymEntry (xkb, keycode, 0, 0) = keysym;
++        }
++      else
++        {
++          /* Reset to NoSymbol */
++          XkbChangeTypesOfKey (xkb, keycode, 0, XkbGroup1Mask, NULL, &changes);
++        }
++
++      changes.changed = XkbKeySymsMask | XkbKeyTypesMask;
++      changes.first_key_sym = keycode;
++      changes.num_key_syms = 1;
++      changes.first_type = 0;
++      changes.num_types = xkb->map->num_types;
++      XkbChangeMap (dpy, xkb, &changes);
++
++      XFlush (dpy);
++
++      return TRUE;
++    }
++
++  return FALSE;
++}
++
+ static void
+ clutter_keymap_x11_finalize (GObject *gobject)
+ {
+   ClutterKeymapX11 *keymap;
+   ClutterEventTranslator *translator;
++  GHashTableIter iter;
++  gpointer key, value;
+ 
+   keymap = CLUTTER_KEYMAP_X11 (gobject);
+   translator = CLUTTER_EVENT_TRANSLATOR (keymap);
+ 
++  clutter_keymap_x11_refresh_reserved_keycodes (keymap);
++  g_hash_table_iter_init (&iter, keymap->reserved_keycodes);
++  while (g_hash_table_iter_next (&iter, &key, &value))
++    {
++      guint keycode = GPOINTER_TO_UINT (key);
++      clutter_keymap_x11_replace_keycode (keymap, keycode, NoSymbol);
++    }
++
++  g_hash_table_destroy (keymap->reserved_keycodes);
++  g_queue_free (keymap->available_keycodes);
++
++
+ #ifdef HAVE_XKB
+   _clutter_backend_remove_event_translator (keymap->backend, translator);
+ 
+@@ -483,6 +569,8 @@ clutter_keymap_x11_init (ClutterKeymapX11 *keymap)
+ {
+   keymap->current_direction = PANGO_DIRECTION_NEUTRAL;
+   keymap->current_group = -1;
++  keymap->reserved_keycodes = g_hash_table_new (NULL, NULL);
++  keymap->available_keycodes = g_queue_new ();
+ }
+ 
+ static ClutterTranslateReturn
+@@ -766,6 +854,72 @@ clutter_keymap_x11_get_entries_for_keyval (ClutterKeymapX11  *keymap_x11,
+     }
+ }
+ 
++static guint
++clutter_keymap_x11_get_available_keycode (ClutterKeymapX11 *keymap_x11)
++{
++  if (CLUTTER_BACKEND_X11 (keymap_x11->backend)->use_xkb)
++    {
++      clutter_keymap_x11_refresh_reserved_keycodes (keymap_x11);
++
++      if (g_hash_table_size (keymap_x11->reserved_keycodes) < 5)
++        {
++          Display *dpy = clutter_x11_get_default_display ();
++          XkbDescPtr xkb = get_xkb (keymap_x11);
++          guint i;
++
++          for (i = xkb->max_key_code; i >= xkb->min_key_code; --i)
++            {
++              if (XkbKeycodeToKeysym (dpy, i, 0, 0) == NoSymbol)
++                return i;
++            }
++        }
++
++      return GPOINTER_TO_UINT (g_queue_pop_head (keymap_x11->available_keycodes));
++    }
++
++  return 0;
++}
++
++gboolean clutter_keymap_x11_reserve_keycode (ClutterKeymapX11 *keymap_x11,
++                                             guint             keyval,
++                                             guint            *keycode_out)
++{
++  g_return_val_if_fail (CLUTTER_IS_KEYMAP_X11 (keymap_x11), FALSE);
++  g_return_val_if_fail (keyval != 0, FALSE);
++  g_return_val_if_fail (keycode_out != NULL, FALSE);
++
++  *keycode_out = clutter_keymap_x11_get_available_keycode (keymap_x11);
++
++  if (*keycode_out == NoSymbol)
++    {
++      g_warning ("Cannot reserve a keycode for keyval %d: no available keycode", keyval);
++      return FALSE;
++    }
++
++  if (!clutter_keymap_x11_replace_keycode (keymap_x11, *keycode_out, keyval))
++    {
++      g_warning ("Failed to remap keycode %d to keyval %d", *keycode_out, keyval);
++      return FALSE;
++    }
++
++  g_hash_table_insert (keymap_x11->reserved_keycodes, GUINT_TO_POINTER (*keycode_out), GUINT_TO_POINTER (keyval));
++  g_queue_remove (keymap_x11->available_keycodes, GUINT_TO_POINTER (*keycode_out));
++
++  return TRUE;
++}
++
++void clutter_keymap_x11_release_keycode_if_needed (ClutterKeymapX11 *keymap_x11,
++                                                   guint             keycode)
++{
++  g_return_if_fail (CLUTTER_IS_KEYMAP_X11 (keymap_x11));
++
++  if (!g_hash_table_contains (keymap_x11->reserved_keycodes, GUINT_TO_POINTER (keycode)) ||
++      g_queue_index (keymap_x11->available_keycodes, GUINT_TO_POINTER (keycode)) != -1)
++    return;
++
++  g_queue_push_tail (keymap_x11->available_keycodes, GUINT_TO_POINTER (keycode));
++}
++
+ void
+ clutter_keymap_x11_latch_modifiers (ClutterKeymapX11 *keymap_x11,
+                                     uint32_t          level,
+diff --git a/clutter/clutter/x11/clutter-keymap-x11.h b/clutter/clutter/x11/clutter-keymap-x11.h
+index 4b5b403c8..4decb44ee 100644
+--- a/clutter/clutter/x11/clutter-keymap-x11.h
++++ b/clutter/clutter/x11/clutter-keymap-x11.h
+@@ -58,7 +58,11 @@ gboolean clutter_keymap_x11_keycode_for_keyval (ClutterKeymapX11 *keymap_x11,
+ void     clutter_keymap_x11_latch_modifiers (ClutterKeymapX11 *keymap_x11,
+                                              uint32_t          level,
+                                              gboolean          enable);
+-
++gboolean clutter_keymap_x11_reserve_keycode (ClutterKeymapX11 *keymap_x11,
++                                             guint             keyval,
++                                             guint            *keycode_out);
++void     clutter_keymap_x11_release_keycode_if_needed (ClutterKeymapX11 *keymap_x11,
++                                                       guint             keycode);
+ G_END_DECLS
+ 
+ #endif /* __CLUTTER_KEYMAP_X11_H__ */
+diff --git a/clutter/clutter/x11/clutter-virtual-input-device-x11.c b/clutter/clutter/x11/clutter-virtual-input-device-x11.c
+index e16ba3fd0..cab26c38c 100644
+--- a/clutter/clutter/x11/clutter-virtual-input-device-x11.c
++++ b/clutter/clutter/x11/clutter-virtual-input-device-x11.c
+@@ -143,8 +143,13 @@ clutter_virtual_input_device_x11_notify_keyval (ClutterVirtualInputDevice *virtu
+ 
+   if (!clutter_keymap_x11_keycode_for_keyval (keymap, keyval, &keycode, &level))
+     {
+-      g_warning ("No keycode found for keyval %x in current group", keyval);
+-      return;
++      level = 0;
++
++      if (!clutter_keymap_x11_reserve_keycode (keymap, keyval, &keycode))
++        {
++          g_warning ("No keycode found for keyval %x in current group", keyval);
++          return;
++        }
+     }
+ 
+   if (!_clutter_keymap_x11_get_is_modifier (keymap, keycode) &&
+@@ -155,9 +160,13 @@ clutter_virtual_input_device_x11_notify_keyval (ClutterVirtualInputDevice *virtu
+                      (KeyCode) keycode,
+                      key_state == CLUTTER_KEY_STATE_PRESSED, 0);
+ 
+-  if (!_clutter_keymap_x11_get_is_modifier (keymap, keycode) &&
+-      key_state == CLUTTER_KEY_STATE_RELEASED)
+-    clutter_keymap_x11_latch_modifiers (keymap, level, FALSE);
++
++  if (key_state == CLUTTER_KEY_STATE_RELEASED)
++    {
++      if (!_clutter_keymap_x11_get_is_modifier (keymap, keycode))
++        clutter_keymap_x11_latch_modifiers (keymap, level, FALSE);
++      clutter_keymap_x11_release_keycode_if_needed (keymap, keycode);
++    }
+ }
+ 
+ static void
+-- 
+2.26.2
+
diff --git a/SPECS/mutter.spec b/SPECS/mutter.spec
index 7c8e110..964b556 100644
--- a/SPECS/mutter.spec
+++ b/SPECS/mutter.spec
@@ -10,7 +10,7 @@
 
 Name:          mutter
 Version:       3.28.3
-Release:       22%{?dist}
+Release:       26%{?dist}
 Summary:       Window and compositing manager based on Clutter
 
 License:       GPLv2+
@@ -115,6 +115,17 @@ Patch284: 0001-core-Hide-close-dialog-before-destroying.patch
 # Add PING_TIMEOUT_DELAY to mutter MetaPreferences #1809164
 Patch285: 0001-display-Make-check-alive-timeout-configureable.patch
 
+# Improve performance under load (#1824869)
+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
+
+# https://bugzilla.redhat.com/show_bug.cgi?id=1625306
+Patch295: fix-extended-osk-characters.patch
+
+# Only treat WM_PROTOCOLS messages as WM_PROTOCOL messages (#1846242)
+Patch296: 0001-stage-x11-Check-that-message-is-WM_PROTOCOLS-before-.patch
+
 BuildRequires: chrpath
 BuildRequires: pango-devel
 BuildRequires: startup-notification-devel
@@ -270,6 +281,22 @@ glib-compile-schemas %{_datadir}/glib-2.0/schemas &> /dev/null || :
 %{_libdir}/pkgconfig/*
 
 %changelog
+* Mon Jun 22 2020 Jonas Ådahl <jadahl@redhat.com>) - 3.28.3-26
+- Only treat WM_PROTOCOLS messages as WM_PROTOCOL messages
+  Resolves: #1846242
+
+* Wed Jun 17 2020 Florian Müllner <fmuellner@redhat.com> - 3.28.3-25
+- Notify workspace layout changes
+  Resolves: #1848103
+
+* Tue May 26 2020 Florian Müllner <fmuellner@redhat.com> - 3.28.3-24
+- Fix extended OSK keys
+  Resolves: #1625306
+
+* Fri Apr 17 2020 Florian Müllner <fmuellner@redhat.com> - 3.28.3-23
+- Improve performance under IO load
+  Resolves: #1824869
+
 * Fri Mar 06 2020 Florian Müllner <fmuellner@redhat.com> - 3.28.3-22
 - Include one more close-dialog backport
   Related: #1753799