diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f2ae9b2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+SOURCES/mutter-3.28.3.tar.xz
diff --git a/.mutter.metadata b/.mutter.metadata
new file mode 100644
index 0000000..6b4e4ce
--- /dev/null
+++ b/.mutter.metadata
@@ -0,0 +1 @@
+a7a01f50c75dc6c1e0fe708656cfce1546624568 SOURCES/mutter-3.28.3.tar.xz
diff --git a/SOURCES/0001-Add-remote-access-controller-API.patch b/SOURCES/0001-Add-remote-access-controller-API.patch
new file mode 100644
index 0000000..b182a20
--- /dev/null
+++ b/SOURCES/0001-Add-remote-access-controller-API.patch
@@ -0,0 +1,672 @@
+From 9fb08133fd89bb09dc6d4f202d9b76d43b42905e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Fri, 20 Jul 2018 16:37:37 +0200
+Subject: [PATCH] Add remote access controller API
+
+Add API to let GNOME Shell have the ability to get notified about remote
+access sessions (remote desktop, remote control and screen cast), and
+with a way to close them.
+
+This is done by adding an abstraction above the remote desktop and
+screen cast session objects, to avoid exposing their objects to outside
+of mutter. Doing that would result in external parts holding references
+to the objects, complicating their lifetimes. By using separate wrapper
+objects, we avoid this issue all together.
+---
+ src/Makefile.am                               |   4 +
+ src/backends/meta-backend.c                   |  23 ++++
+ .../meta-remote-access-controller-private.h   |  31 +++++
+ src/backends/meta-remote-access-controller.c  | 130 ++++++++++++++++++
+ src/backends/meta-remote-desktop-session.c    |  80 +++++++++++
+ src/backends/meta-remote-desktop-session.h    |   6 +
+ src/backends/meta-screen-cast-session.c       |  80 +++++++++++
+ src/backends/meta-screen-cast-session.h       |   7 +
+ src/meta/meta-backend.h                       |   3 +
+ src/meta/meta-remote-access-controller.h      |  47 +++++++
+ 10 files changed, 411 insertions(+)
+ create mode 100644 src/backends/meta-remote-access-controller-private.h
+ create mode 100644 src/backends/meta-remote-access-controller.c
+ create mode 100644 src/meta/meta-remote-access-controller.h
+
+diff --git a/src/Makefile.am b/src/Makefile.am
+index 5bbac70e8d..aaf7c9c80d 100644
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -161,6 +161,9 @@ libmutter_@LIBMUTTER_API_VERSION@_la_SOURCES =	\
+ 	backends/meta-renderer.h		\
+ 	backends/meta-renderer-view.c		\
+ 	backends/meta-renderer-view.h		\
++	backends/meta-remote-access-controller.c	\
++	backends/meta-remote-access-controller-private.h	\
++	meta/meta-remote-access-controller.h	\
+ 	backends/edid-parse.c			\
+ 	backends/edid.h				\
+ 	backends/gsm-inhibitor-flag.h		\
+@@ -546,6 +549,7 @@ libmutterinclude_headers =			\
+ 	meta/meta-idle-monitor.h		\
+ 	meta/meta-plugin.h			\
+ 	meta/meta-monitor-manager.h		\
++	meta/meta-remote-access-controller.h	\
+ 	meta/meta-settings.h			\
+ 	meta/meta-shaped-texture.h		\
+ 	meta/meta-shadow-factory.h		\
+diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c
+index 9348aa1bbd..888e51cd98 100644
+--- a/src/backends/meta-backend.c
++++ b/src/backends/meta-backend.c
+@@ -39,6 +39,7 @@
+ #ifdef HAVE_REMOTE_DESKTOP
+ #include "backends/meta-dbus-session-watcher.h"
+ #include "backends/meta-screen-cast.h"
++#include "backends/meta-remote-access-controller-private.h"
+ #include "backends/meta-remote-desktop.h"
+ #endif
+ 
+@@ -92,6 +93,7 @@ struct _MetaBackendPrivate
+   MetaEgl *egl;
+   MetaSettings *settings;
+ #ifdef HAVE_REMOTE_DESKTOP
++  MetaRemoteAccessController *remote_access_controller;
+   MetaDbusSessionWatcher *dbus_session_watcher;
+   MetaScreenCast *screen_cast;
+   MetaRemoteDesktop *remote_desktop;
+@@ -139,6 +141,7 @@ meta_backend_finalize (GObject *object)
+   g_clear_object (&priv->remote_desktop);
+   g_clear_object (&priv->screen_cast);
+   g_clear_object (&priv->dbus_session_watcher);
++  g_clear_object (&priv->remote_access_controller);
+ #endif
+ 
+   g_object_unref (priv->up_client);
+@@ -450,6 +453,8 @@ 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 (priv->dbus_session_watcher);
+   priv->remote_desktop = meta_remote_desktop_new (priv->dbus_session_watcher);
+@@ -777,6 +782,24 @@ meta_backend_get_remote_desktop (MetaBackend *backend)
+ }
+ #endif /* HAVE_REMOTE_DESKTOP */
+ 
++/**
++ * meta_backend_get_remote_access_controller:
++ * @backend: A #MetaBackend
++ *
++ * Return Value: (transfer none): The #MetaRemoteAccessController
++ */
++MetaRemoteAccessController *
++meta_backend_get_remote_access_controller (MetaBackend *backend)
++{
++#ifdef HAVE_REMOTE_DESKTOP
++  MetaBackendPrivate *priv = meta_backend_get_instance_private (backend);
++
++  return priv->remote_access_controller;
++#else
++  return NULL;
++#endif
++}
++
+ /**
+  * meta_backend_grab_device: (skip)
+  */
+diff --git a/src/backends/meta-remote-access-controller-private.h b/src/backends/meta-remote-access-controller-private.h
+new file mode 100644
+index 0000000000..fce2082bfc
+--- /dev/null
++++ b/src/backends/meta-remote-access-controller-private.h
+@@ -0,0 +1,31 @@
++/*
++ * Copyright (C) 2018 Red Hat Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program 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
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
++ * 02111-1307, USA.
++ *
++ */
++
++#ifndef META_REMOTE_ACCESS_CONTROLLER_PRIVATE_H
++#define META_REMOTE_ACCESS_CONTROLLER_PRIVATE_H
++
++#include "meta/meta-remote-access-controller.h"
++
++void meta_remote_access_controller_notify_new_handle (MetaRemoteAccessController *controller,
++                                                      MetaRemoteAccessHandle     *handle);
++
++void meta_remote_access_handle_notify_stopped (MetaRemoteAccessHandle *handle);
++
++#endif /* META_REMOTE_ACCESS_CONTROLLER_PRIVATE_H */
+diff --git a/src/backends/meta-remote-access-controller.c b/src/backends/meta-remote-access-controller.c
+new file mode 100644
+index 0000000000..0e0ebe2bd8
+--- /dev/null
++++ b/src/backends/meta-remote-access-controller.c
+@@ -0,0 +1,130 @@
++/*
++ * Copyright (C) 2018 Red Hat Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program 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
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
++ * 02111-1307, USA.
++ *
++ */
++
++#include "config.h"
++
++#include "backends/meta-remote-access-controller-private.h"
++
++enum
++{
++  HANDLE_STOPPED,
++
++  N_HANDLE_SIGNALS
++};
++
++static int handle_signals[N_HANDLE_SIGNALS];
++
++enum
++{
++  CONTROLLER_NEW_HANDLE,
++
++  N_CONTROLLER_SIGNALS
++};
++
++static int controller_signals[N_CONTROLLER_SIGNALS];
++
++typedef struct _MetaRemoteAccessHandlePrivate
++{
++  gboolean has_stopped;
++} MetaRemoteAccessHandlePrivate;
++
++G_DEFINE_TYPE_WITH_PRIVATE (MetaRemoteAccessHandle,
++                            meta_remote_access_handle,
++                            G_TYPE_OBJECT)
++
++struct _MetaRemoteAccessController
++{
++  GObject parent;
++};
++
++G_DEFINE_TYPE (MetaRemoteAccessController,
++               meta_remote_access_controller,
++               G_TYPE_OBJECT)
++
++/**
++ * meta_remote_access_handle_stop:
++ * @handle: A #MetaRemoteAccessHandle
++ *
++ * Stop the associated remote access session.
++ */
++void
++meta_remote_access_handle_stop (MetaRemoteAccessHandle *handle)
++{
++  MetaRemoteAccessHandlePrivate *priv =
++    meta_remote_access_handle_get_instance_private (handle);
++
++  if (priv->has_stopped)
++    return;
++
++  META_REMOTE_ACCESS_HANDLE_GET_CLASS (handle)->stop (handle);
++}
++
++void
++meta_remote_access_handle_notify_stopped (MetaRemoteAccessHandle *handle)
++{
++  MetaRemoteAccessHandlePrivate *priv =
++    meta_remote_access_handle_get_instance_private (handle);
++
++  priv->has_stopped = TRUE;
++  g_signal_emit (handle, handle_signals[HANDLE_STOPPED], 0);
++}
++
++void
++meta_remote_access_controller_notify_new_handle (MetaRemoteAccessController *controller,
++                                                 MetaRemoteAccessHandle     *handle)
++{
++  g_signal_emit (controller, controller_signals[CONTROLLER_NEW_HANDLE], 0,
++                 handle);
++}
++
++static void
++meta_remote_access_handle_init (MetaRemoteAccessHandle *handle)
++{
++}
++
++static void
++meta_remote_access_handle_class_init (MetaRemoteAccessHandleClass *klass)
++{
++  handle_signals[HANDLE_STOPPED] =
++    g_signal_new ("stopped",
++                  G_TYPE_FROM_CLASS (klass),
++                  G_SIGNAL_RUN_LAST,
++                  0,
++                  NULL, NULL, NULL,
++                  G_TYPE_NONE, 0);
++}
++
++static void
++meta_remote_access_controller_init (MetaRemoteAccessController *controller)
++{
++}
++
++static void
++meta_remote_access_controller_class_init (MetaRemoteAccessControllerClass *klass)
++{
++  controller_signals[CONTROLLER_NEW_HANDLE] =
++    g_signal_new ("new-handle",
++                  G_TYPE_FROM_CLASS (klass),
++                  G_SIGNAL_RUN_LAST,
++                  0,
++                  NULL, NULL, NULL,
++                  G_TYPE_NONE, 1,
++                  META_TYPE_REMOTE_ACCESS_HANDLE);
++}
+diff --git a/src/backends/meta-remote-desktop-session.c b/src/backends/meta-remote-desktop-session.c
+index 821b16724f..73bcf663be 100644
+--- a/src/backends/meta-remote-desktop-session.c
++++ b/src/backends/meta-remote-desktop-session.c
+@@ -30,6 +30,7 @@
+ 
+ #include "backends/meta-dbus-session-watcher.h"
+ #include "backends/meta-screen-cast-session.h"
++#include "backends/meta-remote-access-controller-private.h"
+ #include "backends/native/meta-backend-native.h"
+ #include "backends/x11/meta-backend-x11.h"
+ #include "cogl/cogl.h"
+@@ -59,6 +60,8 @@ struct _MetaRemoteDesktopSession
+   ClutterVirtualInputDevice *virtual_pointer;
+   ClutterVirtualInputDevice *virtual_keyboard;
+   ClutterVirtualInputDevice *virtual_touchscreen;
++
++  MetaRemoteDesktopSessionHandle *handle;
+ };
+ 
+ static void
+@@ -75,12 +78,41 @@ G_DEFINE_TYPE_WITH_CODE (MetaRemoteDesktopSession,
+                          G_IMPLEMENT_INTERFACE (META_TYPE_DBUS_SESSION,
+                                                 meta_dbus_session_init_iface))
+ 
++struct _MetaRemoteDesktopSessionHandle
++{
++  MetaRemoteAccessHandle parent;
++
++  MetaRemoteDesktopSession *session;
++};
++
++G_DEFINE_TYPE (MetaRemoteDesktopSessionHandle,
++               meta_remote_desktop_session_handle,
++               META_TYPE_REMOTE_ACCESS_HANDLE)
++
++static MetaRemoteDesktopSessionHandle *
++meta_remote_desktop_session_handle_new (MetaRemoteDesktopSession *session);
++
+ static gboolean
+ meta_remote_desktop_session_is_running (MetaRemoteDesktopSession *session)
+ {
+   return !!session->virtual_pointer;
+ }
+ 
++static void
++init_remote_access_handle (MetaRemoteDesktopSession *session)
++{
++  MetaBackend *backend = meta_get_backend ();
++  MetaRemoteAccessController *remote_access_controller;
++  MetaRemoteAccessHandle *remote_access_handle;
++
++  session->handle = meta_remote_desktop_session_handle_new (session);
++
++  remote_access_controller = meta_backend_get_remote_access_controller (backend);
++  remote_access_handle = META_REMOTE_ACCESS_HANDLE (session->handle);
++  meta_remote_access_controller_notify_new_handle (remote_access_controller,
++                                                   remote_access_handle);
++}
++
+ static gboolean
+ meta_remote_desktop_session_start (MetaRemoteDesktopSession *session,
+                                    GError                  **error)
+@@ -107,6 +139,8 @@ meta_remote_desktop_session_start (MetaRemoteDesktopSession *session,
+     clutter_device_manager_create_virtual_device (device_manager,
+                                                   CLUTTER_TOUCHSCREEN_DEVICE);
+ 
++  init_remote_access_handle (session);
++
+   return TRUE;
+ }
+ 
+@@ -131,6 +165,14 @@ meta_remote_desktop_session_close (MetaRemoteDesktopSession *session)
+   meta_dbus_remote_desktop_session_emit_closed (skeleton);
+   g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (session));
+ 
++  if (session->handle)
++    {
++      MetaRemoteAccessHandle *remote_access_handle =
++        META_REMOTE_ACCESS_HANDLE (session->handle);
++
++      meta_remote_access_handle_notify_stopped (remote_access_handle);
++    }
++
+   g_object_unref (session);
+ }
+ 
+@@ -729,6 +771,7 @@ meta_remote_desktop_session_finalize (GObject *object)
+ 
+   g_assert (!meta_remote_desktop_session_is_running (session));
+ 
++  g_clear_object (&session->handle);
+   g_free (session->peer_name);
+   g_free (session->session_id);
+   g_free (session->object_path);
+@@ -763,3 +806,40 @@ meta_remote_desktop_session_class_init (MetaRemoteDesktopSessionClass *klass)
+ 
+   object_class->finalize = meta_remote_desktop_session_finalize;
+ }
++
++static MetaRemoteDesktopSessionHandle *
++meta_remote_desktop_session_handle_new (MetaRemoteDesktopSession *session)
++{
++  MetaRemoteDesktopSessionHandle *handle;
++
++  handle = g_object_new (META_TYPE_REMOTE_DESKTOP_SESSION_HANDLE, NULL);
++  handle->session = session;
++
++  return handle;
++}
++
++static void
++meta_remote_desktop_session_handle_stop (MetaRemoteAccessHandle *handle)
++{
++  MetaRemoteDesktopSession *session;
++
++  session = META_REMOTE_DESKTOP_SESSION_HANDLE (handle)->session;
++  if (!session)
++    return;
++
++  meta_remote_desktop_session_close (session);
++}
++
++static void
++meta_remote_desktop_session_handle_init (MetaRemoteDesktopSessionHandle *handle)
++{
++}
++
++static void
++meta_remote_desktop_session_handle_class_init (MetaRemoteDesktopSessionHandleClass *klass)
++{
++  MetaRemoteAccessHandleClass *remote_access_handle_class =
++    META_REMOTE_ACCESS_HANDLE_CLASS (klass);
++
++  remote_access_handle_class->stop = meta_remote_desktop_session_handle_stop;
++}
+diff --git a/src/backends/meta-remote-desktop-session.h b/src/backends/meta-remote-desktop-session.h
+index 54dce41190..1edb3739b1 100644
+--- a/src/backends/meta-remote-desktop-session.h
++++ b/src/backends/meta-remote-desktop-session.h
+@@ -33,6 +33,12 @@ G_DECLARE_FINAL_TYPE (MetaRemoteDesktopSession, meta_remote_desktop_session,
+                       META, REMOTE_DESKTOP_SESSION,
+                       MetaDBusRemoteDesktopSessionSkeleton)
+ 
++#define META_TYPE_REMOTE_DESKTOP_SESSION_HANDLE (meta_remote_desktop_session_handle_get_type ())
++G_DECLARE_FINAL_TYPE (MetaRemoteDesktopSessionHandle,
++                      meta_remote_desktop_session_handle,
++                      META, REMOTE_DESKTOP_SESSION_HANDLE,
++                      MetaRemoteAccessHandle)
++
+ char * meta_remote_desktop_session_get_object_path (MetaRemoteDesktopSession *session);
+ 
+ char * meta_remote_desktop_session_get_session_id (MetaRemoteDesktopSession *session);
+diff --git a/src/backends/meta-screen-cast-session.c b/src/backends/meta-screen-cast-session.c
+index b95ce514dc..e1b6393e8f 100644
+--- a/src/backends/meta-screen-cast-session.c
++++ b/src/backends/meta-screen-cast-session.c
+@@ -28,6 +28,7 @@
+ #include "backends/meta-dbus-session-watcher.h"
+ #include "backends/meta-screen-cast-monitor-stream.h"
+ #include "backends/meta-screen-cast-stream.h"
++#include "backends/meta-remote-access-controller-private.h"
+ 
+ #define META_SCREEN_CAST_SESSION_DBUS_PATH "/org/gnome/Mutter/ScreenCast/Session"
+ 
+@@ -41,6 +42,8 @@ struct _MetaScreenCastSession
+   char *object_path;
+ 
+   GList *streams;
++
++  MetaScreenCastSessionHandle *handle;
+ };
+ 
+ static void
+@@ -57,6 +60,35 @@ G_DEFINE_TYPE_WITH_CODE (MetaScreenCastSession,
+                          G_IMPLEMENT_INTERFACE (META_TYPE_DBUS_SESSION,
+                                                 meta_dbus_session_init_iface))
+ 
++struct _MetaScreenCastSessionHandle
++{
++  MetaRemoteAccessHandle parent;
++
++  MetaScreenCastSession *session;
++};
++
++G_DEFINE_TYPE (MetaScreenCastSessionHandle,
++               meta_screen_cast_session_handle,
++               META_TYPE_REMOTE_ACCESS_HANDLE)
++
++static MetaScreenCastSessionHandle *
++meta_screen_cast_session_handle_new (MetaScreenCastSession *session);
++
++static void
++init_remote_access_handle (MetaScreenCastSession *session)
++{
++  MetaBackend *backend = meta_get_backend ();
++  MetaRemoteAccessController *remote_access_controller;
++  MetaRemoteAccessHandle *remote_access_handle;
++
++  session->handle = meta_screen_cast_session_handle_new (session);
++
++  remote_access_controller = meta_backend_get_remote_access_controller (backend);
++  remote_access_handle = META_REMOTE_ACCESS_HANDLE (session->handle);
++  meta_remote_access_controller_notify_new_handle (remote_access_controller,
++                                                   remote_access_handle);
++}
++
+ gboolean
+ meta_screen_cast_session_start (MetaScreenCastSession  *session,
+                                 GError                **error)
+@@ -71,6 +103,8 @@ meta_screen_cast_session_start (MetaScreenCastSession  *session,
+         return FALSE;
+     }
+ 
++  init_remote_access_handle (session);
++
+   return TRUE;
+ }
+ 
+@@ -94,6 +128,14 @@ meta_screen_cast_session_close (MetaScreenCastSession *session)
+ 
+   g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (session));
+ 
++  if (session->handle)
++    {
++      MetaRemoteAccessHandle *remote_access_handle =
++        META_REMOTE_ACCESS_HANDLE (session->handle);
++
++      meta_remote_access_handle_notify_stopped (remote_access_handle);
++    }
++
+   g_object_unref (session);
+ }
+ 
+@@ -361,6 +403,7 @@ meta_screen_cast_session_finalize (GObject *object)
+ {
+   MetaScreenCastSession *session = META_SCREEN_CAST_SESSION (object);
+ 
++  g_clear_object (&session->handle);
+   g_free (session->peer_name);
+   g_free (session->object_path);
+ 
+@@ -379,3 +422,40 @@ meta_screen_cast_session_class_init (MetaScreenCastSessionClass *klass)
+ 
+   object_class->finalize = meta_screen_cast_session_finalize;
+ }
++
++static MetaScreenCastSessionHandle *
++meta_screen_cast_session_handle_new (MetaScreenCastSession *session)
++{
++  MetaScreenCastSessionHandle *handle;
++
++  handle = g_object_new (META_TYPE_SCREEN_CAST_SESSION_HANDLE, NULL);
++  handle->session = session;
++
++  return handle;
++}
++
++static void
++meta_screen_cast_session_handle_stop (MetaRemoteAccessHandle *handle)
++{
++  MetaScreenCastSession *session;
++
++  session = META_SCREEN_CAST_SESSION_HANDLE (handle)->session;
++  if (!session)
++    return;
++
++  meta_screen_cast_session_close (session);
++}
++
++static void
++meta_screen_cast_session_handle_init (MetaScreenCastSessionHandle *handle)
++{
++}
++
++static void
++meta_screen_cast_session_handle_class_init (MetaScreenCastSessionHandleClass *klass)
++{
++  MetaRemoteAccessHandleClass *remote_access_handle_class =
++    META_REMOTE_ACCESS_HANDLE_CLASS (klass);
++
++  remote_access_handle_class->stop = meta_screen_cast_session_handle_stop;
++}
+diff --git a/src/backends/meta-screen-cast-session.h b/src/backends/meta-screen-cast-session.h
+index c877912cf9..ac0a31a165 100644
+--- a/src/backends/meta-screen-cast-session.h
++++ b/src/backends/meta-screen-cast-session.h
+@@ -26,6 +26,7 @@
+ #include "backends/meta-screen-cast.h"
+ 
+ #include "backends/meta-screen-cast-stream.h"
++#include "meta/meta-remote-access-controller.h"
+ 
+ typedef enum _MetaScreenCastSessionType
+ {
+@@ -38,6 +39,12 @@ G_DECLARE_FINAL_TYPE (MetaScreenCastSession, meta_screen_cast_session,
+                       META, SCREEN_CAST_SESSION,
+                       MetaDBusScreenCastSessionSkeleton)
+ 
++#define META_TYPE_SCREEN_CAST_SESSION_HANDLE (meta_screen_cast_session_handle_get_type ())
++G_DECLARE_FINAL_TYPE (MetaScreenCastSessionHandle,
++                      meta_screen_cast_session_handle,
++                      META, SCREEN_CAST_SESSION_HANDLE,
++                      MetaRemoteAccessHandle)
++
+ char * meta_screen_cast_session_get_object_path (MetaScreenCastSession *session);
+ 
+ MetaScreenCastSession * meta_screen_cast_session_new (MetaScreenCast             *screen_cast,
+diff --git a/src/meta/meta-backend.h b/src/meta/meta-backend.h
+index 8db2360ccd..0e5947dc12 100644
+--- a/src/meta/meta-backend.h
++++ b/src/meta/meta-backend.h
+@@ -29,6 +29,7 @@
+ 
+ #include <clutter/clutter.h>
+ #include "meta/meta-dnd.h"
++#include "meta/meta-remote-access-controller.h"
+ 
+ typedef struct _MetaBackend        MetaBackend;
+ typedef struct _MetaBackendClass   MetaBackendClass;
+@@ -54,6 +55,8 @@ MetaDnd      *meta_backend_get_dnd   (MetaBackend *backend);
+ 
+ MetaSettings *meta_backend_get_settings (MetaBackend *backend);
+ 
++MetaRemoteAccessController * meta_backend_get_remote_access_controller (MetaBackend *backend);
++
+ void meta_clutter_init (void);
+ 
+ #endif /* META_BACKEND_H */
+diff --git a/src/meta/meta-remote-access-controller.h b/src/meta/meta-remote-access-controller.h
+new file mode 100644
+index 0000000000..1b66f518c7
+--- /dev/null
++++ b/src/meta/meta-remote-access-controller.h
+@@ -0,0 +1,47 @@
++/*
++ * Copyright (C) 2018 Red Hat Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program 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
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
++ * 02111-1307, USA.
++ *
++ */
++
++#ifndef META_REMOTE_ACCESS_CONTROLLER_H
++#define META_REMOTE_ACCESS_CONTROLLER_H
++
++#include <glib-object.h>
++
++#define META_TYPE_REMOTE_ACCESS_HANDLE meta_remote_access_handle_get_type ()
++G_DECLARE_DERIVABLE_TYPE (MetaRemoteAccessHandle,
++                          meta_remote_access_handle,
++                          META, REMOTE_ACCESS_HANDLE,
++                          GObject)
++
++struct _MetaRemoteAccessHandleClass
++{
++  GObjectClass parent_class;
++
++  void (*stop) (MetaRemoteAccessHandle *handle);
++};
++
++void meta_remote_access_handle_stop (MetaRemoteAccessHandle *handle);
++
++#define META_TYPE_REMOTE_ACCESS_CONTROLLER meta_remote_access_controller_get_type ()
++G_DECLARE_FINAL_TYPE (MetaRemoteAccessController,
++                      meta_remote_access_controller,
++                      META, REMOTE_ACCESS_CONTROLLER,
++                      GObject)
++
++#endif /* META_REMOTE_ACCESS_CONTROLLER_H */
+-- 
+2.17.1
+
diff --git a/SOURCES/0001-Add-support-for-quad-buffer-stereo.patch b/SOURCES/0001-Add-support-for-quad-buffer-stereo.patch
new file mode 100644
index 0000000..27abe19
--- /dev/null
+++ b/SOURCES/0001-Add-support-for-quad-buffer-stereo.patch
@@ -0,0 +1,923 @@
+From 592ed744f97f283be98d494028f7cbd44fdfab6b 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
+
+Track the stereo status of windows using the new EXT_stereo_tree
+GLX extension.
+
+When stereo is enabled or disabled, a restart is triggered via
+meta_restart() after a timeout, setting a _META_ENABLE_STEREO
+property on the root window to indicate whether we should
+turn on a stereo stage for clutter. The property avoids a loop,
+since we need to enable stereo *before* initializing Clutter and GL,
+but we need GL to figure out whether we have stereo windows.
+
+Stereo windows are drawn to the stage using new functionality
+in Cogl to setup a stereo context, select which buffer to draw
+to, and draw either the left or right buffer of a stereo
+texture_from_pixmap.
+---
+ src/Makefile.am                              |   2 +
+ src/compositor/compositor-private.h          |   9 ++
+ src/compositor/compositor.c                  | 128 ++++++++++++++++
+ src/compositor/meta-shaped-texture-private.h |   5 +-
+ src/compositor/meta-shaped-texture.c         |  85 +++++++++--
+ src/compositor/meta-surface-actor-wayland.c  |   2 +-
+ src/compositor/meta-surface-actor-x11.c      |  56 ++++++-
+ src/compositor/meta-surface-actor-x11.h      |   5 +
+ src/compositor/meta-window-actor-private.h   |   5 +
+ src/compositor/meta-window-actor.c           |  22 +++
+ src/core/main.c                              |   4 +
+ src/core/stereo.c                            | 153 +++++++++++++++++++
+ src/core/stereo.h                            |  28 ++++
+ src/wayland/meta-wayland-surface.c           |   2 +-
+ 14 files changed, 483 insertions(+), 23 deletions(-)
+ create mode 100644 src/core/stereo.c
+ create mode 100644 src/core/stereo.h
+
+diff --git a/src/Makefile.am b/src/Makefile.am
+index 8856877d8..26e0e55c2 100644
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -315,6 +315,8 @@ libmutter_@LIBMUTTER_API_VERSION@_la_SOURCES =	\
+ 	core/stack.h				\
+ 	core/stack-tracker.c			\
+ 	core/stack-tracker.h			\
++	core/stereo.c				\
++	core/stereo.h				\
+ 	core/util.c				\
+ 	meta/util.h				\
+ 	core/util-private.h			\
+diff --git a/src/compositor/compositor-private.h b/src/compositor/compositor-private.h
+index 40c0344cc..25cab9280 100644
+--- a/src/compositor/compositor-private.h
++++ b/src/compositor/compositor-private.h
+@@ -21,6 +21,10 @@ struct _MetaCompositor
+   gint64          server_time_query_time;
+   gint64          server_time_offset;
+ 
++  int             glx_opcode;
++  guint           stereo_tree_ext : 1;
++  guint           have_stereo_windows : 1;
++
+   guint           server_time_is_monotonic_time : 1;
+   guint           no_mipmaps  : 1;
+ 
+@@ -61,6 +65,11 @@ void     meta_end_modal_for_plugin   (MetaCompositor   *compositor,
+ gint64 meta_compositor_monotonic_time_to_server_time (MetaDisplay *display,
+                                                       gint64       monotonic_time);
+ 
++gboolean meta_compositor_window_is_stereo     (MetaScreen *screen,
++                                               Window      xwindow);
++void     meta_compositor_select_stereo_notify (MetaScreen *screen,
++                                               Window      xwindow);
++
+ void meta_compositor_flash_window (MetaCompositor *compositor,
+                                    MetaWindow     *window);
+ 
+diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c
+index 8c924d256..e12fb7b58 100644
+--- a/src/compositor/compositor.c
++++ b/src/compositor/compositor.c
+@@ -70,6 +70,8 @@
+ #include "meta-window-group-private.h"
+ #include "window-private.h" /* to check window->hidden */
+ #include "display-private.h" /* for meta_display_lookup_x_window() and meta_display_cancel_touch() */
++#include "stack-tracker.h"
++#include "stereo.h"
+ #include "util-private.h"
+ #include "backends/meta-dnd-private.h"
+ #include "frame.h"
+@@ -487,6 +489,97 @@ redirect_windows (MetaScreen *screen)
+     }
+ }
+ 
++#define GLX_STEREO_TREE_EXT        0x20F5
++#define GLX_STEREO_NOTIFY_MASK_EXT 0x00000001
++#define GLX_STEREO_NOTIFY_EXT      0x00000000
++
++typedef struct {
++  int type;
++  unsigned long serial;
++  Bool send_event;
++  Display *display;
++  int extension;
++  int evtype;
++  Drawable window;
++  Bool stereo_tree;
++} StereoNotifyEvent;
++
++static gboolean
++screen_has_stereo_tree_ext (MetaScreen *screen)
++{
++  MetaDisplay *display = meta_screen_get_display (screen);
++  Display     *xdisplay = meta_display_get_xdisplay (display);
++  const char  *extensions_string;
++
++  static const char * (*query_extensions_string) (Display *display,
++                                                  int      screen);
++
++  if (query_extensions_string == NULL)
++    query_extensions_string =
++      (const char * (*) (Display *, int))
++      cogl_get_proc_address ("glXQueryExtensionsString");
++
++  extensions_string = query_extensions_string (xdisplay,
++                                               meta_screen_get_screen_number (screen));
++
++  return extensions_string && strstr (extensions_string, "EXT_stereo_tree") != 0;
++}
++
++#include <GL/gl.h>
++
++gboolean
++meta_compositor_window_is_stereo (MetaScreen *screen,
++                                  Window      xwindow)
++{
++  MetaCompositor *compositor = get_compositor_for_screen (screen);
++  MetaDisplay    *display = meta_screen_get_display (screen);
++  Display        *xdisplay = meta_display_get_xdisplay (display);
++
++  static int (*query_drawable) (Display      *dpy,
++                                Drawable      draw,
++                                int           attribute,
++                                unsigned int *value);
++
++  if (compositor->stereo_tree_ext)
++    {
++      unsigned int stereo_tree = 0;
++
++      if (query_drawable == NULL)
++        query_drawable =
++          (int (*) (Display *, Drawable, int, unsigned int *))
++          cogl_get_proc_address ("glXQueryDrawable");
++
++      query_drawable (xdisplay, xwindow, GLX_STEREO_TREE_EXT, &stereo_tree);
++
++      return stereo_tree != 0;
++    }
++  else
++    return FALSE;
++}
++
++void
++meta_compositor_select_stereo_notify (MetaScreen *screen,
++                                      Window      xwindow)
++{
++  MetaCompositor *compositor = get_compositor_for_screen (screen);
++  MetaDisplay    *display = meta_screen_get_display (screen);
++  Display        *xdisplay = meta_display_get_xdisplay (display);
++
++  static void (*select_event) (Display      *dpy,
++                               Drawable      draw,
++                               unsigned long event_mask);
++
++  if (compositor->stereo_tree_ext)
++    {
++      if (select_event == NULL)
++        select_event =
++          (void (*) (Display *, Drawable, unsigned long))
++          cogl_get_proc_address ("glXSelectEvent");
++
++      select_event (xdisplay, xwindow, GLX_STEREO_NOTIFY_MASK_EXT);
++    }
++}
++
+ void
+ meta_compositor_manage (MetaCompositor *compositor)
+ {
+@@ -495,6 +588,8 @@ meta_compositor_manage (MetaCompositor *compositor)
+   MetaScreen *screen = display->screen;
+   MetaBackend *backend = meta_get_backend ();
+ 
++  compositor->stereo_tree_ext = screen_has_stereo_tree_ext (screen);
++
+   meta_screen_set_cm_selection (display->screen);
+ 
+   compositor->stage = meta_backend_get_stage (backend);
+@@ -759,6 +854,23 @@ meta_compositor_process_event (MetaCompositor *compositor,
+       if (window)
+         process_damage (compositor, (XDamageNotifyEvent *) event, window);
+     }
++  else if (!meta_is_wayland_compositor () &&
++           event->type == GenericEvent &&
++           event->xcookie.extension == compositor->glx_opcode)
++    {
++      if (event->xcookie.evtype == GLX_STEREO_NOTIFY_EXT)
++        {
++          StereoNotifyEvent *stereo_event = (StereoNotifyEvent *)(event->xcookie.data);
++          window = meta_display_lookup_x_window (compositor->display, stereo_event->window);
++
++          if (window != NULL)
++            {
++              MetaWindowActor *window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window));
++              meta_window_actor_stereo_notify (window_actor, stereo_event->stereo_tree);
++              meta_stack_tracker_queue_sync_stack (window->screen->stack_tracker);
++            }
++        }
++    }
+ 
+   if (compositor->have_x11_sync_object)
+     meta_sync_ring_handle_event (event);
+@@ -969,6 +1081,7 @@ meta_compositor_sync_stack (MetaCompositor  *compositor,
+ 			    GList	    *stack)
+ {
+   GList *old_stack;
++  int stereo_window_count = 0;
+ 
+   /* This is painful because hidden windows that we are in the process
+    * of animating out of existence. They'll be at the bottom of the
+@@ -1044,6 +1157,8 @@ meta_compositor_sync_stack (MetaCompositor  *compositor,
+        * near the front of the other.)
+        */
+       compositor->windows = g_list_prepend (compositor->windows, actor);
++      if (meta_window_actor_is_stereo (actor))
++        stereo_window_count++;
+ 
+       stack = g_list_remove (stack, window);
+       old_stack = g_list_remove (old_stack, actor);
+@@ -1051,6 +1166,8 @@ meta_compositor_sync_stack (MetaCompositor  *compositor,
+ 
+   sync_actor_stacking (compositor);
+ 
++  meta_stereo_set_have_stereo_windows (stereo_window_count > 0);
++
+   if (compositor->top_window_actor)
+     g_signal_handlers_disconnect_by_func (compositor->top_window_actor,
+                                           on_top_window_actor_destroyed,
+@@ -1259,6 +1376,17 @@ meta_compositor_new (MetaDisplay *display)
+                                            meta_post_paint_func,
+                                            compositor,
+                                            NULL);
++  if (!meta_is_wayland_compositor ())
++    {
++      Display *xdisplay = meta_display_get_xdisplay (display);
++      int glx_major_opcode, glx_first_event, glx_first_error;
++
++      if (XQueryExtension (xdisplay,
++                           "GLX",
++                           &glx_major_opcode, &glx_first_event, &glx_first_error))
++        compositor->glx_opcode = glx_major_opcode;
++    }
++
+   return compositor;
+ }
+ 
+diff --git a/src/compositor/meta-shaped-texture-private.h b/src/compositor/meta-shaped-texture-private.h
+index 5b3f283c2..189a95312 100644
+--- a/src/compositor/meta-shaped-texture-private.h
++++ b/src/compositor/meta-shaped-texture-private.h
+@@ -30,8 +30,9 @@
+ #include <meta/meta-shaped-texture.h>
+ 
+ ClutterActor *meta_shaped_texture_new (void);
+-void meta_shaped_texture_set_texture (MetaShapedTexture *stex,
+-                                      CoglTexture       *texture);
++void meta_shaped_texture_set_textures (MetaShapedTexture *stex,
++                                       CoglTexture       *texture,
++                                       CoglTexture       *texture_right);
+ void meta_shaped_texture_set_is_y_inverted (MetaShapedTexture *stex,
+                                             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 8de173bf7..e40bf9d9c 100644
+--- a/src/compositor/meta-shaped-texture.c
++++ b/src/compositor/meta-shaped-texture.c
+@@ -89,8 +89,10 @@ typedef struct
+ struct _MetaShapedTexturePrivate
+ {
+   MetaTextureTower *paint_tower;
++  MetaTextureTower *paint_tower_right;
+ 
+   CoglTexture *texture;
++  CoglTexture *texture_right;
+   CoglTexture *mask_texture;
+   CoglSnippet *snippet;
+ 
+@@ -161,8 +163,10 @@ meta_shaped_texture_init (MetaShapedTexture *self)
+   priv = self->priv = META_SHAPED_TEXTURE_GET_PRIVATE (self);
+ 
+   priv->paint_tower = meta_texture_tower_new ();
++  priv->paint_tower_right = NULL; /* demand create */
+ 
+   priv->texture = NULL;
++  priv->texture_right = NULL;
+   priv->mask_texture = NULL;
+   priv->create_mipmaps = TRUE;
+   priv->is_y_inverted = TRUE;
+@@ -229,11 +233,11 @@ meta_shaped_texture_dispose (GObject *object)
+   MetaShapedTexture *self = (MetaShapedTexture *) object;
+   MetaShapedTexturePrivate *priv = self->priv;
+ 
+-  if (priv->paint_tower)
+-    meta_texture_tower_free (priv->paint_tower);
+-  priv->paint_tower = NULL;
++  g_clear_pointer (&priv->paint_tower, meta_texture_tower_free);
++  g_clear_pointer (&priv->paint_tower_right, meta_texture_tower_free);
+ 
+   g_clear_pointer (&priv->texture, cogl_object_unref);
++  g_clear_pointer (&priv->texture_right, cogl_object_unref);
+   g_clear_pointer (&priv->opaque_region, cairo_region_destroy);
+ 
+   meta_shaped_texture_set_mask_texture (self, NULL);
+@@ -431,8 +435,9 @@ paint_clipped_rectangle (CoglFramebuffer       *fb,
+ }
+ 
+ static void
+-set_cogl_texture (MetaShapedTexture *stex,
+-                  CoglTexture       *cogl_tex)
++set_cogl_textures (MetaShapedTexture *stex,
++                   CoglTexture       *cogl_tex,
++                   CoglTexture       *cogl_tex_right)
+ {
+   MetaShapedTexturePrivate *priv;
+   guint width, height;
+@@ -443,10 +448,13 @@ set_cogl_texture (MetaShapedTexture *stex,
+ 
+   if (priv->texture)
+     cogl_object_unref (priv->texture);
++  if (priv->texture_right)
++    cogl_object_unref (priv->texture_right);
+ 
+   g_clear_pointer (&priv->saved_base_surface, cairo_surface_destroy);
+ 
+   priv->texture = cogl_tex;
++  priv->texture_right = cogl_tex_right;
+ 
+   if (cogl_tex != NULL)
+     {
+@@ -460,6 +468,9 @@ set_cogl_texture (MetaShapedTexture *stex,
+       height = 0;
+     }
+ 
++  if (cogl_tex_right != NULL)
++    cogl_object_ref (cogl_tex_right);
++
+   if (priv->tex_width != width ||
+       priv->tex_height != height)
+     {
+@@ -475,8 +486,23 @@ set_cogl_texture (MetaShapedTexture *stex,
+    * previous buffer. We only queue a redraw in response to surface
+    * damage. */
+ 
++  if (cogl_tex_right != NULL)
++    {
++      if (priv->paint_tower_right == NULL)
++        priv->paint_tower_right = meta_texture_tower_new ();
++    }
++  else
++    {
++      g_clear_pointer (&priv->paint_tower_right, meta_texture_tower_free);
++    }
++
+   if (priv->create_mipmaps)
+-    meta_texture_tower_set_base_texture (priv->paint_tower, cogl_tex);
++    {
++      meta_texture_tower_set_base_texture (priv->paint_tower, cogl_tex);
++
++      if (priv->paint_tower_right)
++        meta_texture_tower_set_base_texture (priv->paint_tower_right, cogl_tex_right);
++    }
+ }
+ 
+ static void
+@@ -485,6 +511,7 @@ do_paint (MetaShapedTexture *stex,
+           CoglTexture       *paint_tex,
+           cairo_region_t    *clip_region)
+ {
++  ClutterActor *actor = CLUTTER_ACTOR (stex);
+   MetaShapedTexturePrivate *priv = stex->priv;
+   guint tex_width, tex_height;
+   guchar opacity;
+@@ -700,7 +727,9 @@ meta_shaped_texture_paint (ClutterActor *actor)
+   MetaShapedTexture *stex = META_SHAPED_TEXTURE (actor);
+   MetaShapedTexturePrivate *priv = stex->priv;
+   CoglTexture *paint_tex = NULL;
++  CoglTexture *paint_tex_right = NULL;
+   CoglFramebuffer *fb;
++  gboolean stereo;
+ 
+   if (!priv->texture)
+     return;
+@@ -737,7 +766,32 @@ meta_shaped_texture_paint (ClutterActor *actor)
+     return;
+ 
+   fb = cogl_get_draw_framebuffer ();
+-  do_paint (META_SHAPED_TEXTURE (actor), fb, paint_tex, priv->clip_region);
++
++  stereo = priv->texture_right && cogl_framebuffer_get_is_stereo (fb);
++
++  if (stereo)
++    {
++      if (priv->create_mipmaps)
++	paint_tex_right = meta_texture_tower_get_paint_texture (priv->paint_tower_right);
++
++      if (!paint_tex_right)
++	paint_tex_right = COGL_TEXTURE (priv->texture_right);
++    }
++  else
++    paint_tex_right = NULL;
++
++  if (stereo)
++    cogl_framebuffer_set_stereo_mode (fb, COGL_STEREO_LEFT);
++  do_paint (stex, fb, paint_tex, priv->clip_region);
++  if (stereo)
++    cogl_framebuffer_set_stereo_mode (fb, COGL_STEREO_BOTH);
++
++  if (paint_tex_right != NULL)
++    {
++      cogl_framebuffer_set_stereo_mode (fb, COGL_STEREO_RIGHT);
++      do_paint (stex, fb, paint_tex_right, priv->clip_region);
++      cogl_framebuffer_set_stereo_mode (fb, COGL_STEREO_BOTH);
++    }
+ }
+ 
+ static void
+@@ -859,6 +913,12 @@ meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex,
+       priv->create_mipmaps = create_mipmaps;
+       base_texture = create_mipmaps ? priv->texture : NULL;
+       meta_texture_tower_set_base_texture (priv->paint_tower, base_texture);
++
++      if (priv->paint_tower_right)
++        {
++          base_texture = create_mipmaps ? priv->texture_right : NULL;
++          meta_texture_tower_set_base_texture (priv->paint_tower_right, base_texture);
++        }
+     }
+ }
+ 
+@@ -987,6 +1047,8 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex,
+   shrink_backing_region (stex, &clip);
+ 
+   meta_texture_tower_update_area (priv->paint_tower, x, y, width, height);
++  if (priv->paint_tower_right)
++    meta_texture_tower_update_area (priv->paint_tower_right, x, y, width, height);
+ 
+   unobscured_region = effective_unobscured_region (stex);
+   if (unobscured_region)
+@@ -1019,17 +1081,18 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex,
+ }
+ 
+ /**
+- * meta_shaped_texture_set_texture:
++ * meta_shaped_texture_set_textures:
+  * @stex: The #MetaShapedTexture
+  * @pixmap: The #CoglTexture to display
+  */
+ void
+-meta_shaped_texture_set_texture (MetaShapedTexture *stex,
+-                                 CoglTexture       *texture)
++meta_shaped_texture_set_textures (MetaShapedTexture *stex,
++                                  CoglTexture       *texture,
++                                  CoglTexture       *texture_right)
+ {
+   g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
+ 
+-  set_cogl_texture (stex, texture);
++  set_cogl_textures (stex, texture, texture_right);
+ }
+ 
+ /**
+diff --git a/src/compositor/meta-surface-actor-wayland.c b/src/compositor/meta-surface-actor-wayland.c
+index 7505b7d79..f78bce66c 100644
+--- a/src/compositor/meta-surface-actor-wayland.c
++++ b/src/compositor/meta-surface-actor-wayland.c
+@@ -187,7 +187,7 @@ meta_surface_actor_wayland_dispose (GObject *object)
+   MetaShapedTexture *stex =
+     meta_surface_actor_get_texture (META_SURFACE_ACTOR (self));
+ 
+-  meta_shaped_texture_set_texture (stex, NULL);
++  meta_shaped_texture_set_textures (stex, NULL, NULL);
+   if (priv->surface)
+     {
+       g_object_remove_weak_pointer (G_OBJECT (priv->surface),
+diff --git a/src/compositor/meta-surface-actor-x11.c b/src/compositor/meta-surface-actor-x11.c
+index d32aeb68a..52db3808e 100644
+--- a/src/compositor/meta-surface-actor-x11.c
++++ b/src/compositor/meta-surface-actor-x11.c
+@@ -31,6 +31,7 @@
+ #include <cogl/winsys/cogl-texture-pixmap-x11.h>
+ 
+ #include <meta/errors.h>
++#include "compositor-private.h"
+ #include "window-private.h"
+ #include "meta-shaped-texture-private.h"
+ #include "meta-cullable.h"
+@@ -43,6 +44,7 @@ struct _MetaSurfaceActorX11Private
+   MetaDisplay *display;
+ 
+   CoglTexture *texture;
++  CoglTexture *texture_right;
+   Pixmap pixmap;
+   Damage damage;
+ 
+@@ -58,6 +60,8 @@ struct _MetaSurfaceActorX11Private
+   guint size_changed : 1;
+ 
+   guint unredirected   : 1;
++
++  guint stereo : 1;
+ };
+ typedef struct _MetaSurfaceActorX11Private MetaSurfaceActorX11Private;
+ 
+@@ -94,7 +98,7 @@ detach_pixmap (MetaSurfaceActorX11 *self)
+    * you are supposed to be able to free a GLXPixmap after freeing the underlying
+    * pixmap, but it certainly doesn't work with current DRI/Mesa
+    */
+-  meta_shaped_texture_set_texture (stex, NULL);
++  meta_shaped_texture_set_textures (stex, NULL, NULL);
+   cogl_flush ();
+ 
+   meta_error_trap_push (display);
+@@ -103,6 +107,7 @@ detach_pixmap (MetaSurfaceActorX11 *self)
+   meta_error_trap_pop (display);
+ 
+   g_clear_pointer (&priv->texture, cogl_object_unref);
++  g_clear_pointer (&priv->texture_right, cogl_object_unref);
+ }
+ 
+ static void
+@@ -114,23 +119,35 @@ set_pixmap (MetaSurfaceActorX11 *self,
+   CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
+   MetaShapedTexture *stex = meta_surface_actor_get_texture (META_SURFACE_ACTOR (self));
+   CoglError *error = NULL;
+-  CoglTexture *texture;
++  CoglTexturePixmapX11 *texture;
++  CoglTexturePixmapX11 *texture_right;
+ 
+   g_assert (priv->pixmap == None);
+   priv->pixmap = pixmap;
+ 
+-  texture = COGL_TEXTURE (cogl_texture_pixmap_x11_new (ctx, priv->pixmap, FALSE, &error));
++  if (priv->stereo)
++    texture = cogl_texture_pixmap_x11_new_left (ctx, pixmap, FALSE, &error);
++  else
++    texture = cogl_texture_pixmap_x11_new (ctx, pixmap, FALSE, &error);
++
++  if (priv->stereo)
++    texture_right = cogl_texture_pixmap_x11_new_right (texture);
++  else
++    texture_right = NULL;
+ 
+   if (error != NULL)
+     {
+       g_warning ("Failed to allocate stex texture: %s", error->message);
+       cogl_error_free (error);
+     }
+-  else if (G_UNLIKELY (!cogl_texture_pixmap_x11_is_using_tfp_extension (COGL_TEXTURE_PIXMAP_X11 (texture))))
++  else if (G_UNLIKELY (!cogl_texture_pixmap_x11_is_using_tfp_extension (texture)))
+     g_warning ("NOTE: Not using GLX TFP!\n");
+ 
+-  priv->texture = texture;
+-  meta_shaped_texture_set_texture (stex, texture);
++  priv->texture = COGL_TEXTURE (texture);
++  if (priv->stereo)
++    priv->texture_right = COGL_TEXTURE (texture_right);
++
++  meta_shaped_texture_set_textures (stex, COGL_TEXTURE (texture), COGL_TEXTURE (texture_right));
+ }
+ 
+ static void
+@@ -433,8 +450,8 @@ reset_texture (MetaSurfaceActorX11 *self)
+   /* Setting the texture to NULL will cause all the FBO's cached by the
+    * shaped texture's MetaTextureTower to be discarded and recreated.
+    */
+-  meta_shaped_texture_set_texture (stex, NULL);
+-  meta_shaped_texture_set_texture (stex, priv->texture);
++  meta_shaped_texture_set_textures (stex, NULL, NULL);
++  meta_shaped_texture_set_textures (stex, priv->texture, priv->texture_right);
+ }
+ 
+ MetaSurfaceActor *
+@@ -443,12 +460,17 @@ meta_surface_actor_x11_new (MetaWindow *window)
+   MetaSurfaceActorX11 *self = g_object_new (META_TYPE_SURFACE_ACTOR_X11, NULL);
+   MetaSurfaceActorX11Private *priv = meta_surface_actor_x11_get_instance_private (self);
+   MetaDisplay *display = meta_window_get_display (window);
++  Window xwindow;
+ 
+   g_assert (!meta_is_wayland_compositor ());
+ 
+   priv->window = window;
+   priv->display = display;
+ 
++  xwindow = meta_window_x11_get_toplevel_xwindow (window);
++  priv->stereo = meta_compositor_window_is_stereo (display->screen, xwindow);
++  meta_compositor_select_stereo_notify (display->screen, xwindow);
++
+   g_signal_connect_object (priv->display, "gl-video-memory-purged",
+                            G_CALLBACK (reset_texture), self, G_CONNECT_SWAPPED);
+ 
+@@ -479,3 +501,21 @@ meta_surface_actor_x11_set_size (MetaSurfaceActorX11 *self,
+   priv->last_height = height;
+   meta_shaped_texture_set_fallback_size (stex, width, height);
+ }
++
++void
++meta_surface_actor_x11_stereo_notify (MetaSurfaceActorX11 *self,
++                                      gboolean             stereo_tree)
++{
++  MetaSurfaceActorX11Private *priv = meta_surface_actor_x11_get_instance_private (self);
++
++  priv->stereo = stereo_tree != FALSE;
++  detach_pixmap (self);
++}
++
++gboolean
++meta_surface_actor_x11_is_stereo (MetaSurfaceActorX11 *self)
++{
++  MetaSurfaceActorX11Private *priv = meta_surface_actor_x11_get_instance_private (self);
++
++  return priv->stereo;
++}
+diff --git a/src/compositor/meta-surface-actor-x11.h b/src/compositor/meta-surface-actor-x11.h
+index 0e692ee0f..4b2ecccb1 100644
+--- a/src/compositor/meta-surface-actor-x11.h
++++ b/src/compositor/meta-surface-actor-x11.h
+@@ -64,6 +64,11 @@ MetaSurfaceActor * meta_surface_actor_x11_new (MetaWindow *window);
+ void meta_surface_actor_x11_set_size (MetaSurfaceActorX11 *self,
+                                       int width, int height);
+ 
++void meta_surface_actor_x11_stereo_notify (MetaSurfaceActorX11 *self,
++                                           gboolean             stereo_tree);
++
++gboolean meta_surface_actor_x11_is_stereo (MetaSurfaceActorX11 *self);
++
+ G_END_DECLS
+ 
+ #endif /* __META_SURFACE_ACTOR_X11_H__ */
+diff --git a/src/compositor/meta-window-actor-private.h b/src/compositor/meta-window-actor-private.h
+index ce5e7eadc..5b011cec1 100644
+--- a/src/compositor/meta-window-actor-private.h
++++ b/src/compositor/meta-window-actor-private.h
+@@ -60,4 +60,9 @@ MetaSurfaceActor *meta_window_actor_get_surface (MetaWindowActor *self);
+ void meta_window_actor_update_surface (MetaWindowActor *self);
+ MetaWindowActor *meta_window_actor_from_window (MetaWindow *window);
+ 
++void meta_window_actor_stereo_notify (MetaWindowActor *actor,
++                                      gboolean         stereo_tree);
++
++gboolean meta_window_actor_is_stereo (MetaWindowActor *actor);
++
+ #endif /* META_WINDOW_ACTOR_PRIVATE_H */
+diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c
+index b67fb3ce1..80ff5fdd3 100644
+--- a/src/compositor/meta-window-actor.c
++++ b/src/compositor/meta-window-actor.c
+@@ -2321,3 +2321,25 @@ screen_cast_window_iface_init (MetaScreenCastWindowInterface *iface)
+   iface->transform_relative_position = meta_window_actor_transform_relative_position;
+   iface->capture_into = meta_window_actor_capture_into;
+ }
++
++void
++meta_window_actor_stereo_notify (MetaWindowActor *self,
++                                 gboolean         stereo_tree)
++{
++  MetaWindowActorPrivate *priv = self->priv;
++
++  if (META_IS_SURFACE_ACTOR_X11 (priv->surface))
++    meta_surface_actor_x11_stereo_notify (META_SURFACE_ACTOR_X11 (priv->surface),
++                                          stereo_tree);
++}
++
++gboolean
++meta_window_actor_is_stereo (MetaWindowActor *self)
++{
++  MetaWindowActorPrivate *priv = self->priv;
++
++  if (META_IS_SURFACE_ACTOR_X11 (priv->surface))
++    return meta_surface_actor_x11_is_stereo (META_SURFACE_ACTOR_X11 (priv->surface));
++  else
++    return FALSE;
++}
+diff --git a/src/core/main.c b/src/core/main.c
+index be7a90feb..828037635 100644
+--- a/src/core/main.c
++++ b/src/core/main.c
+@@ -47,6 +47,7 @@
+ #include <meta/main.h>
+ #include "util-private.h"
+ #include "display-private.h"
++#include "stereo.h"
+ #include <meta/errors.h>
+ #include "ui.h"
+ #include <meta/prefs.h>
+@@ -582,6 +583,9 @@ meta_init (void)
+ 
+   meta_init_backend (backend_gtype);
+ 
++  if (!meta_is_wayland_compositor ())
++    meta_stereo_init ();
++
+   meta_clutter_init ();
+ 
+ #ifdef HAVE_WAYLAND
+diff --git a/src/core/stereo.c b/src/core/stereo.c
+new file mode 100644
+index 000000000..5a232b67c
+--- /dev/null
++++ b/src/core/stereo.c
+@@ -0,0 +1,153 @@
++/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
++
++/*
++ * Copyright (C) 2014 Red Hat, Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program 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
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, see <http://www.gnu.org/licenses/>.
++ */
++
++/*
++ * SECTION:stereo
++ * @short_description: Keep track of whether we are a stereo compositor
++ *
++ * With GLX, we need to use a different GL context for stereo and
++ * non-stereo support. Support for multiple GL contexts is unfinished
++ * in Cogl and entirely lacking in Clutter, so it's by far easier
++ * to just restart Mutter when we detect a stereo window.
++ *
++ * A property _MUTTER_ENABLE_STEREO is maintained on the root window
++ * to know whether we should initialize clutter for stereo or not.
++ * When the presence or absence of stereo windows mismatches the
++ * stereo-enabled state for a sufficiently long period of time,
++ * we restart Mutter.
++ */
++
++#include <config.h>
++
++#include <clutter/x11/clutter-x11.h>
++#include <gio/gunixinputstream.h>
++#include <X11/Xatom.h>
++
++#include <meta/main.h>
++#include "ui.h"
++#include <meta/util.h>
++#include "display-private.h"
++#include "stereo.h"
++#include "util-private.h"
++
++static guint stereo_switch_id = 0;
++static gboolean stereo_enabled = FALSE;
++/* -1 so the first time meta_stereo_set_have_stereo_windows() is called
++ * we avoid the short-circuit and set up a timeout to restart
++ * if necessary */
++static gboolean stereo_have_windows = (gboolean)-1;
++static gboolean stereo_restart = FALSE;
++
++#define STEREO_ENABLE_WAIT 1000
++#define STEREO_DISABLE_WAIT 5000
++
++void
++meta_stereo_init (void)
++{
++  Display *xdisplay;
++  Window root;
++  Atom atom_enable_stereo;
++  Atom type;
++  int format;
++  unsigned long n_items, bytes_after;
++  guchar *data;
++
++  xdisplay = XOpenDisplay (NULL);
++  if (xdisplay == NULL)
++    meta_fatal ("Unable to open X display %s\n", XDisplayName (NULL));
++
++  root = DefaultRootWindow (xdisplay);
++  atom_enable_stereo = XInternAtom (xdisplay, "_MUTTER_ENABLE_STEREO", False);
++
++  XGetWindowProperty (xdisplay, root, atom_enable_stereo,
++                      0, 1, False, XA_INTEGER,
++                      &type, &format, &n_items, &bytes_after, &data);
++  if (type == XA_INTEGER)
++    {
++      if (format == 32 && n_items == 1 && bytes_after == 0)
++        {
++          stereo_enabled = *(long *)data;
++        }
++      else
++        {
++          meta_warning ("Bad value for _MUTTER_ENABLE_STEREO property\n");
++        }
++
++      XFree (data);
++    }
++  else if (type != None)
++    {
++      meta_warning ("Bad type for _MUTTER_ENABLE_STEREO property\n");
++    }
++
++  meta_verbose ("On startup, _MUTTER_ENABLE_STEREO=%s",
++                stereo_enabled ? "yes" : "no");
++  clutter_x11_set_use_stereo_stage (stereo_enabled);
++  XCloseDisplay (xdisplay);
++}
++
++static gboolean
++meta_stereo_switch (gpointer data)
++{
++  stereo_switch_id = 0;
++  stereo_restart = TRUE;
++
++  meta_restart (stereo_have_windows ?
++                _("Enabling stereo...") :
++                _("Disabling stereo..."));
++
++  return FALSE;
++}
++
++void
++meta_stereo_set_have_stereo_windows (gboolean have_windows)
++{
++  have_windows = have_windows != FALSE;
++
++  if (!stereo_restart && have_windows != stereo_have_windows)
++    {
++      MetaDisplay *display = meta_get_display ();
++      Display *xdisplay = meta_display_get_xdisplay (display);
++      Window root = DefaultRootWindow (xdisplay);
++      Atom atom_enable_stereo = XInternAtom (xdisplay, "_MUTTER_ENABLE_STEREO", False);
++      long value;
++
++      stereo_have_windows = have_windows;
++
++      if (stereo_have_windows)
++        meta_verbose ("Detected stereo windows\n");
++      else
++        meta_verbose ("No stereo windows detected\n");
++
++      value = stereo_have_windows;
++      XChangeProperty (xdisplay, root,
++                       atom_enable_stereo, XA_INTEGER, 32,
++                       PropModeReplace, (guchar *)&value, 1);
++
++      if (stereo_switch_id != 0)
++        {
++          g_source_remove (stereo_switch_id);
++          stereo_switch_id = 0;
++        }
++
++      if (stereo_have_windows != stereo_enabled)
++        stereo_switch_id = g_timeout_add (stereo_have_windows ? STEREO_ENABLE_WAIT : STEREO_DISABLE_WAIT,
++                                          meta_stereo_switch, NULL);
++    }
++}
+diff --git a/src/core/stereo.h b/src/core/stereo.h
+new file mode 100644
+index 000000000..ccd1d702a
+--- /dev/null
++++ b/src/core/stereo.h
+@@ -0,0 +1,28 @@
++/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
++
++/*
++ * Copyright (C) 2014 Red Hat, Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program 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
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, see <http://www.gnu.org/licenses/>.
++ */
++
++#ifndef META_STEREO_H
++#define META_STEREO_H
++
++void     meta_stereo_init                    (void);
++void     meta_stereo_set_have_stereo_windows (gboolean have_windows);
++gboolean meta_stereo_is_restart              (void);
++void     meta_stereo_finish_restart          (void);
++
++#endif
+diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c
+index c9c229e6c..7bc72f166 100644
+--- a/src/wayland/meta-wayland-surface.c
++++ b/src/wayland/meta-wayland-surface.c
+@@ -676,7 +676,7 @@ meta_wayland_surface_apply_pending_state (MetaWaylandSurface      *surface,
+               snippet = meta_wayland_buffer_create_snippet (pending->buffer);
+               is_y_inverted = meta_wayland_buffer_is_y_inverted (pending->buffer);
+ 
+-              meta_shaped_texture_set_texture (stex, texture);
++              meta_shaped_texture_set_textures (stex, texture, NULL);
+               meta_shaped_texture_set_snippet (stex, snippet);
+               meta_shaped_texture_set_is_y_inverted (stex, is_y_inverted);
+               g_clear_pointer (&snippet, cogl_object_unref);
+-- 
+2.20.1
+
diff --git a/SOURCES/0001-Make-screen-cast-and-remote-desktop-non-experimental.patch b/SOURCES/0001-Make-screen-cast-and-remote-desktop-non-experimental.patch
new file mode 100644
index 0000000..c1633bb
--- /dev/null
+++ b/SOURCES/0001-Make-screen-cast-and-remote-desktop-non-experimental.patch
@@ -0,0 +1,145 @@
+From 19539bd93c521002222a895dca3abf3f3c46a1b5 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Thu, 7 Jun 2018 10:21:06 +0200
+Subject: [PATCH] Make screen cast and remote desktop non-experimental
+
+It's time to make this feature more accessible by not requiring editing
+an array in gsettings.
+---
+ data/org.gnome.mutter.gschema.xml.in |  4 ---
+ src/backends/meta-backend.c          | 51 ++--------------------------
+ src/backends/meta-settings-private.h |  4 +--
+ src/backends/meta-settings.c         |  4 ---
+ 4 files changed, 3 insertions(+), 60 deletions(-)
+
+diff --git a/data/org.gnome.mutter.gschema.xml.in b/data/org.gnome.mutter.gschema.xml.in
+index d926312c89..6cbd9c1b50 100644
+--- a/data/org.gnome.mutter.gschema.xml.in
++++ b/data/org.gnome.mutter.gschema.xml.in
+@@ -120,10 +120,6 @@
+                                         framebuffers instead of window content,
+                                         to manage HiDPI monitors. Does not
+                                         require a restart.
+-        • “remote-desktop”            — enables remote desktop support. To support
+-                                        remote desktop with screen sharing,
+-                                        “screen-cast” must also be enabled.
+-        • “screen-cast”               — enables screen cast support.
+       </description>
+     </key>
+ 
+diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c
+index 4ebceb4ea6..b523a91d4d 100644
+--- a/src/backends/meta-backend.c
++++ b/src/backends/meta-backend.c
+@@ -402,28 +402,6 @@ meta_backend_create_input_settings (MetaBackend *backend)
+   return META_BACKEND_GET_CLASS (backend)->create_input_settings (backend);
+ }
+ 
+-#ifdef HAVE_REMOTE_DESKTOP
+-static gboolean
+-is_screen_cast_enabled (MetaBackend *backend)
+-{
+-  MetaSettings *settings = meta_backend_get_settings (backend);
+-
+-  return meta_settings_is_experimental_feature_enabled (
+-    settings,
+-    META_EXPERIMENTAL_FEATURE_SCREEN_CAST);
+-}
+-
+-static gboolean
+-is_remote_desktop_enabled (MetaBackend *backend)
+-{
+-  MetaSettings *settings = meta_backend_get_settings (backend);
+-
+-  return meta_settings_is_experimental_feature_enabled (
+-    settings,
+-    META_EXPERIMENTAL_FEATURE_REMOTE_DESKTOP);
+-}
+-#endif /* HAVE_REMOTE_DESKTOP */
+-
+ static void
+ meta_backend_real_post_init (MetaBackend *backend)
+ {
+@@ -457,10 +435,8 @@ meta_backend_real_post_init (MetaBackend *backend)
+ 
+ #ifdef HAVE_REMOTE_DESKTOP
+   priv->dbus_session_watcher = g_object_new (META_TYPE_DBUS_SESSION_WATCHER, NULL);
+-  if (is_screen_cast_enabled (backend))
+-    priv->screen_cast = meta_screen_cast_new (priv->dbus_session_watcher);
+-  if (is_remote_desktop_enabled (backend))
+-    priv->remote_desktop = meta_remote_desktop_new (priv->dbus_session_watcher);
++  priv->screen_cast = meta_screen_cast_new (priv->dbus_session_watcher);
++  priv->remote_desktop = meta_remote_desktop_new (priv->dbus_session_watcher);
+ #endif /* HAVE_REMOTE_DESKTOP */
+ 
+   if (!meta_monitor_manager_is_headless (priv->monitor_manager))
+@@ -552,26 +528,6 @@ meta_backend_class_init (MetaBackendClass *klass)
+   stage_views_disabled = g_strcmp0 (mutter_stage_views, "0") == 0;
+ }
+ 
+-static void
+-experimental_features_changed (MetaSettings            *settings,
+-                               MetaExperimentalFeature  old_experimental_features,
+-                               MetaBackend             *backend)
+-{
+-#ifdef HAVE_REMOTE_DESKTOP
+-  MetaBackendPrivate *priv = meta_backend_get_instance_private (backend);
+-
+-  if (is_screen_cast_enabled (backend) && !priv->screen_cast)
+-    priv->screen_cast = meta_screen_cast_new (priv->dbus_session_watcher);
+-  else if (!is_screen_cast_enabled (backend))
+-    g_clear_object (&priv->screen_cast);
+-
+-  if (is_remote_desktop_enabled (backend) && !priv->remote_desktop)
+-    priv->remote_desktop = meta_remote_desktop_new (priv->dbus_session_watcher);
+-  else if (!is_remote_desktop_enabled (backend))
+-    g_clear_object (&priv->remote_desktop);
+-#endif /* HAVE_REMOTE_DESKTOP */
+-}
+-
+ static MetaMonitorManager *
+ meta_backend_create_monitor_manager (MetaBackend *backend,
+                                      GError     **error)
+@@ -599,9 +555,6 @@ meta_backend_initable_init (GInitable     *initable,
+   MetaBackendPrivate *priv = meta_backend_get_instance_private (backend);
+ 
+   priv->settings = meta_settings_new (backend);
+-  g_signal_connect (priv->settings, "experimental-features-changed",
+-                    G_CALLBACK (experimental_features_changed),
+-                    backend);
+ 
+   priv->egl = g_object_new (META_TYPE_EGL, NULL);
+ 
+diff --git a/src/backends/meta-settings-private.h b/src/backends/meta-settings-private.h
+index a8cb6a5499..90bfb439f0 100644
+--- a/src/backends/meta-settings-private.h
++++ b/src/backends/meta-settings-private.h
+@@ -31,9 +31,7 @@ typedef enum _MetaExperimentalFeature
+ {
+   META_EXPERIMENTAL_FEATURE_NONE = 0,
+   META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER = (1 << 0),
+-  META_EXPERIMENTAL_FEATURE_SCREEN_CAST = (1 << 1),
+-  META_EXPERIMENTAL_FEATURE_REMOTE_DESKTOP  = (1 << 2),
+-  META_EXPERIMENTAL_FEATURE_KMS_MODIFIERS = (1 << 3),
++  META_EXPERIMENTAL_FEATURE_KMS_MODIFIERS  = (1 << 1),
+ } MetaExperimentalFeature;
+ 
+ #define META_TYPE_SETTINGS (meta_settings_get_type ())
+diff --git a/src/backends/meta-settings.c b/src/backends/meta-settings.c
+index 6f47c5f58c..a73850ed67 100644
+--- a/src/backends/meta-settings.c
++++ b/src/backends/meta-settings.c
+@@ -263,10 +263,6 @@ experimental_features_handler (GVariant *features_variant,
+       /* So far no experimental features defined. */
+       if (g_str_equal (feature, "scale-monitor-framebuffer"))
+         features |= META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER;
+-      else if (g_str_equal (feature, "screen-cast"))
+-        features |= META_EXPERIMENTAL_FEATURE_SCREEN_CAST;
+-      else if (g_str_equal (feature, "remote-desktop"))
+-        features |= META_EXPERIMENTAL_FEATURE_REMOTE_DESKTOP;
+       else if (g_str_equal (feature, "kms-modifiers"))
+         features |= META_EXPERIMENTAL_FEATURE_KMS_MODIFIERS;
+       else
+-- 
+2.17.1
+
diff --git a/SOURCES/0001-backends-x11-Only-free-cursor-if-it-was-created-succ.patch b/SOURCES/0001-backends-x11-Only-free-cursor-if-it-was-created-succ.patch
new file mode 100644
index 0000000..608cbb6
--- /dev/null
+++ b/SOURCES/0001-backends-x11-Only-free-cursor-if-it-was-created-succ.patch
@@ -0,0 +1,34 @@
+From f76cacd15430bcc22d9f66e0b500e3ec8a242c23 Mon Sep 17 00:00:00 2001
+From: Sebastian Keller <sebastian-keller@gmx.de>
+Date: Thu, 2 Aug 2018 00:58:56 +0200
+Subject: [PATCH] backends/x11: Only free cursor if it was created successfully
+
+XcursorLibraryLoadCursor can return 'None' if the current cursor theme
+is missing the requested icon. If XFreeCursor is then called on this
+cursor, it generates a BadCursor error causing gnome-shell to crash.
+
+Fixes https://gitlab.gnome.org/GNOME/mutter/issues/254
+
+(cherry picked from commit 1bfa20929b36d06cc23667d1122175149615b56d)
+---
+ src/core/screen.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/src/core/screen.c b/src/core/screen.c
+index c14bba0cf..d7623f3fb 100644
+--- a/src/core/screen.c
++++ b/src/core/screen.c
+@@ -1394,7 +1394,9 @@ meta_screen_update_cursor (MetaScreen *screen)
+ 
+   XDefineCursor (display->xdisplay, screen->xroot, xcursor);
+   XFlush (display->xdisplay);
+-  XFreeCursor (display->xdisplay, xcursor);
++
++  if (xcursor)
++    XFreeCursor (display->xdisplay, xcursor);
+ }
+ 
+ void
+-- 
+2.19.0
+
diff --git a/SOURCES/0001-backends-x11-Support-synaptics-configuration.patch b/SOURCES/0001-backends-x11-Support-synaptics-configuration.patch
new file mode 100644
index 0000000..545f784
--- /dev/null
+++ b/SOURCES/0001-backends-x11-Support-synaptics-configuration.patch
@@ -0,0 +1,347 @@
+From 23996c5290918e264d9385dbf68b90247423187d Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho <carlosg@gnome.org>
+Date: Thu, 19 Jan 2017 15:03:41 +0100
+Subject: [PATCH] backends/x11: Support synaptics configuration
+
+The code is taken mostly as-is from g-s-d, so we can drag the
+dead horse a bit longer.
+---
+ src/backends/x11/meta-input-settings-x11.c | 266 +++++++++++++++++++++
+ 1 file changed, 266 insertions(+)
+
+diff --git a/src/backends/x11/meta-input-settings-x11.c b/src/backends/x11/meta-input-settings-x11.c
+index 9687fb36f..1d84dfccf 100644
+--- a/src/backends/x11/meta-input-settings-x11.c
++++ b/src/backends/x11/meta-input-settings-x11.c
+@@ -26,6 +26,7 @@
+ #include "meta-backend-x11.h"
+ #include "meta-input-settings-x11.h"
+ 
++#include <stdlib.h>
+ #include <string.h>
+ #include <gdk/gdkx.h>
+ #include <X11/Xatom.h>
+@@ -159,6 +160,178 @@ change_property (ClutterInputDevice *device,
+   meta_XFree (data_ret);
+ }
+ 
++static gboolean
++is_device_synaptics (ClutterInputDevice *device)
++{
++  guchar *has_setting;
++
++  /* We just need looking for a synaptics-specific property */
++  has_setting = get_property (device, "Synaptics Off", XA_INTEGER, 8, 1);
++  if (!has_setting)
++    return FALSE;
++
++  meta_XFree (has_setting);
++  return TRUE;
++}
++
++static void
++change_synaptics_tap_left_handed (ClutterInputDevice *device,
++                                  gboolean            tap_enabled,
++                                  gboolean            left_handed)
++{
++  MetaDisplay *display = meta_get_display ();
++  MetaBackend *backend = meta_get_backend ();
++  Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
++  XDevice *xdevice;
++  guchar *tap_action, *buttons;
++  guint buttons_capacity = 16, n_buttons;
++
++  xdevice = XOpenDevice(xdisplay, clutter_input_device_get_device_id (device));
++  if (!xdevice)
++    return;
++
++  tap_action = get_property (device, "Synaptics Tap Action",
++                             XA_INTEGER, 8, 7);
++  if (!tap_action)
++    goto out;
++
++  tap_action[4] = tap_enabled ? (left_handed ? 3 : 1) : 0;
++  tap_action[5] = tap_enabled ? (left_handed ? 1 : 3) : 0;
++  tap_action[6] = tap_enabled ? 2 : 0;
++
++  change_property (device, "Synaptics Tap Action",
++                   XA_INTEGER, 8, tap_action, 7);
++  meta_XFree (tap_action);
++
++  if (display)
++    meta_error_trap_push (display);
++  buttons = g_new (guchar, buttons_capacity);
++  n_buttons = XGetDeviceButtonMapping (xdisplay, xdevice,
++                                       buttons, buttons_capacity);
++
++  while (n_buttons > buttons_capacity)
++    {
++      buttons_capacity = n_buttons;
++      buttons = (guchar *) g_realloc (buttons,
++                                      buttons_capacity * sizeof (guchar));
++
++      n_buttons = XGetDeviceButtonMapping (xdisplay, xdevice,
++                                           buttons, buttons_capacity);
++    }
++
++  buttons[0] = left_handed ? 3 : 1;
++  buttons[2] = left_handed ? 1 : 3;
++  XSetDeviceButtonMapping (xdisplay, xdevice, buttons, n_buttons);
++  g_free (buttons);
++
++  if (display && meta_error_trap_pop_with_return (display))
++    {
++      g_warning ("Could not set synaptics touchpad left-handed for %s",
++                 clutter_input_device_get_device_name (device));
++    }
++
++ out:
++  XCloseDevice (xdisplay, xdevice);
++}
++
++static void
++change_synaptics_speed (ClutterInputDevice *device,
++                        gdouble             speed)
++{
++  MetaDisplay *display = meta_get_display ();
++  MetaBackend *backend = meta_get_backend ();
++  Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
++  XDevice *xdevice;
++  XPtrFeedbackControl feedback;
++  XFeedbackState *states, *state;
++  int i, num_feedbacks, motion_threshold, numerator, denominator;
++  gfloat motion_acceleration;
++
++  xdevice = XOpenDevice(xdisplay, clutter_input_device_get_device_id (device));
++  if (!xdevice)
++    return;
++  /* Get the list of feedbacks for the device */
++  states = XGetFeedbackControl (xdisplay, xdevice, &num_feedbacks);
++  if (!states)
++    return;
++
++  /* Calculate acceleration and threshold */
++  motion_acceleration = (speed + 1) * 5; /* speed is [-1..1], map to [0..10] */
++  motion_threshold = CLAMP (10 - floor (motion_acceleration), 1, 10);
++
++  if (motion_acceleration >= 1.0)
++    {
++      /* we want to get the acceleration, with a resolution of 0.5
++       */
++      if ((motion_acceleration - floor (motion_acceleration)) < 0.25)
++        {
++          numerator = floor (motion_acceleration);
++          denominator = 1;
++        }
++      else if ((motion_acceleration - floor (motion_acceleration)) < 0.5)
++        {
++          numerator = ceil (2.0 * motion_acceleration);
++          denominator = 2;
++        }
++      else if ((motion_acceleration - floor (motion_acceleration)) < 0.75)
++        {
++          numerator = floor (2.0 *motion_acceleration);
++          denominator = 2;
++        }
++      else
++        {
++          numerator = ceil (motion_acceleration);
++          denominator = 1;
++        }
++    }
++  else if (motion_acceleration < 1.0 && motion_acceleration > 0)
++    {
++      /* This we do to 1/10ths */
++      numerator = floor (motion_acceleration * 10) + 1;
++      denominator= 10;
++    }
++  else
++    {
++      numerator = -1;
++      denominator = -1;
++    }
++
++  if (display)
++    meta_error_trap_push (display);
++
++  state = (XFeedbackState *) states;
++
++  for (i = 0; i < num_feedbacks; i++)
++    {
++      if (state->class == PtrFeedbackClass)
++        {
++          /* And tell the device */
++          feedback.class      = PtrFeedbackClass;
++          feedback.length     = sizeof (XPtrFeedbackControl);
++          feedback.id         = state->id;
++          feedback.threshold  = motion_threshold;
++          feedback.accelNum   = numerator;
++          feedback.accelDenom = denominator;
++
++          XChangeFeedbackControl (xdisplay, xdevice,
++                                  DvAccelNum | DvAccelDenom | DvThreshold,
++                                  (XFeedbackControl *) &feedback);
++          break;
++        }
++
++      state = (XFeedbackState *) ((char *) state + state->length);
++    }
++
++  if (display && meta_error_trap_pop_with_return (display))
++    {
++      g_warning ("Could not set synaptics touchpad acceleration for %s",
++                 clutter_input_device_get_device_name (device));
++    }
++
++  XFreeFeedbackList (states);
++  XCloseDevice (xdisplay, xdevice);
++}
++
+ static void
+ meta_input_settings_x11_set_send_events (MetaInputSettings        *settings,
+                                          ClutterInputDevice       *device,
+@@ -167,6 +340,13 @@ meta_input_settings_x11_set_send_events (MetaInputSettings        *settings,
+   guchar values[2] = { 0 }; /* disabled, disabled-on-external-mouse */
+   guchar *available;
+ 
++  if (is_device_synaptics (device))
++    {
++      values[0] = mode != G_DESKTOP_DEVICE_SEND_EVENTS_ENABLED;
++      change_property (device, "Synaptics Off", XA_INTEGER, 8, &values, 1);
++      return;
++    }
++
+   available = get_property (device, "libinput Send Events Modes Available",
+                             XA_INTEGER, 8, 2);
+   if (!available)
+@@ -219,6 +399,12 @@ meta_input_settings_x11_set_speed (MetaInputSettings  *settings,
+   Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
+   gfloat value = speed;
+ 
++  if (is_device_synaptics (device))
++    {
++      change_synaptics_speed (device, speed);
++      return;
++    }
++
+   change_property (device, "libinput Accel Speed",
+                    XInternAtom (xdisplay, "FLOAT", False),
+                    32, &value, 1);
+@@ -245,6 +431,19 @@ meta_input_settings_x11_set_left_handed (MetaInputSettings  *settings,
+   else
+     {
+       value = enabled ? 1 : 0;
++
++      if (is_device_synaptics (device))
++        {
++          GSettings *settings;
++
++          settings = g_settings_new ("org.gnome.desktop.peripherals.touchpad");
++          change_synaptics_tap_left_handed (device,
++                                            g_settings_get_boolean (settings, "tap-to-click"),
++                                            enabled);
++          g_object_unref (settings);
++          return;
++        }
++
+       change_property (device, "libinput Left Handed Enabled",
+                        XA_INTEGER, 8, &value, 1);
+     }
+@@ -268,6 +467,20 @@ meta_input_settings_x11_set_tap_enabled (MetaInputSettings  *settings,
+ {
+   guchar value = (enabled) ? 1 : 0;
+ 
++  if (is_device_synaptics (device))
++    {
++      GDesktopTouchpadHandedness handedness;
++      GSettings *settings;
++
++      settings = g_settings_new ("org.gnome.desktop.peripherals.touchpad");
++      handedness = g_settings_get_enum (settings, "left-handed");
++      g_object_unref (settings);
++
++      change_synaptics_tap_left_handed (device, enabled,
++                                        handedness == G_DESKTOP_TOUCHPAD_HANDEDNESS_LEFT);
++      return;
++    }
++
+   change_property (device, "libinput Tapping Enabled",
+                    XA_INTEGER, 8, &value, 1);
+ }
+@@ -290,6 +503,27 @@ meta_input_settings_x11_set_invert_scroll (MetaInputSettings  *settings,
+ {
+   guchar value = (inverted) ? 1 : 0;
+ 
++  if (is_device_synaptics (device))
++    {
++      gint32 *scrolling_distance;
++
++      scrolling_distance = get_property (device, "Synaptics Scrolling Distance",
++                                         XA_INTEGER, 32, 2);
++      if (scrolling_distance)
++        {
++          scrolling_distance[0] = inverted ?
++            -abs (scrolling_distance[0]) : abs (scrolling_distance[0]);
++          scrolling_distance[1] = inverted ?
++            -abs (scrolling_distance[1]) : abs (scrolling_distance[1]);
++
++          change_property (device, "Synaptics Scrolling Distance",
++                           XA_INTEGER, 32, scrolling_distance, 2);
++          meta_XFree (scrolling_distance);
++        }
++
++      return;
++    }
++
+   change_property (device, "libinput Natural Scrolling Enabled",
+                    XA_INTEGER, 8, &value, 1);
+ }
+@@ -303,6 +537,22 @@ meta_input_settings_x11_set_edge_scroll (MetaInputSettings            *settings,
+   guchar *current = NULL;
+   guchar *available = NULL;
+ 
++  if (is_device_synaptics (device))
++    {
++      current = get_property (device, "Synaptics Edge Scrolling",
++                              XA_INTEGER, 8, 3);
++      if (current)
++        {
++          current[0] = !!edge_scroll_enabled;
++          current[1] = !!edge_scroll_enabled;
++          change_property (device, "Synaptics Edge Scrolling",
++                           XA_INTEGER, 8, current, 3);
++          meta_XFree (current);
++        }
++
++      return;
++    }
++
+   available = get_property (device, "libinput Scroll Methods Available",
+                             XA_INTEGER, 8, SCROLL_METHOD_NUM_FIELDS);
+   if (!available || !available[SCROLL_METHOD_FIELD_EDGE])
+@@ -332,6 +582,22 @@ meta_input_settings_x11_set_two_finger_scroll (MetaInputSettings            *set
+   guchar *current = NULL;
+   guchar *available = NULL;
+ 
++  if (is_device_synaptics (device))
++    {
++      current = get_property (device, "Synaptics Two-Finger Scrolling",
++                              XA_INTEGER, 8, 2);
++      if (current)
++        {
++          current[0] = !!two_finger_scroll_enabled;
++          current[1] = !!two_finger_scroll_enabled;
++          change_property (device, "Synaptics Two-Finger Scrolling",
++                           XA_INTEGER, 8, current, 2);
++          meta_XFree (current);
++        }
++
++      return;
++    }
++
+   available = get_property (device, "libinput Scroll Methods Available",
+                             XA_INTEGER, 8, SCROLL_METHOD_NUM_FIELDS);
+   if (!available || !available[SCROLL_METHOD_FIELD_2FG])
+-- 
+2.20.1
+
diff --git a/SOURCES/0001-clutter-Add-clutter_input_method_forward_key-method.patch b/SOURCES/0001-clutter-Add-clutter_input_method_forward_key-method.patch
new file mode 100644
index 0000000..1586758
--- /dev/null
+++ b/SOURCES/0001-clutter-Add-clutter_input_method_forward_key-method.patch
@@ -0,0 +1,94 @@
+From 83808bbb02c0e4db40771358bf16237bd32ba2f2 Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho <carlosg@gnome.org>
+Date: Thu, 27 Sep 2018 21:06:52 +0200
+Subject: [PATCH] clutter: Add clutter_input_method_forward_key() method.
+
+This allows input methods to inject key events with specific keyval/keycode,
+those events will be flagged with CLUTTER_EVENT_FLAG_INPUT_METHOD so they
+won't be processed by the IM again.
+
+https://gitlab.gnome.org/GNOME/gnome-shell/issues/531
+---
+ clutter/clutter/clutter-input-method.c | 44 ++++++++++++++++++++++++++
+ clutter/clutter/clutter-input-method.h |  8 +++++
+ 2 files changed, 52 insertions(+)
+
+diff --git a/clutter/clutter/clutter-input-method.c b/clutter/clutter/clutter-input-method.c
+index 11d726d7b..cb5364b21 100644
+--- a/clutter/clutter/clutter-input-method.c
++++ b/clutter/clutter/clutter-input-method.c
+@@ -25,6 +25,7 @@
+ #include "clutter/clutter-input-method.h"
+ #include "clutter/clutter-input-method-private.h"
+ #include "clutter/clutter-input-focus-private.h"
++#include "clutter/clutter-device-manager-private.h"
+ 
+ typedef struct _ClutterInputMethodPrivate ClutterInputMethodPrivate;
+ 
+@@ -442,3 +443,46 @@ clutter_input_method_filter_key_event (ClutterInputMethod    *im,
+ 
+   return im_class->filter_key_event (im, (const ClutterEvent *) key);
+ }
++
++void
++clutter_input_method_forward_key (ClutterInputMethod *im,
++                                  uint32_t            keyval,
++                                  uint32_t            keycode,
++                                  uint32_t            state,
++                                  uint64_t            time_,
++                                  gboolean            press)
++{
++  ClutterInputMethodPrivate *priv;
++  ClutterDeviceManager *device_manager;
++  ClutterInputDevice *keyboard;
++  ClutterStage *stage;
++  ClutterEvent *event;
++
++  g_return_if_fail (CLUTTER_IS_INPUT_METHOD (im));
++
++  priv = clutter_input_method_get_instance_private (im);
++  if (!priv->focus)
++    return;
++
++  device_manager = clutter_device_manager_get_default ();
++  keyboard = clutter_device_manager_get_core_device (device_manager,
++                                                     CLUTTER_KEYBOARD_DEVICE);
++  stage = _clutter_input_device_get_stage (keyboard);
++  if (stage == NULL)
++    return;
++
++  event = clutter_event_new (press ? CLUTTER_KEY_PRESS : CLUTTER_KEY_RELEASE);
++  event->key.time = time_;
++  event->key.flags = CLUTTER_EVENT_FLAG_INPUT_METHOD;
++  event->key.modifier_state = state;
++  event->key.keyval = keyval;
++  event->key.hardware_keycode = keycode;
++  event->key.unicode_value = clutter_keysym_to_unicode (keyval);
++
++  clutter_event_set_device (event, keyboard);
++  clutter_event_set_source_device (event, keyboard);
++  clutter_event_set_stage (event, stage);
++
++  clutter_event_put (event);
++  clutter_event_free (event);
++}
+diff --git a/clutter/clutter/clutter-input-method.h b/clutter/clutter/clutter-input-method.h
+index d37522d14..3aa549e24 100644
+--- a/clutter/clutter/clutter-input-method.h
++++ b/clutter/clutter/clutter-input-method.h
+@@ -85,4 +85,12 @@ void clutter_input_method_notify_key_event (ClutterInputMethod *im,
+ CLUTTER_AVAILABLE_IN_MUTTER
+ void clutter_input_method_request_toggle_input_panel (ClutterInputMethod *im);
+ 
++CLUTTER_AVAILABLE_IN_MUTTER
++void clutter_input_method_forward_key (ClutterInputMethod *im,
++                                       uint32_t            keyval,
++                                       uint32_t            keycode,
++                                       uint32_t            state,
++                                       uint64_t            time_,
++                                       gboolean            press);
++
+ #endif /* __CLUTTER_INPUT_METHOD_H__ */
+-- 
+2.20.1
+
diff --git a/SOURCES/0001-clutter-Do-not-latch-modifiers-on-modifier-keys.patch b/SOURCES/0001-clutter-Do-not-latch-modifiers-on-modifier-keys.patch
new file mode 100644
index 0000000..062cebb
--- /dev/null
+++ b/SOURCES/0001-clutter-Do-not-latch-modifiers-on-modifier-keys.patch
@@ -0,0 +1,44 @@
+From c604ada385e046e9307571fa078972d2b1815987 Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho <carlosg@gnome.org>
+Date: Wed, 3 Oct 2018 22:43:21 +0200
+Subject: [PATCH] clutter: Do not latch modifiers on modifier keys
+
+If the user maps eg. Alt+F2 to a pad button, the MetaInputSettings will
+send the full Alt press, F2 press, F2 release, Alt release sequence.
+However the keycode corresponding to Alt is found in level 1, so the
+Shift modifier gets unintendedly latched in addition to the Alt key
+press/release pair.
+
+We could probably improve keycode lookup heuristics so level=0 (and
+no modifier latching) is preferred, but we can do without it altogether
+for modifier keys.
+---
+ clutter/clutter/x11/clutter-virtual-input-device-x11.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/clutter/clutter/x11/clutter-virtual-input-device-x11.c b/clutter/clutter/x11/clutter-virtual-input-device-x11.c
+index b86ded0d0..e16ba3fd0 100644
+--- a/clutter/clutter/x11/clutter-virtual-input-device-x11.c
++++ b/clutter/clutter/x11/clutter-virtual-input-device-x11.c
+@@ -147,14 +147,16 @@ clutter_virtual_input_device_x11_notify_keyval (ClutterVirtualInputDevice *virtu
+       return;
+     }
+ 
+-  if (key_state == CLUTTER_KEY_STATE_PRESSED)
++  if (!_clutter_keymap_x11_get_is_modifier (keymap, keycode) &&
++      key_state == CLUTTER_KEY_STATE_PRESSED)
+     clutter_keymap_x11_latch_modifiers (keymap, level, TRUE);
+ 
+   XTestFakeKeyEvent (clutter_x11_get_default_display (),
+                      (KeyCode) keycode,
+                      key_state == CLUTTER_KEY_STATE_PRESSED, 0);
+ 
+-  if (key_state == CLUTTER_KEY_STATE_RELEASED)
++  if (!_clutter_keymap_x11_get_is_modifier (keymap, keycode) &&
++      key_state == CLUTTER_KEY_STATE_RELEASED)
+     clutter_keymap_x11_latch_modifiers (keymap, level, FALSE);
+ }
+ 
+-- 
+2.20.1
+
diff --git a/SOURCES/0001-clutter-Extend-touchpad-device-property-check-for-Sy.patch b/SOURCES/0001-clutter-Extend-touchpad-device-property-check-for-Sy.patch
new file mode 100644
index 0000000..d7dff0d
--- /dev/null
+++ b/SOURCES/0001-clutter-Extend-touchpad-device-property-check-for-Sy.patch
@@ -0,0 +1,61 @@
+From dd49a4985c8e0bbe51d5784fd89363d496eae350 Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho <carlosg@gnome.org>
+Date: Tue, 13 Feb 2018 11:44:40 +0100
+Subject: [PATCH] clutter: Extend touchpad device property check for Synaptics
+
+So we reliably get CLUTTER_TOUCHPAD_DEVICE for those. The other heuristics
+to get the device type may fall short.
+---
+ .../clutter/x11/clutter-device-manager-xi2.c  | 22 ++++++++++++++++---
+ 1 file changed, 19 insertions(+), 3 deletions(-)
+
+diff --git a/clutter/clutter/x11/clutter-device-manager-xi2.c b/clutter/clutter/x11/clutter-device-manager-xi2.c
+index 62f558380..6794a2e0c 100644
+--- a/clutter/clutter/x11/clutter-device-manager-xi2.c
++++ b/clutter/clutter/x11/clutter-device-manager-xi2.c
+@@ -267,8 +267,9 @@ is_touch_device (XIAnyClassInfo         **classes,
+ }
+ 
+ static gboolean
+-is_touchpad_device (ClutterBackendX11 *backend_x11,
+-                    XIDeviceInfo      *info)
++query_exists_device_property (ClutterBackendX11 *backend_x11,
++                              XIDeviceInfo      *info,
++                              const gchar       *property)
+ {
+   gulong nitems, bytes_after;
+   guint32 *data = NULL;
+@@ -276,7 +277,7 @@ is_touchpad_device (ClutterBackendX11 *backend_x11,
+   Atom type;
+   Atom prop;
+ 
+-  prop = XInternAtom (backend_x11->xdpy, "libinput Tapping Enabled", True);
++  prop = XInternAtom (backend_x11->xdpy, property, True);
+   if (prop == None)
+     return FALSE;
+ 
+@@ -297,6 +298,21 @@ is_touchpad_device (ClutterBackendX11 *backend_x11,
+   return TRUE;
+ }
+ 
++static gboolean
++is_touchpad_device (ClutterBackendX11 *backend_x11,
++                    XIDeviceInfo      *info)
++{
++  if (query_exists_device_property (backend_x11, info,
++                                    "libinput Tapping Enabled"))
++    return TRUE;
++
++  if (query_exists_device_property (backend_x11, info,
++                                    "Synaptics Off"))
++    return TRUE;
++
++  return FALSE;
++}
++
+ static gboolean
+ get_device_ids (ClutterBackendX11  *backend_x11,
+                 XIDeviceInfo       *info,
+-- 
+2.20.1
+
diff --git a/SOURCES/0001-clutter-Only-reset-scroll-axes-on-slave-devices.patch b/SOURCES/0001-clutter-Only-reset-scroll-axes-on-slave-devices.patch
new file mode 100644
index 0000000..fbe7a48
--- /dev/null
+++ b/SOURCES/0001-clutter-Only-reset-scroll-axes-on-slave-devices.patch
@@ -0,0 +1,27 @@
+From 351f04e42c31d9c06274250988d05b439d50df2b Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho <carlosg@gnome.org>
+Date: Wed, 13 Jun 2018 13:48:24 +0200
+Subject: [PATCH] clutter: Only reset scroll axes on slave devices
+
+As a plus, unknown source device IDs will just warn instead of crash.
+---
+ clutter/clutter/x11/clutter-device-manager-xi2.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/clutter/clutter/x11/clutter-device-manager-xi2.c b/clutter/clutter/x11/clutter-device-manager-xi2.c
+index 6794a2e0c..0718cd975 100644
+--- a/clutter/clutter/x11/clutter-device-manager-xi2.c
++++ b/clutter/clutter/x11/clutter-device-manager-xi2.c
+@@ -1819,7 +1819,8 @@ clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator,
+             _clutter_input_device_set_stage (device, NULL);
+           }
+ 
+-        _clutter_input_device_reset_scroll_info (source_device);
++        if (clutter_input_device_get_device_mode (source_device) == CLUTTER_INPUT_MODE_SLAVE)
++          _clutter_input_device_reset_scroll_info (source_device);
+ 
+         clutter_event_set_device (event, device);
+         clutter_event_set_source_device (event, source_device);
+-- 
+2.20.1
+
diff --git a/SOURCES/0001-clutter-evdev-Use-clutter_event_set_device.patch b/SOURCES/0001-clutter-evdev-Use-clutter_event_set_device.patch
new file mode 100644
index 0000000..75b2849
--- /dev/null
+++ b/SOURCES/0001-clutter-evdev-Use-clutter_event_set_device.patch
@@ -0,0 +1,123 @@
+From 580f7958efffd9548a1449c87f7f2c5e6642e6fd Mon Sep 17 00:00:00 2001
+From: Olivier Fourdan <ofourdan@redhat.com>
+Date: Mon, 19 Nov 2018 14:55:51 +0100
+Subject: [PATCH 01/12] clutter/evdev: Use `clutter_event_set_device()`
+
+Use the relevant clutter device API `clutter_event_set_device()` instead
+of setting the device directly in the event field.
+---
+ clutter/clutter/evdev/clutter-device-manager-evdev.c | 3 +--
+ clutter/clutter/evdev/clutter-seat-evdev.c           | 4 ----
+ clutter/clutter/evdev/clutter-xkb-utils.c            | 2 +-
+ clutter/tests/performance/test-common.h              | 4 ++--
+ 4 files changed, 4 insertions(+), 9 deletions(-)
+
+diff --git a/clutter/clutter/evdev/clutter-device-manager-evdev.c b/clutter/clutter/evdev/clutter-device-manager-evdev.c
+index f2aeda6..a885b90 100644
+--- a/clutter/clutter/evdev/clutter-device-manager-evdev.c
++++ b/clutter/clutter/evdev/clutter-device-manager-evdev.c
+@@ -324,7 +324,6 @@ new_absolute_motion_event (ClutterInputDevice *input_device,
+   _clutter_evdev_event_set_time_usec (event, time_us);
+   event->motion.time = us2ms (time_us);
+   event->motion.stage = stage;
+-  event->motion.device = seat->core_pointer;
+   _clutter_xkb_translate_state (event, seat->xkb, seat->button_state);
+   event->motion.x = x;
+   event->motion.y = y;
+@@ -332,6 +331,7 @@ new_absolute_motion_event (ClutterInputDevice *input_device,
+                                                     &event->motion.x,
+                                                     &event->motion.y);
+   event->motion.axes = axes;
++  clutter_event_set_device (event, seat->core_pointer);
+   clutter_event_set_source_device (event, input_device);
+ 
+   if (clutter_input_device_get_device_type (input_device) == CLUTTER_TABLET_DEVICE)
+@@ -519,7 +519,6 @@ notify_proximity (ClutterInputDevice *input_device,
+ 
+   event->proximity.time = us2ms (time_us);
+   event->proximity.stage = CLUTTER_STAGE (stage);
+-  event->proximity.device = seat->core_pointer;
+   clutter_event_set_device_tool (event, device_evdev->last_tool);
+   clutter_event_set_device (event, seat->core_pointer);
+   clutter_event_set_source_device (event, input_device);
+diff --git a/clutter/clutter/evdev/clutter-seat-evdev.c b/clutter/clutter/evdev/clutter-seat-evdev.c
+index a453b11..7cba516 100644
+--- a/clutter/clutter/evdev/clutter-seat-evdev.c
++++ b/clutter/clutter/evdev/clutter-seat-evdev.c
+@@ -385,7 +385,6 @@ new_absolute_motion_event (ClutterSeatEvdev   *seat,
+   _clutter_evdev_event_set_time_usec (event, time_us);
+   event->motion.time = us2ms (time_us);
+   event->motion.stage = stage;
+-  event->motion.device = seat->core_pointer;
+   _clutter_xkb_translate_state (event, seat->xkb, seat->button_state);
+   event->motion.x = x;
+   event->motion.y = y;
+@@ -631,7 +630,6 @@ notify_scroll (ClutterInputDevice       *input_device,
+   _clutter_evdev_event_set_time_usec (event, time_us);
+   event->scroll.time = us2ms (time_us);
+   event->scroll.stage = CLUTTER_STAGE (stage);
+-  event->scroll.device = seat->core_pointer;
+   _clutter_xkb_translate_state (event, seat->xkb, seat->button_state);
+ 
+   /* libinput pointer axis events are in pointer motion coordinate space.
+@@ -684,7 +682,6 @@ notify_discrete_scroll (ClutterInputDevice     *input_device,
+   _clutter_evdev_event_set_time_usec (event, time_us);
+   event->scroll.time = us2ms (time_us);
+   event->scroll.stage = CLUTTER_STAGE (stage);
+-  event->scroll.device = seat->core_pointer;
+   _clutter_xkb_translate_state (event, seat->xkb, seat->button_state);
+ 
+   event->scroll.direction = direction;
+@@ -813,7 +810,6 @@ clutter_seat_evdev_notify_touch_event (ClutterSeatEvdev   *seat,
+   _clutter_evdev_event_set_time_usec (event, time_us);
+   event->touch.time = us2ms (time_us);
+   event->touch.stage = CLUTTER_STAGE (stage);
+-  event->touch.device = seat->core_pointer;
+   event->touch.x = x;
+   event->touch.y = y;
+   clutter_input_device_evdev_translate_coordinates (input_device, stage,
+diff --git a/clutter/clutter/evdev/clutter-xkb-utils.c b/clutter/clutter/evdev/clutter-xkb-utils.c
+index 7f47682..0a9a838 100644
+--- a/clutter/clutter/evdev/clutter-xkb-utils.c
++++ b/clutter/clutter/evdev/clutter-xkb-utils.c
+@@ -76,12 +76,12 @@ _clutter_key_event_new_from_evdev (ClutterInputDevice *device,
+   else
+     sym = XKB_KEY_NoSymbol;
+ 
+-  event->key.device = core_device;
+   event->key.stage = stage;
+   event->key.time = _time;
+   _clutter_xkb_translate_state (event, xkb_state, button_state);
+   event->key.hardware_keycode = key;
+   event->key.keyval = sym;
++  clutter_event_set_device (event, core_device);
+   clutter_event_set_source_device (event, device);
+ 
+   n = xkb_keysym_to_utf8 (sym, buffer, sizeof (buffer));
+diff --git a/clutter/tests/performance/test-common.h b/clutter/tests/performance/test-common.h
+index b0bed10..919fa74 100644
+--- a/clutter/tests/performance/test-common.h
++++ b/clutter/tests/performance/test-common.h
+@@ -92,9 +92,9 @@ static gboolean perf_fake_mouse_cb (gpointer stage)
+       event2->crossing.source = stage;
+       event2->crossing.x = 10;
+       event2->crossing.y = 10;
+-      event2->crossing.device = device;
+       event2->crossing.related = NULL;
+ 
++      clutter_event_set_device (event2, device);
+       clutter_input_device_update_from_event (device, event2, TRUE);
+ 
+       clutter_event_put (event2);
+@@ -104,7 +104,7 @@ static gboolean perf_fake_mouse_cb (gpointer stage)
+ 
+   clutter_actor_get_size (stage, &w, &h);
+   event->motion.stage = stage;
+-  event->motion.device = device;
++  clutter_event_set_device (event, device);
+ 
+   /* called about every 60fps, and do 10 picks per stage */
+   for (i = 0; i < 10; i++)
+-- 
+2.19.2
+
diff --git a/SOURCES/0001-clutter-x11-Implement-keycode-lookup-from-keysyms-on.patch b/SOURCES/0001-clutter-x11-Implement-keycode-lookup-from-keysyms-on.patch
new file mode 100644
index 0000000..89cf4ed
--- /dev/null
+++ b/SOURCES/0001-clutter-x11-Implement-keycode-lookup-from-keysyms-on.patch
@@ -0,0 +1,317 @@
+From 5b0bb0715f0b3b4aa8874d23dcfe4172b0742f25 Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho <carlosg@gnome.org>
+Date: Fri, 29 Jun 2018 14:31:23 +0200
+Subject: [PATCH] clutter/x11: Implement keycode lookup from keysyms on virtual
+ key devices
+
+Unfortunately XKeysymToKeycode() falls short in that it coalesces keysyms
+into keycodes pertaining to the first level (i.e. lowercase). Add a
+ClutterKeymapX11 method (much alike its GdkKeymap counterpart) to look up
+all matches for the given keysym.
+
+Two other helper methods have been added so the virtual device can fetch
+the current keyboard group, and latch modifiers for key emission. Combining
+all this, the virtual device is now able to handle keycodes in further
+levels.
+
+Closes: https://gitlab.gnome.org/GNOME/gnome-shell/issues/135
+
+(cherry picked from commit 85284acb000ddc70afcf716b6c198b4b5bf5741e)
+---
+ clutter/clutter/x11/clutter-keymap-x11.c      | 178 +++++++++++++++++-
+ clutter/clutter/x11/clutter-keymap-x11.h      |   8 +
+ .../x11/clutter-virtual-input-device-x11.c    |  22 ++-
+ 3 files changed, 204 insertions(+), 4 deletions(-)
+
+diff --git a/clutter/clutter/x11/clutter-keymap-x11.c b/clutter/clutter/x11/clutter-keymap-x11.c
+index 914e31434..c34e676a4 100644
+--- a/clutter/clutter/x11/clutter-keymap-x11.c
++++ b/clutter/clutter/x11/clutter-keymap-x11.c
+@@ -38,6 +38,14 @@
+ 
+ typedef struct _ClutterKeymapX11Class   ClutterKeymapX11Class;
+ typedef struct _DirectionCacheEntry     DirectionCacheEntry;
++typedef struct _ClutterKeymapKey        ClutterKeymapKey;
++
++struct _ClutterKeymapKey
++{
++  guint keycode;
++  guint group;
++  guint level;
++};
+ 
+ struct _DirectionCacheEntry
+ {
+@@ -59,6 +67,7 @@ struct _ClutterKeymapX11
+ 
+   ClutterModifierType num_lock_mask;
+   ClutterModifierType scroll_lock_mask;
++  ClutterModifierType level3_shift_mask;
+ 
+   PangoDirection current_direction;
+ 
+@@ -69,6 +78,7 @@ struct _ClutterKeymapX11
+   Atom current_group_atom;
+   guint current_cache_serial;
+   DirectionCacheEntry group_direction_cache[4];
++  int current_group;
+ #endif
+ 
+   guint caps_lock_state : 1;
+@@ -198,6 +208,9 @@ get_xkb (ClutterKeymapX11 *keymap_x11)
+   if (keymap_x11->scroll_lock_mask == 0)
+     keymap_x11->scroll_lock_mask = XkbKeysymToModifiers (backend_x11->xdpy,
+                                                          XK_Scroll_Lock);
++  if (keymap_x11->level3_shift_mask == 0)
++    keymap_x11->level3_shift_mask = XkbKeysymToModifiers (backend_x11->xdpy,
++                                                          XK_ISO_Level3_Shift);
+ 
+   return keymap_x11->xkb_desc;
+ }
+@@ -469,6 +482,7 @@ static void
+ clutter_keymap_x11_init (ClutterKeymapX11 *keymap)
+ {
+   keymap->current_direction = PANGO_DIRECTION_NEUTRAL;
++  keymap->current_group = -1;
+ }
+ 
+ static ClutterTranslateReturn
+@@ -498,7 +512,8 @@ clutter_keymap_x11_translate_event (ClutterEventTranslator *translator,
+         {
+         case XkbStateNotify:
+           CLUTTER_NOTE (EVENT, "Updating keyboard state");
+-          update_direction (keymap_x11, XkbStateGroup (&xkb_event->state));
++          keymap_x11->current_group = XkbStateGroup (&xkb_event->state);
++          update_direction (keymap_x11, keymap_x11->current_group);
+           update_locked_mods (keymap_x11, xkb_event->state.locked_mods);
+           retval = CLUTTER_TRANSLATE_REMOVE;
+           break;
+@@ -665,3 +680,164 @@ _clutter_keymap_x11_get_direction (ClutterKeymapX11 *keymap)
+ #endif
+     return PANGO_DIRECTION_NEUTRAL;
+ }
++
++static gboolean
++clutter_keymap_x11_get_entries_for_keyval (ClutterKeymapX11  *keymap_x11,
++                                           guint              keyval,
++                                           ClutterKeymapKey **keys,
++                                           gint              *n_keys)
++{
++#ifdef HAVE_XKB
++  if (CLUTTER_BACKEND_X11 (keymap_x11->backend)->use_xkb)
++    {
++      XkbDescRec *xkb = get_xkb (keymap_x11);
++      GArray *retval;
++      gint keycode;
++
++      keycode = keymap_x11->min_keycode;
++      retval = g_array_new (FALSE, FALSE, sizeof (ClutterKeymapKey));
++
++      while (keycode <= keymap_x11->max_keycode)
++        {
++          gint max_shift_levels = XkbKeyGroupsWidth (xkb, keycode);
++          gint group = 0;
++          gint level = 0;
++          gint total_syms = XkbKeyNumSyms (xkb, keycode);
++          gint i = 0;
++          KeySym *entry;
++
++          /* entry is an array with all syms for group 0, all
++           * syms for group 1, etc. and for each group the
++           * shift level syms are in order
++           */
++          entry = XkbKeySymsPtr (xkb, keycode);
++
++          while (i < total_syms)
++            {
++              g_assert (i == (group * max_shift_levels + level));
++
++              if (entry[i] == keyval)
++                {
++                  ClutterKeymapKey key;
++
++                  key.keycode = keycode;
++                  key.group = group;
++                  key.level = level;
++
++                  g_array_append_val (retval, key);
++
++                  g_assert (XkbKeySymEntry (xkb, keycode, level, group) ==
++                            keyval);
++                }
++
++              ++level;
++
++              if (level == max_shift_levels)
++                {
++                  level = 0;
++                  ++group;
++                }
++
++              ++i;
++            }
++
++          ++keycode;
++        }
++
++      if (retval->len > 0)
++        {
++          *keys = (ClutterKeymapKey*) retval->data;
++          *n_keys = retval->len;
++        }
++      else
++        {
++          *keys = NULL;
++          *n_keys = 0;
++        }
++
++      g_array_free (retval, retval->len > 0 ? FALSE : TRUE);
++
++      return *n_keys > 0;
++    }
++  else
++#endif
++    {
++      return FALSE;
++    }
++}
++
++void
++clutter_keymap_x11_latch_modifiers (ClutterKeymapX11 *keymap_x11,
++                                    uint32_t          level,
++                                    gboolean          enable)
++{
++#ifdef HAVE_XKB
++  ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (keymap_x11->backend);
++  uint32_t modifiers[] = {
++    0,
++    ShiftMask,
++    keymap_x11->level3_shift_mask,
++    keymap_x11->level3_shift_mask | ShiftMask,
++  };
++  uint32_t value = 0;
++
++  if (!backend_x11->use_xkb)
++    return;
++
++  level = CLAMP (level, 0, G_N_ELEMENTS (modifiers) - 1);
++
++  if (enable)
++    value = modifiers[level];
++  else
++    value = 0;
++
++  XkbLatchModifiers (clutter_x11_get_default_display (),
++                     XkbUseCoreKbd, modifiers[level],
++                     value);
++#endif
++}
++
++static uint32_t
++clutter_keymap_x11_get_current_group (ClutterKeymapX11 *keymap_x11)
++{
++  ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (keymap_x11->backend);
++  XkbStateRec state_rec;
++
++  if (keymap_x11->current_group >= 0)
++    return keymap_x11->current_group;
++
++  XkbGetState (backend_x11->xdpy, XkbUseCoreKbd, &state_rec);
++  return XkbStateGroup (&state_rec);
++}
++
++gboolean
++clutter_keymap_x11_keycode_for_keyval (ClutterKeymapX11 *keymap_x11,
++                                       guint             keyval,
++                                       guint            *keycode_out,
++                                       guint            *level_out)
++{
++  ClutterKeymapKey *keys;
++  gint i, n_keys, group;
++  gboolean found = FALSE;
++
++  g_return_val_if_fail (keycode_out != NULL, FALSE);
++  g_return_val_if_fail (level_out != NULL, FALSE);
++
++  group = clutter_keymap_x11_get_current_group (keymap_x11);
++
++  if (!clutter_keymap_x11_get_entries_for_keyval (keymap_x11, keyval, &keys, &n_keys))
++    return FALSE;
++
++  for (i = 0; i < n_keys && !found; i++)
++    {
++      if (keys[i].group == group)
++        {
++          *keycode_out = keys[i].keycode;
++          *level_out = keys[i].level;
++          found = TRUE;
++        }
++    }
++
++  g_free (keys);
++  return found;
++}
+diff --git a/clutter/clutter/x11/clutter-keymap-x11.h b/clutter/clutter/x11/clutter-keymap-x11.h
+index ad673a2a7..4b5b403c8 100644
+--- a/clutter/clutter/x11/clutter-keymap-x11.h
++++ b/clutter/clutter/x11/clutter-keymap-x11.h
+@@ -51,6 +51,14 @@ gboolean _clutter_keymap_x11_get_is_modifier     (ClutterKeymapX11    *keymap,
+ 
+ PangoDirection _clutter_keymap_x11_get_direction (ClutterKeymapX11    *keymap);
+ 
++gboolean clutter_keymap_x11_keycode_for_keyval (ClutterKeymapX11 *keymap_x11,
++                                                guint             keyval,
++                                                guint            *keycode_out,
++                                                guint            *level_out);
++void     clutter_keymap_x11_latch_modifiers (ClutterKeymapX11 *keymap_x11,
++                                             uint32_t          level,
++                                             gboolean          enable);
++
+ 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 416c944b3..b86ded0d0 100644
+--- a/clutter/clutter/x11/clutter-virtual-input-device-x11.c
++++ b/clutter/clutter/x11/clutter-virtual-input-device-x11.c
+@@ -32,6 +32,8 @@
+ 
+ #include "clutter-virtual-input-device.h"
+ #include "x11/clutter-virtual-input-device-x11.h"
++#include "x11/clutter-backend-x11.h"
++#include "x11/clutter-keymap-x11.h"
+ 
+ struct _ClutterVirtualInputDeviceX11
+ {
+@@ -135,11 +137,25 @@ clutter_virtual_input_device_x11_notify_keyval (ClutterVirtualInputDevice *virtu
+ 						uint32_t                   keyval,
+ 						ClutterKeyState            key_state)
+ {
+-  KeyCode keycode;
++  ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (clutter_get_default_backend ());
++  ClutterKeymapX11 *keymap = backend_x11->keymap;
++  uint32_t keycode, level;
++
++  if (!clutter_keymap_x11_keycode_for_keyval (keymap, keyval, &keycode, &level))
++    {
++      g_warning ("No keycode found for keyval %x in current group", keyval);
++      return;
++    }
++
++  if (key_state == CLUTTER_KEY_STATE_PRESSED)
++    clutter_keymap_x11_latch_modifiers (keymap, level, TRUE);
+ 
+-  keycode = XKeysymToKeycode (clutter_x11_get_default_display (), keyval);
+   XTestFakeKeyEvent (clutter_x11_get_default_display (),
+-                     keycode, key_state == CLUTTER_KEY_STATE_PRESSED, 0);
++                     (KeyCode) keycode,
++                     key_state == CLUTTER_KEY_STATE_PRESSED, 0);
++
++  if (key_state == CLUTTER_KEY_STATE_RELEASED)
++    clutter_keymap_x11_latch_modifiers (keymap, level, FALSE);
+ }
+ 
+ static void
+-- 
+2.20.1
+
diff --git a/SOURCES/0001-cogl-add-new-UNSTABLE_TEXTURES-feature.patch b/SOURCES/0001-cogl-add-new-UNSTABLE_TEXTURES-feature.patch
new file mode 100644
index 0000000..9a60c8a
--- /dev/null
+++ b/SOURCES/0001-cogl-add-new-UNSTABLE_TEXTURES-feature.patch
@@ -0,0 +1,440 @@
+From bac385833fddb5dadb3be1eaf7b6071991c4b048 Mon Sep 17 00:00:00 2001
+From: Ray Strode <rstrode@redhat.com>
+Date: Tue, 15 Jan 2019 11:01:38 -0500
+Subject: [PATCH 1/9] cogl: add new UNSTABLE_TEXTURES feature
+
+The proprietary nvidia driver garbles texture memory on suspend.
+
+Before we can address that, we need to be able to detect it.
+
+This commit adds a new UNSTABLE_TEXTURES feature that gets set if
+the proprietary nvidia driver is in use.
+---
+ cogl/cogl/cogl-context.h           |  1 +
+ cogl/cogl/cogl-types.h             |  5 ++++-
+ cogl/cogl/winsys/cogl-winsys-egl.c | 11 +++++++++++
+ cogl/cogl/winsys/cogl-winsys-glx.c | 13 +++++++++++--
+ 4 files changed, 27 insertions(+), 3 deletions(-)
+
+diff --git a/cogl/cogl/cogl-context.h b/cogl/cogl/cogl-context.h
+index add575b49..985ce336d 100644
+--- a/cogl/cogl/cogl-context.h
++++ b/cogl/cogl/cogl-context.h
+@@ -236,60 +236,61 @@ cogl_is_context (void *object);
+  * Since: 1.10
+  */
+ typedef enum _CoglFeatureID
+ {
+   COGL_FEATURE_ID_TEXTURE_NPOT_BASIC = 1,
+   COGL_FEATURE_ID_TEXTURE_NPOT_MIPMAP,
+   COGL_FEATURE_ID_TEXTURE_NPOT_REPEAT,
+   COGL_FEATURE_ID_TEXTURE_NPOT,
+   COGL_FEATURE_ID_TEXTURE_RECTANGLE,
+   COGL_FEATURE_ID_TEXTURE_3D,
+   COGL_FEATURE_ID_GLSL,
+   COGL_FEATURE_ID_ARBFP,
+   COGL_FEATURE_ID_OFFSCREEN,
+   COGL_FEATURE_ID_OFFSCREEN_MULTISAMPLE,
+   COGL_FEATURE_ID_ONSCREEN_MULTIPLE,
+   COGL_FEATURE_ID_UNSIGNED_INT_INDICES,
+   COGL_FEATURE_ID_DEPTH_RANGE,
+   COGL_FEATURE_ID_POINT_SPRITE,
+   COGL_FEATURE_ID_MAP_BUFFER_FOR_READ,
+   COGL_FEATURE_ID_MAP_BUFFER_FOR_WRITE,
+   COGL_FEATURE_ID_MIRRORED_REPEAT,
+   COGL_FEATURE_ID_SWAP_BUFFERS_EVENT,
+   COGL_FEATURE_ID_GLES2_CONTEXT,
+   COGL_FEATURE_ID_DEPTH_TEXTURE,
+   COGL_FEATURE_ID_PRESENTATION_TIME,
+   COGL_FEATURE_ID_FENCE,
+   COGL_FEATURE_ID_PER_VERTEX_POINT_SIZE,
+   COGL_FEATURE_ID_TEXTURE_RG,
+   COGL_FEATURE_ID_BUFFER_AGE,
+   COGL_FEATURE_ID_TEXTURE_EGL_IMAGE_EXTERNAL,
++  COGL_FEATURE_ID_UNSTABLE_TEXTURES,
+ 
+   /*< private >*/
+   _COGL_N_FEATURE_IDS   /*< skip >*/
+ } CoglFeatureID;
+ 
+ 
+ /**
+  * cogl_has_feature:
+  * @context: A #CoglContext pointer
+  * @feature: A #CoglFeatureID
+  *
+  * Checks if a given @feature is currently available
+  *
+  * Cogl does not aim to be a lowest common denominator API, it aims to
+  * expose all the interesting features of GPUs to application which
+  * means applications have some responsibility to explicitly check
+  * that certain features are available before depending on them.
+  *
+  * Returns: %TRUE if the @feature is currently supported or %FALSE if
+  * not.
+  *
+  * Since: 1.10
+  * Stability: unstable
+  */
+ CoglBool
+ cogl_has_feature (CoglContext *context, CoglFeatureID feature);
+ 
+ /**
+  * cogl_has_features:
+  * @context: A #CoglContext pointer
+diff --git a/cogl/cogl/cogl-types.h b/cogl/cogl/cogl-types.h
+index f67895dd9..8338e284d 100644
+--- a/cogl/cogl/cogl-types.h
++++ b/cogl/cogl/cogl-types.h
+@@ -371,91 +371,94 @@ typedef enum { /*< prefix=COGL_PIXEL_FORMAT >*/
+  * @COGL_FEATURE_OFFSCREEN_MULTISAMPLE: Multisample support on FBOs
+  * @COGL_FEATURE_OFFSCREEN_BLIT: Blit support on FBOs
+  * @COGL_FEATURE_FOUR_CLIP_PLANES: At least 4 clip planes available
+  * @COGL_FEATURE_STENCIL_BUFFER: Stencil buffer support
+  * @COGL_FEATURE_VBOS: VBO support
+  * @COGL_FEATURE_PBOS: PBO support
+  * @COGL_FEATURE_UNSIGNED_INT_INDICES: Set if
+  *     %COGL_INDICES_TYPE_UNSIGNED_INT is supported in
+  *     cogl_vertex_buffer_indices_new().
+  * @COGL_FEATURE_DEPTH_RANGE: cogl_material_set_depth_range() support
+  * @COGL_FEATURE_TEXTURE_NPOT_BASIC: The hardware supports non power
+  *     of two textures, but you also need to check the
+  *     %COGL_FEATURE_TEXTURE_NPOT_MIPMAP and %COGL_FEATURE_TEXTURE_NPOT_REPEAT
+  *     features to know if the hardware supports npot texture mipmaps
+  *     or repeat modes other than
+  *     %COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE respectively.
+  * @COGL_FEATURE_TEXTURE_NPOT_MIPMAP: Mipmapping is supported in
+  *     conjuntion with non power of two textures.
+  * @COGL_FEATURE_TEXTURE_NPOT_REPEAT: Repeat modes other than
+  *     %COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE are supported by the
+  *     hardware.
+  * @COGL_FEATURE_POINT_SPRITE: Whether
+  *     cogl_material_set_layer_point_sprite_coords_enabled() is supported.
+  * @COGL_FEATURE_TEXTURE_3D: 3D texture support
+  * @COGL_FEATURE_MAP_BUFFER_FOR_READ: Whether cogl_buffer_map() is
+  *     supported with CoglBufferAccess including read support.
+  * @COGL_FEATURE_MAP_BUFFER_FOR_WRITE: Whether cogl_buffer_map() is
+  *     supported with CoglBufferAccess including write support.
+  * @COGL_FEATURE_DEPTH_TEXTURE: Whether #CoglFramebuffer support rendering the
+  *     depth buffer to a texture.
++ * @COGL_FEATURE_UNSTABLE_TEXTURES: Whether textures require redrawing on
++ *     resume or not.
+  *
+  * Flags for the supported features.
+  *
+  * Since: 0.8
+  */
+ typedef enum
+ {
+   COGL_FEATURE_TEXTURE_RECTANGLE      = (1 << 1),
+   COGL_FEATURE_TEXTURE_NPOT           = (1 << 2),
+   COGL_FEATURE_TEXTURE_YUV            = (1 << 3),
+   COGL_FEATURE_TEXTURE_READ_PIXELS    = (1 << 4),
+   COGL_FEATURE_SHADERS_GLSL           = (1 << 5),
+   COGL_FEATURE_OFFSCREEN              = (1 << 6),
+   COGL_FEATURE_OFFSCREEN_MULTISAMPLE  = (1 << 7),
+   COGL_FEATURE_OFFSCREEN_BLIT         = (1 << 8),
+   COGL_FEATURE_FOUR_CLIP_PLANES       = (1 << 9),
+   COGL_FEATURE_STENCIL_BUFFER         = (1 << 10),
+   COGL_FEATURE_VBOS		      = (1 << 11),
+   COGL_FEATURE_PBOS		      = (1 << 12),
+   COGL_FEATURE_UNSIGNED_INT_INDICES   = (1 << 13),
+   COGL_FEATURE_DEPTH_RANGE            = (1 << 14),
+   COGL_FEATURE_TEXTURE_NPOT_BASIC     = (1 << 15),
+   COGL_FEATURE_TEXTURE_NPOT_MIPMAP    = (1 << 16),
+   COGL_FEATURE_TEXTURE_NPOT_REPEAT    = (1 << 17),
+   COGL_FEATURE_POINT_SPRITE           = (1 << 18),
+   COGL_FEATURE_TEXTURE_3D             = (1 << 19),
+   COGL_FEATURE_SHADERS_ARBFP          = (1 << 20),
+   COGL_FEATURE_MAP_BUFFER_FOR_READ    = (1 << 21),
+   COGL_FEATURE_MAP_BUFFER_FOR_WRITE   = (1 << 22),
+   COGL_FEATURE_ONSCREEN_MULTIPLE      = (1 << 23),
+-  COGL_FEATURE_DEPTH_TEXTURE          = (1 << 24)
++  COGL_FEATURE_DEPTH_TEXTURE          = (1 << 24),
++  COGL_FEATURE_UNSTABLE_TEXTURES      = (1 << 25)
+ } CoglFeatureFlags;
+ 
+ /**
+  * CoglBufferTarget:
+  * @COGL_WINDOW_BUFFER: FIXME
+  * @COGL_OFFSCREEN_BUFFER: FIXME
+  *
+  * Target flags for FBOs.
+  *
+  * Since: 0.8
+  */
+ typedef enum
+ {
+   COGL_WINDOW_BUFFER      = (1 << 1),
+   COGL_OFFSCREEN_BUFFER   = (1 << 2)
+ } CoglBufferTarget;
+ 
+ /**
+  * CoglColor:
+  * @red: amount of red
+  * @green: amount of green
+  * @blue: amount of green
+  * @alpha: alpha
+  *
+  * A structure for holding a color definition. The contents of
+  * the CoglColor structure are private and should never by accessed
+  * directly.
+  *
+  * Since: 1.0
+  */
+diff --git a/cogl/cogl/winsys/cogl-winsys-egl.c b/cogl/cogl/winsys/cogl-winsys-egl.c
+index 73648f663..66c2661b3 100644
+--- a/cogl/cogl/winsys/cogl-winsys-egl.c
++++ b/cogl/cogl/winsys/cogl-winsys-egl.c
+@@ -475,72 +475,83 @@ _cogl_winsys_display_setup (CoglDisplay *display,
+       CoglRendererEGL *egl_renderer = display->renderer->winsys;
+ 
+       if (egl_renderer->pf_eglBindWaylandDisplay)
+ 	egl_renderer->pf_eglBindWaylandDisplay (egl_renderer->edpy,
+ 						wayland_display);
+     }
+ #endif
+ 
+   if (egl_renderer->platform_vtable->display_setup &&
+       !egl_renderer->platform_vtable->display_setup (display, error))
+     goto error;
+ 
+   if (!try_create_context (display, error))
+     goto error;
+ 
+   egl_display->found_egl_config = TRUE;
+ 
+   return TRUE;
+ 
+ error:
+   _cogl_winsys_display_destroy (display);
+   return FALSE;
+ }
+ 
+ static CoglBool
+ _cogl_winsys_context_init (CoglContext *context, CoglError **error)
+ {
+   CoglRenderer *renderer = context->display->renderer;
+   CoglDisplayEGL *egl_display = context->display->winsys;
+   CoglRendererEGL *egl_renderer = renderer->winsys;
++  CoglGpuInfo *info;
+ 
+   context->winsys = g_new0 (CoglContextEGL, 1);
+ 
+   _COGL_RETURN_VAL_IF_FAIL (egl_display->egl_context, FALSE);
+ 
+   memset (context->winsys_features, 0, sizeof (context->winsys_features));
+ 
+   check_egl_extensions (renderer);
+ 
+   if (!_cogl_context_update_features (context, error))
+     return FALSE;
+ 
++  info = &context->gpu;
++
++  if (info->vendor == COGL_GPU_INFO_VENDOR_NVIDIA)
++    {
++      context->feature_flags |= COGL_FEATURE_UNSTABLE_TEXTURES;
++      COGL_FLAGS_SET (context->features,
++                      COGL_FEATURE_ID_UNSTABLE_TEXTURES,
++                      TRUE);
++    }
++
+   if (egl_renderer->private_features & COGL_EGL_WINSYS_FEATURE_SWAP_REGION)
+     {
+       COGL_FLAGS_SET (context->winsys_features,
+                       COGL_WINSYS_FEATURE_SWAP_REGION, TRUE);
+       COGL_FLAGS_SET (context->winsys_features,
+                       COGL_WINSYS_FEATURE_SWAP_REGION_THROTTLE, TRUE);
+     }
+ 
+   if ((egl_renderer->private_features & COGL_EGL_WINSYS_FEATURE_FENCE_SYNC) &&
+       _cogl_has_private_feature (context, COGL_PRIVATE_FEATURE_OES_EGL_SYNC))
+     COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_FENCE, TRUE);
+ 
+   if (egl_renderer->private_features & COGL_EGL_WINSYS_FEATURE_BUFFER_AGE)
+     {
+       COGL_FLAGS_SET (context->winsys_features,
+                       COGL_WINSYS_FEATURE_BUFFER_AGE,
+                       TRUE);
+       COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_BUFFER_AGE, TRUE);
+     }
+ 
+   /* NB: We currently only support creating standalone GLES2 contexts
+    * for offscreen rendering and so we need a dummy (non-visible)
+    * surface to be able to bind those contexts */
+   if (egl_display->dummy_surface != EGL_NO_SURFACE &&
+       context->driver == COGL_DRIVER_GLES2)
+     COGL_FLAGS_SET (context->features,
+                     COGL_FEATURE_ID_GLES2_CONTEXT, TRUE);
+ 
+   if (egl_renderer->platform_vtable->context_init &&
+       !egl_renderer->platform_vtable->context_init (context, error))
+diff --git a/cogl/cogl/winsys/cogl-winsys-glx.c b/cogl/cogl/winsys/cogl-winsys-glx.c
+index 74b0895d1..4a033c0c6 100644
+--- a/cogl/cogl/winsys/cogl-winsys-glx.c
++++ b/cogl/cogl/winsys/cogl-winsys-glx.c
+@@ -805,149 +805,158 @@ _cogl_winsys_renderer_connect (CoglRenderer *renderer,
+   /* XXX: Note: For a long time Mesa exported a hybrid GLX, exporting
+    * extensions specified to require GLX 1.3, but still reporting 1.2
+    * via glXQueryVersion. */
+   if (!glx_renderer->glXQueryVersion (xlib_renderer->xdpy,
+                                       &glx_renderer->glx_major,
+                                       &glx_renderer->glx_minor)
+       || !(glx_renderer->glx_major == 1 && glx_renderer->glx_minor >= 2))
+     {
+       _cogl_set_error (error, COGL_WINSYS_ERROR,
+                    COGL_WINSYS_ERROR_INIT,
+                    "XServer appears to lack required GLX 1.2 support");
+       goto error;
+     }
+ 
+   update_base_winsys_features (renderer);
+ 
+   glx_renderer->dri_fd = -1;
+ 
+   return TRUE;
+ 
+ error:
+   _cogl_winsys_renderer_disconnect (renderer);
+   return FALSE;
+ }
+ 
+ static CoglBool
+ update_winsys_features (CoglContext *context, CoglError **error)
+ {
+   CoglGLXDisplay *glx_display = context->display->winsys;
+   CoglGLXRenderer *glx_renderer = context->display->renderer->winsys;
++  CoglGpuInfo *info;
+ 
+   _COGL_RETURN_VAL_IF_FAIL (glx_display->glx_context, FALSE);
+ 
+   if (!_cogl_context_update_features (context, error))
+     return FALSE;
+ 
++  info = &context->gpu;
++
+   memcpy (context->winsys_features,
+           glx_renderer->base_winsys_features,
+           sizeof (context->winsys_features));
+ 
+   context->feature_flags |= glx_renderer->legacy_feature_flags;
+ 
+   context->feature_flags |= COGL_FEATURE_ONSCREEN_MULTIPLE;
+   COGL_FLAGS_SET (context->features,
+                   COGL_FEATURE_ID_ONSCREEN_MULTIPLE, TRUE);
+ 
+   if (glx_renderer->glXCopySubBuffer || context->glBlitFramebuffer)
+     {
+-      CoglGpuInfo *info = &context->gpu;
+       CoglGpuInfoArchitecture arch = info->architecture;
+ 
+       COGL_FLAGS_SET (context->winsys_features, COGL_WINSYS_FEATURE_SWAP_REGION, TRUE);
+ 
+       /*
+        * "The "drisw" binding in Mesa for loading sofware renderers is
+        * broken, and neither glBlitFramebuffer nor glXCopySubBuffer
+        * work correctly."
+        * - ajax
+        * - https://bugzilla.gnome.org/show_bug.cgi?id=674208
+        *
+        * This is broken in software Mesa at least as of 7.10 and got
+        * fixed in Mesa 10.1
+        */
+ 
+       if (info->driver_package == COGL_GPU_INFO_DRIVER_PACKAGE_MESA &&
+           info->driver_package_version < COGL_VERSION_ENCODE (10, 1, 0) &&
+           (arch == COGL_GPU_INFO_ARCHITECTURE_LLVMPIPE ||
+            arch == COGL_GPU_INFO_ARCHITECTURE_SOFTPIPE ||
+            arch == COGL_GPU_INFO_ARCHITECTURE_SWRAST))
+ 	{
+ 	  COGL_FLAGS_SET (context->winsys_features,
+ 			  COGL_WINSYS_FEATURE_SWAP_REGION, FALSE);
+ 	}
+     }
+ 
+   /* Note: glXCopySubBuffer and glBlitFramebuffer won't be throttled
+    * by the SwapInterval so we have to throttle swap_region requests
+    * manually... */
+   if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_REGION) &&
+       (glx_display->have_vblank_counter || glx_display->can_vblank_wait))
+     COGL_FLAGS_SET (context->winsys_features,
+                     COGL_WINSYS_FEATURE_SWAP_REGION_THROTTLE, TRUE);
+ 
+   if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT))
+     {
+       COGL_FLAGS_SET (context->winsys_features,
+ 		      COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT, TRUE);
+       /* TODO: remove this deprecated feature */
+       COGL_FLAGS_SET (context->features,
+                       COGL_FEATURE_ID_SWAP_BUFFERS_EVENT,
+                       TRUE);
+       COGL_FLAGS_SET (context->features,
+                       COGL_FEATURE_ID_PRESENTATION_TIME,
+                       TRUE);
+     }
+   else
+     {
+-      CoglGpuInfo *info = &context->gpu;
+       if (glx_display->have_vblank_counter &&
+ 	  context->display->renderer->xlib_enable_threaded_swap_wait &&
+ 	  info->vendor == COGL_GPU_INFO_VENDOR_NVIDIA)
+         {
+           COGL_FLAGS_SET (context->winsys_features,
+                           COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT, TRUE);
+           COGL_FLAGS_SET (context->winsys_features,
+                           COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT, TRUE);
+           /* TODO: remove this deprecated feature */
+           COGL_FLAGS_SET (context->features,
+                           COGL_FEATURE_ID_SWAP_BUFFERS_EVENT,
+                           TRUE);
+           COGL_FLAGS_SET (context->features,
+                           COGL_FEATURE_ID_PRESENTATION_TIME,
+                           TRUE);
+           COGL_FLAGS_SET (context->private_features,
+                           COGL_PRIVATE_FEATURE_THREADED_SWAP_WAIT,
+                           TRUE);
+         }
+     }
+ 
++  if (info->vendor == COGL_GPU_INFO_VENDOR_NVIDIA)
++    {
++      context->feature_flags |= COGL_FEATURE_UNSTABLE_TEXTURES;
++      COGL_FLAGS_SET (context->features,
++                      COGL_FEATURE_ID_UNSTABLE_TEXTURES,
++                      TRUE);
++    }
++
+   /* We'll manually handle queueing dirty events in response to
+    * Expose events from X */
+   COGL_FLAGS_SET (context->private_features,
+                   COGL_PRIVATE_FEATURE_DIRTY_EVENTS,
+                   TRUE);
+ 
+   if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE))
+     COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_BUFFER_AGE, TRUE);
+ 
+   return TRUE;
+ }
+ 
+ static void
+ glx_attributes_from_framebuffer_config (CoglDisplay *display,
+                                         CoglFramebufferConfig *config,
+                                         int *attributes)
+ {
+   CoglGLXRenderer *glx_renderer = display->renderer->winsys;
+   int i = 0;
+ 
+   attributes[i++] = GLX_DRAWABLE_TYPE;
+   attributes[i++] = GLX_WINDOW_BIT;
+ 
+   attributes[i++] = GLX_RENDER_TYPE;
+   attributes[i++] = GLX_RGBA_BIT;
+ 
+   attributes[i++] = GLX_DOUBLEBUFFER;
+   attributes[i++] = GL_TRUE;
+ 
+   attributes[i++] = GLX_RED_SIZE;
+-- 
+2.18.1
+
diff --git a/SOURCES/0001-events-Don-t-move-sloppy-focus-while-buttons-are-pre.patch b/SOURCES/0001-events-Don-t-move-sloppy-focus-while-buttons-are-pre.patch
new file mode 100644
index 0000000..190d471
--- /dev/null
+++ b/SOURCES/0001-events-Don-t-move-sloppy-focus-while-buttons-are-pre.patch
@@ -0,0 +1,42 @@
+From fc8acff3338364e8ace9558924c762a7a59d8450 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
+Date: Thu, 21 Jul 2016 15:43:12 +0200
+Subject: [PATCH] events: Don't move (sloppy) focus while buttons are pressed
+
+(https://bugzilla.redhat.com/show_bug.cgi?id=1358535)
+---
+ src/x11/events.c | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/src/x11/events.c b/src/x11/events.c
+index f60f650a6..291b7114a 100644
+--- a/src/x11/events.c
++++ b/src/x11/events.c
+@@ -830,6 +830,16 @@ crossing_serial_is_ignored (MetaDisplay  *display,
+   return FALSE;
+ }
+ 
++static gboolean
++event_has_button_mask (XIEnterEvent *enter_event)
++{
++  int i;
++  for (i = 0; i < enter_event->buttons.mask_len; i++)
++    if (enter_event->buttons.mask[i] != '\0')
++      return TRUE;
++  return FALSE;
++}
++
+ static gboolean
+ handle_input_xevent (MetaDisplay  *display,
+                      XIEvent      *input_event,
+@@ -871,6 +881,7 @@ handle_input_xevent (MetaDisplay  *display,
+        * avoid races.
+        */
+       if (window && !crossing_serial_is_ignored (display, serial) &&
++          !event_has_button_mask (enter_event) &&
+           enter_event->mode != XINotifyGrab &&
+           enter_event->mode != XINotifyUngrab &&
+           enter_event->detail != XINotifyInferior &&
+-- 
+2.20.1
+
diff --git a/SOURCES/0001-gpu-kms-Don-t-crash-if-drmModeGetResources-returns-N.patch b/SOURCES/0001-gpu-kms-Don-t-crash-if-drmModeGetResources-returns-N.patch
new file mode 100644
index 0000000..6350f55
--- /dev/null
+++ b/SOURCES/0001-gpu-kms-Don-t-crash-if-drmModeGetResources-returns-N.patch
@@ -0,0 +1,37 @@
+From 62660bbd1526c6135d149df397b2470c6e71969d Mon Sep 17 00:00:00 2001
+From: Aaron Plattner <aplattner@nvidia.com>
+Date: Thu, 19 Jul 2018 10:51:34 -0700
+Subject: [PATCH] gpu-kms: Don't crash if drmModeGetResources returns NULL
+
+DRM drivers can be opened by meta_launcher_open_restricted() even if they don't
+implement modesetting. However, drmModeGetResources() will return NULL.
+
+Check whether that happened in meta_gpu_kms_new() and return with an error
+instead of crashing.
+
+Fixes #223.
+---
+ src/backends/native/meta-gpu-kms.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/src/backends/native/meta-gpu-kms.c b/src/backends/native/meta-gpu-kms.c
+index 613a60705..259cd7122 100644
+--- a/src/backends/native/meta-gpu-kms.c
++++ b/src/backends/native/meta-gpu-kms.c
+@@ -801,6 +801,13 @@ meta_gpu_kms_new (MetaMonitorManagerKms  *monitor_manager_kms,
+    */
+   drm_resources = drmModeGetResources (kms_fd);
+ 
++  if (!drm_resources)
++    {
++      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "No resources");
++      meta_launcher_close_restricted (launcher, kms_fd);
++      return NULL;
++    }
++
+   n_connectors = drm_resources->count_connectors;
+ 
+   drmModeFreeResources (drm_resources);
+-- 
+2.19.0
+
diff --git a/SOURCES/0001-gpu-kms-Handle-drmModeGetResources-failing.patch b/SOURCES/0001-gpu-kms-Handle-drmModeGetResources-failing.patch
new file mode 100644
index 0000000..91b6ff5
--- /dev/null
+++ b/SOURCES/0001-gpu-kms-Handle-drmModeGetResources-failing.patch
@@ -0,0 +1,99 @@
+From 255146e4a3d16c74390f8fdd6cae46b5d0d59ad5 Mon Sep 17 00:00:00 2001
+From: Iain Lane <iainl@gnome.org>
+Date: Fri, 10 Aug 2018 09:46:51 +0000
+Subject: [PATCH 1/2] gpu-kms: Handle drmModeGetResources() failing
+
+Avoid dereferencing the NULL return value if it fails. We still create
+the MetaGpu, but we treat it as if it has no outputs.
+
+Closes: https://gitlab.gnome.org/GNOME/mutter/issues/223
+
+
+(cherry picked from commit 29cc526e2eab65d856d1fd0a652f22dcdb5d72dd)
+---
+ src/backends/native/meta-gpu-kms.c | 36 +++++++++++++++++++++++++-----
+ 1 file changed, 31 insertions(+), 5 deletions(-)
+
+diff --git a/src/backends/native/meta-gpu-kms.c b/src/backends/native/meta-gpu-kms.c
+index 1d1c28809..7e7607c46 100644
+--- a/src/backends/native/meta-gpu-kms.c
++++ b/src/backends/native/meta-gpu-kms.c
+@@ -62,6 +62,8 @@ struct _MetaGpuKms
+   int max_buffer_height;
+ 
+   gboolean page_flips_not_supported;
++
++  gboolean resources_init_failed_before;
+ };
+ 
+ G_DEFINE_QUARK (MetaGpuKmsError, meta_gpu_kms_error)
+@@ -709,20 +711,34 @@ init_outputs (MetaGpuKms       *gpu_kms,
+   setup_output_clones (gpu);
+ }
+ 
+-static void
+-meta_kms_resources_init (MetaKmsResources *resources,
+-                         int               fd)
++static gboolean
++meta_kms_resources_init (MetaKmsResources  *resources,
++                         int                fd,
++                         GError           **error)
++
+ {
+   drmModeRes *drm_resources;
+   unsigned int i;
+ 
+   drm_resources = drmModeGetResources (fd);
++
++  if (!drm_resources)
++    {
++      g_set_error (error,
++                   G_IO_ERROR,
++                   G_IO_ERROR_FAILED,
++                   "Calling drmModeGetResources() failed");
++      return FALSE;
++    }
++
+   resources->resources = drm_resources;
+ 
+   resources->n_encoders = (unsigned int) drm_resources->count_encoders;
+   resources->encoders = g_new (drmModeEncoder *, resources->n_encoders);
+   for (i = 0; i < resources->n_encoders; i++)
+     resources->encoders[i] = drmModeGetEncoder (fd, drm_resources->encoders[i]);
++
++  return TRUE;
+ }
+ 
+ static void
+@@ -734,7 +750,7 @@ meta_kms_resources_release (MetaKmsResources *resources)
+     drmModeFreeEncoder (resources->encoders[i]);
+   g_free (resources->encoders);
+ 
+-  drmModeFreeResources (resources->resources);
++  g_clear_pointer (&resources->resources, drmModeFreeResources);
+ }
+ 
+ static gboolean
+@@ -745,8 +761,18 @@ meta_gpu_kms_read_current (MetaGpu  *gpu,
+   MetaMonitorManager *monitor_manager =
+     meta_gpu_get_monitor_manager (gpu);
+   MetaKmsResources resources;
++  g_autoptr (GError) local_error = NULL;
+ 
+-  meta_kms_resources_init (&resources, gpu_kms->fd);
++  if (!meta_kms_resources_init (&resources, gpu_kms->fd, &local_error))
++    {
++      if (!gpu_kms->resources_init_failed_before)
++        {
++          g_warning ("meta_kms_resources_init failed: %s, assuming we have no outputs",
++                     local_error->message);
++          gpu_kms->resources_init_failed_before = TRUE;
++        }
++      return TRUE;
++    }
+ 
+   gpu_kms->max_buffer_width = resources.resources->max_width;
+   gpu_kms->max_buffer_height = resources.resources->max_height;
+-- 
+2.19.0
+
diff --git a/SOURCES/0001-idle-monitor-Don-t-try-to-auto-start-SessionManager.patch b/SOURCES/0001-idle-monitor-Don-t-try-to-auto-start-SessionManager.patch
new file mode 100644
index 0000000..f913a17
--- /dev/null
+++ b/SOURCES/0001-idle-monitor-Don-t-try-to-auto-start-SessionManager.patch
@@ -0,0 +1,32 @@
+From 9d88fc8cb7565ce21563c1195c2950f8f050a3fc Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
+Date: Sun, 29 Apr 2018 20:43:47 +0000
+Subject: [PATCH] idle-monitor: Don't try to auto-start SessionManager
+
+The interface is provided by gnome-session and not activatable.
+
+https://gitlab.gnome.org/GNOME/mutter/issues/134
+
+
+(cherry picked from commit 2319cd9c4014fcc0c2487d7c02e67857f940687e)
+---
+ src/backends/meta-idle-monitor.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/backends/meta-idle-monitor.c b/src/backends/meta-idle-monitor.c
+index 7a217f761..76a7ee786 100644
+--- a/src/backends/meta-idle-monitor.c
++++ b/src/backends/meta-idle-monitor.c
+@@ -246,7 +246,8 @@ meta_idle_monitor_init (MetaIdleMonitor *monitor)
+   /* Monitor inhibitors */
+   monitor->session_proxy =
+     g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+-                                   G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
++                                   G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
++                                   G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+                                    NULL,
+                                    "org.gnome.SessionManager",
+                                    "/org/gnome/SessionManager",
+-- 
+2.19.0
+
diff --git a/SOURCES/0001-main-be-more-aggressive-in-assuming-X11-backend.patch b/SOURCES/0001-main-be-more-aggressive-in-assuming-X11-backend.patch
new file mode 100644
index 0000000..174cf87
--- /dev/null
+++ b/SOURCES/0001-main-be-more-aggressive-in-assuming-X11-backend.patch
@@ -0,0 +1,49 @@
+From f829fc770c35f4bc5451a98602fe8e6478c7be10 Mon Sep 17 00:00:00 2001
+From: Ray Strode <rstrode@redhat.com>
+Date: Tue, 13 Feb 2018 09:44:50 -0500
+Subject: [PATCH] main: be more aggressive in assuming X11 backend
+
+If the session is started by vncserver right now, the
+XDG_SESSION_TYPE won't be X11.  Ideally that would be
+fixed, but for backward compatibility we should default
+to X11 if the session type isn't set to wayland explicitly.
+---
+ src/core/main.c | 8 +++-----
+ 1 file changed, 3 insertions(+), 5 deletions(-)
+
+diff --git a/src/core/main.c b/src/core/main.c
+index 828037635..faa20818b 100644
+--- a/src/core/main.c
++++ b/src/core/main.c
+@@ -332,7 +332,6 @@ find_session_type (void)
+   char *session_id;
+   char *session_type;
+   const char *session_type_env;
+-  gboolean is_tty = FALSE;
+   int ret, i;
+ 
+   ret = sd_pid_get_session (0, &session_id);
+@@ -345,8 +344,7 @@ find_session_type (void)
+         {
+           if (session_type_is_supported (session_type))
+             goto out;
+-          else
+-            is_tty = g_strcmp0 (session_type, "tty") == 0;
++
+           free (session_type);
+         }
+     }
+@@ -378,8 +376,8 @@ find_session_type (void)
+       goto out;
+     }
+ 
+-  /* Legacy support for starting through xinit */
+-  if (is_tty && (g_getenv ("MUTTER_DISPLAY") || g_getenv ("DISPLAY")))
++  /* Legacy support for starting through xinit or vncserver */
++  if (g_getenv ("MUTTER_DISPLAY") || g_getenv ("DISPLAY"))
+     {
+       session_type = strdup ("x11");
+       goto out;
+-- 
+2.20.1
+
diff --git a/SOURCES/0001-monitor-Use-current-monitor-mode-to-check-whether-ac.patch b/SOURCES/0001-monitor-Use-current-monitor-mode-to-check-whether-ac.patch
new file mode 100644
index 0000000..2aa2ddc
--- /dev/null
+++ b/SOURCES/0001-monitor-Use-current-monitor-mode-to-check-whether-ac.patch
@@ -0,0 +1,85 @@
+From d0d8078013749bb84dc611dfdf6b317512c8559e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Tue, 31 Jul 2018 11:18:51 +0000
+Subject: [PATCH] monitor: Use current monitor mode to check whether active
+
+For historical reasons meta_monitor_is_active() checked whether it is
+active by checking whether the main output have a CRTC assigned and
+whether that CRTC has a current mode. At a later point, the MetaMonitor
+got its own mode abstraction (MetaMonitorMode), but
+meta_monitor_is_active() was never updated to use this.
+
+An issue with checking the main output's CRTC state is that, if there is
+some CRTC mode combination that for some reason isn't properly detected
+by the MetaMonitorMode abstraction (e.g. some tiling configuration not
+yet handled), meta_monitor_is_active() would return TRUE, even though no
+(abstracted) mode was set. This would cause confusion here and there,
+leading to NULL pointer dereferences due to the assumption that if a
+monitor is active, it has an active mode.
+
+Instead, change meta_monitor_is_active() to directly check the current
+monitor mode, and log a warning if the main output still happen to have
+a CRTC with a mode assigned to it. This way, when an not undrestood CRTC
+mode combination is encountered, instead of dereferencing NULL pointers,
+simply assume the monitor is not active, which means that it will not be
+managed or rendered by mutter at all.
+
+https://gitlab.gnome.org/GNOME/mutter/issues/130
+
+
+(cherry picked from commit 4d465eac0806eb1ead375e2852d4a9d6bc24524f)
+---
+ src/backends/meta-monitor.c | 22 ++++++++++++++++------
+ 1 file changed, 16 insertions(+), 6 deletions(-)
+
+diff --git a/src/backends/meta-monitor.c b/src/backends/meta-monitor.c
+index 92c61c037..60f36741a 100644
+--- a/src/backends/meta-monitor.c
++++ b/src/backends/meta-monitor.c
+@@ -203,13 +203,9 @@ meta_monitor_get_main_output (MetaMonitor *monitor)
+ gboolean
+ meta_monitor_is_active (MetaMonitor *monitor)
+ {
+-  MetaOutput *output;
+-  MetaCrtc *crtc;
+-
+-  output = meta_monitor_get_main_output (monitor);
+-  crtc = meta_output_get_assigned_crtc (output);
++  MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor);
+ 
+-  return crtc && crtc->current_mode;
++  return !!priv->current_mode;
+ }
+ 
+ gboolean
+@@ -1411,6 +1407,18 @@ meta_monitor_get_current_mode (MetaMonitor *monitor)
+   return priv->current_mode;
+ }
+ 
++static gboolean
++is_current_mode_known (MetaMonitor *monitor)
++{
++  MetaOutput *output;
++  MetaCrtc *crtc;
++
++  output = meta_monitor_get_main_output (monitor);
++  crtc = meta_output_get_assigned_crtc (output);
++
++  return meta_monitor_is_active (monitor) == (crtc && crtc->current_mode);
++}
++
+ void
+ meta_monitor_derive_current_mode (MetaMonitor *monitor)
+ {
+@@ -1430,6 +1438,8 @@ meta_monitor_derive_current_mode (MetaMonitor *monitor)
+     }
+ 
+   priv->current_mode = current_mode;
++
++  g_warn_if_fail (is_current_mode_known (monitor));
+ }
+ 
+ void
+-- 
+2.19.0
+
diff --git a/SOURCES/0001-monitor-manager-Consider-external-layout-before-defa.patch b/SOURCES/0001-monitor-manager-Consider-external-layout-before-defa.patch
new file mode 100644
index 0000000..183443a
--- /dev/null
+++ b/SOURCES/0001-monitor-manager-Consider-external-layout-before-defa.patch
@@ -0,0 +1,151 @@
+From 68b47a018d26186f06ac08b8e271f325d247275a Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
+Date: Thu, 28 Jan 2016 15:26:33 +0100
+Subject: [PATCH] monitor-manager: Consider external layout before default
+ linear config
+
+In case of no existing configuration, we use a default layout of
+aligning attached displays horizontally. This sidesteps any layout
+configuration that is done externally, for instance via xorg.conf,
+which is not desirable. Instead, base the initial configuration on
+the existing layout if it passes some sanity checks before falling
+back to the default linear config.
+---
+ src/backends/meta-monitor-config-manager.c | 77 ++++++++++++++++++++++
+ src/backends/meta-monitor-config-manager.h |  1 +
+ src/backends/meta-monitor-manager.c        | 19 ++++++
+ 3 files changed, 97 insertions(+)
+
+diff --git a/src/backends/meta-monitor-config-manager.c b/src/backends/meta-monitor-config-manager.c
+index 197892bf2..585ee7034 100644
+--- a/src/backends/meta-monitor-config-manager.c
++++ b/src/backends/meta-monitor-config-manager.c
+@@ -559,6 +559,83 @@ create_preferred_logical_monitor_config (MetaMonitorManager          *monitor_ma
+   return logical_monitor_config;
+ }
+ 
++static MetaLogicalMonitorConfig *
++create_logical_monitor_config_from_output (MetaMonitorManager           *monitor_manager,
++                                           MetaMonitor                  *monitor,
++                                           MetaLogicalMonitorConfig     *primary_logical_monitor_config,
++                                           MetaLogicalMonitorLayoutMode  layout_mode)
++{
++    MetaOutput *output;
++    MetaCrtc *crtc;
++
++    output = meta_monitor_get_main_output (monitor);
++    crtc = meta_output_get_assigned_crtc (output);
++    return create_preferred_logical_monitor_config (monitor_manager,
++                                                    monitor,
++                                                    crtc->rect.x,
++                                                    crtc->rect.y,
++                                                    primary_logical_monitor_config,
++                                                    layout_mode);
++}
++
++MetaMonitorsConfig *
++meta_monitor_config_manager_create_current (MetaMonitorConfigManager *config_manager)
++{
++  MetaMonitorManager *monitor_manager = config_manager->monitor_manager;
++  GList *logical_monitor_configs;
++  MetaMonitor *primary_monitor;
++  MetaLogicalMonitorLayoutMode layout_mode;
++  MetaLogicalMonitorConfig *primary_logical_monitor_config;
++  GList *monitors;
++  GList *l;
++
++  if (meta_monitor_config_store_get_config_count (config_manager->config_store) > 0)
++    return NULL;
++
++  primary_monitor = find_primary_monitor (monitor_manager);
++  if (!primary_monitor || !meta_monitor_is_active (primary_monitor))
++    return NULL;
++
++  layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager);
++
++  primary_logical_monitor_config =
++    create_logical_monitor_config_from_output (monitor_manager,
++                                               primary_monitor,
++                                               NULL,
++                                               layout_mode);
++
++  primary_logical_monitor_config->is_primary = TRUE;
++  logical_monitor_configs = g_list_append (NULL,
++                                           primary_logical_monitor_config);
++
++  monitors = meta_monitor_manager_get_monitors (monitor_manager);
++  for (l = monitors; l; l = l->next)
++    {
++      MetaMonitor *monitor = l->data;
++      MetaLogicalMonitorConfig *logical_monitor_config;
++
++      if (monitor == primary_monitor)
++        continue;
++
++      if (!meta_monitor_is_active (monitor))
++        continue;
++
++      logical_monitor_config =
++        create_logical_monitor_config_from_output (monitor_manager,
++                                                   monitor,
++                                                   primary_logical_monitor_config,
++                                                   layout_mode);
++
++      logical_monitor_configs = g_list_append (logical_monitor_configs,
++                                               logical_monitor_config);
++    }
++
++  return meta_monitors_config_new (monitor_manager,
++                                   logical_monitor_configs,
++                                   layout_mode,
++                                   META_MONITORS_CONFIG_FLAG_NONE);
++}
++
+ MetaMonitorsConfig *
+ meta_monitor_config_manager_create_linear (MetaMonitorConfigManager *config_manager)
+ {
+diff --git a/src/backends/meta-monitor-config-manager.h b/src/backends/meta-monitor-config-manager.h
+index 269d8e1b7..c36df38e6 100644
+--- a/src/backends/meta-monitor-config-manager.h
++++ b/src/backends/meta-monitor-config-manager.h
+@@ -87,6 +87,7 @@ gboolean meta_monitor_config_manager_assign (MetaMonitorManager *manager,
+ 
+ MetaMonitorsConfig * meta_monitor_config_manager_get_stored (MetaMonitorConfigManager *config_manager);
+ 
++MetaMonitorsConfig * meta_monitor_config_manager_create_current (MetaMonitorConfigManager *config_manager);
+ MetaMonitorsConfig * meta_monitor_config_manager_create_linear (MetaMonitorConfigManager *config_manager);
+ 
+ MetaMonitorsConfig * meta_monitor_config_manager_create_fallback (MetaMonitorConfigManager *config_manager);
+diff --git a/src/backends/meta-monitor-manager.c b/src/backends/meta-monitor-manager.c
+index 5bd34ad47..4503eb841 100644
+--- a/src/backends/meta-monitor-manager.c
++++ b/src/backends/meta-monitor-manager.c
+@@ -576,6 +576,25 @@ meta_monitor_manager_ensure_configured (MetaMonitorManager *manager)
+       g_clear_object (&config);
+     }
+ 
++  config = meta_monitor_config_manager_create_current (manager->config_manager);
++  if (config)
++    {
++      if (!meta_monitor_manager_apply_monitors_config (manager,
++                                                       config,
++                                                       method,
++                                                       &error))
++        {
++          g_clear_object (&config);
++          g_warning ("Failed to use current monitor configuration: %s",
++                     error->message);
++          g_clear_error (&error);
++        }
++      else
++        {
++          goto done;
++        }
++    }
++
+   config = meta_monitor_config_manager_create_linear (manager->config_manager);
+   if (config)
+     {
+-- 
+2.20.1
+
diff --git a/SOURCES/0001-monitor-manager-only-reuse-initial-config-if-monitor.patch b/SOURCES/0001-monitor-manager-only-reuse-initial-config-if-monitor.patch
new file mode 100644
index 0000000..b4e141d
--- /dev/null
+++ b/SOURCES/0001-monitor-manager-only-reuse-initial-config-if-monitor.patch
@@ -0,0 +1,144 @@
+From 7f73a6ed3ac3244d8d1ec3ab7a74b5a45a1350d9 Mon Sep 17 00:00:00 2001
+From: rpm-build <rpm-build>
+Date: Tue, 11 Sep 2018 10:19:44 -0400
+Subject: [PATCH] monitor-manager: only reuse initial-config if monitor
+ topology matches startup
+
+Right now we try to apply the current monitor config when a new
+monitor is attached.  The current config obviously doesn't include the
+new monitor, so the new monitor isn't lit up.
+
+The only reason we apply the current config at all is to handle the
+startup case:  We want to reuse the config set in Xorg when first
+logging in.
+
+This commit changes the code to look at the *initial config* instead
+of the current config, and only if the new monitor topology matches
+the start up topology.
+---
+ src/backends/meta-monitor-config-manager.c | 20 +++++++++++++++-----
+ src/backends/meta-monitor-config-manager.h |  2 +-
+ src/backends/meta-monitor-manager.c        | 16 +++++++++++++++-
+ 3 files changed, 31 insertions(+), 7 deletions(-)
+
+diff --git a/src/backends/meta-monitor-config-manager.c b/src/backends/meta-monitor-config-manager.c
+index 585ee7034..1ad342a44 100644
+--- a/src/backends/meta-monitor-config-manager.c
++++ b/src/backends/meta-monitor-config-manager.c
+@@ -40,6 +40,7 @@ struct _MetaMonitorConfigManager
+   MetaMonitorConfigStore *config_store;
+ 
+   MetaMonitorsConfig *current_config;
++  MetaMonitorsConfig *initial_config;
+   GQueue config_history;
+ };
+ 
+@@ -579,9 +580,10 @@ create_logical_monitor_config_from_output (MetaMonitorManager           *monitor
+ }
+ 
+ MetaMonitorsConfig *
+-meta_monitor_config_manager_create_current (MetaMonitorConfigManager *config_manager)
++meta_monitor_config_manager_create_initial (MetaMonitorConfigManager *config_manager)
+ {
+   MetaMonitorManager *monitor_manager = config_manager->monitor_manager;
++  MetaMonitorsConfig *initial_config;
+   GList *logical_monitor_configs;
+   MetaMonitor *primary_monitor;
+   MetaLogicalMonitorLayoutMode layout_mode;
+@@ -589,6 +591,9 @@ meta_monitor_config_manager_create_current (MetaMonitorConfigManager *config_man
+   GList *monitors;
+   GList *l;
+ 
++  if (config_manager->initial_config != NULL)
++    return g_object_ref (config_manager->initial_config);
++
+   if (meta_monitor_config_store_get_config_count (config_manager->config_store) > 0)
+     return NULL;
+ 
+@@ -630,10 +635,14 @@ meta_monitor_config_manager_create_current (MetaMonitorConfigManager *config_man
+                                                logical_monitor_config);
+     }
+ 
+-  return meta_monitors_config_new (monitor_manager,
+-                                   logical_monitor_configs,
+-                                   layout_mode,
+-                                   META_MONITORS_CONFIG_FLAG_NONE);
++  initial_config = meta_monitors_config_new (monitor_manager,
++                                             logical_monitor_configs,
++                                             layout_mode,
++                                             META_MONITORS_CONFIG_FLAG_NONE);
++
++  config_manager->initial_config = g_object_ref (initial_config);
++
++  return initial_config;
+ }
+ 
+ MetaMonitorsConfig *
+@@ -1162,6 +1171,7 @@ meta_monitor_config_manager_dispose (GObject *object)
+     META_MONITOR_CONFIG_MANAGER (object);
+ 
+   g_clear_object (&config_manager->current_config);
++  g_clear_object (&config_manager->initial_config);
+   meta_monitor_config_manager_clear_history (config_manager);
+ 
+   G_OBJECT_CLASS (meta_monitor_config_manager_parent_class)->dispose (object);
+diff --git a/src/backends/meta-monitor-config-manager.h b/src/backends/meta-monitor-config-manager.h
+index c36df38e6..29ef8f8ce 100644
+--- a/src/backends/meta-monitor-config-manager.h
++++ b/src/backends/meta-monitor-config-manager.h
+@@ -87,7 +87,7 @@ gboolean meta_monitor_config_manager_assign (MetaMonitorManager *manager,
+ 
+ MetaMonitorsConfig * meta_monitor_config_manager_get_stored (MetaMonitorConfigManager *config_manager);
+ 
+-MetaMonitorsConfig * meta_monitor_config_manager_create_current (MetaMonitorConfigManager *config_manager);
++MetaMonitorsConfig * meta_monitor_config_manager_create_initial (MetaMonitorConfigManager *config_manager);
+ MetaMonitorsConfig * meta_monitor_config_manager_create_linear (MetaMonitorConfigManager *config_manager);
+ 
+ MetaMonitorsConfig * meta_monitor_config_manager_create_fallback (MetaMonitorConfigManager *config_manager);
+diff --git a/src/backends/meta-monitor-manager.c b/src/backends/meta-monitor-manager.c
+index 4503eb841..f7ada0136 100644
+--- a/src/backends/meta-monitor-manager.c
++++ b/src/backends/meta-monitor-manager.c
+@@ -496,9 +496,11 @@ should_use_stored_config (MetaMonitorManager *manager)
+ MetaMonitorsConfig *
+ meta_monitor_manager_ensure_configured (MetaMonitorManager *manager)
+ {
++  g_autoptr (MetaMonitorsConfig) initial_config = NULL;
+   MetaMonitorsConfig *config = NULL;
+   GError *error = NULL;
+   gboolean use_stored_config;
++  MetaMonitorsConfigKey *current_state_key;
+   MetaMonitorsConfigMethod method;
+   MetaMonitorsConfigMethod fallback_method =
+     META_MONITORS_CONFIG_METHOD_TEMPORARY;
+@@ -509,6 +511,18 @@ meta_monitor_manager_ensure_configured (MetaMonitorManager *manager)
+   else
+     method = META_MONITORS_CONFIG_METHOD_TEMPORARY;
+ 
++  initial_config = meta_monitor_config_manager_create_initial (manager->config_manager);
++
++  if (initial_config)
++    {
++      current_state_key = meta_create_monitors_config_key_for_current_state (manager);
++
++      /* don't ever reuse initial configuration, if the monitor topology changed
++       */
++      if (current_state_key && !meta_monitors_config_key_equal (current_state_key, initial_config->key))
++        g_clear_object (&initial_config);
++    }
++
+   if (use_stored_config)
+     {
+       config = meta_monitor_config_manager_get_stored (manager->config_manager);
+@@ -576,7 +590,7 @@ meta_monitor_manager_ensure_configured (MetaMonitorManager *manager)
+       g_clear_object (&config);
+     }
+ 
+-  config = meta_monitor_config_manager_create_current (manager->config_manager);
++  config = g_steal_pointer (&initial_config);
+   if (config)
+     {
+       if (!meta_monitor_manager_apply_monitors_config (manager,
+-- 
+2.20.1
+
diff --git a/SOURCES/0001-monitor-manager-xrandr-Force-an-update-when-resuming.patch b/SOURCES/0001-monitor-manager-xrandr-Force-an-update-when-resuming.patch
new file mode 100644
index 0000000..b9b4a8a
--- /dev/null
+++ b/SOURCES/0001-monitor-manager-xrandr-Force-an-update-when-resuming.patch
@@ -0,0 +1,274 @@
+From 5bdc464aa6b15af1cf310427c360351db83388da Mon Sep 17 00:00:00 2001
+From: Rui Matos <tiagomatos@gmail.com>
+Date: Mon, 4 Jun 2018 16:35:04 -0400
+Subject: [PATCH] monitor-manager-xrandr: Force an update when resuming from
+ suspend
+
+The stack below us isn't as reliable as we'd like and in some cases
+doesn't generate RRScreenChangeNotify events when e.g. resuming a
+laptop on a dock, meaning that we'd miss newly attached outputs.
+---
+ src/backends/meta-gpu.c                       |  7 ++
+ src/backends/meta-gpu.h                       |  2 +
+ src/backends/x11/meta-gpu-xrandr.c            | 26 ++++-
+ .../x11/meta-monitor-manager-xrandr.c         | 96 +++++++++++++++++--
+ 4 files changed, 121 insertions(+), 10 deletions(-)
+
+diff --git a/src/backends/meta-gpu.c b/src/backends/meta-gpu.c
+index 3577391e5..946f72387 100644
+--- a/src/backends/meta-gpu.c
++++ b/src/backends/meta-gpu.c
+@@ -64,6 +64,13 @@ meta_gpu_has_hotplug_mode_update (MetaGpu *gpu)
+   return FALSE;
+ }
+ 
++void
++meta_gpu_poll_hardware (MetaGpu *gpu)
++{
++  if (META_GPU_GET_CLASS (gpu)->poll_hardware)
++    META_GPU_GET_CLASS (gpu)->poll_hardware (gpu);
++}
++
+ gboolean
+ meta_gpu_read_current (MetaGpu  *gpu,
+                        GError  **error)
+diff --git a/src/backends/meta-gpu.h b/src/backends/meta-gpu.h
+index 4badcbd26..3cec8e5b0 100644
+--- a/src/backends/meta-gpu.h
++++ b/src/backends/meta-gpu.h
+@@ -35,12 +35,14 @@ struct _MetaGpuClass
+ 
+   gboolean (* read_current) (MetaGpu  *gpu,
+                              GError  **error);
++  void     (* poll_hardware) (MetaGpu *gpu);
+ };
+ 
+ int meta_gpu_get_kms_fd (MetaGpu *gpu);
+ 
+ const char * meta_gpu_get_kms_file_path (MetaGpu *gpu);
+ 
++void meta_gpu_poll_hardware (MetaGpu *gpu);
+ gboolean meta_gpu_read_current (MetaGpu  *gpu,
+                                 GError  **error);
+ 
+diff --git a/src/backends/x11/meta-gpu-xrandr.c b/src/backends/x11/meta-gpu-xrandr.c
+index 14b46d530..add80c0d2 100644
+--- a/src/backends/x11/meta-gpu-xrandr.c
++++ b/src/backends/x11/meta-gpu-xrandr.c
+@@ -44,6 +44,8 @@ struct _MetaGpuXrandr
+ 
+   int max_screen_width;
+   int max_screen_height;
++
++  gboolean need_hardware_poll;
+ };
+ 
+ G_DEFINE_TYPE (MetaGpuXrandr, meta_gpu_xrandr, META_TYPE_GPU)
+@@ -81,6 +83,14 @@ get_xmode_name (XRRModeInfo *xmode)
+   return g_strdup_printf ("%dx%d", width, height);
+ }
+ 
++static void
++meta_gpu_xrandr_poll_hardware (MetaGpu *gpu)
++{
++  MetaGpuXrandr *gpu_xrandr = META_GPU_XRANDR (gpu);
++
++  gpu_xrandr->need_hardware_poll = TRUE;
++}
++
+ static gboolean
+ meta_gpu_xrandr_read_current (MetaGpu  *gpu,
+                               GError  **error)
+@@ -148,8 +158,18 @@ meta_gpu_xrandr_read_current (MetaGpu  *gpu,
+   monitor_manager->screen_width = WidthOfScreen (screen);
+   monitor_manager->screen_height = HeightOfScreen (screen);
+ 
+-  resources = XRRGetScreenResourcesCurrent (xdisplay,
+-                                            DefaultRootWindow (xdisplay));
++  if (gpu_xrandr->need_hardware_poll)
++    {
++      resources = XRRGetScreenResources (xdisplay,
++                                         DefaultRootWindow (xdisplay));
++      gpu_xrandr->need_hardware_poll = FALSE;
++    }
++  else
++    {
++      resources = XRRGetScreenResourcesCurrent (xdisplay,
++                                                DefaultRootWindow (xdisplay));
++    }
++
+   if (!resources)
+     {
+       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+@@ -282,6 +302,7 @@ meta_gpu_xrandr_finalize (GObject *object)
+ static void
+ meta_gpu_xrandr_init (MetaGpuXrandr *gpu_xrandr)
+ {
++  gpu_xrandr->need_hardware_poll = TRUE;
+ }
+ 
+ static void
+@@ -293,4 +314,5 @@ meta_gpu_xrandr_class_init (MetaGpuXrandrClass *klass)
+   object_class->finalize = meta_gpu_xrandr_finalize;
+ 
+   gpu_class->read_current = meta_gpu_xrandr_read_current;
++  gpu_class->poll_hardware = meta_gpu_xrandr_poll_hardware;
+ }
+diff --git a/src/backends/x11/meta-monitor-manager-xrandr.c b/src/backends/x11/meta-monitor-manager-xrandr.c
+index c6eb4b6aa..26dccb247 100644
+--- a/src/backends/x11/meta-monitor-manager-xrandr.c
++++ b/src/backends/x11/meta-monitor-manager-xrandr.c
+@@ -60,6 +60,10 @@ struct _MetaMonitorManagerXrandr
+   Display *xdisplay;
+   int rr_event_base;
+   int rr_error_base;
++
++  guint logind_watch_id;
++  guint logind_signal_sub_id;
++
+   gboolean has_randr15;
+ 
+   /*
+@@ -95,6 +99,8 @@ typedef struct _MetaMonitorXrandrData
+ GQuark quark_meta_monitor_xrandr_data;
+ #endif /* HAVE_RANDR15 */
+ 
++static void meta_monitor_manager_xrandr_update (MetaMonitorManagerXrandr *manager_xrandr);
++
+ Display *
+ meta_monitor_manager_xrandr_get_xdisplay (MetaMonitorManagerXrandr *manager_xrandr)
+ {
+@@ -966,6 +972,62 @@ meta_monitor_manager_xrandr_get_default_layout_mode (MetaMonitorManager *manager
+   return META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL;
+ }
+ 
++static void
++logind_signal_handler (GDBusConnection *connection,
++                       const gchar     *sender_name,
++                       const gchar     *object_path,
++                       const gchar     *interface_name,
++                       const gchar     *signal_name,
++                       GVariant        *parameters,
++                       gpointer         user_data)
++{
++  MetaMonitorManagerXrandr *manager_xrandr = user_data;
++  gboolean suspending;
++
++  if (!g_str_equal (signal_name, "PrepareForSleep"))
++    return;
++
++  g_variant_get (parameters, "(b)", &suspending);
++  if (!suspending)
++    {
++      meta_gpu_poll_hardware (manager_xrandr->gpu);
++      meta_monitor_manager_xrandr_update (manager_xrandr);
++    }
++}
++
++static void
++logind_appeared (GDBusConnection *connection,
++                 const gchar     *name,
++                 const gchar     *name_owner,
++                 gpointer         user_data)
++{
++  MetaMonitorManagerXrandr *manager_xrandr = user_data;
++
++  manager_xrandr->logind_signal_sub_id = g_dbus_connection_signal_subscribe (connection,
++                                                                             "org.freedesktop.login1",
++                                                                             "org.freedesktop.login1.Manager",
++                                                                             "PrepareForSleep",
++                                                                             "/org/freedesktop/login1",
++                                                                             NULL,
++                                                                             G_DBUS_SIGNAL_FLAGS_NONE,
++                                                                             logind_signal_handler,
++                                                                             manager_xrandr,
++                                                                             NULL);
++}
++
++static void
++logind_vanished (GDBusConnection *connection,
++                 const gchar     *name,
++                 gpointer         user_data)
++{
++  MetaMonitorManagerXrandr *manager_xrandr = user_data;
++
++  if (connection && manager_xrandr->logind_signal_sub_id > 0)
++    g_dbus_connection_signal_unsubscribe (connection, manager_xrandr->logind_signal_sub_id);
++
++  manager_xrandr->logind_signal_sub_id = 0;
++}
++
+ static void
+ meta_monitor_manager_xrandr_constructed (GObject *object)
+ {
+@@ -1024,12 +1086,23 @@ meta_monitor_manager_xrandr_finalize (GObject *object)
+   g_hash_table_destroy (manager_xrandr->tiled_monitor_atoms);
+   g_free (manager_xrandr->supported_scales);
+ 
++  if (manager_xrandr->logind_watch_id > 0)
++    g_bus_unwatch_name (manager_xrandr->logind_watch_id);
++  manager_xrandr->logind_watch_id = 0;
++
+   G_OBJECT_CLASS (meta_monitor_manager_xrandr_parent_class)->finalize (object);
+ }
+ 
+ static void
+ meta_monitor_manager_xrandr_init (MetaMonitorManagerXrandr *manager_xrandr)
+ {
++  manager_xrandr->logind_watch_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM,
++                                                      "org.freedesktop.login1",
++                                                      G_BUS_NAME_WATCHER_FLAGS_NONE,
++                                                      logind_appeared,
++                                                      logind_vanished,
++                                                      manager_xrandr,
++                                                      NULL);
+ }
+ 
+ static void
+@@ -1076,9 +1149,8 @@ is_xvnc (MetaMonitorManager *manager)
+   return FALSE;
+ }
+ 
+-gboolean
+-meta_monitor_manager_xrandr_handle_xevent (MetaMonitorManagerXrandr *manager_xrandr,
+-					   XEvent                   *event)
++static void
++meta_monitor_manager_xrandr_update (MetaMonitorManagerXrandr *manager_xrandr)
+ {
+   MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_xrandr);
+   MetaGpuXrandr *gpu_xrandr;
+@@ -1087,11 +1159,6 @@ meta_monitor_manager_xrandr_handle_xevent (MetaMonitorManagerXrandr *manager_xra
+   gboolean is_our_configuration;
+   unsigned int timestamp;
+ 
+-  if ((event->type - manager_xrandr->rr_event_base) != RRScreenChangeNotify)
+-    return FALSE;
+-
+-  XRRUpdateConfiguration (event);
+-
+   meta_monitor_manager_read_current_state (manager);
+ 
+   gpu_xrandr = META_GPU_XRANDR (manager_xrandr->gpu);
+@@ -1126,6 +1193,19 @@ meta_monitor_manager_xrandr_handle_xevent (MetaMonitorManagerXrandr *manager_xra
+ 
+       meta_monitor_manager_xrandr_rebuild_derived (manager, config);
+     }
++}
++
++gboolean
++meta_monitor_manager_xrandr_handle_xevent (MetaMonitorManagerXrandr *manager_xrandr,
++					   XEvent                   *event)
++{
++
++  if ((event->type - manager_xrandr->rr_event_base) != RRScreenChangeNotify)
++    return FALSE;
++
++  XRRUpdateConfiguration (event);
++
++  meta_monitor_manager_xrandr_update (manager_xrandr);
+ 
+   return TRUE;
+ }
+-- 
+2.20.1
+
diff --git a/SOURCES/0001-monitor-manager-xrandr-Work-around-spurious-hotplugs.patch b/SOURCES/0001-monitor-manager-xrandr-Work-around-spurious-hotplugs.patch
new file mode 100644
index 0000000..b27453a
--- /dev/null
+++ b/SOURCES/0001-monitor-manager-xrandr-Work-around-spurious-hotplugs.patch
@@ -0,0 +1,62 @@
+From f562a522a30f3048da0f2fff7ee4950e53e8c462 Mon Sep 17 00:00:00 2001
+From: Rui Matos <tiagomatos@gmail.com>
+Date: Tue, 6 Oct 2015 21:16:18 +0200
+Subject: [PATCH] monitor-manager-xrandr: Work around spurious hotplugs on Xvnc
+
+Xvnc turns its outputs off/on on every mode set which makes us believe
+there was an hotplug when there actually wasn't. Work around this by
+requiring new randr configuration timestamps to be ahead of the last
+set timestamp by at least 100 ms for us to consider them an actual
+hotplug.
+---
+ .../x11/meta-monitor-manager-xrandr.c         | 20 ++++++++++++++++++-
+ 1 file changed, 19 insertions(+), 1 deletion(-)
+
+diff --git a/src/backends/x11/meta-monitor-manager-xrandr.c b/src/backends/x11/meta-monitor-manager-xrandr.c
+index e438d8f98..c6eb4b6aa 100644
+--- a/src/backends/x11/meta-monitor-manager-xrandr.c
++++ b/src/backends/x11/meta-monitor-manager-xrandr.c
+@@ -1063,6 +1063,19 @@ meta_monitor_manager_xrandr_class_init (MetaMonitorManagerXrandrClass *klass)
+     g_quark_from_static_string ("-meta-monitor-xrandr-data");
+ }
+ 
++static gboolean
++is_xvnc (MetaMonitorManager *manager)
++{
++  MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager);
++  GList *l;
++
++  for (l = meta_gpu_get_outputs (manager_xrandr->gpu); l; l = l->next)
++    if (g_str_has_prefix (((MetaOutput *)l->data)->name, "VNC-"))
++      return TRUE;
++
++  return FALSE;
++}
++
+ gboolean
+ meta_monitor_manager_xrandr_handle_xevent (MetaMonitorManagerXrandr *manager_xrandr,
+ 					   XEvent                   *event)
+@@ -1072,6 +1085,7 @@ meta_monitor_manager_xrandr_handle_xevent (MetaMonitorManagerXrandr *manager_xra
+   XRRScreenResources *resources;
+   gboolean is_hotplug;
+   gboolean is_our_configuration;
++  unsigned int timestamp;
+ 
+   if ((event->type - manager_xrandr->rr_event_base) != RRScreenChangeNotify)
+     return FALSE;
+@@ -1083,7 +1097,11 @@ meta_monitor_manager_xrandr_handle_xevent (MetaMonitorManagerXrandr *manager_xra
+   gpu_xrandr = META_GPU_XRANDR (manager_xrandr->gpu);
+   resources = meta_gpu_xrandr_get_resources (gpu_xrandr);
+ 
+-  is_hotplug = resources->timestamp < resources->configTimestamp;
++  timestamp = resources->timestamp;
++  if (is_xvnc (manager))
++    timestamp += 100;
++
++  is_hotplug = (timestamp < resources->configTimestamp);
+   is_our_configuration = (resources->timestamp ==
+                           manager_xrandr->last_xrandr_set_timestamp);
+   if (is_hotplug)
+-- 
+2.20.1
+
diff --git a/SOURCES/0001-native-gpu-Handle-drmModeSetCrtc-failing-gracefully.patch b/SOURCES/0001-native-gpu-Handle-drmModeSetCrtc-failing-gracefully.patch
new file mode 100644
index 0000000..3c6bc9c
--- /dev/null
+++ b/SOURCES/0001-native-gpu-Handle-drmModeSetCrtc-failing-gracefully.patch
@@ -0,0 +1,36 @@
+From 44682a2cc5550ae2d51f606f800405a05b72a240 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Tue, 24 Apr 2018 09:26:33 +0000
+Subject: [PATCH] native/gpu: Handle drmModeSetCrtc() failing gracefully
+
+If drmModeSetCrtc() is called with no fb, mode or connectors for some
+CRTC it may still fail, and we should handle that gracefully instead of
+assuming it failed to set a non-disabled state.
+
+Closes https://gitlab.gnome.org/GNOME/mutter/issues/70
+
+
+(cherry picked from commit 6e953e2725d5d5b10d14c7bd479bd99f6853addc)
+---
+ src/backends/native/meta-gpu-kms.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/src/backends/native/meta-gpu-kms.c b/src/backends/native/meta-gpu-kms.c
+index 259cd7122..1d1c28809 100644
+--- a/src/backends/native/meta-gpu-kms.c
++++ b/src/backends/native/meta-gpu-kms.c
+@@ -143,7 +143,10 @@ meta_gpu_kms_apply_crtc_mode (MetaGpuKms *gpu_kms,
+                       connectors, n_connectors,
+                       mode) != 0)
+     {
+-      g_warning ("Failed to set CRTC mode %s: %m", crtc->current_mode->name);
++      if (mode)
++        g_warning ("Failed to set CRTC mode %s: %m", crtc->current_mode->name);
++      else
++        g_warning ("Failed to disable CRTC");
+       g_free (connectors);
+       return FALSE;
+     }
+-- 
+2.19.0
+
diff --git a/SOURCES/0001-renderer-native-Also-wrap-flip-closures-for-EGLStrea.patch b/SOURCES/0001-renderer-native-Also-wrap-flip-closures-for-EGLStrea.patch
new file mode 100644
index 0000000..932311c
--- /dev/null
+++ b/SOURCES/0001-renderer-native-Also-wrap-flip-closures-for-EGLStrea.patch
@@ -0,0 +1,170 @@
+From 1c77f0613220b026cbc7eebeb0a00844a675a721 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Tue, 14 Nov 2017 16:08:52 +0800
+Subject: [PATCH] renderer/native: Also wrap flip closures for EGLStreams
+
+When using the EGLStream backend, the MetaRendererNative passed a
+GClosure to KMS when using EGLStreams, but KMS flip callback event
+handler in meta-gpu-kms.c expected a closure wrapped in a closure
+container, meaning it'd instead crash when using EGLStreams. Make the
+flip handler get what it expects also when using EGLStreams by wrapping
+the flip closure in the container before handing it over to EGL.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=790316
+(cherry picked from commit 8ee14a7cb7e8f072d2731d59c7dc735f83a9bb0b)
+---
+ src/backends/native/meta-gpu-kms.c         | 44 +++++++++++++++-------
+ src/backends/native/meta-gpu-kms.h         |  7 ++++
+ src/backends/native/meta-renderer-native.c |  7 +++-
+ 3 files changed, 44 insertions(+), 14 deletions(-)
+
+diff --git a/src/backends/native/meta-gpu-kms.c b/src/backends/native/meta-gpu-kms.c
+index 08c1e7dbe..815caf501 100644
+--- a/src/backends/native/meta-gpu-kms.c
++++ b/src/backends/native/meta-gpu-kms.c
+@@ -47,6 +47,12 @@ typedef struct _MetaKmsSource
+   MetaGpuKms *gpu_kms;
+ } MetaKmsSource;
+ 
++typedef struct _MetaGpuKmsFlipClosureContainer
++{
++  GClosure *flip_closure;
++  MetaGpuKms *gpu_kms;
++} MetaGpuKmsFlipClosureContainer;
++
+ struct _MetaGpuKms
+ {
+   MetaGpu parent;
+@@ -209,11 +215,26 @@ meta_gpu_kms_is_crtc_active (MetaGpuKms *gpu_kms,
+   return TRUE;
+ }
+ 
+-typedef struct _GpuClosureContainer
++MetaGpuKmsFlipClosureContainer *
++meta_gpu_kms_wrap_flip_closure (MetaGpuKms *gpu_kms,
++                                GClosure   *flip_closure)
+ {
+-  GClosure *flip_closure;
+-  MetaGpuKms *gpu_kms;
+-} GpuClosureContainer;
++  MetaGpuKmsFlipClosureContainer *closure_container;
++
++  closure_container = g_new0 (MetaGpuKmsFlipClosureContainer, 1);
++  *closure_container = (MetaGpuKmsFlipClosureContainer) {
++    .flip_closure = flip_closure,
++    .gpu_kms = gpu_kms
++  };
++
++  return closure_container;
++}
++
++void
++meta_gpu_kms_flip_closure_container_free (MetaGpuKmsFlipClosureContainer *closure_container)
++{
++  g_free (closure_container);
++}
+ 
+ gboolean
+ meta_gpu_kms_flip_crtc (MetaGpuKms *gpu_kms,
+@@ -239,14 +260,11 @@ meta_gpu_kms_flip_crtc (MetaGpuKms *gpu_kms,
+ 
+   if (!gpu_kms->page_flips_not_supported)
+     {
+-      GpuClosureContainer *closure_container;
++      MetaGpuKmsFlipClosureContainer *closure_container;
+       int kms_fd = meta_gpu_kms_get_fd (gpu_kms);
+ 
+-      closure_container = g_new0 (GpuClosureContainer, 1);
+-      *closure_container = (GpuClosureContainer) {
+-        .flip_closure = flip_closure,
+-        .gpu_kms = gpu_kms
+-      };
++      closure_container = meta_gpu_kms_wrap_flip_closure (gpu_kms,
++                                                          flip_closure);
+ 
+       ret = drmModePageFlip (kms_fd,
+                              crtc->crtc_id,
+@@ -255,7 +273,7 @@ meta_gpu_kms_flip_crtc (MetaGpuKms *gpu_kms,
+                              closure_container);
+       if (ret != 0 && ret != -EACCES)
+         {
+-          g_free (closure_container);
++          meta_gpu_kms_flip_closure_container_free (closure_container);
+           g_warning ("Failed to flip: %s", strerror (-ret));
+           gpu_kms->page_flips_not_supported = TRUE;
+         }
+@@ -286,12 +304,12 @@ page_flip_handler (int           fd,
+                    unsigned int  usec,
+                    void         *user_data)
+ {
+-  GpuClosureContainer *closure_container = user_data;
++  MetaGpuKmsFlipClosureContainer *closure_container = user_data;
+   GClosure *flip_closure = closure_container->flip_closure;
+   MetaGpuKms *gpu_kms = closure_container->gpu_kms;
+ 
+   invoke_flip_closure (flip_closure, gpu_kms);
+-  g_free (closure_container);
++  meta_gpu_kms_flip_closure_container_free (closure_container);
+ }
+ 
+ gboolean
+diff --git a/src/backends/native/meta-gpu-kms.h b/src/backends/native/meta-gpu-kms.h
+index d53ed98c8..01c3f2c0c 100644
+--- a/src/backends/native/meta-gpu-kms.h
++++ b/src/backends/native/meta-gpu-kms.h
+@@ -40,6 +40,8 @@ GQuark meta_gpu_kms_error_quark (void);
+ #define META_TYPE_GPU_KMS (meta_gpu_kms_get_type ())
+ G_DECLARE_FINAL_TYPE (MetaGpuKms, meta_gpu_kms, META, GPU_KMS, MetaGpu)
+ 
++typedef struct _MetaGpuKmsFlipClosureContainer MetaGpuKmsFlipClosureContainer;
++
+ typedef struct _MetaKmsResources
+ {
+   drmModeRes *resources;
+@@ -94,4 +96,9 @@ gboolean meta_drm_mode_equal (const drmModeModeInfo *one,
+ 
+ float meta_calculate_drm_mode_refresh_rate (const drmModeModeInfo *mode);
+ 
++MetaGpuKmsFlipClosureContainer * meta_gpu_kms_wrap_flip_closure (MetaGpuKms *gpu_kms,
++                                                                 GClosure   *flip_closure);
++
++void meta_gpu_kms_flip_closure_container_free (MetaGpuKmsFlipClosureContainer *closure_container);
++
+ #endif /* META_GPU_KMS_H */
+diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c
+index d0da98f74..470da8845 100644
+--- a/src/backends/native/meta-renderer-native.c
++++ b/src/backends/native/meta-renderer-native.c
+@@ -1286,6 +1286,7 @@ flip_egl_stream (MetaOnscreenNative *onscreen_native,
+   MetaRendererNativeGpuData *renderer_gpu_data;
+   EGLDisplay *egl_display;
+   MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native);
++  MetaGpuKmsFlipClosureContainer *closure_container;
+   EGLAttrib *acquire_attribs;
+   GError *error = NULL;
+ 
+@@ -1295,9 +1296,12 @@ flip_egl_stream (MetaOnscreenNative *onscreen_native,
+   if (renderer_gpu_data->egl.no_egl_output_drm_flip_event)
+     return FALSE;
+ 
++  closure_container =
++    meta_gpu_kms_wrap_flip_closure (onscreen_native->render_gpu, flip_closure);
++
+   acquire_attribs = (EGLAttrib[]) {
+     EGL_DRM_FLIP_EVENT_DATA_NV,
+-    (EGLAttrib) flip_closure,
++    (EGLAttrib) closure_container,
+     EGL_NONE
+   };
+ 
+@@ -1316,6 +1320,7 @@ flip_egl_stream (MetaOnscreenNative *onscreen_native,
+           renderer_gpu_data->egl.no_egl_output_drm_flip_event = TRUE;
+         }
+       g_error_free (error);
++      meta_gpu_kms_flip_closure_container_free (closure_container);
+       return FALSE;
+     }
+ 
+-- 
+2.19.1
+
diff --git a/SOURCES/0001-renderer-native-Check-calculated-transform-when-crea.patch b/SOURCES/0001-renderer-native-Check-calculated-transform-when-crea.patch
new file mode 100644
index 0000000..1079d4c
--- /dev/null
+++ b/SOURCES/0001-renderer-native-Check-calculated-transform-when-crea.patch
@@ -0,0 +1,88 @@
+From 91c5c94434b22895f97b3ae47a889ccb902b86aa Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Mon, 23 Jul 2018 21:36:57 +0200
+Subject: [PATCH] renderer/native: Check calculated transform when creating
+ view
+
+The "backends: Move MetaOutput::crtc field into private struct"
+accidentally changed the view transform calculation code to assume that
+"MetaCrtc::transform" corresponds to the transform of the CRTC; so is
+not the case yet; one must calculate the transform from the logical
+monitor, and check whether it is supported by the CRTC using
+meta_monitor_manager_is_transform_handled(). This commit restores the
+old behaviour that doesn't use MetaCrtc::transform when calculating the
+view transform.
+
+Fixes: https://gitlab.gnome.org/GNOME/mutter/issues/216
+---
+ src/backends/native/meta-renderer-native.c         | 9 +++++++--
+ src/backends/x11/nested/meta-renderer-x11-nested.c | 8 ++++++--
+ 2 files changed, 13 insertions(+), 4 deletions(-)
+
+diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c
+index fc6b223026..8dc0da7104 100644
+--- a/src/backends/native/meta-renderer-native.c
++++ b/src/backends/native/meta-renderer-native.c
+@@ -2720,9 +2720,14 @@ calculate_view_transform (MetaMonitorManager *monitor_manager,
+   MetaMonitor *main_monitor;
+   MetaOutput *main_output;
+   MetaCrtc *crtc;
++  MetaMonitorTransform crtc_transform;
++
+   main_monitor = meta_logical_monitor_get_monitors (logical_monitor)->data;
+   main_output = meta_monitor_get_main_output (main_monitor);
+   crtc = meta_output_get_assigned_crtc (main_output);
++  crtc_transform =
++    meta_monitor_logical_to_crtc_transform (main_monitor,
++                                            logical_monitor->transform);
+ 
+   /*
+    * Pick any monitor and output and check; all CRTCs of a logical monitor will
+@@ -2731,10 +2736,10 @@ calculate_view_transform (MetaMonitorManager *monitor_manager,
+ 
+   if (meta_monitor_manager_is_transform_handled (monitor_manager,
+                                                  crtc,
+-                                                 crtc->transform))
++                                                 crtc_transform))
+     return META_MONITOR_TRANSFORM_NORMAL;
+   else
+-    return crtc->transform;
++    return crtc_transform;
+ }
+ 
+ static MetaRendererView *
+diff --git a/src/backends/x11/nested/meta-renderer-x11-nested.c b/src/backends/x11/nested/meta-renderer-x11-nested.c
+index 8fdf46b0b5..b29b9c69e2 100644
+--- a/src/backends/x11/nested/meta-renderer-x11-nested.c
++++ b/src/backends/x11/nested/meta-renderer-x11-nested.c
+@@ -51,10 +51,14 @@ calculate_view_transform (MetaMonitorManager *monitor_manager,
+   MetaMonitor *main_monitor;
+   MetaOutput *main_output;
+   MetaCrtc *crtc;
++  MetaMonitorTransform crtc_transform;
+ 
+   main_monitor = meta_logical_monitor_get_monitors (logical_monitor)->data;
+   main_output = meta_monitor_get_main_output (main_monitor);
+   crtc = meta_output_get_assigned_crtc (main_output);
++  crtc_transform =
++    meta_monitor_logical_to_crtc_transform (main_monitor,
++                                            logical_monitor->transform);
+   /*
+    * Pick any monitor and output and check; all CRTCs of a logical monitor will
+    * always have the same transform assigned to them.
+@@ -62,10 +66,10 @@ calculate_view_transform (MetaMonitorManager *monitor_manager,
+ 
+   if (meta_monitor_manager_is_transform_handled (monitor_manager,
+                                                  crtc,
+-                                                 crtc->transform))
++                                                 crtc_transform))
+     return META_MONITOR_TRANSFORM_NORMAL;
+   else
+-    return crtc->transform;
++    return crtc_transform;
+ }
+ 
+ static MetaRendererView *
+-- 
+2.17.1
+
diff --git a/SOURCES/0001-renderer-native-Choose-first-EGL-config-for-non-GBM-.patch b/SOURCES/0001-renderer-native-Choose-first-EGL-config-for-non-GBM-.patch
new file mode 100644
index 0000000..94a9629
--- /dev/null
+++ b/SOURCES/0001-renderer-native-Choose-first-EGL-config-for-non-GBM-.patch
@@ -0,0 +1,124 @@
+From c647bd7807d90d6b268aecc02f51ee5df1692684 Mon Sep 17 00:00:00 2001
+From: "Miguel A. Vico" <mvicomoya@nvidia.com>
+Date: Thu, 7 Jun 2018 23:29:44 +0000
+Subject: [PATCH] renderer/native: Choose first EGL config for non-GBM backends
+
+Commit 712ec30cd9be1f180c3789e7e6a042c5f7b5781d added the logic to only
+choose EGL configs that match the GBM_FORMAT_XRGB8888 pixel format.
+However, there won't be any EGL config satisfying such criteria for
+non-GBM backends, such as EGLDevice.
+
+This change will let us choose the first EGL config for the EGLDevice
+backend, while still forcing GBM_FORMAT_XRGB8888 configs for the GBM
+one.
+
+Related to: https://gitlab.gnome.org/GNOME/mutter/issues/2
+
+
+(cherry picked from commit 1bf2eb95b502ed0419b0fe8979c022cacaf79e84)
+---
+ src/backends/native/meta-renderer-native.c | 65 ++++++++++++++++------
+ 1 file changed, 48 insertions(+), 17 deletions(-)
+
+diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c
+index 250a6b6c1..d0da98f74 100644
+--- a/src/backends/native/meta-renderer-native.c
++++ b/src/backends/native/meta-renderer-native.c
+@@ -993,14 +993,29 @@ meta_renderer_native_choose_egl_config (CoglDisplay  *cogl_display,
+   CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
+   MetaBackend *backend = meta_get_backend ();
+   MetaEgl *egl = meta_backend_get_egl (backend);
++  MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
+   EGLDisplay egl_display = cogl_renderer_egl->edpy;
+ 
+-  return choose_egl_config_from_gbm_format (egl,
+-                                            egl_display,
+-                                            attributes,
+-                                            GBM_FORMAT_XRGB8888,
+-                                            out_config,
+-                                            error);
++  switch (renderer_gpu_data->mode)
++    {
++    case META_RENDERER_NATIVE_MODE_GBM:
++      return choose_egl_config_from_gbm_format (egl,
++                                                egl_display,
++                                                attributes,
++                                                GBM_FORMAT_XRGB8888,
++                                                out_config,
++                                                error);
++#ifdef HAVE_EGL_DEVICE
++    case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
++      return meta_egl_choose_first_config (egl,
++                                           egl_display,
++                                           attributes,
++                                           out_config,
++                                           error);
++#endif
++    }
++
++  return FALSE;
+ }
+ 
+ static gboolean
+@@ -2923,10 +2938,11 @@ meta_renderer_native_set_property (GObject      *object,
+ }
+ 
+ static gboolean
+-create_secondary_egl_config (MetaEgl   *egl,
+-                             EGLDisplay egl_display,
+-                             EGLConfig *egl_config,
+-                             GError   **error)
++create_secondary_egl_config (MetaEgl               *egl,
++                             MetaRendererNativeMode mode,
++                             EGLDisplay             egl_display,
++                             EGLConfig             *egl_config,
++                             GError               **error)
+ {
+   EGLint attributes[] = {
+     EGL_RED_SIZE, 1,
+@@ -2939,12 +2955,26 @@ create_secondary_egl_config (MetaEgl   *egl,
+     EGL_NONE
+   };
+ 
+-  return choose_egl_config_from_gbm_format (egl,
+-                                            egl_display,
+-                                            attributes,
+-                                            GBM_FORMAT_XRGB8888,
+-                                            egl_config,
+-                                            error);
++  switch (mode)
++    {
++    case META_RENDERER_NATIVE_MODE_GBM:
++      return choose_egl_config_from_gbm_format (egl,
++                                                egl_display,
++                                                attributes,
++                                                GBM_FORMAT_XRGB8888,
++                                                egl_config,
++                                                error);
++#ifdef HAVE_EGL_DEVICE
++    case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
++      return meta_egl_choose_first_config (egl,
++                                           egl_display,
++                                           attributes,
++                                           egl_config,
++                                           error);
++#endif
++    }
++
++  return FALSE;
+ }
+ 
+ static EGLContext
+@@ -2988,7 +3018,8 @@ init_secondary_gpu_data_gpu (MetaRendererNativeGpuData *renderer_gpu_data,
+   EGLContext egl_context;
+   char **missing_gl_extensions;
+ 
+-  if (!create_secondary_egl_config (egl,egl_display, &egl_config, error))
++  if (!create_secondary_egl_config (egl, renderer_gpu_data->mode, egl_display,
++                                    &egl_config, error))
+     return FALSE;
+ 
+   egl_context = create_secondary_egl_context (egl, egl_display, egl_config, error);
+-- 
+2.19.1
+
diff --git a/SOURCES/0001-renderer-native-Fallback-to-non-planar-API-if-gbm_bo.patch b/SOURCES/0001-renderer-native-Fallback-to-non-planar-API-if-gbm_bo.patch
new file mode 100644
index 0000000..5d401e8
--- /dev/null
+++ b/SOURCES/0001-renderer-native-Fallback-to-non-planar-API-if-gbm_bo.patch
@@ -0,0 +1,65 @@
+From 1276cc97d1e6437c7fbc43fdd5cbcea39f60acee Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Alex=20Villac=C3=ADs=20Lasso?= <a_villacis@palosanto.com>
+Date: Fri, 27 Jul 2018 16:08:52 +0000
+Subject: [PATCH] renderer/native: Fallback to non-planar API if
+ gbm_bo_get_handle_for_plane fails
+
+Commit c0d9b08ef9bf2be865aad9bf1bc74ba24c655d9f replaced the old GBM API calls
+with the multi-plane GBM API. However, the call to gbm_bo_get_handle_for_plane
+fails for some DRI drivers (in particular i915). Due to missing error checks,
+the subsequent call to drmModeAddFB[2] fails and the screen output locks up.
+
+This commit adds the missing error checks and falls back to the old GBM API
+(non-planar) if necessary.
+
+v5: test success of gbm_bo_get_handle_for_plane instead of errno
+
+This commit adopts solution proposed by Daniel van Vugt to check the return
+value of gbm_bo_get_handle_for_plane on plane 0 and fall back to old
+non-planar method if the call fails. This removes the errno check (for
+ENOSYS) that could abort if mesa ever sets a different value.
+
+Related to: https://gitlab.gnome.org/GNOME/mutter/issues/127
+
+
+(cherry picked from commit f7af32a3eaefabbea3ebbda3a93eff98dd105ab9)
+---
+ src/backends/native/meta-renderer-native.c | 21 ++++++++++++++++-----
+ 1 file changed, 16 insertions(+), 5 deletions(-)
+
+diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c
+index fc6b22302..59003b397 100644
+--- a/src/backends/native/meta-renderer-native.c
++++ b/src/backends/native/meta-renderer-native.c
+@@ -1607,12 +1607,23 @@ gbm_get_next_fb_id (MetaGpuKms         *gpu_kms,
+       return FALSE;
+     }
+ 
+-  for (i = 0; i < gbm_bo_get_plane_count (next_bo); i++)
++  if (gbm_bo_get_handle_for_plane (next_bo, 0).s32 == -1)
+     {
+-      strides[i] = gbm_bo_get_stride_for_plane (next_bo, i);
+-      handles[i] = gbm_bo_get_handle_for_plane (next_bo, i).u32;
+-      offsets[i] = gbm_bo_get_offset (next_bo, i);
+-      modifiers[i] = gbm_bo_get_modifier (next_bo);
++      /* Failed to fetch handle to plane, falling back to old method */
++      strides[0] = gbm_bo_get_stride (next_bo);
++      handles[0] = gbm_bo_get_handle (next_bo).u32;
++      offsets[0] = 0;
++      modifiers[0] = DRM_FORMAT_MOD_INVALID;
++    }
++  else
++    {
++      for (i = 0; i < gbm_bo_get_plane_count (next_bo); i++)
++        {
++          strides[i] = gbm_bo_get_stride_for_plane (next_bo, i);
++          handles[i] = gbm_bo_get_handle_for_plane (next_bo, i).u32;
++          offsets[i] = gbm_bo_get_offset (next_bo, i);
++          modifiers[i] = gbm_bo_get_modifier (next_bo);
++        }
+     }
+ 
+   kms_fd = meta_gpu_kms_get_fd (gpu_kms);
+-- 
+2.19.0
+
diff --git a/SOURCES/0001-renderer-native-Use-shadow-fb-on-software-GL-if-pref.patch b/SOURCES/0001-renderer-native-Use-shadow-fb-on-software-GL-if-pref.patch
new file mode 100644
index 0000000..446324a
--- /dev/null
+++ b/SOURCES/0001-renderer-native-Use-shadow-fb-on-software-GL-if-pref.patch
@@ -0,0 +1,187 @@
+From d05751b1a28d7e99a089203401b0ec940373cbd0 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Tue, 9 Oct 2018 15:55:23 +0200
+Subject: [PATCH] renderer/native: Use shadow fb on software GL if preferred
+
+If a KMS device has the DRM_CAP_DUMB_PREFER_SHADOW and a software based
+GL driver is used, always use a shadow fb. This will speed up read backs
+in the llvmpipe OpenGL implementation, making blend operations faster.
+
+Fixes: https://gitlab.gnome.org/GNOME/mutter/issues/106
+---
+ src/backends/native/meta-renderer-native.c | 61 ++++++++++++++++++++--
+ 1 file changed, 57 insertions(+), 4 deletions(-)
+
+diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c
+index 470da8845..b15a6da96 100644
+--- a/src/backends/native/meta-renderer-native.c
++++ b/src/backends/native/meta-renderer-native.c
+@@ -2746,108 +2746,161 @@ meta_onscreen_native_set_view (CoglOnscreen     *onscreen,
+ 
+ static MetaMonitorTransform
+ calculate_view_transform (MetaMonitorManager *monitor_manager,
+                           MetaLogicalMonitor *logical_monitor)
+ {
+   MetaMonitor *main_monitor;
+   MetaOutput *main_output;
+   MetaCrtc *crtc;
+   MetaMonitorTransform crtc_transform;
+ 
+   main_monitor = meta_logical_monitor_get_monitors (logical_monitor)->data;
+   main_output = meta_monitor_get_main_output (main_monitor);
+   crtc = meta_output_get_assigned_crtc (main_output);
+   crtc_transform =
+     meta_monitor_logical_to_crtc_transform (main_monitor,
+                                             logical_monitor->transform);
+ 
+   /*
+    * Pick any monitor and output and check; all CRTCs of a logical monitor will
+    * always have the same transform assigned to them.
+    */
+ 
+   if (meta_monitor_manager_is_transform_handled (monitor_manager,
+                                                  crtc,
+                                                  crtc_transform))
+     return META_MONITOR_TRANSFORM_NORMAL;
+   else
+     return crtc_transform;
+ }
+ 
++static CoglContext *
++cogl_context_from_renderer_native (MetaRendererNative *renderer_native)
++{
++  MetaMonitorManager *monitor_manager =
++    META_MONITOR_MANAGER (renderer_native->monitor_manager_kms);
++  MetaBackend *backend = meta_monitor_manager_get_backend (monitor_manager);
++  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)
++{
++  CoglContext *cogl_context =
++    cogl_context_from_renderer_native (renderer_native);
++  CoglGpuInfo *info = &cogl_context->gpu;
++  int kms_fd;
++  uint64_t prefer_shadow = 0;
++
++  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 FALSE;
++    case COGL_GPU_INFO_ARCHITECTURE_LLVMPIPE:
++    case COGL_GPU_INFO_ARCHITECTURE_SOFTPIPE:
++    case COGL_GPU_INFO_ARCHITECTURE_SWRAST:
++      break;
++    }
++
++  kms_fd = meta_gpu_kms_get_fd (primary_gpu);
++  if (drmGetCap (kms_fd, DRM_CAP_DUMB_PREFER_SHADOW, &prefer_shadow) == 0)
++    {
++      if (prefer_shadow)
++        {
++          static gboolean logged_once = FALSE;
++
++          if (!logged_once)
++            {
++              g_message ("Forcing shadow framebuffer");
++              logged_once = TRUE;
++            }
++
++          return TRUE;
++        }
++    }
++
++  return FALSE;
++}
++
+ static MetaRendererView *
+ meta_renderer_native_create_view (MetaRenderer       *renderer,
+                                   MetaLogicalMonitor *logical_monitor)
+ {
+   MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer);
+   MetaMonitorManagerKms *monitor_manager_kms =
+     renderer_native->monitor_manager_kms;
+   MetaMonitorManager *monitor_manager =
+     META_MONITOR_MANAGER (monitor_manager_kms);
+-  MetaBackend *backend = meta_monitor_manager_get_backend (monitor_manager);
+-  ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
+   CoglContext *cogl_context =
+-    clutter_backend_get_cogl_context (clutter_backend);
++    cogl_context_from_renderer_native (renderer_native);
+   CoglDisplay *cogl_display = cogl_context_get_display (cogl_context);
+   MetaGpuKms *primary_gpu;
+   CoglDisplayEGL *cogl_display_egl;
+   CoglOnscreenEGL *onscreen_egl;
+   MetaMonitorTransform view_transform;
+   CoglOnscreen *onscreen = NULL;
+   CoglOffscreen *offscreen = NULL;
+   float scale;
+   int width, height;
+   MetaRendererView *view;
+   GError *error = NULL;
+ 
+   view_transform = calculate_view_transform (monitor_manager, logical_monitor);
+ 
+   if (meta_is_stage_views_scaled ())
+     scale = meta_logical_monitor_get_scale (logical_monitor);
+   else
+     scale = 1.0;
+ 
+   width = roundf (logical_monitor->rect.width * scale);
+   height = roundf (logical_monitor->rect.height * scale);
+ 
+   primary_gpu = meta_monitor_manager_kms_get_primary_gpu (monitor_manager_kms);
+   onscreen = meta_renderer_native_create_onscreen (renderer_native,
+                                                    primary_gpu,
+                                                    logical_monitor,
+                                                    cogl_context,
+                                                    view_transform,
+                                                    width,
+                                                    height,
+                                                    &error);
+   if (!onscreen)
+     g_error ("Failed to allocate onscreen framebuffer: %s", error->message);
+ 
+-  if (view_transform != META_MONITOR_TRANSFORM_NORMAL)
++  if (view_transform != META_MONITOR_TRANSFORM_NORMAL ||
++      should_force_shadow_fb (renderer_native, primary_gpu))
+     {
+       offscreen = meta_renderer_native_create_offscreen (renderer_native,
+                                                          cogl_context,
+                                                          view_transform,
+                                                          width,
+                                                          height,
+                                                          &error);
+       if (!offscreen)
+         g_error ("Failed to allocate back buffer texture: %s", error->message);
+     }
+ 
+   view = g_object_new (META_TYPE_RENDERER_VIEW,
+                        "layout", &logical_monitor->rect,
+                        "scale", scale,
+                        "framebuffer", onscreen,
+                        "offscreen", offscreen,
+                        "logical-monitor", logical_monitor,
+                        "transform", view_transform,
+                        NULL);
+   g_clear_pointer (&offscreen, cogl_object_unref);
+ 
+   meta_onscreen_native_set_view (onscreen, view);
+ 
+   if (!meta_onscreen_native_allocate (onscreen, &error))
+     {
+       g_warning ("Could not create onscreen: %s", error->message);
+       cogl_object_unref (onscreen);
+       g_object_unref (view);
+       g_error_free (error);
+       return NULL;
+-- 
+2.17.1
+
diff --git a/SOURCES/0001-screen-cast-Fix-monitor-recording-on-HiDPI.patch b/SOURCES/0001-screen-cast-Fix-monitor-recording-on-HiDPI.patch
new file mode 100644
index 0000000..13cfe0b
--- /dev/null
+++ b/SOURCES/0001-screen-cast-Fix-monitor-recording-on-HiDPI.patch
@@ -0,0 +1,36 @@
+From 3fa6a92cc5dda6ab3939c3e982185f6caf453360 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Fri, 25 Jan 2019 22:06:39 +0100
+Subject: [PATCH] screen-cast: Fix monitor recording on HiDPI
+
+It scaled the logical monitor rect with scale to get the stream
+dimensions, but that is only valid when having
+'scale-monitor-framebuffers' enabled. Even when it was, it didn't work
+properly, as clutter_stage_capture_into() doesn't work properly with
+scaled monitor framebuffers yet.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/415
+---
+ src/backends/meta-screen-cast-monitor-stream-src.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 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 897b86ae6..036c573a5 100644
+--- a/src/backends/meta-screen-cast-monitor-stream-src.c
++++ b/src/backends/meta-screen-cast-monitor-stream-src.c
+@@ -102,7 +102,11 @@ meta_screen_cast_monitor_stream_src_get_specs (MetaScreenCastStreamSrc *src,
+   logical_monitor = meta_monitor_get_logical_monitor (monitor);
+   mode = meta_monitor_get_current_mode (monitor);
+ 
+-  scale = logical_monitor->scale;
++  if (meta_is_stage_views_scaled ())
++    scale = logical_monitor->scale;
++  else
++    scale = 1.0;
++
+   *width = (int) roundf (logical_monitor->rect.width * scale);
+   *height = (int) roundf (logical_monitor->rect.height * scale);
+   *frame_rate = meta_monitor_mode_get_refresh_rate (mode);
+-- 
+2.20.1
+
diff --git a/SOURCES/0001-virtual-input-evdev-Translate-from-button-codes-inte.patch b/SOURCES/0001-virtual-input-evdev-Translate-from-button-codes-inte.patch
new file mode 100644
index 0000000..880ee57
--- /dev/null
+++ b/SOURCES/0001-virtual-input-evdev-Translate-from-button-codes-inte.patch
@@ -0,0 +1,86 @@
+From f73524d6fcc45f42790d1fdad718f52f98ef793a Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Mon, 6 Aug 2018 21:00:23 +0200
+Subject: [PATCH] virtual-input/evdev: Translate from button codes internal to
+ evdev
+
+Sending button events to a ClutterVirtualInputDevice, the API expects
+button codes to be of the internal clutter type. The evdev
+implementation incorrectly assumed it was already prepared evdev event
+codes, which was not the case. Fix the evdev implementation to translate
+from the internal representation to evdev before passing it along to
+ClutterSeatEvdev.
+---
+ .../clutter-virtual-input-device-evdev.c      | 31 +++++++++++++++----
+ 1 file changed, 25 insertions(+), 6 deletions(-)
+
+diff --git a/clutter/clutter/evdev/clutter-virtual-input-device-evdev.c b/clutter/clutter/evdev/clutter-virtual-input-device-evdev.c
+index fe628da855..e491416c4c 100644
+--- a/clutter/clutter/evdev/clutter-virtual-input-device-evdev.c
++++ b/clutter/clutter/evdev/clutter-virtual-input-device-evdev.c
+@@ -185,6 +185,22 @@ clutter_virtual_input_device_evdev_notify_absolute_motion (ClutterVirtualInputDe
+                                              NULL);
+ }
+ 
++static int
++translate_to_evdev_button (int button)
++{
++  switch (button)
++    {
++    case CLUTTER_BUTTON_PRIMARY:
++      return BTN_LEFT;
++    case CLUTTER_BUTTON_SECONDARY:
++      return BTN_RIGHT;
++    case CLUTTER_BUTTON_MIDDLE:
++      return BTN_MIDDLE;
++    default:
++      return button + (BTN_LEFT - 1) - 4;
++    }
++}
++
+ static void
+ clutter_virtual_input_device_evdev_notify_button (ClutterVirtualInputDevice *virtual_device,
+                                                   uint64_t                   time_us,
+@@ -194,30 +210,33 @@ clutter_virtual_input_device_evdev_notify_button (ClutterVirtualInputDevice *vir
+   ClutterVirtualInputDeviceEvdev *virtual_evdev =
+     CLUTTER_VIRTUAL_INPUT_DEVICE_EVDEV (virtual_device);
+   int button_count;
++  int evdev_button;
+ 
+   if (time_us == CLUTTER_CURRENT_TIME)
+     time_us = g_get_monotonic_time ();
+ 
+-  if (get_button_type (button) != EVDEV_BUTTON_TYPE_BUTTON)
++  evdev_button = translate_to_evdev_button (button);
++
++  if (get_button_type (evdev_button) != EVDEV_BUTTON_TYPE_BUTTON)
+     {
+       g_warning ("Unknown/invalid virtual device button 0x%x pressed",
+-                 button);
++                 evdev_button);
+       return;
+     }
+ 
+-  button_count = update_button_count (virtual_evdev, button, button_state);
++  button_count = update_button_count (virtual_evdev, evdev_button, button_state);
+   if (button_count < 0 || button_count > 1)
+     {
+-      g_warning ("Received multiple virtual 0x%x button %s (ignoring)", button,
++      g_warning ("Received multiple virtual 0x%x button %s (ignoring)", evdev_button,
+                  button_state == CLUTTER_BUTTON_STATE_PRESSED ? "presses" : "releases");
+-      update_button_count (virtual_evdev, button, 1 - button_state);
++      update_button_count (virtual_evdev, evdev_button, 1 - button_state);
+       return;
+     }
+ 
+   clutter_seat_evdev_notify_button (virtual_evdev->seat,
+                                     virtual_evdev->device,
+                                     time_us,
+-                                    button,
++                                    evdev_button,
+                                     button_state);
+ }
+ 
+-- 
+2.17.1
+
diff --git a/SOURCES/0001-wayland-Allow-Xwayland-grabs-on-selected-apps.patch b/SOURCES/0001-wayland-Allow-Xwayland-grabs-on-selected-apps.patch
new file mode 100644
index 0000000..2c14c03
--- /dev/null
+++ b/SOURCES/0001-wayland-Allow-Xwayland-grabs-on-selected-apps.patch
@@ -0,0 +1,35 @@
+From d951730695ec30987f8bf1cb95910720ecca8de7 Mon Sep 17 00:00:00 2001
+From: Olivier Fourdan <ofourdan@redhat.com>
+Date: Fri, 26 Oct 2018 08:49:39 +0200
+Subject: [PATCH] wayland: Allow Xwayland grabs on selected apps
+
+Allow Xwayland grabs on a selected set of X11 applications.
+---
+ data/org.gnome.mutter.wayland.gschema.xml.in | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/data/org.gnome.mutter.wayland.gschema.xml.in b/data/org.gnome.mutter.wayland.gschema.xml.in
+index 48241296e..7a6ab9288 100644
+--- a/data/org.gnome.mutter.wayland.gschema.xml.in
++++ b/data/org.gnome.mutter.wayland.gschema.xml.in
+@@ -60,7 +60,7 @@
+           gettext-domain="@GETTEXT_DOMAIN@">
+ 
+     <key name="xwayland-allow-grabs" type="b">
+-      <default>false</default>
++      <default>true</default>
+       <summary>Allow grabs with Xwayland</summary>
+       <description>
+         Allow keyboard grabs issued by X11 applications running in Xwayland
+@@ -73,7 +73,7 @@
+     </key>
+ 
+     <key name="xwayland-grab-access-rules" type="as">
+-      <default>[]</default>
++      <default>['@XWAYLAND_GRAB_DEFAULT_ACCESS_RULES@']</default>
+       <summary>Xwayland applications allowed to issue keyboard grabs</summary>
+       <description>
+         List the resource names or resource class of X11 windows either
+-- 
+2.19.1
+
diff --git a/SOURCES/0001-wayland-Always-realize-buffers-at-surface-commit-tim.patch b/SOURCES/0001-wayland-Always-realize-buffers-at-surface-commit-tim.patch
new file mode 100644
index 0000000..53e001d
--- /dev/null
+++ b/SOURCES/0001-wayland-Always-realize-buffers-at-surface-commit-tim.patch
@@ -0,0 +1,107 @@
+From f965b963844ef41e75b42f81005e4675df94e10c Mon Sep 17 00:00:00 2001
+From: "Miguel A. Vico" <mvicomoya@nvidia.com>
+Date: Sat, 6 May 2017 03:41:51 +0200
+Subject: [PATCH] wayland: Always realize buffers at surface commit time
+
+Clients using EGLStream-backed buffers will expect the stream to be
+functional after wl_surface::attach(). That means the compositor-side
+stream must be created and a consumer attached to it.
+
+To resolve the above, this change realizes buffers even when the attach
+operation is deferred (e.g. synchronized subsurfaces).
+
+https://bugzilla.gnome.org/show_bug.cgi?id=782575
+
+(cherry picked from commit 22723ca37173955d8ef4c6981795e91a85f4feb9)
+---
+ src/wayland/meta-wayland-buffer.c  | 21 +++++++++------------
+ src/wayland/meta-wayland-buffer.h  |  2 ++
+ src/wayland/meta-wayland-surface.c |  4 ++++
+ 3 files changed, 15 insertions(+), 12 deletions(-)
+
+diff --git a/src/wayland/meta-wayland-buffer.c b/src/wayland/meta-wayland-buffer.c
+index 3396f45..b4b1e78 100644
+--- a/src/wayland/meta-wayland-buffer.c
++++ b/src/wayland/meta-wayland-buffer.c
+@@ -94,13 +94,13 @@ meta_wayland_buffer_get_resource (MetaWaylandBuffer *buffer)
+   return buffer->resource;
+ }
+ 
+-static gboolean
++gboolean
+ meta_wayland_buffer_is_realized (MetaWaylandBuffer *buffer)
+ {
+   return buffer->type != META_WAYLAND_BUFFER_TYPE_UNKNOWN;
+ }
+ 
+-static gboolean
++gboolean
+ meta_wayland_buffer_realize (MetaWaylandBuffer *buffer)
+ {
+   EGLint format;
+@@ -131,13 +131,12 @@ meta_wayland_buffer_realize (MetaWaylandBuffer *buffer)
+     {
+       CoglTexture2D *texture;
+ 
+-      buffer->egl_stream.stream = stream;
+-      buffer->type = META_WAYLAND_BUFFER_TYPE_EGL_STREAM;
+-
+       texture = meta_wayland_egl_stream_create_texture (stream, NULL);
+       if (!texture)
+         return FALSE;
+ 
++      buffer->egl_stream.stream = stream;
++      buffer->type = META_WAYLAND_BUFFER_TYPE_EGL_STREAM;
+       buffer->texture = COGL_TEXTURE (texture);
+       buffer->is_y_inverted = meta_wayland_egl_stream_is_y_inverted (stream);
+ 
+@@ -344,13 +343,11 @@ meta_wayland_buffer_attach (MetaWaylandBuffer *buffer,
+ 
+   if (!meta_wayland_buffer_is_realized (buffer))
+     {
+-      if (!meta_wayland_buffer_realize (buffer))
+-        {
+-          g_set_error (error, G_IO_ERROR,
+-                       G_IO_ERROR_FAILED,
+-                       "Unknown buffer type");
+-          return FALSE;
+-        }
++      /* The buffer should have been realized at surface commit time */
++      g_set_error (error, G_IO_ERROR,
++                   G_IO_ERROR_FAILED,
++                   "Unknown buffer type");
++      return FALSE;
+     }
+ 
+   switch (buffer->type)
+diff --git a/src/wayland/meta-wayland-buffer.h b/src/wayland/meta-wayland-buffer.h
+index e00a41e..036f9d4 100644
+--- a/src/wayland/meta-wayland-buffer.h
++++ b/src/wayland/meta-wayland-buffer.h
+@@ -69,6 +69,8 @@ G_DECLARE_FINAL_TYPE (MetaWaylandBuffer, meta_wayland_buffer,
+ 
+ MetaWaylandBuffer *     meta_wayland_buffer_from_resource       (struct wl_resource    *resource);
+ struct wl_resource *    meta_wayland_buffer_get_resource        (MetaWaylandBuffer     *buffer);
++gboolean                meta_wayland_buffer_is_realized         (MetaWaylandBuffer     *buffer);
++gboolean                meta_wayland_buffer_realize             (MetaWaylandBuffer     *buffer);
+ gboolean                meta_wayland_buffer_attach              (MetaWaylandBuffer     *buffer,
+                                                                  GError               **error);
+ CoglTexture *           meta_wayland_buffer_get_texture         (MetaWaylandBuffer     *buffer);
+diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c
+index c9c229e..3c9a404 100644
+--- a/src/wayland/meta-wayland-surface.c
++++ b/src/wayland/meta-wayland-surface.c
+@@ -765,6 +765,10 @@ cleanup:
+ static void
+ meta_wayland_surface_commit (MetaWaylandSurface *surface)
+ {
++  if (surface->pending->buffer &&
++      !meta_wayland_buffer_is_realized (surface->pending->buffer))
++    meta_wayland_buffer_realize (surface->pending->buffer);
++
+   /*
+    * If this is a sub-surface and it is in effective synchronous mode, only
+    * cache the pending surface state until either one of the following two
+-- 
+2.19.1
+
diff --git a/SOURCES/0001-wayland-Clean-up-xwayland-grabs-even-if-surface-is-g.patch b/SOURCES/0001-wayland-Clean-up-xwayland-grabs-even-if-surface-is-g.patch
new file mode 100644
index 0000000..d0c51ba
--- /dev/null
+++ b/SOURCES/0001-wayland-Clean-up-xwayland-grabs-even-if-surface-is-g.patch
@@ -0,0 +1,52 @@
+From 5b9f5329bd922f7048084977102f23ad2845311e Mon Sep 17 00:00:00 2001
+From: Olivier Fourdan <ofourdan@redhat.com>
+Date: Fri, 3 Aug 2018 10:50:32 +0200
+Subject: [PATCH] wayland: Clean up xwayland grabs even if surface is gone
+
+If the surface is gone before `meta_xwayland_keyboard_grab_end()` is
+called, we would bail out early leaving an empty grab, which will cause
+a segfault as soon as a key is pressed later on.
+
+Make sure we clean up the keyboard grab even if the surface is gone.
+
+Closes: https://gitlab.gnome.org/GNOME/mutter/issues/255
+(cherry picked from commit 252dd524390dcdbdd89534c0014d22a796957f55)
+---
+ src/wayland/meta-xwayland-grab-keyboard.c | 14 +++++++-------
+ 1 file changed, 7 insertions(+), 7 deletions(-)
+
+diff --git a/src/wayland/meta-xwayland-grab-keyboard.c b/src/wayland/meta-xwayland-grab-keyboard.c
+index 42b194a77..db2d5f0bd 100644
+--- a/src/wayland/meta-xwayland-grab-keyboard.c
++++ b/src/wayland/meta-xwayland-grab-keyboard.c
+@@ -85,6 +85,13 @@ meta_xwayland_keyboard_grab_end (MetaXwaylandKeyboardActiveGrab *active_grab)
+ {
+   MetaWaylandSeat *seat = active_grab->seat;
+ 
++  if (seat->keyboard->grab->interface->key == meta_xwayland_keyboard_grab_key)
++    {
++      meta_wayland_keyboard_end_grab (active_grab->keyboard_grab.keyboard);
++      meta_wayland_keyboard_set_focus (active_grab->keyboard_grab.keyboard, NULL);
++      meta_display_sync_wayland_input_focus (meta_get_display ());
++    }
++
+   if (!active_grab->surface)
+     return;
+ 
+@@ -104,13 +111,6 @@ meta_xwayland_keyboard_grab_end (MetaXwaylandKeyboardActiveGrab *active_grab)
+       active_grab->window_associate_handler = 0;
+     }
+ 
+-  if (seat->keyboard->grab->interface->key == meta_xwayland_keyboard_grab_key)
+-    {
+-      meta_wayland_keyboard_end_grab (active_grab->keyboard_grab.keyboard);
+-      meta_wayland_keyboard_set_focus (active_grab->keyboard_grab.keyboard, NULL);
+-      meta_display_sync_wayland_input_focus (meta_get_display ());
+-    }
+-
+   active_grab->surface = NULL;
+ }
+ 
+-- 
+2.19.0
+
diff --git a/SOURCES/0001-wayland-Create-EGLStream-backed-buffers-through-wl_e.patch b/SOURCES/0001-wayland-Create-EGLStream-backed-buffers-through-wl_e.patch
new file mode 100644
index 0000000..ac932e6
--- /dev/null
+++ b/SOURCES/0001-wayland-Create-EGLStream-backed-buffers-through-wl_e.patch
@@ -0,0 +1,245 @@
+From 04f59617e14d266c9d82b3f37a79db9467697dcf Mon Sep 17 00:00:00 2001
+From: "Miguel A. Vico" <mvicomoya@nvidia.com>
+Date: Tue, 27 Jun 2017 16:37:30 -0700
+Subject: [PATCH] wayland: Create EGLStream-backed buffers through
+ wl_eglstream_controller
+
+One of the current limitations of EGLStreams is that there's no way to
+resize a surface consumer without re-creating the entire stream.
+
+Therefore, while resizing, clients will send wl_surface::attach requests
+so the compositor can re-create its endpoint of the stream, but no
+buffer will be available actually. If we proceed with the rest of the
+attach operation we'll be presenting an empty buffer.
+
+In order to fix this, a separate wl_eglstream_controller protocol has
+been introduced that clients can use to request a stream re-creation
+without overloading wl_surface::attach for that purpose.
+
+This change adds the required logic to create the corresponding
+wl_eglstream_controller global interface that clients can bind to.
+
+Whenever a client requests a stream to be created, we just need to
+create and realize the new EGLStream buffer. The same buffer resource
+will be given at a later time to wl_surface::attach, whenever new
+content is made available by the application, so we can proceed to
+acquire the stream buffer and update the surface state.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=782575
+
+(cherry picked from commit 435b3c4bdb62edcbb4b400223ddf7e44613efa54)
+---
+ configure.ac                          | 12 ++++
+ src/Makefile.am                       |  8 +++
+ src/wayland/meta-wayland-egl-stream.c | 98 +++++++++++++++++++++++++++
+ src/wayland/meta-wayland-egl-stream.h |  2 +
+ src/wayland/meta-wayland.c            |  4 ++
+ 6 files changed, 125 insertions(+)
+
+diff --git a/configure.ac b/configure.ac
+index e795159a1..cfb098d20 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -290,13 +290,24 @@ AS_IF([test "$have_native_backend" = "yes"], [
+ ])
+ AM_CONDITIONAL([HAVE_NATIVE_BACKEND],[test "$have_native_backend" = "yes"])
+ 
++MUTTER_WAYLAND_EGLSTREAM_MODULES="wayland-eglstream-protocols"
++
+ AC_ARG_ENABLE(egl-device,
+   AS_HELP_STRING([--enable-egl-device], [enable support for EGLDevice on top of KMS]),,
+   enable_egl_device=no
++  have_wayland_eglstream=no
+ )
+ AS_IF([test "$enable_egl_device" = "yes"], [
+   AC_DEFINE([HAVE_EGL_DEVICE],[1], [Defined if EGLDevice support is enabled])
++  PKG_CHECK_EXISTS([$MUTTER_WAYLAND_EGLSTREAM_MODULES], [have_wayland_eglstream=yes], [have_wayland_eglstream=no])
++])
++AS_IF([test "$have_wayland_eglstream" = "yes"], [
++  AC_DEFINE([HAVE_WAYLAND_EGLSTREAM],[1],[Defined if Wayland EGLStream protocols are available])
++  PKG_CHECK_MODULES(WAYLAND_EGLSTREAM, [$MUTTER_WAYLAND_EGLSTREAM_MODULES],
++                    [ac_wayland_eglstream_pkgdatadir=`$PKG_CONFIG --variable=pkgdatadir $MUTTER_WAYLAND_EGLSTREAM_MODULES`])
++  AC_SUBST(WAYLAND_EGLSTREAM_DATADIR, $ac_wayland_eglstream_pkgdatadir)
+ ])
++AM_CONDITIONAL([HAVE_WAYLAND_EGLSTREAM],[test "$have_wayland_eglstream" = "yes"])
+ 
+ MUTTER_WAYLAND_MODULES="wayland-server >= 1.13.0"
+ 
+@@ -549,6 +560,7 @@ mutter-$VERSION
+ 	Introspection:            ${found_introspection}
+ 	Session management:       ${found_sm}
+ 	Wayland:                  ${have_wayland}
++	Wayland EGLStream:        ${have_wayland_eglstream}
+ 	Native (KMS) backend:     ${have_native_backend}
+ 	EGLDevice:                ${enable_egl_device}
+ 	Remote desktop:           ${enable_remote_desktop}
+diff --git a/src/Makefile.am b/src/Makefile.am
+index bcb3505c7..6c4f872cb 100644
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -91,6 +91,12 @@ mutter_built_sources += \
+ 	gtk-text-input-protocol.c					\
+ 	gtk-text-input-server-protocol.h				\
+ 	$(NULL)
++
++if HAVE_WAYLAND_EGLSTREAM
++mutter_built_sources += \
++	wayland-eglstream-controller-server-protocol.h \
++	$(NULL)
++endif
+ endif
+ 
+ wayland_protocols =				\
+@@ -760,3 +766,5 @@ endef
+ 	$(AM_V_GEN)$(WAYLAND_SCANNER) code $< $@
+ %-server-protocol.h : $(srcdir)/wayland/protocol/%.xml
+ 	$(AM_V_GEN)$(WAYLAND_SCANNER) server-header $< $@
++%-server-protocol.h : $(WAYLAND_EGLSTREAM_DATADIR)/%.xml
++	$(AM_V_GEN)$(WAYLAND_SCANNER) server-header $< $@
+diff --git a/src/wayland/meta-wayland-egl-stream.c b/src/wayland/meta-wayland-egl-stream.c
+index 39ff93433..0d7ce2304 100644
+--- a/src/wayland/meta-wayland-egl-stream.c
++++ b/src/wayland/meta-wayland-egl-stream.c
+@@ -32,6 +32,104 @@
+ #include "backends/meta-egl-ext.h"
+ #include "meta/meta-backend.h"
+ #include "wayland/meta-wayland-buffer.h"
++#include "wayland/meta-wayland-private.h"
++
++#ifdef HAVE_WAYLAND_EGLSTREAM
++
++#include "wayland-eglstream-controller-server-protocol.h"
++#include <dlfcn.h>
++
++static struct wl_interface *wl_eglstream_controller_interface_ptr = NULL;
++
++static void
++attach_eglstream_consumer (struct wl_client   *client,
++                           struct wl_resource *resource,
++                           struct wl_resource *wl_surface,
++                           struct wl_resource *wl_eglstream)
++{
++  MetaWaylandBuffer *buffer = meta_wayland_buffer_from_resource (wl_eglstream);
++
++  if (!meta_wayland_buffer_is_realized (buffer))
++    meta_wayland_buffer_realize (buffer);
++}
++
++static const struct wl_eglstream_controller_interface
++meta_eglstream_controller_interface = {
++  attach_eglstream_consumer
++};
++
++static void
++bind_eglstream_controller (struct wl_client *client,
++                           void             *data,
++                           uint32_t          version,
++                           uint32_t          id)
++{
++  struct wl_resource *resource;
++
++  g_assert (wl_eglstream_controller_interface_ptr != NULL);
++
++  resource = wl_resource_create (client,
++                                 wl_eglstream_controller_interface_ptr,
++                                 version,
++                                 id);
++
++  if (resource == NULL)
++    {
++      wl_client_post_no_memory(client);
++      return;
++    }
++
++  wl_resource_set_implementation (resource,
++                                  &meta_eglstream_controller_interface,
++                                  data,
++                                  NULL);
++}
++
++#endif /* HAVE_WAYLAND_EGLSTREAM */
++
++gboolean
++meta_wayland_eglstream_controller_init (MetaWaylandCompositor *compositor)
++{
++#ifdef HAVE_WAYLAND_EGLSTREAM
++  /*
++   * wl_eglstream_controller_interface is provided by
++   * libnvidia-egl-wayland.so.1
++   *
++   * Since it might not be available on the
++   * system, dynamically load it at runtime and resolve the needed
++   * symbols. If available, it should be found under any of the search
++   * directories of dlopen()
++   *
++   * Failure to initialize wl_eglstream_controller is non-fatal
++   */
++
++  void *lib = dlopen ("libnvidia-egl-wayland.so.1", RTLD_NOW | RTLD_LAZY);
++  if (!lib)
++    goto fail;
++
++  wl_eglstream_controller_interface_ptr =
++    dlsym (lib, "wl_eglstream_controller_interface");
++
++  if (!wl_eglstream_controller_interface_ptr)
++    goto fail;
++
++  if (wl_global_create (compositor->wayland_display,
++                        wl_eglstream_controller_interface_ptr, 1,
++                        NULL,
++                        bind_eglstream_controller) == NULL)
++    goto fail;
++
++  return TRUE;
++
++fail:
++  if (lib)
++    dlclose(lib);
++
++  g_debug ("WL: Unable to initialize wl_eglstream_controller.");
++#endif
++
++  return FALSE;
++}
+ 
+ struct _MetaWaylandEglStream
+ {
+diff --git a/src/wayland/meta-wayland-egl-stream.h b/src/wayland/meta-wayland-egl-stream.h
+index 12a011f78..fe488ed54 100644
+--- a/src/wayland/meta-wayland-egl-stream.h
++++ b/src/wayland/meta-wayland-egl-stream.h
+@@ -31,6 +31,8 @@
+ #include "cogl/cogl.h"
+ #include "wayland/meta-wayland-types.h"
+ 
++gboolean meta_wayland_eglstream_controller_init (MetaWaylandCompositor *compositor);
++
+ #define META_TYPE_WAYLAND_EGL_STREAM (meta_wayland_egl_stream_get_type ())
+ G_DECLARE_FINAL_TYPE (MetaWaylandEglStream, meta_wayland_egl_stream,
+                       META, WAYLAND_EGL_STREAM, GObject);
+diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c
+index bab6b4ac4..9d416b20e 100644
+--- a/src/wayland/meta-wayland.c
++++ b/src/wayland/meta-wayland.c
+@@ -46,6 +46,8 @@
+ #include "meta-wayland-inhibit-shortcuts.h"
+ #include "meta-wayland-inhibit-shortcuts-dialog.h"
+ #include "meta-xwayland-grab-keyboard.h"
++#include "meta-xwayland.h"
++#include "meta-wayland-egl-stream.h"
+ 
+ static MetaWaylandCompositor _meta_wayland_compositor;
+ static char *_display_name_override;
+@@ -394,6 +396,8 @@ meta_wayland_init (void)
+                                   meta_xwayland_global_filter,
+                                   compositor);
+ 
++  meta_wayland_eglstream_controller_init (compositor);
++
+   if (!meta_xwayland_start (&compositor->xwayland_manager, compositor->wayland_display))
+     g_error ("Failed to start X Wayland");
+ 
+-- 
+2.19.1
+
diff --git a/SOURCES/0001-wayland-No-xdg-output-events-without-a-logical-monit.patch b/SOURCES/0001-wayland-No-xdg-output-events-without-a-logical-monit.patch
new file mode 100644
index 0000000..4963f52
--- /dev/null
+++ b/SOURCES/0001-wayland-No-xdg-output-events-without-a-logical-monit.patch
@@ -0,0 +1,45 @@
+From 6ed36cd6e36e9bd15ae4ca39f5b8163c4740c484 Mon Sep 17 00:00:00 2001
+From: Olivier Fourdan <ofourdan@redhat.com>
+Date: Tue, 25 Sep 2018 13:50:09 +0200
+Subject: [PATCH] wayland: No xdg-output events without a logical monitor
+
+To avoid a known race condition in the wl_output protocol documented in
+https://phabricator.freedesktop.org/T7722, mutter delays the `wl_output`
+destruction but nullify the `logical_monitor` associated with the
+`wl_output` and the binding routine `bind_output()` makes sure not to
+send wl_output events if the `logical_monitor` is `NULL` (see commit
+1923db97).
+
+The binding routine for `xdg_output` however does not check for such a
+condition, hence if the output configuration changes while a client is
+binding to xdg-output (typically Xwayland at startup), mutter would
+crash while trying to access the `logical_monitor` which was nullified
+by the change in configuration.
+
+Just like `bind_output()` does for wl_output, do not send xdg-output
+events if there is no `logical_monitor` yet.
+
+Closes: https://gitlab.gnome.org/GNOME/mutter/issues/194
+
+(cherry picked from commit 68ec9ac017157def9b7c25dd8141dc0e93d9f918)
+---
+ src/wayland/meta-wayland-outputs.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/src/wayland/meta-wayland-outputs.c b/src/wayland/meta-wayland-outputs.c
+index bc67b90be..1f99a163d 100644
+--- a/src/wayland/meta-wayland-outputs.c
++++ b/src/wayland/meta-wayland-outputs.c
+@@ -592,6 +592,9 @@ meta_xdg_output_manager_get_xdg_output (struct wl_client   *client,
+   wayland_output->xdg_output_resources =
+     g_list_prepend (wayland_output->xdg_output_resources, xdg_output_resource);
+ 
++  if (!wayland_output->logical_monitor)
++    return;
++
+   send_xdg_output_events (xdg_output_resource,
+                           wayland_output,
+                           wayland_output->logical_monitor,
+-- 
+2.19.0
+
diff --git a/SOURCES/0001-wayland-Nullify-monitor-resources-when-updating-outp.patch b/SOURCES/0001-wayland-Nullify-monitor-resources-when-updating-outp.patch
new file mode 100644
index 0000000..cc25ac4
--- /dev/null
+++ b/SOURCES/0001-wayland-Nullify-monitor-resources-when-updating-outp.patch
@@ -0,0 +1,103 @@
+From f45af3315d4f11e16fc998322e4b407437259ce1 Mon Sep 17 00:00:00 2001
+From: Olivier Fourdan <ofourdan@redhat.com>
+Date: Fri, 27 Jul 2018 16:32:31 +0000
+Subject: [PATCH] wayland: Nullify monitor resources when updating outputs
+
+If a client asks for xdg-output before we have set the output's logical
+monitor, we would end up crashing with a NULL pointer dereference.
+
+Make sure we clear the resource's user data when marking an output as
+inert on monitor change so that we don't end up with a Wayland output
+without a logical monitor.
+
+Closes: https://gitlab.gnome.org/GNOME/mutter/issues/194
+
+
+(cherry picked from commit 48eaa36d41bb88d4831e40e9c3ef3c7afda195bc)
+---
+ src/wayland/meta-wayland-outputs.c | 52 ++++++++++++++++++------------
+ 1 file changed, 31 insertions(+), 21 deletions(-)
+
+diff --git a/src/wayland/meta-wayland-outputs.c b/src/wayland/meta-wayland-outputs.c
+index 509527d38..bc67b90be 100644
+--- a/src/wayland/meta-wayland-outputs.c
++++ b/src/wayland/meta-wayland-outputs.c
+@@ -365,12 +365,38 @@ meta_wayland_output_new (MetaWaylandCompositor *compositor,
+ }
+ 
+ static void
+-nullify_logical_monitor (gpointer key,
+-                         gpointer value,
+-                         gpointer data)
++make_output_resources_inert (MetaWaylandOutput *wayland_output)
++{
++  GList *l;
++
++  for (l = wayland_output->resources; l; l = l->next)
++    {
++      struct wl_resource *output_resource = l->data;
++
++      wl_resource_set_user_data (output_resource, NULL);
++    }
++  g_list_free (wayland_output->resources);
++  wayland_output->resources = NULL;
++
++  for (l = wayland_output->xdg_output_resources; l; l = l->next)
++    {
++      struct wl_resource *xdg_output_resource = l->data;
++
++      wl_resource_set_user_data (xdg_output_resource, NULL);
++    }
++  g_list_free (wayland_output->xdg_output_resources);
++  wayland_output->xdg_output_resources = NULL;
++}
++
++static void
++make_output_inert (gpointer key,
++                   gpointer value,
++                   gpointer data)
+ {
+   MetaWaylandOutput *wayland_output = value;
++
+   wayland_output->logical_monitor = NULL;
++  make_output_resources_inert (wayland_output);
+ }
+ 
+ static gboolean
+@@ -420,7 +446,7 @@ meta_wayland_compositor_update_outputs (MetaWaylandCompositor *compositor,
+                            wayland_output);
+     }
+ 
+-  g_hash_table_foreach (compositor->outputs, nullify_logical_monitor, NULL);
++  g_hash_table_foreach (compositor->outputs, make_output_inert, NULL);
+   g_timeout_add_seconds (10, delayed_destroy_outputs, compositor->outputs);
+ 
+   return new_table;
+@@ -449,23 +475,7 @@ meta_wayland_output_finalize (GObject *object)
+   /* Make sure the wl_output destructor doesn't try to access MetaWaylandOutput
+    * after we have freed it.
+    */
+-  for (l = wayland_output->resources; l; l = l->next)
+-    {
+-      struct wl_resource *output_resource = l->data;
+-
+-      wl_resource_set_user_data (output_resource, NULL);
+-    }
+-
+-  g_list_free (wayland_output->resources);
+-
+-  for (l = wayland_output->xdg_output_resources; l; l = l->next)
+-    {
+-      struct wl_resource *xdg_output_resource = l->data;
+-
+-      wl_resource_set_user_data (xdg_output_resource, NULL);
+-    }
+-
+-  g_list_free (wayland_output->xdg_output_resources);
++  make_output_resources_inert (wayland_output);
+ 
+   G_OBJECT_CLASS (meta_wayland_output_parent_class)->finalize (object);
+ }
+-- 
+2.19.0
+
diff --git a/SOURCES/0001-wayland-Realize-dmabuf-buffers-before-trying-to-atta.patch b/SOURCES/0001-wayland-Realize-dmabuf-buffers-before-trying-to-atta.patch
new file mode 100644
index 0000000..8327800
--- /dev/null
+++ b/SOURCES/0001-wayland-Realize-dmabuf-buffers-before-trying-to-atta.patch
@@ -0,0 +1,39 @@
+From 9ac07b40052b02d5b3aeb268fa64b4583dd53abd Mon Sep 17 00:00:00 2001
+From: "Miguel A. Vico" <mvicomoya@nvidia.com>
+Date: Thu, 21 Jun 2018 02:53:15 +0000
+Subject: [PATCH] wayland: Realize dmabuf buffers before trying to attach them
+
+Commit 22723ca37 moved buffer realization to
+meta_wayland_surface_commit() so that it wouldn't be part of
+meta_wayland_buffer_attach().
+
+However, creation of dmabuf buffers would call into
+meta_wayland_buffer_attach() directly without realizing the buffer
+first. attach() would then fail and mutter would effectively shut down
+any clients using the zwp_linux_dmabuf protocol (note that if such
+client was Xwayland, mutter itself would shut down as well).
+
+Add the missing bit in order to make zwp_linux_dmabuf protocol work
+again.
+
+
+(cherry picked from commit 54709c16b56a308d2254c56a35d042bf8b8e124f)
+---
+ src/wayland/meta-wayland-dma-buf.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/wayland/meta-wayland-dma-buf.c b/src/wayland/meta-wayland-dma-buf.c
+index 76b8aa219..e72c53935 100644
+--- a/src/wayland/meta-wayland-dma-buf.c
++++ b/src/wayland/meta-wayland-dma-buf.c
+@@ -365,6 +365,7 @@ buffer_params_create_common (struct wl_client   *client,
+                                   dma_buf, NULL);
+   buffer = meta_wayland_buffer_from_resource (buffer_resource);
+ 
++  meta_wayland_buffer_realize (buffer);
+   if (!meta_wayland_buffer_attach (buffer, &error))
+     {
+       if (buffer_id == 0)
+-- 
+2.19.1
+
diff --git a/SOURCES/0001-wayland-Relax-requirements-for-evdev-events-to-have-.patch b/SOURCES/0001-wayland-Relax-requirements-for-evdev-events-to-have-.patch
new file mode 100644
index 0000000..b632f2c
--- /dev/null
+++ b/SOURCES/0001-wayland-Relax-requirements-for-evdev-events-to-have-.patch
@@ -0,0 +1,36 @@
+From 35b69c1af4af996fb4cdac329a941a155569d810 Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho <carlosg@gnome.org>
+Date: Thu, 27 Sep 2018 21:05:30 +0200
+Subject: [PATCH] wayland: Relax requirements for evdev events to have a evcode
+
+There may be emulated events that don't contain those, it's fine to
+go through the fallback paths for these.
+---
+ src/wayland/meta-wayland-keyboard.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/wayland/meta-wayland-keyboard.c b/src/wayland/meta-wayland-keyboard.c
+index e3ba4f000..4ffe146d5 100644
+--- a/src/wayland/meta-wayland-keyboard.c
++++ b/src/wayland/meta-wayland-keyboard.c
+@@ -633,7 +633,7 @@ default_grab_key (MetaWaylandKeyboardGrab *grab,
+ {
+   MetaWaylandKeyboard *keyboard = grab->keyboard;
+   gboolean is_press = event->type == CLUTTER_KEY_PRESS;
+-  guint32 code;
++  guint32 code = 0;
+ #ifdef HAVE_NATIVE_BACKEND
+   MetaBackend *backend = meta_get_backend ();
+ #endif
+@@ -647,7 +647,7 @@ default_grab_key (MetaWaylandKeyboardGrab *grab,
+ #ifdef HAVE_NATIVE_BACKEND
+   if (META_IS_BACKEND_NATIVE (backend))
+     code = clutter_evdev_event_get_event_code (event);
+-  else
++  if (code == 0)
+ #endif
+     code = evdev_code (&event->key);
+ 
+-- 
+2.20.1
+
diff --git a/SOURCES/0001-wayland-buffer-Create-EGLStream-texture-at-buffer_re.patch b/SOURCES/0001-wayland-buffer-Create-EGLStream-texture-at-buffer_re.patch
new file mode 100644
index 0000000..6a3b8fc
--- /dev/null
+++ b/SOURCES/0001-wayland-buffer-Create-EGLStream-texture-at-buffer_re.patch
@@ -0,0 +1,73 @@
+From 53b040be5c55bf4b283599d2a912cb072937afd4 Mon Sep 17 00:00:00 2001
+From: "Miguel A. Vico" <mvicomoya@nvidia.com>
+Date: Sat, 6 May 2017 01:23:57 +0000
+Subject: [PATCH] wayland-buffer: Create EGLStream texture at buffer_realize
+ time
+
+When dealing with synchronized subsurfaces, we defer buffer attachments
+until the parent surface state is applied.
+
+That causes interaction issues with EGLStream backed buffers, as the
+client expects the compositor-side stream to be functional after it
+requests a wl_surface::attach.
+
+By allowing the compositor to realize buffers without attaching them, we
+could resolve the issue above if we define a realized EGLStream buffer
+as a functional EGLStream (EGLStream + attached consumer).
+
+This change moves the texture consumer creation part from the attach
+function to the realize one.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=782575
+
+
+(cherry picked from commit edd3634bb52a8efc687c612152fda5c996e94d26)
+---
+ src/wayland/meta-wayland-buffer.c | 22 ++++++++++------------
+ 1 file changed, 10 insertions(+), 12 deletions(-)
+
+diff --git a/src/wayland/meta-wayland-buffer.c b/src/wayland/meta-wayland-buffer.c
+index 55564492a..6f4fd24bf 100644
+--- a/src/wayland/meta-wayland-buffer.c
++++ b/src/wayland/meta-wayland-buffer.c
+@@ -123,8 +123,18 @@ meta_wayland_buffer_realize (MetaWaylandBuffer *buffer)
+   stream = meta_wayland_egl_stream_new (buffer, NULL);
+   if (stream)
+     {
++      CoglTexture2D *texture;
++
+       buffer->egl_stream.stream = stream;
+       buffer->type = META_WAYLAND_BUFFER_TYPE_EGL_STREAM;
++
++      texture = meta_wayland_egl_stream_create_texture (stream, NULL);
++      if (!texture)
++        return FALSE;
++
++      buffer->texture = COGL_TEXTURE (texture);
++      buffer->is_y_inverted = meta_wayland_egl_stream_is_y_inverted (stream);
++
+       return TRUE;
+     }
+ 
+@@ -314,18 +324,6 @@ egl_stream_buffer_attach (MetaWaylandBuffer  *buffer,
+ 
+   g_assert (stream);
+ 
+-  if (!buffer->texture)
+-    {
+-      CoglTexture2D *texture;
+-
+-      texture = meta_wayland_egl_stream_create_texture (stream, error);
+-      if (!texture)
+-        return FALSE;
+-
+-      buffer->texture = COGL_TEXTURE (texture);
+-      buffer->is_y_inverted = meta_wayland_egl_stream_is_y_inverted (stream);
+-    }
+-
+   if (!meta_wayland_egl_stream_attach (stream, error))
+     return FALSE;
+ 
+-- 
+2.19.1
+
diff --git a/SOURCES/0001-wayland-keyboard-Create-a-separate-keymap-shm-file-p.patch b/SOURCES/0001-wayland-keyboard-Create-a-separate-keymap-shm-file-p.patch
new file mode 100644
index 0000000..57d66f2
--- /dev/null
+++ b/SOURCES/0001-wayland-keyboard-Create-a-separate-keymap-shm-file-p.patch
@@ -0,0 +1,250 @@
+From 0d134522d83ee263cdc83ea899af59fd264bafa7 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Thu, 29 Jun 2017 12:22:47 +0800
+Subject: [PATCH] wayland/keyboard: Create a separate keymap shm file per
+ resource
+
+By using the shm file when sending the keymap to all clients, we
+effectively allows any client to change the keymap, as any client has
+the ability to change the content of the file. Sending a read-only file
+descriptor, or making the file itself read-only before unlinking, can
+be worked around by the client by using chmod(2) and open(2) on
+/proc/<pid>/<fd>.
+
+Using memfd could potentially solve this issue, but as the usage of
+mmap with MAP_SHARED is wide spread among clients, such a change can
+not be introduced without causing wide spread compatibility issues.
+
+So, to avoid allowing clients to interfere with each other, create a
+separate shm file for each wl_keyboard resource when sending the
+keymap. We could eventually do this per client, but in most cases,
+there will only be one wl_keyboard resource per client anyway.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=784206
+---
+ src/wayland/meta-wayland-keyboard.c | 136 +++++++++++-----------------
+ src/wayland/meta-wayland-keyboard.h |   3 +-
+ 2 files changed, 56 insertions(+), 83 deletions(-)
+
+diff --git a/src/wayland/meta-wayland-keyboard.c b/src/wayland/meta-wayland-keyboard.c
+index 167293ca7..aca1fe411 100644
+--- a/src/wayland/meta-wayland-keyboard.c
++++ b/src/wayland/meta-wayland-keyboard.c
+@@ -127,34 +127,65 @@ create_anonymous_file (off_t    size,
+ }
+ 
+ static void
+-inform_clients_of_new_keymap (MetaWaylandKeyboard *keyboard)
++send_keymap (MetaWaylandKeyboard *keyboard,
++             struct wl_resource  *resource)
+ {
+-  struct wl_resource *keyboard_resource;
++  MetaWaylandXkbInfo *xkb_info = &keyboard->xkb_info;
++  GError *error = NULL;
++  int fd;
++  char *keymap_area;
+ 
+-  wl_resource_for_each (keyboard_resource, &keyboard->resource_list)
++  if (!xkb_info->keymap_string)
++    return;
++
++  fd = create_anonymous_file (xkb_info->keymap_size, &error);
++  if (fd < 0)
+     {
+-      wl_keyboard_send_keymap (keyboard_resource,
+-			       WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
+-			       keyboard->xkb_info.keymap_fd,
+-			       keyboard->xkb_info.keymap_size);
++      g_warning ("Creating a keymap file for %lu bytes failed: %s",
++                 (unsigned long) xkb_info->keymap_size,
++                 error->message);
++      g_clear_error (&error);
++      return;
+     }
+-  wl_resource_for_each (keyboard_resource, &keyboard->focus_resource_list)
++
++
++  keymap_area = mmap (NULL, xkb_info->keymap_size,
++                      PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
++  if (keymap_area == MAP_FAILED)
+     {
+-      wl_keyboard_send_keymap (keyboard_resource,
+-                               WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
+-                               keyboard->xkb_info.keymap_fd,
+-                               keyboard->xkb_info.keymap_size);
++      g_warning ("Failed to mmap() %lu bytes\n",
++                 (unsigned long) xkb_info->keymap_size);
++      close (fd);
++      return;
+     }
++
++  strcpy (keymap_area, xkb_info->keymap_string);
++
++  munmap (keymap_area, xkb_info->keymap_size);
++
++  wl_keyboard_send_keymap (resource,
++                           WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
++                           fd,
++                           keyboard->xkb_info.keymap_size);
++  close (fd);
++}
++
++static void
++inform_clients_of_new_keymap (MetaWaylandKeyboard *keyboard)
++{
++  struct wl_resource *keyboard_resource;
++
++  wl_resource_for_each (keyboard_resource, &keyboard->resource_list)
++    send_keymap (keyboard, keyboard_resource);
++  wl_resource_for_each (keyboard_resource, &keyboard->focus_resource_list)
++    send_keymap (keyboard, keyboard_resource);
+ }
+ 
+ static void
+ meta_wayland_keyboard_take_keymap (MetaWaylandKeyboard *keyboard,
+ 				   struct xkb_keymap   *keymap)
+ {
+-  MetaWaylandXkbInfo  *xkb_info = &keyboard->xkb_info;
+-  GError *error = NULL;
+-  char *keymap_str;
+-  size_t previous_size;
++  MetaWaylandXkbInfo *xkb_info = &keyboard->xkb_info;
+ 
+   if (keymap == NULL)
+     {
+@@ -162,60 +193,24 @@ meta_wayland_keyboard_take_keymap (MetaWaylandKeyboard *keyboard,
+       return;
+     }
+ 
++  g_clear_pointer (&xkb_info->keymap_string, g_free);
+   xkb_keymap_unref (xkb_info->keymap);
+   xkb_info->keymap = xkb_keymap_ref (keymap);
+ 
+   meta_wayland_keyboard_update_xkb_state (keyboard);
+ 
+-  keymap_str = xkb_map_get_as_string (xkb_info->keymap);
+-  if (keymap_str == NULL)
++  xkb_info->keymap_string =
++    xkb_keymap_get_as_string (xkb_info->keymap, XKB_KEYMAP_FORMAT_TEXT_V1);
++  if (!xkb_info->keymap_string)
+     {
+-      g_warning ("failed to get string version of keymap");
++      g_warning ("Failed to get string version of keymap");
+       return;
+     }
+-  previous_size = xkb_info->keymap_size;
+-  xkb_info->keymap_size = strlen (keymap_str) + 1;
+-
+-  if (xkb_info->keymap_fd >= 0)
+-    close (xkb_info->keymap_fd);
+-
+-  xkb_info->keymap_fd = create_anonymous_file (xkb_info->keymap_size, &error);
+-  if (xkb_info->keymap_fd < 0)
+-    {
+-      g_warning ("creating a keymap file for %lu bytes failed: %s",
+-                 (unsigned long) xkb_info->keymap_size,
+-                 error->message);
+-      g_clear_error (&error);
+-      goto err_keymap_str;
+-    }
+-
+-  if (xkb_info->keymap_area)
+-    munmap (xkb_info->keymap_area, previous_size);
+-
+-  xkb_info->keymap_area = mmap (NULL, xkb_info->keymap_size,
+-                                PROT_READ | PROT_WRITE,
+-                                MAP_SHARED, xkb_info->keymap_fd, 0);
+-  if (xkb_info->keymap_area == MAP_FAILED)
+-    {
+-      g_warning ("failed to mmap() %lu bytes\n",
+-                 (unsigned long) xkb_info->keymap_size);
+-      goto err_dev_zero;
+-    }
+-  strcpy (xkb_info->keymap_area, keymap_str);
+-  free (keymap_str);
++  xkb_info->keymap_size = strlen (xkb_info->keymap_string) + 1;
+ 
+   inform_clients_of_new_keymap (keyboard);
+ 
+   notify_modifiers (keyboard);
+-
+-  return;
+-
+-err_dev_zero:
+-  close (xkb_info->keymap_fd);
+-  xkb_info->keymap_fd = -1;
+-err_keymap_str:
+-  free (keymap_str);
+-  return;
+ }
+ 
+ static xkb_mod_mask_t
+@@ -707,28 +702,12 @@ meta_wayland_keyboard_enable (MetaWaylandKeyboard *keyboard)
+   maybe_restore_numlock_state (keyboard);
+ }
+ 
+-static void
+-meta_wayland_xkb_info_init (MetaWaylandXkbInfo *xkb_info)
+-{
+-  xkb_info->keymap_fd = -1;
+-}
+-
+ static void
+ meta_wayland_xkb_info_destroy (MetaWaylandXkbInfo *xkb_info)
+ {
+   g_clear_pointer (&xkb_info->keymap, xkb_keymap_unref);
+   g_clear_pointer (&xkb_info->state, xkb_state_unref);
+-
+-  if (xkb_info->keymap_area)
+-    {
+-      munmap (xkb_info->keymap_area, xkb_info->keymap_size);
+-      xkb_info->keymap_area = NULL;
+-    }
+-  if (xkb_info->keymap_fd >= 0)
+-    {
+-      close (xkb_info->keymap_fd);
+-      xkb_info->keymap_fd = -1;
+-    }
++  g_clear_pointer (&xkb_info->keymap_string, g_free);
+ }
+ 
+ void
+@@ -1001,10 +980,7 @@ meta_wayland_keyboard_create_new_resource (MetaWaylandKeyboard *keyboard,
+   wl_resource_set_implementation (resource, &keyboard_interface,
+                                   keyboard, unbind_resource);
+ 
+-  wl_keyboard_send_keymap (resource,
+-                           WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
+-                           keyboard->xkb_info.keymap_fd,
+-                           keyboard->xkb_info.keymap_size);
++  send_keymap (keyboard, resource);
+ 
+   notify_key_repeat_for_resource (keyboard, resource);
+ 
+@@ -1050,8 +1026,6 @@ meta_wayland_keyboard_init (MetaWaylandKeyboard *keyboard)
+   wl_list_init (&keyboard->resource_list);
+   wl_list_init (&keyboard->focus_resource_list);
+ 
+-  meta_wayland_xkb_info_init (&keyboard->xkb_info);
+-
+   keyboard->default_grab.interface = &default_keyboard_grab_interface;
+   keyboard->default_grab.keyboard = keyboard;
+   keyboard->grab = &keyboard->default_grab;
+diff --git a/src/wayland/meta-wayland-keyboard.h b/src/wayland/meta-wayland-keyboard.h
+index 39f06ef17..dba9fda0c 100644
+--- a/src/wayland/meta-wayland-keyboard.h
++++ b/src/wayland/meta-wayland-keyboard.h
+@@ -74,9 +74,8 @@ typedef struct
+ {
+   struct xkb_keymap *keymap;
+   struct xkb_state *state;
+-  int keymap_fd;
+   size_t keymap_size;
+-  char *keymap_area;
++  char *keymap_string;
+ } MetaWaylandXkbInfo;
+ 
+ struct _MetaWaylandKeyboard
+-- 
+2.19.0
+
diff --git a/SOURCES/0001-wayland-xdg-shell-Queue-frame-callbacks-on-new-actor.patch b/SOURCES/0001-wayland-xdg-shell-Queue-frame-callbacks-on-new-actor.patch
new file mode 100644
index 0000000..dd2516c
--- /dev/null
+++ b/SOURCES/0001-wayland-xdg-shell-Queue-frame-callbacks-on-new-actor.patch
@@ -0,0 +1,86 @@
+From 998e3279c24278c947832917706cf5a31b80fa26 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Wed, 25 Jul 2018 11:49:36 +0200
+Subject: [PATCH 1/8] wayland/xdg-shell: Queue frame callbacks on new actor
+ after resetting
+
+When a xdg-toplevel is reset, the window and actor are recreated, and
+all state is cleared. When this happened, we earlied out from the
+xdg-toplevel commit handler, which would mean that if the client had
+queued frame callbacks when resetting, they'd be left in the pending
+commit state, later hitting an assert as they were not handled.
+
+Fix this by queuing the frame callbacks no the new actor, so that they
+are emitted whenever the actor is eventually painted.
+
+https://gitlab.gnome.org/GNOME/mutter/issues/240
+(cherry picked from commit d791710197205028be40750d6ee4119f2b7f0e26)
+---
+ src/wayland/meta-wayland-actor-surface.c | 12 ++++++++----
+ src/wayland/meta-wayland-actor-surface.h |  3 +++
+ src/wayland/meta-wayland-xdg-shell.c     |  5 +++++
+ 3 files changed, 16 insertions(+), 4 deletions(-)
+
+diff --git a/src/wayland/meta-wayland-actor-surface.c b/src/wayland/meta-wayland-actor-surface.c
+index c10694a2a..b6dbd3326 100644
+--- a/src/wayland/meta-wayland-actor-surface.c
++++ b/src/wayland/meta-wayland-actor-surface.c
+@@ -47,10 +47,14 @@ meta_wayland_actor_surface_assigned (MetaWaylandSurfaceRole *surface_role)
+   wl_list_init (&surface->pending_frame_callback_list);
+ }
+ 
+-static void
+-queue_surface_actor_frame_callbacks (MetaWaylandSurface      *surface,
+-                                     MetaWaylandPendingState *pending)
++void
++meta_wayland_actor_surface_queue_frame_callbacks (MetaWaylandActorSurface *actor_surface,
++                                                  MetaWaylandPendingState *pending)
+ {
++  MetaWaylandSurfaceRole *surface_role =
++    META_WAYLAND_SURFACE_ROLE (actor_surface);
++  MetaWaylandSurface *surface =
++    meta_wayland_surface_role_get_surface (surface_role);
+   MetaSurfaceActorWayland *surface_actor =
+     META_SURFACE_ACTOR_WAYLAND (surface->surface_actor);
+ 
+@@ -167,7 +171,7 @@ meta_wayland_actor_surface_commit (MetaWaylandSurfaceRole  *surface_role,
+     meta_wayland_surface_role_get_surface (surface_role);
+   MetaWaylandSurface *toplevel_surface;
+ 
+-  queue_surface_actor_frame_callbacks (surface, pending);
++  meta_wayland_actor_surface_queue_frame_callbacks (actor_surface, pending);
+ 
+   toplevel_surface = meta_wayland_surface_get_toplevel (surface);
+   if (!toplevel_surface || !toplevel_surface->window)
+diff --git a/src/wayland/meta-wayland-actor-surface.h b/src/wayland/meta-wayland-actor-surface.h
+index a5cc24455..ab9b229ed 100644
+--- a/src/wayland/meta-wayland-actor-surface.h
++++ b/src/wayland/meta-wayland-actor-surface.h
+@@ -40,4 +40,7 @@ void meta_wayland_actor_surface_sync_actor_state (MetaWaylandActorSurface *actor
+ 
+ double meta_wayland_actor_surface_calculate_scale (MetaWaylandActorSurface *actor_surface);
+ 
++void meta_wayland_actor_surface_queue_frame_callbacks (MetaWaylandActorSurface *actor_surface,
++                                                       MetaWaylandPendingState *pending);
++
+ #endif /* META_WAYLAND_ACTOR_SURFACE_H */
+diff --git a/src/wayland/meta-wayland-xdg-shell.c b/src/wayland/meta-wayland-xdg-shell.c
+index 2b1a71b19..4ab638f74 100644
+--- a/src/wayland/meta-wayland-xdg-shell.c
++++ b/src/wayland/meta-wayland-xdg-shell.c
+@@ -626,7 +626,12 @@ meta_wayland_xdg_toplevel_commit (MetaWaylandSurfaceRole  *surface_role,
+ 
+   if (!surface->buffer_ref.buffer && xdg_surface_priv->first_buffer_attached)
+     {
++      MetaWaylandActorSurface *actor_surface =
++        META_WAYLAND_ACTOR_SURFACE (xdg_toplevel);
++
+       meta_wayland_xdg_surface_reset (xdg_surface);
++      meta_wayland_actor_surface_queue_frame_callbacks (actor_surface,
++                                                        pending);
+       return;
+     }
+ 
+-- 
+2.19.0
+
diff --git a/SOURCES/0001-window-Don-t-refuse-to-move-focus-to-the-grab-window.patch b/SOURCES/0001-window-Don-t-refuse-to-move-focus-to-the-grab-window.patch
new file mode 100644
index 0000000..5e1caeb
--- /dev/null
+++ b/SOURCES/0001-window-Don-t-refuse-to-move-focus-to-the-grab-window.patch
@@ -0,0 +1,39 @@
+From 513cc535f3adb4414126f2d0e18bb11bbd93d218 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
+Date: Fri, 25 May 2018 18:18:23 +0000
+Subject: [PATCH 1/3] window: Don't refuse to move focus to the grab window
+
+We refuse to move focus while a grab operation is in place. While this
+generally makes sense, there's no reason why the window that owns the
+grab shouldn't be given the regular input focus as well - we pretty
+much assume that the grab window is also the focus window anyway.
+
+In fact there's a strong reason for allowing the focus change here:
+If the grab window isn't the focus window, it probably has a modal
+transient that is focused instead, and a likely reason for the focus
+request is that the transient is being unmanaged and we must move
+the focus elsewhere.
+
+https://gitlab.gnome.org/GNOME/mutter/issues/15
+
+
+(cherry picked from commit 148da24f9510ebd23d750b8224aa0ab3a549e69e)
+---
+ src/core/window.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/core/window.c b/src/core/window.c
+index 743326c60..5b1eb5b68 100644
+--- a/src/core/window.c
++++ b/src/core/window.c
+@@ -4620,6 +4620,7 @@ meta_window_focus (MetaWindow  *window,
+               window->desc, window->input, window->take_focus);
+ 
+   if (window->display->grab_window &&
++      window->display->grab_window != window &&
+       window->display->grab_window->all_keys_grabbed &&
+       !window->display->grab_window->unmanaging)
+     {
+-- 
+2.19.0
+
diff --git a/SOURCES/0001-window-Pass-flag-to-meta_window_update_monitor-inste.patch b/SOURCES/0001-window-Pass-flag-to-meta_window_update_monitor-inste.patch
new file mode 100644
index 0000000..617c634
--- /dev/null
+++ b/SOURCES/0001-window-Pass-flag-to-meta_window_update_monitor-inste.patch
@@ -0,0 +1,218 @@
+From 30266605b58e02b017e793fc898917874590e01c Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Fri, 24 Aug 2018 12:54:50 +0200
+Subject: [PATCH 1/2] window: Pass flag to meta_window_update_monitor() instead
+ of bool
+
+The bool determines whether the call was directly from a user operation
+or not. To add more state into the call without having to add more
+boolenas, change the boolean to a flag (so far with 'none' and 'user-op'
+as possible values). No functional changes were made.
+
+https://gitlab.gnome.org/GNOME/mutter/issues/192
+
+(cherry picked from commit f4d07caa38e51d09ee73bab20334a6b5c28952d6)
+---
+ src/core/window-private.h                | 14 ++++++++++----
+ src/core/window.c                        | 24 ++++++++++++++++--------
+ src/wayland/meta-wayland-shell-surface.c |  2 +-
+ src/wayland/meta-window-wayland.c        |  8 ++++----
+ src/x11/window-x11.c                     |  6 +++---
+ 5 files changed, 34 insertions(+), 20 deletions(-)
+
+diff --git a/src/core/window-private.h b/src/core/window-private.h
+index eb86b642c..bed2fc7f6 100644
+--- a/src/core/window-private.h
++++ b/src/core/window-private.h
+@@ -121,6 +121,12 @@ typedef enum
+   META_PLACEMENT_CONSTRAINT_ADJUSTMENT_RESIZE_Y = 1 << 5,
+ } MetaPlacementConstraintAdjustment;
+ 
++typedef enum _MetaWindowUpdateMonitorFlags
++{
++  META_WINDOW_UPDATE_MONITOR_FLAGS_NONE = 0,
++  META_WINDOW_UPDATE_MONITOR_FLAGS_USER_OP = 1 << 0,
++} MetaWindowUpdateMonitorFlags;
++
+ typedef struct _MetaPlacementRule
+ {
+   MetaRectangle anchor_rect;
+@@ -549,8 +555,8 @@ struct _MetaWindowClass
+                                   cairo_surface_t **icon,
+                                   cairo_surface_t **mini_icon);
+   uint32_t (*get_client_pid)     (MetaWindow *window);
+-  void (*update_main_monitor)    (MetaWindow *window,
+-                                  gboolean    user_op);
++  void (*update_main_monitor)    (MetaWindow                   *window,
++                                  MetaWindowUpdateMonitorFlags  flags);
+   void (*main_monitor_changed)   (MetaWindow *window,
+                                   const MetaLogicalMonitor *old);
+   void (*force_restore_shortcuts) (MetaWindow         *window,
+@@ -768,8 +774,8 @@ void meta_window_activate_full (MetaWindow     *window,
+ MetaLogicalMonitor * meta_window_calculate_main_logical_monitor (MetaWindow *window);
+ 
+ MetaLogicalMonitor * meta_window_get_main_logical_monitor (MetaWindow *window);
+-void meta_window_update_monitor (MetaWindow *window,
+-                                 gboolean    user_op);
++void meta_window_update_monitor (MetaWindow                   *window,
++                                 MetaWindowUpdateMonitorFlags  flags);
+ 
+ void meta_window_set_urgent (MetaWindow *window,
+                              gboolean    urgent);
+diff --git a/src/core/window.c b/src/core/window.c
+index cc0813ac4..5547169d8 100644
+--- a/src/core/window.c
++++ b/src/core/window.c
+@@ -3804,7 +3804,8 @@ meta_window_update_for_monitors_changed (MetaWindow *window)
+ 
+   if (window->override_redirect || window->type == META_WINDOW_DESKTOP)
+     {
+-      meta_window_update_monitor (window, FALSE);
++      meta_window_update_monitor (window,
++                                  META_WINDOW_UPDATE_MONITOR_FLAGS_NONE);
+       return;
+     }
+ 
+@@ -3839,18 +3840,19 @@ meta_window_update_for_monitors_changed (MetaWindow *window)
+     }
+   else
+     {
+-      meta_window_update_monitor (window, FALSE);
++      meta_window_update_monitor (window,
++                                  META_WINDOW_UPDATE_MONITOR_FLAGS_NONE);
+     }
+ }
+ 
+ void
+-meta_window_update_monitor (MetaWindow *window,
+-                            gboolean    user_op)
++meta_window_update_monitor (MetaWindow                   *window,
++                            MetaWindowUpdateMonitorFlags  flags)
+ {
+   const MetaLogicalMonitor *old;
+ 
+   old = window->monitor;
+-  META_WINDOW_GET_CLASS (window)->update_main_monitor (window, user_op);
++  META_WINDOW_GET_CLASS (window)->update_main_monitor (window, flags);
+   if (old != window->monitor)
+     {
+       meta_window_on_all_workspaces_changed (window);
+@@ -3864,7 +3866,8 @@ meta_window_update_monitor (MetaWindow *window,
+        * That should be handled by explicitly moving the window before changing the
+        * workspace.
+        */
+-      if (meta_prefs_get_workspaces_only_on_primary () && user_op &&
++      if (meta_prefs_get_workspaces_only_on_primary () &&
++          flags & META_WINDOW_UPDATE_MONITOR_FLAGS_USER_OP &&
+           meta_window_is_on_primary_monitor (window)  &&
+           window->screen->active_workspace != window->workspace)
+         meta_window_change_workspace (window, window->screen->active_workspace);
+@@ -3907,6 +3910,7 @@ meta_window_move_resize_internal (MetaWindow          *window,
+   MetaRectangle constrained_rect;
+   MetaMoveResizeResultFlags result = 0;
+   gboolean moved_or_resized = FALSE;
++  MetaWindowUpdateMonitorFlags update_monitor_flags;
+ 
+   g_return_if_fail (!window->override_redirect);
+ 
+@@ -4007,13 +4011,17 @@ meta_window_move_resize_internal (MetaWindow          *window,
+                                             did_placement);
+     }
+ 
++  update_monitor_flags = META_WINDOW_UPDATE_MONITOR_FLAGS_NONE;
++  if (flags & META_MOVE_RESIZE_USER_ACTION)
++    update_monitor_flags |= META_WINDOW_UPDATE_MONITOR_FLAGS_USER_OP;
++
+   if (window->monitor)
+     {
+       guint old_output_winsys_id;
+ 
+       old_output_winsys_id = window->monitor->winsys_id;
+ 
+-      meta_window_update_monitor (window, flags & META_MOVE_RESIZE_USER_ACTION);
++      meta_window_update_monitor (window, update_monitor_flags);
+ 
+       if (old_output_winsys_id != window->monitor->winsys_id &&
+           flags & META_MOVE_RESIZE_MOVE_ACTION && flags & META_MOVE_RESIZE_USER_ACTION)
+@@ -4021,7 +4029,7 @@ meta_window_move_resize_internal (MetaWindow          *window,
+     }
+   else
+     {
+-      meta_window_update_monitor (window, flags & META_MOVE_RESIZE_USER_ACTION);
++      meta_window_update_monitor (window, update_monitor_flags);
+     }
+ 
+   if ((result & META_MOVE_RESIZE_RESULT_FRAME_SHAPE_CHANGED) && window->frame_bounds)
+diff --git a/src/wayland/meta-wayland-shell-surface.c b/src/wayland/meta-wayland-shell-surface.c
+index b5f3acacf..72b3557c7 100644
+--- a/src/wayland/meta-wayland-shell-surface.c
++++ b/src/wayland/meta-wayland-shell-surface.c
+@@ -83,7 +83,7 @@ meta_wayland_shell_surface_set_window (MetaWaylandShellSurface *shell_surface,
+     meta_wayland_surface_role_get_surface (surface_role);
+ 
+   meta_wayland_surface_set_window (surface, window);
+-  meta_window_update_monitor (window, FALSE);
++  meta_window_update_monitor (window, META_WINDOW_UPDATE_MONITOR_FLAGS_NONE);
+ }
+ 
+ void
+diff --git a/src/wayland/meta-window-wayland.c b/src/wayland/meta-window-wayland.c
+index 8bafb252f..544c60b5b 100644
+--- a/src/wayland/meta-window-wayland.c
++++ b/src/wayland/meta-window-wayland.c
+@@ -371,8 +371,8 @@ scale_rect_size (MetaRectangle *rect,
+ }
+ 
+ static void
+-meta_window_wayland_update_main_monitor (MetaWindow *window,
+-                                         gboolean    user_op)
++meta_window_wayland_update_main_monitor (MetaWindow                   *window,
++                                         MetaWindowUpdateMonitorFlags  flags)
+ {
+   MetaBackend *backend = meta_get_backend ();
+   MetaMonitorManager *monitor_manager =
+@@ -392,7 +392,7 @@ meta_window_wayland_update_main_monitor (MetaWindow *window,
+   toplevel_window = meta_wayland_surface_get_toplevel_window (window->surface);
+   if (toplevel_window != window)
+     {
+-      meta_window_update_monitor (toplevel_window, user_op);
++      meta_window_update_monitor (window, flags);
+       window->monitor = toplevel_window->monitor;
+       return;
+     }
+@@ -413,7 +413,7 @@ meta_window_wayland_update_main_monitor (MetaWindow *window,
+       return;
+     }
+ 
+-  if (!user_op)
++  if (flags & META_WINDOW_UPDATE_MONITOR_FLAGS_USER_OP)
+     {
+       window->monitor = to;
+       return;
+diff --git a/src/x11/window-x11.c b/src/x11/window-x11.c
+index e9bd8ae65..e1c34412d 100644
+--- a/src/x11/window-x11.c
++++ b/src/x11/window-x11.c
+@@ -1483,8 +1483,8 @@ meta_window_x11_update_icon (MetaWindow       *window,
+ }
+ 
+ static void
+-meta_window_x11_update_main_monitor (MetaWindow *window,
+-                                     gboolean    user_op)
++meta_window_x11_update_main_monitor (MetaWindow                   *window,
++                                     MetaWindowUpdateMonitorFlags  flags)
+ {
+   window->monitor = meta_window_calculate_main_logical_monitor (window);
+ }
+@@ -3314,7 +3314,7 @@ meta_window_x11_configure_notify (MetaWindow      *window,
+   priv->client_rect = window->rect;
+   window->buffer_rect = window->rect;
+ 
+-  meta_window_update_monitor (window, FALSE);
++  meta_window_update_monitor (window, META_WINDOW_UPDATE_MONITOR_FLAGS_NONE);
+ 
+   /* Whether an override-redirect window is considered fullscreen depends
+    * on its geometry.
+-- 
+2.19.0
+
diff --git a/SOURCES/0001-window-Really-force-update-monitor-on-hot-plugs.patch b/SOURCES/0001-window-Really-force-update-monitor-on-hot-plugs.patch
new file mode 100644
index 0000000..1969bd1
--- /dev/null
+++ b/SOURCES/0001-window-Really-force-update-monitor-on-hot-plugs.patch
@@ -0,0 +1,110 @@
+From fa495286a1037d610051ab49f0aa1e8b20e38066 Mon Sep 17 00:00:00 2001
+From: Olivier Fourdan <ofourdan@redhat.com>
+Date: Tue, 6 Nov 2018 13:35:31 +0100
+Subject: [PATCH] window: Really force update monitor on hot plugs
+
+Commit 8d3e05305 ("window: Force update monitor on hot plugs") added the
+flag `META_WINDOW_UPDATE_MONITOR_FLAGS_FORCE` passed to
+`update_monitor()` from `update_for_monitors_changed()`.
+
+However, `update_for_monitors_changed()` may choose to call another code
+path to `move_between_rects()` and `meta_window_move_resize_internal()`
+eventually.
+
+As `meta_window_move_resize_internal()` does not use the "force" flag,
+we may still end up in case where the window->monitor is left unchanged.
+
+To avoid that problem, add a new `MetaMoveResizeFlags` that
+`update_for_monitors_changed()` can use to force the monitor update from
+`meta_window_move_resize_internal()`.
+
+Fixes: 8d3e05305 ("window: Force update monitor on hot plugs")
+Closes: https://gitlab.gnome.org/GNOME/mutter/issues/189
+---
+ src/core/window-private.h |  1 +
+ src/core/window.c         | 16 +++++++++++++---
+ 2 files changed, 14 insertions(+), 3 deletions(-)
+
+diff --git a/src/core/window-private.h b/src/core/window-private.h
+index 6fc943f72..4cc05d934 100644
+--- a/src/core/window-private.h
++++ b/src/core/window-private.h
+@@ -80,6 +80,7 @@ typedef enum
+   META_MOVE_RESIZE_UNMAXIMIZE = 1 << 6,
+   META_MOVE_RESIZE_FORCE_MOVE = 1 << 7,
+   META_MOVE_RESIZE_WAYLAND_STATE_CHANGED = 1 << 8,
++  META_MOVE_RESIZE_FORCE_UPDATE_MONITOR = 1 << 9,
+ } MetaMoveResizeFlags;
+ 
+ typedef enum
+diff --git a/src/core/window.c b/src/core/window.c
+index d00719e23..4d9e51d8c 100644
+--- a/src/core/window.c
++++ b/src/core/window.c
+@@ -120,6 +120,7 @@ static gboolean queue_calc_showing_func (MetaWindow *window,
+                                          void       *data);
+ 
+ static void meta_window_move_between_rects (MetaWindow          *window,
++                                            MetaMoveResizeFlags  move_resize_flags,
+                                             const MetaRectangle *old_area,
+                                             const MetaRectangle *new_area);
+ 
+@@ -3864,6 +3865,7 @@ meta_window_update_for_monitors_changed (MetaWindow *window)
+        * monitors changed and the same index could be refereing
+        * to a different monitor. */
+       meta_window_move_between_rects (window,
++                                      META_MOVE_RESIZE_FORCE_UPDATE_MONITOR,
+                                       &old->rect,
+                                       &new->rect);
+     }
+@@ -4045,6 +4047,8 @@ meta_window_move_resize_internal (MetaWindow          *window,
+   update_monitor_flags = META_WINDOW_UPDATE_MONITOR_FLAGS_NONE;
+   if (flags & META_MOVE_RESIZE_USER_ACTION)
+     update_monitor_flags |= META_WINDOW_UPDATE_MONITOR_FLAGS_USER_OP;
++  if (flags & META_MOVE_RESIZE_FORCE_UPDATE_MONITOR)
++    update_monitor_flags |= META_WINDOW_UPDATE_MONITOR_FLAGS_FORCE;
+ 
+   if (window->monitor)
+     {
+@@ -4104,6 +4108,7 @@ meta_window_move_frame (MetaWindow *window,
+ 
+ static void
+ meta_window_move_between_rects (MetaWindow  *window,
++                                MetaMoveResizeFlags  move_resize_flags,
+                                 const MetaRectangle *old_area,
+                                 const MetaRectangle *new_area)
+ {
+@@ -4127,7 +4132,12 @@ meta_window_move_between_rects (MetaWindow  *window,
+   window->saved_rect.x = window->unconstrained_rect.x;
+   window->saved_rect.y = window->unconstrained_rect.y;
+ 
+-  meta_window_move_resize_now (window);
++  meta_window_move_resize_internal (window,
++                                    move_resize_flags |
++                                    META_MOVE_RESIZE_MOVE_ACTION |
++                                    META_MOVE_RESIZE_RESIZE_ACTION,
++                                    NorthWestGravity,
++                                    window->unconstrained_rect);
+ }
+ 
+ /**
+@@ -4188,14 +4198,14 @@ meta_window_move_to_monitor (MetaWindow  *window,
+       window->unconstrained_rect.height == 0 ||
+       !meta_rectangle_overlap (&window->unconstrained_rect, &old_area))
+     {
+-      meta_window_move_between_rects (window, NULL, &new_area);
++      meta_window_move_between_rects (window, 0, NULL, &new_area);
+     }
+   else
+     {
+       if (monitor == window->monitor->number)
+         return;
+ 
+-      meta_window_move_between_rects (window, &old_area, &new_area);
++      meta_window_move_between_rects (window, 0, &old_area, &new_area);
+     }
+ 
+   window->preferred_output_winsys_id = window->monitor->winsys_id;
+-- 
+2.20.1
+
diff --git a/SOURCES/0001-window-Return-1-if-meta_window_get_monitor-is-called.patch b/SOURCES/0001-window-Return-1-if-meta_window_get_monitor-is-called.patch
new file mode 100644
index 0000000..4dd4bf0
--- /dev/null
+++ b/SOURCES/0001-window-Return-1-if-meta_window_get_monitor-is-called.patch
@@ -0,0 +1,41 @@
+From 007864ac645715c0588b0dd5ae88c1e2900fe2a4 Mon Sep 17 00:00:00 2001
+From: Sam Spilsbury <sam@endlessm.com>
+Date: Tue, 10 Oct 2017 16:39:40 +0000
+Subject: [PATCH] window: Return -1 if meta_window_get_monitor is called on an
+ unmanaged window
+
+As opposed to crashing. In this case, letting the caller deal with
+it is the best policy, since this is public API.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=788834
+
+
+(cherry picked from commit 8626c69c2ffa806f854041a3738340c6fb20c112)
+---
+ src/core/window.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/src/core/window.c b/src/core/window.c
+index d69720a4d..e68f5245c 100644
+--- a/src/core/window.c
++++ b/src/core/window.c
+@@ -3752,11 +3752,15 @@ maybe_move_attached_dialog (MetaWindow *window,
+  *
+  * Gets index of the monitor that this window is on.
+  *
+- * Return Value: The index of the monitor in the screens monitor list
++ * Return Value: The index of the monitor in the screens monitor list, or -1
++ * if the window has been recently unmanaged and does not have a monitor.
+  */
+ int
+ meta_window_get_monitor (MetaWindow *window)
+ {
++  if (!window->monitor)
++    return -1;
++
+   return window->monitor->number;
+ }
+ 
+-- 
+2.19.0
+
diff --git a/SOURCES/0001-window-actor-Special-case-shaped-Java-windows.patch b/SOURCES/0001-window-actor-Special-case-shaped-Java-windows.patch
new file mode 100644
index 0000000..0f2f285
--- /dev/null
+++ b/SOURCES/0001-window-actor-Special-case-shaped-Java-windows.patch
@@ -0,0 +1,35 @@
+From ec74bb9607f5187f8fda37dba1882efe1693e24f Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
+Date: Fri, 12 May 2017 13:40:31 +0200
+Subject: [PATCH] window-actor: Special-case shaped Java windows
+
+OpenJDK wrongly assumes that shaping a window implies no shadows.
+They got lucky until commit b975676c changed the fallback case,
+but now their compliance tests are broken. Make them happy again
+by special-casing shaped Java windows.
+---
+ src/compositor/meta-window-actor.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c
+index 381d3ea85..b67fb3ce1 100644
+--- a/src/compositor/meta-window-actor.c
++++ b/src/compositor/meta-window-actor.c
+@@ -865,6 +865,14 @@ meta_window_actor_has_shadow (MetaWindowActor *self)
+   if (priv->window->has_custom_frame_extents)
+     return FALSE;
+ 
++  /*
++   * OpenJDK wrongly assumes that shaping a window implies no compositor
++   * shadows; make its compliance tests happy to give it what it wants ...
++   */
++  if (g_strcmp0 (priv->window->res_name, "sun-awt-X11-XWindowPeer") == 0 &&
++      priv->window->shape_region != NULL)
++    return FALSE;
++
+   /*
+    * Generate shadows for all other windows.
+    */
+-- 
+2.20.1
+
diff --git a/SOURCES/0001-window-actor-Use-actual-image-size-for-capture.patch b/SOURCES/0001-window-actor-Use-actual-image-size-for-capture.patch
new file mode 100644
index 0000000..c05de7d
--- /dev/null
+++ b/SOURCES/0001-window-actor-Use-actual-image-size-for-capture.patch
@@ -0,0 +1,86 @@
+From 1587447b47130affe437acce4770f82d13176227 Mon Sep 17 00:00:00 2001
+From: Olivier Fourdan <ofourdan@redhat.com>
+Date: Tue, 22 Jan 2019 10:22:27 +0100
+Subject: [PATCH] window-actor: Use actual image size for capture
+
+Previously, the clipping rectangle passed to
+`meta_surface_actor_get_image()` was updated with the actual texture
+size, but recent changes in `meta_shaped_texture_get_image()` now keep
+the caller's clipping rectangle unchanged.
+
+The implementation of `meta_window_actor_capture_into()` was relying on
+the old behavior of updating the passed clipping rectangle, but now that
+it's kept unchanged, the actual clipping rectangle used to copy the data
+is wrong, which causes either a distorded image or worse, a crash of
+mutter.
+
+Use the resulting cairo image size to copy the data instead of the
+clipping rectangle to avoid the issue and get the expected size.
+
+Fixes: https://gitlab.gnome.org/GNOME/mutter/issues/442
+---
+ src/compositor/meta-window-actor.c | 18 ++++++++++--------
+ 1 file changed, 10 insertions(+), 8 deletions(-)
+
+diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c
+index 396fef1..756e6c0 100644
+--- a/src/compositor/meta-window-actor.c
++++ b/src/compositor/meta-window-actor.c
+@@ -2260,36 +2260,38 @@ meta_window_actor_capture_into (MetaScreenCastWindow *screen_cast_window,
+ {
+   MetaWindowActor *window_actor = META_WINDOW_ACTOR (screen_cast_window);
+   cairo_surface_t *image;
+-  MetaRectangle clip_rect;
+   uint8_t *cr_data;
+   int cr_stride;
++  int cr_width;
++  int cr_height;
+   int bpp = 4;
+ 
+   if (meta_window_actor_is_destroyed (window_actor))
+     return;
+ 
+-  clip_rect = *bounds;
+-  image = meta_surface_actor_get_image (window_actor->priv->surface, &clip_rect);
++  image = meta_surface_actor_get_image (window_actor->priv->surface, 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);
+   cr_stride = cairo_image_surface_get_stride (image);
+ 
+-  if (clip_rect.width < bounds->width || clip_rect.height < bounds->height)
++  if (cr_width < bounds->width || cr_height < bounds->height)
+     {
+       uint8_t *src, *dst;
+       src = cr_data;
+       dst = data;
+ 
+-      for (int i = 0; i < clip_rect.height; i++)
++      for (int i = 0; i < cr_height; i++)
+         {
+           memcpy (dst, src, cr_stride);
+-          if (clip_rect.width < bounds->width)
++          if (cr_width < bounds->width)
+             memset (dst + cr_stride, 0, (bounds->width * bpp) - cr_stride);
+ 
+           src += cr_stride;
+           dst += bounds->width * bpp;
+         }
+ 
+-      for (int i = clip_rect.height; i < bounds->height; i++)
++      for (int i = cr_height; i < bounds->height; i++)
+         {
+           memset (dst, 0, bounds->width * bpp);
+           dst += bounds->width * bpp;
+@@ -2297,7 +2299,7 @@ meta_window_actor_capture_into (MetaScreenCastWindow *screen_cast_window,
+     }
+   else
+     {
+-      memcpy (data, cr_data, clip_rect.height * cr_stride);
++      memcpy (data, cr_data, cr_height * cr_stride);
+     }
+ 
+   cairo_surface_destroy (image);
+-- 
+2.20.1
+
diff --git a/SOURCES/0001-window-unmanage-dialog-when-clearing-transient_for.patch b/SOURCES/0001-window-unmanage-dialog-when-clearing-transient_for.patch
new file mode 100644
index 0000000..09aacc8
--- /dev/null
+++ b/SOURCES/0001-window-unmanage-dialog-when-clearing-transient_for.patch
@@ -0,0 +1,46 @@
+From 398f03bd0fcd4eb31f9a0fc4990c21db4072488c Mon Sep 17 00:00:00 2001
+From: Olivier Fourdan <ofourdan@redhat.com>
+Date: Tue, 10 Jul 2018 14:42:42 +0000
+Subject: [PATCH] window: unmanage dialog when clearing transient_for
+
+On Wayland, xdg-foreign would leave a modal dialog managed even after
+the imported surface is destroyed.
+
+This is sub-optimal and this breaks the atomic relationship one would
+expect between the parent and its modal dialog.
+
+Make sure we unmanage the dialog if transient_for is unset even for
+Wayland native windows.
+
+Related: https://gitlab.gnome.org/GNOME/mutter/issues/174
+Related: https://gitlab.gnome.org/GNOME/mutter/issues/221
+
+
+(cherry picked from commit b443bd42ac738092817addf48e0b363d140cad26)
+---
+ src/core/window.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/src/core/window.c b/src/core/window.c
+index e68f5245c..a1f4aa4f3 100644
+--- a/src/core/window.c
++++ b/src/core/window.c
+@@ -7942,7 +7942,15 @@ meta_window_set_transient_for (MetaWindow *window,
+             }
+         }
+     }
++  else if (window->attached && parent == NULL)
++    {
++      guint32 timestamp;
+ 
++      timestamp =
++        meta_display_get_current_time_roundtrip (window->display);
++      meta_window_unmanage (window, timestamp);
++      return;
++    }
+   /* We know this won't create a reference cycle because we check for loops */
+   g_clear_object (&window->transient_for);
+   window->transient_for = parent ? g_object_ref (parent) : NULL;
+-- 
+2.19.0
+
diff --git a/SOURCES/0001-window-wayland-Always-update-monitor-for-non-user-op.patch b/SOURCES/0001-window-wayland-Always-update-monitor-for-non-user-op.patch
new file mode 100644
index 0000000..22318d4
--- /dev/null
+++ b/SOURCES/0001-window-wayland-Always-update-monitor-for-non-user-op.patch
@@ -0,0 +1,45 @@
+From d74a046060df81ea766e999b4110e926b121cf2e Mon Sep 17 00:00:00 2001
+From: Olivier Fourdan <ofourdan@redhat.com>
+Date: Mon, 18 Jun 2018 10:39:11 +0000
+Subject: [PATCH] window/wayland: Always update monitor for non-user ops
+
+meta_window_wayland_update_main_monitor() would skip the monitor update
+if the difference in scale between the old and the new monitor would
+cause another monitor change.
+
+While this is suitable when the monitor change results from a user
+interactively moving the surface between monitors of different scales,
+this can leave dangling pointers to freed monitors when this is
+triggered by a change of monitor configuration.
+
+Make sure we update the monitor unconditionally if not from a user
+operation.
+
+Closes: https://gitlab.gnome.org/GNOME/mutter/issues/189
+
+
+(cherry picked from commit a3da4b8d5bd217c0262fd9361036877d155a300f)
+---
+ src/wayland/meta-window-wayland.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/src/wayland/meta-window-wayland.c b/src/wayland/meta-window-wayland.c
+index 705bc4981..8bafb252f 100644
+--- a/src/wayland/meta-window-wayland.c
++++ b/src/wayland/meta-window-wayland.c
+@@ -413,6 +413,12 @@ meta_window_wayland_update_main_monitor (MetaWindow *window,
+       return;
+     }
+ 
++  if (!user_op)
++    {
++      window->monitor = to;
++      return;
++    }
++
+   from_scale = meta_logical_monitor_get_scale (from);
+   to_scale = meta_logical_monitor_get_scale (to);
+ 
+-- 
+2.19.0
+
diff --git a/SOURCES/0001-window-wayland-Don-t-recursive-indefinitely-when-upd.patch b/SOURCES/0001-window-wayland-Don-t-recursive-indefinitely-when-upd.patch
new file mode 100644
index 0000000..b8b7ffd
--- /dev/null
+++ b/SOURCES/0001-window-wayland-Don-t-recursive-indefinitely-when-upd.patch
@@ -0,0 +1,35 @@
+From 1514880e92841d51a557edd505555de3e4a5b6ae Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Mon, 27 Aug 2018 17:49:52 +0200
+Subject: [PATCH] window/wayland: Don't recursive indefinitely when updating
+ monitor
+
+When we update the main monitor, there is a rule that makes it so that
+popup windows use the same main monitor as their parent. In the commit
+f4d07caa38e51d09ee73bab20334a6b5c28952d6 the call that updates and
+fetches the main monitor of the toplevel accidentally changed to update
+from itself, causing a indefinite recursion eventually resulting in a
+crash.
+
+Closes: https://gitlab.gnome.org/GNOME/mutter/issues/279
+(cherry picked from commit e191c21e04cfaa560f8dd51f4f91013af98ccf4e)
+---
+ src/wayland/meta-window-wayland.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/wayland/meta-window-wayland.c b/src/wayland/meta-window-wayland.c
+index e837215de..241e6b61c 100644
+--- a/src/wayland/meta-window-wayland.c
++++ b/src/wayland/meta-window-wayland.c
+@@ -392,7 +392,7 @@ meta_window_wayland_update_main_monitor (MetaWindow                   *window,
+   toplevel_window = meta_wayland_surface_get_toplevel_window (window->surface);
+   if (toplevel_window != window)
+     {
+-      meta_window_update_monitor (window, flags);
++      meta_window_update_monitor (toplevel_window, flags);
+       window->monitor = toplevel_window->monitor;
+       return;
+     }
+-- 
+2.19.0
+
diff --git a/SOURCES/0001-x11-window-props-Do-not-convert-WM_NAME.patch b/SOURCES/0001-x11-window-props-Do-not-convert-WM_NAME.patch
new file mode 100644
index 0000000..4542944
--- /dev/null
+++ b/SOURCES/0001-x11-window-props-Do-not-convert-WM_NAME.patch
@@ -0,0 +1,38 @@
+From 432a509346c0727b21139fdf5c923b78cf818c4e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
+Date: Tue, 9 Oct 2018 00:40:33 +0200
+Subject: [PATCH 1/2] x11/window-props: Do not convert WM_NAME
+
+The WM_NAME property is of type TEXT_PROPERTY, which is supposed to be
+returned as UTF-8. Commit 840378ae68 broke that assumption, resulting
+in crashes with non-UTF8 locales; however the "fix" of converting from
+LATIN1 to UTF8 is wrong as well, as the conversion will spit out garbage
+when the input encoding isn't actually LATIN1.
+
+Now that the original issue in text_property_to_utf8() has been fixed,
+we can simply revert the relevant bits of commit d62491f46eba748e.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/227
+---
+ src/x11/window-props.c | 5 +----
+ 1 file changed, 1 insertion(+), 4 deletions(-)
+
+diff --git a/src/x11/window-props.c b/src/x11/window-props.c
+index 2f4dfeb31d..7c6b25fdbf 100644
+--- a/src/x11/window-props.c
++++ b/src/x11/window-props.c
+@@ -645,10 +645,7 @@ reload_wm_name (MetaWindow    *window,
+ 
+   if (value->type != META_PROP_VALUE_INVALID)
+     {
+-      g_autofree gchar *title = g_convert (value->v.str, -1,
+-                                           "UTF-8", "LATIN1",
+-                                           NULL, NULL, NULL);
+-      set_window_title (window, title);
++      set_window_title (window, value->v.str);
+ 
+       meta_verbose ("Using WM_NAME for new title of %s: \"%s\"\n",
+                     window->desc, window->title);
+-- 
+2.17.1
+
diff --git a/SOURCES/0002-backend-switch-to-using-generated-logind-proxy.patch b/SOURCES/0002-backend-switch-to-using-generated-logind-proxy.patch
new file mode 100644
index 0000000..e6bb1a1
--- /dev/null
+++ b/SOURCES/0002-backend-switch-to-using-generated-logind-proxy.patch
@@ -0,0 +1,369 @@
+From 76c34858d18c7ec2bdef6ceeee22b047a0a1bdcb Mon Sep 17 00:00:00 2001
+From: Ray Strode <rstrode@redhat.com>
+Date: Mon, 14 Jan 2019 11:11:01 -0500
+Subject: [PATCH 2/9] backend: switch to using generated logind proxy
+
+Right now we listen to prepare-for-sleep using
+raw gdbus calls.
+
+This commit switches it over to use a generated
+proxy, which will become useful in a future commit,
+for adding suspending inhibitors.
+---
+ src/backends/meta-backend.c    | 60 ++++++++++++++++++++++------------
+ src/org.freedesktop.login1.xml | 14 ++++++++
+ 2 files changed, 53 insertions(+), 21 deletions(-)
+
+diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c
+index 28f1cd92f..2f090e233 100644
+--- a/src/backends/meta-backend.c
++++ b/src/backends/meta-backend.c
+@@ -8,60 +8,61 @@
+  * published by the Free Software Foundation; either version 2 of the
+  * License, or (at your option) any later version.
+  *
+  * This program 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
+  * General Public License for more details.
+  *
+  * You should have received a copy of the GNU General Public License
+  * along with this program; if not, write to the Free Software
+  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+  * 02111-1307, USA.
+  *
+  * Written by:
+  *     Jasper St. Pierre <jstpierre@mecheye.net>
+  */
+ 
+ #include "config.h"
+ 
+ #include <stdlib.h>
+ 
+ #include <clutter/clutter-mutter.h>
+ #include <meta/meta-backend.h>
+ #include <meta/main.h>
+ #include <meta/util.h>
+ #include "meta-backend-private.h"
+ #include "meta-input-settings-private.h"
+ #include "backends/x11/meta-backend-x11.h"
+ #include "meta-cursor-tracker-private.h"
+ #include "meta-stage-private.h"
++#include "meta-dbus-login1.h"
+ 
+ #ifdef HAVE_REMOTE_DESKTOP
+ #include "backends/meta-dbus-session-watcher.h"
+ #include "backends/meta-screen-cast.h"
+ #include "backends/meta-remote-access-controller-private.h"
+ #include "backends/meta-remote-desktop.h"
+ #endif
+ 
+ #ifdef HAVE_NATIVE_BACKEND
+ #include "backends/native/meta-backend-native.h"
+ #endif
+ 
+ #include "backends/meta-idle-monitor-private.h"
+ #include "backends/meta-logical-monitor.h"
+ #include "backends/meta-monitor-manager-dummy.h"
+ #include "backends/meta-settings-private.h"
+ 
+ #define META_IDLE_MONITOR_CORE_DEVICE 0
+ 
+ enum
+ {
+   KEYMAP_CHANGED,
+   KEYMAP_LAYOUT_GROUP_CHANGED,
+   LAST_DEVICE_CHANGED,
+ 
+   N_SIGNALS
+ };
+ 
+ static guint signals[N_SIGNALS];
+ 
+@@ -87,93 +88,97 @@ struct _MetaBackendPrivate
+   MetaMonitorManager *monitor_manager;
+   MetaOrientationManager *orientation_manager;
+   MetaCursorTracker *cursor_tracker;
+   MetaCursorRenderer *cursor_renderer;
+   MetaInputSettings *input_settings;
+   MetaRenderer *renderer;
+   MetaEgl *egl;
+   MetaSettings *settings;
+ #ifdef HAVE_REMOTE_DESKTOP
+   MetaRemoteAccessController *remote_access_controller;
+   MetaDbusSessionWatcher *dbus_session_watcher;
+   MetaScreenCast *screen_cast;
+   MetaRemoteDesktop *remote_desktop;
+ #endif
+ 
+   ClutterBackend *clutter_backend;
+   ClutterActor *stage;
+ 
+   gboolean is_pointer_position_initialized;
+ 
+   guint device_update_idle_id;
+ 
+   GHashTable *device_monitors;
+ 
+   int current_device_id;
+ 
+   MetaPointerConstraint *client_pointer_constraint;
+   MetaDnd *dnd;
+ 
+   UpClient *up_client;
+-  guint sleep_signal_id;
+   GCancellable *cancellable;
+   GDBusConnection *system_bus;
++
++  Login1Manager *logind_proxy;
++  int            inhibit_sleep_fd;
+ };
+ typedef struct _MetaBackendPrivate MetaBackendPrivate;
+ 
+ static void
+ initable_iface_init (GInitableIface *initable_iface);
+ 
++
++static void prepare_for_sleep_cb (MetaBackend *backend,
++                                  gboolean     suspending);
++
+ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (MetaBackend, meta_backend, G_TYPE_OBJECT,
+                                   G_ADD_PRIVATE (MetaBackend)
+                                   G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+                                                          initable_iface_init));
+ 
+ static void
+ meta_backend_finalize (GObject *object)
+ {
+   MetaBackend *backend = META_BACKEND (object);
+   MetaBackendPrivate *priv = meta_backend_get_instance_private (backend);
+ 
+   g_clear_object (&priv->monitor_manager);
+   g_clear_object (&priv->orientation_manager);
+   g_clear_object (&priv->input_settings);
+ #ifdef HAVE_REMOTE_DESKTOP
+   g_clear_object (&priv->remote_desktop);
+   g_clear_object (&priv->screen_cast);
+   g_clear_object (&priv->dbus_session_watcher);
+   g_clear_object (&priv->remote_access_controller);
+ #endif
+ 
+   g_object_unref (priv->up_client);
+-  if (priv->sleep_signal_id)
+-    g_dbus_connection_signal_unsubscribe (priv->system_bus, priv->sleep_signal_id);
+   g_cancellable_cancel (priv->cancellable);
+   g_clear_object (&priv->cancellable);
+   g_clear_object (&priv->system_bus);
+ 
+   if (priv->device_update_idle_id)
+     g_source_remove (priv->device_update_idle_id);
+ 
+   g_hash_table_destroy (priv->device_monitors);
+ 
+   g_clear_object (&priv->settings);
+ 
+   G_OBJECT_CLASS (meta_backend_parent_class)->finalize (object);
+ }
+ 
+ static void
+ meta_backend_sync_screen_size (MetaBackend *backend)
+ {
+   MetaBackendPrivate *priv = meta_backend_get_instance_private (backend);
+   int width, height;
+ 
+   meta_monitor_manager_get_screen_size (priv->monitor_manager, &width, &height);
+ 
+   META_BACKEND_GET_CLASS (backend)->update_screen_size (backend, width, height);
+ }
+ 
+ static void
+ reset_pointer_position (MetaBackend *backend)
+ {
+   MetaBackendPrivate *priv = meta_backend_get_instance_private (backend);
+   MetaMonitorManager *monitor_manager = priv->monitor_manager;
+@@ -553,101 +558,114 @@ meta_backend_class_init (MetaBackendClass *klass)
+ static MetaMonitorManager *
+ meta_backend_create_monitor_manager (MetaBackend *backend,
+                                      GError     **error)
+ {
+   if (g_getenv ("META_DUMMY_MONITORS"))
+     return g_object_new (META_TYPE_MONITOR_MANAGER_DUMMY, NULL);
+ 
+   return META_BACKEND_GET_CLASS (backend)->create_monitor_manager (backend,
+                                                                    error);
+ }
+ 
+ static MetaRenderer *
+ meta_backend_create_renderer (MetaBackend *backend,
+                               GError     **error)
+ {
+   return META_BACKEND_GET_CLASS (backend)->create_renderer (backend, error);
+ }
+ 
+ static void
+ lid_is_closed_changed_cb (UpClient   *client,
+                           GParamSpec *pspec,
+                           gpointer    user_data)
+ {
+   if (up_client_get_lid_is_closed (client))
+     return;
+ 
+   meta_idle_monitor_reset_idletime (meta_idle_monitor_get_core ());
+ }
+ 
+ static void
+-prepare_for_sleep_cb (GDBusConnection *connection,
+-                      const gchar     *sender_name,
+-                      const gchar     *object_path,
+-                      const gchar     *interface_name,
+-                      const gchar     *signal_name,
+-                      GVariant        *parameters,
+-                      gpointer         user_data)
++prepare_for_sleep_cb (MetaBackend *backend,
++                      gboolean     suspending)
+ {
+   gboolean suspending;
+ 
+   g_variant_get (parameters, "(b)", &suspending);
+   if (suspending)
+     return;
+   meta_idle_monitor_reset_idletime (meta_idle_monitor_get_core ());
+ }
+ 
++static Login1Manager *
++get_logind_proxy (GCancellable *cancellable,
++                  GError      **error)
++{
++  Login1Manager *proxy;
++
++  proxy =
++    login1_manager_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
++                                           G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
++                                           "org.freedesktop.login1",
++                                           "/org/freedesktop/login1",
++                                           cancellable, error);
++  if (!proxy)
++    g_prefix_error (error, "Could not get logind proxy: ");
++
++  return proxy;
++}
++
+ static void
+ system_bus_gotten_cb (GObject      *object,
+                       GAsyncResult *res,
+                       gpointer      user_data)
+ {
+   MetaBackendPrivate *priv;
++  g_autoptr (GError) error = NULL;
+   GDBusConnection *bus;
+ 
+   bus = g_bus_get_finish (res, NULL);
+   if (!bus)
+     return;
+ 
+   priv = meta_backend_get_instance_private (user_data);
+   priv->system_bus = bus;
+-  priv->sleep_signal_id =
+-    g_dbus_connection_signal_subscribe (priv->system_bus,
+-                                        "org.freedesktop.login1",
+-                                        "org.freedesktop.login1.Manager",
+-                                        "PrepareForSleep",
+-                                        "/org/freedesktop/login1",
+-                                        NULL,
+-                                        G_DBUS_SIGNAL_FLAGS_NONE,
+-                                        prepare_for_sleep_cb,
+-                                        NULL,
+-                                        NULL);
++  priv->logind_proxy = get_logind_proxy (priv->cancellable, &error);
++
++  if (!priv->logind_proxy)
++    g_warning ("Failed to get logind proxy: %s", error->message);
++
++  g_signal_connect_object (priv->logind_proxy,
++                           "prepare-for-sleep",
++                           G_CALLBACK (prepare_for_sleep_cb),
++                           user_data,
++                           G_CONNECT_SWAPPED);
+ }
+ 
+ static gboolean
+ meta_backend_initable_init (GInitable     *initable,
+                             GCancellable  *cancellable,
+                             GError       **error)
+ {
+   MetaBackend *backend = META_BACKEND (initable);
+   MetaBackendPrivate *priv = meta_backend_get_instance_private (backend);
+ 
+   priv->settings = meta_settings_new (backend);
+ 
+   priv->egl = g_object_new (META_TYPE_EGL, NULL);
+ 
+   priv->orientation_manager = g_object_new (META_TYPE_ORIENTATION_MANAGER, NULL);
+ 
+   priv->monitor_manager = meta_backend_create_monitor_manager (backend, error);
+   if (!priv->monitor_manager)
+     return FALSE;
+ 
+   priv->renderer = meta_backend_create_renderer (backend, error);
+   if (!priv->renderer)
+     return FALSE;
+ 
+   priv->cursor_tracker = g_object_new (META_TYPE_CURSOR_TRACKER, NULL);
+ 
+   priv->dnd = g_object_new (META_TYPE_DND, NULL);
+ 
+   priv->up_client = up_client_new ();
+   g_signal_connect (priv->up_client, "notify::lid-is-closed",
+diff --git a/src/org.freedesktop.login1.xml b/src/org.freedesktop.login1.xml
+index 765475132..7db8f373c 100644
+--- a/src/org.freedesktop.login1.xml
++++ b/src/org.freedesktop.login1.xml
+@@ -16,31 +16,45 @@
+       <arg name="major" type="u" direction="in"/>
+       <arg name="minor" type="u" direction="in"/>
+       <arg name="fd" type="h" direction="out"/>
+       <arg name="paused" type="b" direction="out"/>
+     </method>
+     <method name="ReleaseDevice">
+       <arg name="major" type="u"/>
+       <arg name="minor" type="u"/>
+     </method>
+     <method name="PauseDeviceComplete">
+       <arg name="major" type="u"/>
+       <arg name="minor" type="u"/>
+     </method>
+     <signal name="PauseDevice">
+       <arg name="major" type="u"/>
+       <arg name="minor" type="u"/>
+       <arg name="type" type="s"/>
+     </signal>
+     <signal name="ResumeDevice">
+       <arg name="major" type="u"/>
+       <arg name="minor" type="u"/>
+       <arg name="fd" type="h"/>
+     </signal>
+   </interface>
+ 
+   <interface name="org.freedesktop.login1.Seat">
+     <method name="SwitchTo">
+       <arg name="vt" type="u"/>
+     </method>
+   </interface>
++
++  <interface name="org.freedesktop.login1.Manager">
++    <method name="Inhibit">
++      <annotation name="org.gtk.GDBus.C.UnixFD" value="true"/>
++      <arg name="what" type="s" direction="in"/>
++      <arg name="who" type="s" direction="in"/>
++      <arg name="why" type="s" direction="in"/>
++      <arg name="mode" type="s" direction="in"/>
++      <arg name="fd" type="h" direction="out"/>
++    </method>
++    <signal name="PrepareForSleep">
++      <arg name="active" type="b"/>
++    </signal>
++  </interface>
+ </node>
+-- 
+2.18.1
+
diff --git a/SOURCES/0002-clutter-Keep-a-device-reference-with-events.patch b/SOURCES/0002-clutter-Keep-a-device-reference-with-events.patch
new file mode 100644
index 0000000..2879849
--- /dev/null
+++ b/SOURCES/0002-clutter-Keep-a-device-reference-with-events.patch
@@ -0,0 +1,68 @@
+From f41e9517ffd362505ca91c21387e6457e7c53a2d Mon Sep 17 00:00:00 2001
+From: Olivier Fourdan <ofourdan@redhat.com>
+Date: Mon, 19 Nov 2018 11:25:57 +0100
+Subject: [PATCH 02/12] clutter: Keep a device reference with events
+
+If a device (virtual or real) is removed while there are remaining
+events queued for that device, the event loop may try to access the
+event freed memory.
+
+To avoid the issue, add a reference to the device when the event is
+created or copied, and remove the reference once the device is freed.
+
+Closes: https://gitlab.gnome.org/GNOME/mutter/issues/393
+---
+ clutter/clutter/clutter-event.c | 16 ++++++++++++----
+ 1 file changed, 12 insertions(+), 4 deletions(-)
+
+diff --git a/clutter/clutter/clutter-event.c b/clutter/clutter/clutter-event.c
+index 10b6c00..1b21b6a 100644
+--- a/clutter/clutter/clutter-event.c
++++ b/clutter/clutter/clutter-event.c
+@@ -1095,7 +1095,7 @@ clutter_event_set_device (ClutterEvent       *event,
+     {
+       ClutterEventPrivate *real_event = (ClutterEventPrivate *) event;
+ 
+-      real_event->device = device;
++      g_set_object (&real_event->device, device);
+     }
+ 
+   switch (event->type)
+@@ -1364,8 +1364,8 @@ clutter_event_copy (const ClutterEvent *event)
+     {
+       ClutterEventPrivate *real_event = (ClutterEventPrivate *) event;
+ 
+-      new_real_event->device = real_event->device;
+-      new_real_event->source_device = real_event->source_device;
++      g_set_object (&new_real_event->device, real_event->device);
++      g_set_object (&new_real_event->source_device, real_event->source_device);
+       new_real_event->delta_x = real_event->delta_x;
+       new_real_event->delta_y = real_event->delta_y;
+       new_real_event->is_pointer_emulated = real_event->is_pointer_emulated;
+@@ -1435,6 +1435,14 @@ clutter_event_free (ClutterEvent *event)
+     {
+       _clutter_backend_free_event_data (clutter_get_default_backend (), event);
+ 
++      if (is_event_allocated (event))
++        {
++          ClutterEventPrivate *real_event = (ClutterEventPrivate *) event;
++
++          g_clear_object (&real_event->device);
++          g_clear_object (&real_event->source_device);
++        }
++
+       switch (event->type)
+         {
+         case CLUTTER_BUTTON_PRESS:
+@@ -1689,7 +1697,7 @@ clutter_event_set_source_device (ClutterEvent       *event,
+     return;
+ 
+   real_event = (ClutterEventPrivate *) event;
+-  real_event->source_device = device;
++  g_set_object (&real_event->source_device, device);
+ }
+ 
+ /**
+-- 
+2.19.2
+
diff --git a/SOURCES/0002-monitor-manager-kms-Check-if-GPUs-can-have-outputs.patch b/SOURCES/0002-monitor-manager-kms-Check-if-GPUs-can-have-outputs.patch
new file mode 100644
index 0000000..71a591e
--- /dev/null
+++ b/SOURCES/0002-monitor-manager-kms-Check-if-GPUs-can-have-outputs.patch
@@ -0,0 +1,104 @@
+From aa6d1614c069496bab3f6fa5d374c727fa352742 Mon Sep 17 00:00:00 2001
+From: Iain Lane <iainl@gnome.org>
+Date: Mon, 13 Aug 2018 10:22:22 +0000
+Subject: [PATCH 2/2] monitor-manager-kms: Check if GPUs can have outputs
+
+We need a way for mutter to exit if no available GPUs are going to work.
+For example if gdm starts gnome-shell and we're using a DRM driver that
+doesn't work with KMS then we should exit so that GDM can try with Xorg,
+rather than operating in headless mode.
+
+Related: https://gitlab.gnome.org/GNOME/mutter/issues/223
+
+
+(cherry picked from commit deb541ef5ac2cc07d0259c259bff8d1523388d47)
+---
+ src/backends/native/meta-gpu-kms.c            |  8 ++++++++
+ src/backends/native/meta-gpu-kms.h            |  2 ++
+ .../native/meta-monitor-manager-kms.c         | 20 +++++++++++++++++++
+ 3 files changed, 30 insertions(+)
+
+diff --git a/src/backends/native/meta-gpu-kms.c b/src/backends/native/meta-gpu-kms.c
+index 7e7607c46..08c1e7dbe 100644
+--- a/src/backends/native/meta-gpu-kms.c
++++ b/src/backends/native/meta-gpu-kms.c
+@@ -797,6 +797,12 @@ meta_gpu_kms_read_current (MetaGpu  *gpu,
+   return TRUE;
+ }
+ 
++gboolean
++meta_gpu_kms_can_have_outputs (MetaGpuKms *gpu_kms)
++{
++  return gpu_kms->n_connectors > 0;
++}
++
+ MetaGpuKms *
+ meta_gpu_kms_new (MetaMonitorManagerKms  *monitor_manager_kms,
+                   const char             *kms_file_path,
+@@ -860,6 +866,8 @@ meta_gpu_kms_new (MetaMonitorManagerKms  *monitor_manager_kms,
+ 
+   drmSetClientCap (gpu_kms->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+ 
++  meta_gpu_kms_read_current (META_GPU (gpu_kms), NULL);
++
+   source = g_source_new (&kms_event_funcs, sizeof (MetaKmsSource));
+   kms_source = (MetaKmsSource *) source;
+   kms_source->fd_tag = g_source_add_unix_fd (source,
+diff --git a/src/backends/native/meta-gpu-kms.h b/src/backends/native/meta-gpu-kms.h
+index 3d0f5f65e..d53ed98c8 100644
+--- a/src/backends/native/meta-gpu-kms.h
++++ b/src/backends/native/meta-gpu-kms.h
+@@ -59,6 +59,8 @@ gboolean meta_gpu_kms_apply_crtc_mode (MetaGpuKms *gpu_kms,
+                                        int         y,
+                                        uint32_t    fb_id);
+ 
++gboolean meta_gpu_kms_can_have_outputs (MetaGpuKms *gpu_kms);
++
+ gboolean meta_gpu_kms_is_crtc_active (MetaGpuKms *gpu_kms,
+                                       MetaCrtc   *crtc);
+ 
+diff --git a/src/backends/native/meta-monitor-manager-kms.c b/src/backends/native/meta-monitor-manager-kms.c
+index d8e7b848d..438069110 100644
+--- a/src/backends/native/meta-monitor-manager-kms.c
++++ b/src/backends/native/meta-monitor-manager-kms.c
+@@ -642,10 +642,12 @@ meta_monitor_manager_kms_initable_init (GInitable    *initable,
+                                         GError      **error)
+ {
+   MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (initable);
++  MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_kms);
+   const char *subsystems[2] = { "drm", NULL };
+   GList *gpu_paths;
+   g_autofree char *primary_gpu_path = NULL;
+   GList *l;
++  gboolean can_have_outputs;
+ 
+   manager_kms->udev = g_udev_client_new (subsystems);
+ 
+@@ -694,6 +696,24 @@ meta_monitor_manager_kms_initable_init (GInitable    *initable,
+     }
+   g_list_free_full (gpu_paths, g_free);
+ 
++  can_have_outputs = FALSE;
++  for (l = meta_monitor_manager_get_gpus (manager); l; l = l->next)
++    {
++      MetaGpuKms *gpu_kms = l->data;
++
++      if (meta_gpu_kms_can_have_outputs (gpu_kms))
++        {
++          can_have_outputs = TRUE;
++          break;
++        }
++    }
++  if (!can_have_outputs)
++    {
++      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
++                   "No GPUs with outputs found");
++      return FALSE;
++    }
++
+   return TRUE;
+ }
+ 
+-- 
+2.19.0
+
diff --git a/SOURCES/0002-wayland-surface-Add-API-to-cache-frame-callbacks.patch b/SOURCES/0002-wayland-surface-Add-API-to-cache-frame-callbacks.patch
new file mode 100644
index 0000000..fb481f6
--- /dev/null
+++ b/SOURCES/0002-wayland-surface-Add-API-to-cache-frame-callbacks.patch
@@ -0,0 +1,70 @@
+From 4dfe4256ae66a151fb5a570f34af0f85f3e5e0f5 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Mon, 27 Aug 2018 12:05:58 +0200
+Subject: [PATCH 2/8] wayland/surface: Add API to cache frame callbacks
+
+Sometimes it may be useful for roles to put callbacks in the generic
+surface frame callback queue. The surface frame callback queue will
+either eventually be processed on the next surface role assignment that
+places the frame callbacks in a role specific queue, processed at some
+other point in time by a role, or cleaned up on surface destruction.
+
+https://gitlab.gnome.org/GNOME/mutter/issues/240
+(cherry picked from commit 0ace58d05f3e50f4fdcf4971baab163e6ae5a1e9)
+---
+ src/wayland/meta-wayland-surface.c | 17 ++++++++++-------
+ src/wayland/meta-wayland-surface.h |  3 +++
+ 2 files changed, 13 insertions(+), 7 deletions(-)
+
+diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c
+index d2f5ffccf..3c9a404c7 100644
+--- a/src/wayland/meta-wayland-surface.c
++++ b/src/wayland/meta-wayland-surface.c
+@@ -602,6 +602,15 @@ parent_surface_state_applied (gpointer data,
+   meta_wayland_subsurface_parent_state_applied (subsurface);
+ }
+ 
++void
++meta_wayland_surface_cache_pending_frame_callbacks (MetaWaylandSurface      *surface,
++                                                    MetaWaylandPendingState *pending)
++{
++  wl_list_insert_list (&surface->pending_frame_callback_list,
++                       &pending->frame_callback_list);
++  wl_list_init (&pending->frame_callback_list);
++}
++
+ void
+ meta_wayland_surface_apply_pending_state (MetaWaylandSurface      *surface,
+                                           MetaWaylandPendingState *pending)
+@@ -722,13 +731,7 @@ meta_wayland_surface_apply_pending_state (MetaWaylandSurface      *surface,
+     }
+   else
+     {
+-      /* Since there is no role assigned to the surface yet, keep frame
+-       * callbacks queued until a role is assigned and we know how
+-       * the surface will be drawn.
+-       */
+-      wl_list_insert_list (&surface->pending_frame_callback_list,
+-                           &pending->frame_callback_list);
+-      wl_list_init (&pending->frame_callback_list);
++      meta_wayland_surface_cache_pending_frame_callbacks (surface, pending);
+ 
+       if (pending->newly_attached)
+         {
+diff --git a/src/wayland/meta-wayland-surface.h b/src/wayland/meta-wayland-surface.h
+index 05d2a0a24..46accd900 100644
+--- a/src/wayland/meta-wayland-surface.h
++++ b/src/wayland/meta-wayland-surface.h
+@@ -261,6 +261,9 @@ MetaWaylandSurface *meta_wayland_surface_get_toplevel (MetaWaylandSurface *surfa
+ 
+ MetaWindow *        meta_wayland_surface_get_toplevel_window (MetaWaylandSurface *surface);
+ 
++void                meta_wayland_surface_cache_pending_frame_callbacks (MetaWaylandSurface      *surface,
++                                                                        MetaWaylandPendingState *pending);
++
+ void                meta_wayland_surface_queue_pending_frame_callbacks (MetaWaylandSurface *surface);
+ 
+ void                meta_wayland_surface_queue_pending_state_frame_callbacks (MetaWaylandSurface      *surface,
+-- 
+2.19.0
+
diff --git a/SOURCES/0002-window-Explicitly-exclude-unmanaging-window-from-foc.patch b/SOURCES/0002-window-Explicitly-exclude-unmanaging-window-from-foc.patch
new file mode 100644
index 0000000..21a81c5
--- /dev/null
+++ b/SOURCES/0002-window-Explicitly-exclude-unmanaging-window-from-foc.patch
@@ -0,0 +1,36 @@
+From 137f22236cba5f2583d8db3d957e3926d1f7f668 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
+Date: Fri, 25 May 2018 21:24:17 +0200
+Subject: [PATCH 2/3] window: Explicitly exclude unmanaging window from focus
+ again
+
+Since commit b3b9d9e16 we no longer have to pass the unmanaging window
+to make sure we don't try to focus it again, however the parameter also
+influences the focus policy by giving ancestors preference over the normal
+stack order.
+
+https://gitlab.gnome.org/GNOME/mutter/issues/15
+
+(cherry picked from commit d99442d6e6b7c9b383863cc754db609398d4e65b)
+---
+ src/core/window.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/src/core/window.c b/src/core/window.c
+index 5b1eb5b68..cc0813ac4 100644
+--- a/src/core/window.c
++++ b/src/core/window.c
+@@ -1469,7 +1469,9 @@ meta_window_unmanage (MetaWindow  *window,
+       meta_topic (META_DEBUG_FOCUS,
+                   "Focusing default window since we're unmanaging %s\n",
+                   window->desc);
+-      meta_workspace_focus_default_window (window->screen->active_workspace, NULL, timestamp);
++      meta_workspace_focus_default_window (window->screen->active_workspace,
++                                           window,
++                                           timestamp);
+     }
+   else
+     {
+-- 
+2.19.0
+
diff --git a/SOURCES/0002-window-Force-update-monitor-on-hot-plugs.patch b/SOURCES/0002-window-Force-update-monitor-on-hot-plugs.patch
new file mode 100644
index 0000000..c627e71
--- /dev/null
+++ b/SOURCES/0002-window-Force-update-monitor-on-hot-plugs.patch
@@ -0,0 +1,203 @@
+From 83b7b17b64b3e42a87f524ac3504be81789ec0fc Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Fri, 24 Aug 2018 12:56:31 +0200
+Subject: [PATCH 2/2] window: Force update monitor on hot plugs
+
+Commit a3da4b8d5bd217c0262fd9361036877d155a300f changed updating of
+window monitors to always use take affect when it was done from a
+non-user operation. This could cause feed back loops when a non-user
+driven operation would trigger the changing of a monitor, which itself
+would trigger changing of the monitor again due to a window scale
+change.
+
+The reason for the change, was that when the window monitor changed due
+to a hot plug, if it didn't actually change, eventually the window
+monitor pointer would be pointing to freed memory.
+
+Instead of force updating the monitor on all non-user operations, just
+do it on hot plugs. This allows for the feedback loop preventing logic
+to still do what its supposed to do, without risking dangling pointers
+on hot plugs.
+
+Related: https://gitlab.gnome.org/GNOME/mutter/issues/189
+Closes: https://gitlab.gnome.org/GNOME/mutter/issues/192
+
+(cherry picked from commit 8d3e053059cd202e740076caedfc8e3ac149066a)
+---
+ src/core/window-private.h | 1 +
+ src/core/window.c         | 4 ++--
+ 2 files changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/src/core/window-private.h b/src/core/window-private.h
+index bed2fc7f6..8552829b2 100644
+--- a/src/core/window-private.h
++++ b/src/core/window-private.h
+@@ -98,60 +98,61 @@ typedef enum
+   META_PLACEMENT_GRAVITY_TOP    = 1 << 0,
+   META_PLACEMENT_GRAVITY_BOTTOM = 1 << 1,
+   META_PLACEMENT_GRAVITY_LEFT   = 1 << 2,
+   META_PLACEMENT_GRAVITY_RIGHT  = 1 << 3,
+ } MetaPlacementGravity;
+ 
+ typedef enum
+ {
+   META_PLACEMENT_ANCHOR_NONE   = 0,
+   META_PLACEMENT_ANCHOR_TOP    = 1 << 0,
+   META_PLACEMENT_ANCHOR_BOTTOM = 1 << 1,
+   META_PLACEMENT_ANCHOR_LEFT   = 1 << 2,
+   META_PLACEMENT_ANCHOR_RIGHT  = 1 << 3,
+ } MetaPlacementAnchor;
+ 
+ typedef enum
+ {
+   META_PLACEMENT_CONSTRAINT_ADJUSTMENT_NONE     = 0,
+   META_PLACEMENT_CONSTRAINT_ADJUSTMENT_SLIDE_X  = 1 << 0,
+   META_PLACEMENT_CONSTRAINT_ADJUSTMENT_SLIDE_Y  = 1 << 1,
+   META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_X   = 1 << 2,
+   META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_Y   = 1 << 3,
+   META_PLACEMENT_CONSTRAINT_ADJUSTMENT_RESIZE_X = 1 << 4,
+   META_PLACEMENT_CONSTRAINT_ADJUSTMENT_RESIZE_Y = 1 << 5,
+ } MetaPlacementConstraintAdjustment;
+ 
+ typedef enum _MetaWindowUpdateMonitorFlags
+ {
+   META_WINDOW_UPDATE_MONITOR_FLAGS_NONE = 0,
+   META_WINDOW_UPDATE_MONITOR_FLAGS_USER_OP = 1 << 0,
++  META_WINDOW_UPDATE_MONITOR_FLAGS_FORCE = 1 << 1,
+ } MetaWindowUpdateMonitorFlags;
+ 
+ typedef struct _MetaPlacementRule
+ {
+   MetaRectangle anchor_rect;
+   MetaPlacementGravity gravity;
+   MetaPlacementAnchor anchor;
+   MetaPlacementConstraintAdjustment constraint_adjustment;
+   int offset_x;
+   int offset_y;
+   int width;
+   int height;
+ } MetaPlacementRule;
+ 
+ typedef enum
+ {
+   META_EDGE_CONSTRAINT_NONE    = 0,
+   META_EDGE_CONSTRAINT_WINDOW  = 1,
+   META_EDGE_CONSTRAINT_MONITOR = 2,
+ } MetaEdgeConstraint;
+ 
+ struct _MetaWindow
+ {
+   GObject parent_instance;
+ 
+   MetaDisplay *display;
+   MetaScreen *screen;
+   guint64 stamp;
+   MetaLogicalMonitor *monitor;
+   MetaWorkspace *workspace;
+diff --git a/src/core/window.c b/src/core/window.c
+index ad4059704..07f826bb5 100644
+--- a/src/core/window.c
++++ b/src/core/window.c
+@@ -3776,97 +3776,97 @@ find_monitor_by_winsys_id (MetaWindow *window,
+   logical_monitors =
+     meta_monitor_manager_get_logical_monitors (monitor_manager);
+ 
+   for (l = logical_monitors; l; l = l->next)
+     {
+       MetaLogicalMonitor *logical_monitor = l->data;
+ 
+       if (logical_monitor->winsys_id == winsys_id)
+         return logical_monitor;
+     }
+ 
+   return NULL;
+ }
+ 
+ /* This is called when the monitor setup has changed. The window->monitor
+  * reference is still "valid", but refer to the previous monitor setup */
+ void
+ meta_window_update_for_monitors_changed (MetaWindow *window)
+ {
+   MetaBackend *backend = meta_get_backend ();
+   MetaMonitorManager *monitor_manager =
+     meta_backend_get_monitor_manager (backend);
+   const MetaLogicalMonitor *old, *new;
+ 
+   if (meta_window_has_fullscreen_monitors (window))
+     meta_window_clear_fullscreen_monitors (window);
+ 
+   if (window->override_redirect || window->type == META_WINDOW_DESKTOP)
+     {
+       meta_window_update_monitor (window,
+-                                  META_WINDOW_UPDATE_MONITOR_FLAGS_NONE);
++                                  META_WINDOW_UPDATE_MONITOR_FLAGS_FORCE);
+       return;
+     }
+ 
+   old = window->monitor;
+ 
+   /* Try the preferred output first */
+   new = find_monitor_by_winsys_id (window, window->preferred_output_winsys_id);
+ 
+   /* Otherwise, try to find the old output on a new monitor */
+   if (old && !new)
+     new = find_monitor_by_winsys_id (window, old->winsys_id);
+ 
+   /* Fall back to primary if everything else failed */
+   if (!new)
+     new = meta_monitor_manager_get_primary_logical_monitor (monitor_manager);
+ 
+   if (new && old)
+     {
+       if (window->tile_mode != META_TILE_NONE)
+         window->tile_monitor_number = new->number;
+ 
+       /* This will eventually reach meta_window_update_monitor that
+        * will send leave/enter-monitor events. The old != new monitor
+        * check will always fail (due to the new logical_monitors set) so
+        * we will always send the events, even if the new and old monitor
+        * index is the same. That is right, since the enumeration of the
+        * monitors changed and the same index could be refereing
+        * to a different monitor. */
+       meta_window_move_between_rects (window,
+                                       &old->rect,
+                                       &new->rect);
+     }
+   else
+     {
+       meta_window_update_monitor (window,
+-                                  META_WINDOW_UPDATE_MONITOR_FLAGS_NONE);
++                                  META_WINDOW_UPDATE_MONITOR_FLAGS_FORCE);
+     }
+ }
+ 
+ void
+ meta_window_update_monitor (MetaWindow                   *window,
+                             MetaWindowUpdateMonitorFlags  flags)
+ {
+   const MetaLogicalMonitor *old;
+ 
+   old = window->monitor;
+   META_WINDOW_GET_CLASS (window)->update_main_monitor (window, flags);
+   if (old != window->monitor)
+     {
+       meta_window_on_all_workspaces_changed (window);
+ 
+       /* If workspaces only on primary and we moved back to primary due to a user action,
+        * ensure that the window is now in that workspace. We do this because while
+        * the window is on a non-primary monitor it is always visible, so it would be
+        * very jarring if it disappeared when it crossed the monitor border.
+        * The one time we want it to both change to the primary monitor and a non-active
+        * workspace is when dropping the window on some other workspace thumbnail directly.
+        * That should be handled by explicitly moving the window before changing the
+        * workspace.
+        */
+       if (meta_prefs_get_workspaces_only_on_primary () &&
+           flags & META_WINDOW_UPDATE_MONITOR_FLAGS_USER_OP &&
+           meta_window_is_on_primary_monitor (window)  &&
+           window->screen->active_workspace != window->workspace)
+         meta_window_change_workspace (window, window->screen->active_workspace);
+ 
+-- 
+2.18.1
+
diff --git a/SOURCES/0002-xprops-Make-sure-text_property_to_utf8-returns-UTF8.patch b/SOURCES/0002-xprops-Make-sure-text_property_to_utf8-returns-UTF8.patch
new file mode 100644
index 0000000..61c7c2d
--- /dev/null
+++ b/SOURCES/0002-xprops-Make-sure-text_property_to_utf8-returns-UTF8.patch
@@ -0,0 +1,44 @@
+From 72221729a46a6466203d218e82ac3cbb1623a44c Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
+Date: Tue, 9 Oct 2018 00:29:01 +0200
+Subject: [PATCH 2/2] xprops: Make sure text_property_to_utf8() returns UTF8
+
+Commit 840378ae682 changed the code to use XmbTextPropertyToTextList()
+instead of gdk_text_property_to_utf8_list_for_display(), but didn't
+take into account that the replacement returns text in the current
+locale's encoding, while any callers (rightfully) expect UTF8.
+
+Fix this by converting the text if necessary.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/227
+---
+ src/x11/xprops.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/src/x11/xprops.c b/src/x11/xprops.c
+index cffa5958ad..ca686b2fa8 100644
+--- a/src/x11/xprops.c
++++ b/src/x11/xprops.c
+@@ -637,6 +637,7 @@ text_property_to_utf8 (Display *xdisplay,
+ {
+   char *ret = NULL;
+   char **local_list = NULL;
++  const char *charset = NULL;
+   int count = 0;
+   int res;
+ 
+@@ -647,7 +648,10 @@ text_property_to_utf8 (Display *xdisplay,
+   if (count == 0)
+     goto out;
+ 
+-  ret = g_strdup (local_list[0]);
++  if (g_get_charset (&charset))
++    ret = g_strdup (local_list[0]);
++  else
++    ret = g_convert (local_list[0], -1, "UTF-8", charset, NULL, NULL, NULL);
+ 
+  out:
+   XFreeStringList (local_list);
+-- 
+2.17.1
+
diff --git a/SOURCES/0003-backend-add-signals-for-reporting-suspend-and-resume.patch b/SOURCES/0003-backend-add-signals-for-reporting-suspend-and-resume.patch
new file mode 100644
index 0000000..d0ab718
--- /dev/null
+++ b/SOURCES/0003-backend-add-signals-for-reporting-suspend-and-resume.patch
@@ -0,0 +1,352 @@
+From 4ac28995769b7820d802683bf86f1dc6c821a52b Mon Sep 17 00:00:00 2001
+From: Ray Strode <rstrode@redhat.com>
+Date: Thu, 10 Jan 2019 10:47:19 -0500
+Subject: [PATCH 3/9] backend: add signals for reporting suspend and resume
+
+This commit adds "suspending" and "resuming" signals
+to MetaBackend.
+
+It's preliminary work needed for tracking when to purge
+and recreate all textures (needed by nvidia).
+---
+ src/backends/meta-backend.c | 99 ++++++++++++++++++++++++++++++++-----
+ 1 file changed, 87 insertions(+), 12 deletions(-)
+
+diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c
+index 2f090e233..eb8790967 100644
+--- a/src/backends/meta-backend.c
++++ b/src/backends/meta-backend.c
+@@ -1,93 +1,96 @@
+ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+ 
+ /*
+  * Copyright (C) 2014 Red Hat
+  *
+  * This program is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU General Public License as
+  * published by the Free Software Foundation; either version 2 of the
+  * License, or (at your option) any later version.
+  *
+  * This program 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
+  * General Public License for more details.
+  *
+  * You should have received a copy of the GNU General Public License
+  * along with this program; if not, write to the Free Software
+  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+  * 02111-1307, USA.
+  *
+  * Written by:
+  *     Jasper St. Pierre <jstpierre@mecheye.net>
+  */
+ 
+ #include "config.h"
+ 
+ #include <stdlib.h>
+ 
++#include <gio/gunixfdlist.h>
++
+ #include <clutter/clutter-mutter.h>
+ #include <meta/meta-backend.h>
+ #include <meta/main.h>
+ #include <meta/util.h>
+ #include "meta-backend-private.h"
+ #include "meta-input-settings-private.h"
+ #include "backends/x11/meta-backend-x11.h"
+ #include "meta-cursor-tracker-private.h"
+ #include "meta-stage-private.h"
+ #include "meta-dbus-login1.h"
+ 
+ #ifdef HAVE_REMOTE_DESKTOP
+ #include "backends/meta-dbus-session-watcher.h"
+ #include "backends/meta-screen-cast.h"
+ #include "backends/meta-remote-access-controller-private.h"
+ #include "backends/meta-remote-desktop.h"
+ #endif
+ 
+ #ifdef HAVE_NATIVE_BACKEND
+ #include "backends/native/meta-backend-native.h"
+ #endif
+ 
+ #include "backends/meta-idle-monitor-private.h"
+ #include "backends/meta-logical-monitor.h"
+ #include "backends/meta-monitor-manager-dummy.h"
+ #include "backends/meta-settings-private.h"
+ 
+ #define META_IDLE_MONITOR_CORE_DEVICE 0
+ 
+ enum
+ {
+   KEYMAP_CHANGED,
+   KEYMAP_LAYOUT_GROUP_CHANGED,
+   LAST_DEVICE_CHANGED,
+-
++  SUSPENDING,
++  RESUMING,
+   N_SIGNALS
+ };
+ 
+ static guint signals[N_SIGNALS];
+ 
+ static MetaBackend *_backend;
+ 
+ static gboolean stage_views_disabled = FALSE;
+ 
+ /**
+  * meta_get_backend:
+  *
+  * Accessor for the singleton MetaBackend.
+  *
+  * Returns: (transfer none): The only #MetaBackend there is.
+  */
+ MetaBackend *
+ meta_get_backend (void)
+ {
+   return _backend;
+ }
+ 
+ struct _MetaBackendPrivate
+ {
+   MetaMonitorManager *monitor_manager;
+   MetaOrientationManager *orientation_manager;
+   MetaCursorTracker *cursor_tracker;
+   MetaCursorRenderer *cursor_renderer;
+   MetaInputSettings *input_settings;
+   MetaRenderer *renderer;
+@@ -523,149 +526,221 @@ meta_backend_class_init (MetaBackendClass *klass)
+   object_class->finalize = meta_backend_finalize;
+ 
+   klass->post_init = meta_backend_real_post_init;
+   klass->create_cursor_renderer = meta_backend_real_create_cursor_renderer;
+   klass->grab_device = meta_backend_real_grab_device;
+   klass->ungrab_device = meta_backend_real_ungrab_device;
+   klass->select_stage_events = meta_backend_real_select_stage_events;
+   klass->get_relative_motion_deltas = meta_backend_real_get_relative_motion_deltas;
+ 
+   signals[KEYMAP_CHANGED] =
+     g_signal_new ("keymap-changed",
+                   G_TYPE_FROM_CLASS (object_class),
+                   G_SIGNAL_RUN_LAST,
+                   0,
+                   NULL, NULL, NULL,
+                   G_TYPE_NONE, 0);
+   signals[KEYMAP_LAYOUT_GROUP_CHANGED] =
+     g_signal_new ("keymap-layout-group-changed",
+                   G_TYPE_FROM_CLASS (object_class),
+                   G_SIGNAL_RUN_LAST,
+                   0,
+                   NULL, NULL, NULL,
+                   G_TYPE_NONE, 1, G_TYPE_UINT);
+   signals[LAST_DEVICE_CHANGED] =
+     g_signal_new ("last-device-changed",
+                   G_TYPE_FROM_CLASS (object_class),
+                   G_SIGNAL_RUN_LAST,
+                   0,
+                   NULL, NULL, NULL,
+                   G_TYPE_NONE, 1, G_TYPE_INT);
++  signals[SUSPENDING] =
++    g_signal_new ("suspending",
++                  G_TYPE_FROM_CLASS (object_class),
++                  G_SIGNAL_RUN_LAST,
++                  0,
++                  NULL, NULL, NULL,
++                  G_TYPE_NONE, 0);
++  signals[RESUMING] =
++    g_signal_new ("resuming",
++                  G_TYPE_FROM_CLASS (object_class),
++                  G_SIGNAL_RUN_LAST,
++                  0,
++                  NULL, NULL, NULL,
++                  G_TYPE_NONE, 0);
+ 
+   mutter_stage_views = g_getenv ("MUTTER_STAGE_VIEWS");
+   stage_views_disabled = g_strcmp0 (mutter_stage_views, "0") == 0;
+ }
+ 
+ static MetaMonitorManager *
+ meta_backend_create_monitor_manager (MetaBackend *backend,
+                                      GError     **error)
+ {
+   if (g_getenv ("META_DUMMY_MONITORS"))
+     return g_object_new (META_TYPE_MONITOR_MANAGER_DUMMY, NULL);
+ 
+   return META_BACKEND_GET_CLASS (backend)->create_monitor_manager (backend,
+                                                                    error);
+ }
+ 
+ static MetaRenderer *
+ meta_backend_create_renderer (MetaBackend *backend,
+                               GError     **error)
+ {
+   return META_BACKEND_GET_CLASS (backend)->create_renderer (backend, error);
+ }
+ 
+ static void
+ lid_is_closed_changed_cb (UpClient   *client,
+                           GParamSpec *pspec,
+                           gpointer    user_data)
+ {
+   if (up_client_get_lid_is_closed (client))
+     return;
+ 
+   meta_idle_monitor_reset_idletime (meta_idle_monitor_get_core ());
+ }
+ 
++static void
++inhibit_sleep (MetaBackend *backend)
++{
++  MetaBackendPrivate *priv = meta_backend_get_instance_private (backend);
++  g_autoptr (GVariant) fd_variant = NULL;
++  g_autoptr (GUnixFDList) fd_list = NULL;
++  g_autoptr (GError) error = NULL;
++  int handle, fd;
++
++  if (priv->inhibit_sleep_fd >= 0)
++    return;
++
++  if (!login1_manager_call_inhibit_sync (priv->logind_proxy,
++                                         "sleep",
++                                         "Display Server",
++                                         "Prepare for suspend",
++                                         "delay",
++                                         NULL,
++                                         &fd_variant,
++                                         &fd_list,
++                                         priv->cancellable,
++                                         &error))
++    {
++      g_warning ("Failed to inhibit sleep: %s", error->message);
++      return;
++    }
++
++  handle = g_variant_get_handle (fd_variant);
++  fd = g_unix_fd_list_get (fd_list, handle, &error);
++
++  if (fd < 0)
++    {
++      g_warning ("Failed to fetch sleep inhibitor fd: %s", error->message);
++      return;
++    }
++
++  priv->inhibit_sleep_fd = fd;
++}
++
++static void
++uninhibit_sleep (MetaBackend *backend)
++{
++  MetaBackendPrivate *priv = meta_backend_get_instance_private (backend);
++
++  close (priv->inhibit_sleep_fd);
++  priv->inhibit_sleep_fd = -1;
++}
++
+ static void
+ prepare_for_sleep_cb (MetaBackend *backend,
+                       gboolean     suspending)
+ {
+-  gboolean suspending;
+-
+-  g_variant_get (parameters, "(b)", &suspending);
+-  if (suspending)
++  if (suspending) {
++    g_signal_emit (backend, signals[SUSPENDING], 0);
++    uninhibit_sleep (backend);
+     return;
++  }
++
++  inhibit_sleep (backend);
++  g_signal_emit (backend, signals[RESUMING], 0);
+   meta_idle_monitor_reset_idletime (meta_idle_monitor_get_core ());
+ }
+ 
+ static Login1Manager *
+ get_logind_proxy (GCancellable *cancellable,
+                   GError      **error)
+ {
+   Login1Manager *proxy;
+ 
+   proxy =
+     login1_manager_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+                                            G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+                                            "org.freedesktop.login1",
+                                            "/org/freedesktop/login1",
+                                            cancellable, error);
+   if (!proxy)
+     g_prefix_error (error, "Could not get logind proxy: ");
+ 
+   return proxy;
+ }
+ 
+ static void
+ system_bus_gotten_cb (GObject      *object,
+                       GAsyncResult *res,
+                       gpointer      user_data)
+ {
++  MetaBackend *backend = META_BACKEND (user_data);
+   MetaBackendPrivate *priv;
+   g_autoptr (GError) error = NULL;
+   GDBusConnection *bus;
+ 
+   bus = g_bus_get_finish (res, NULL);
+   if (!bus)
+     return;
+ 
+   priv = meta_backend_get_instance_private (user_data);
+   priv->system_bus = bus;
+   priv->logind_proxy = get_logind_proxy (priv->cancellable, &error);
++  priv->inhibit_sleep_fd = -1;
+ 
+   if (!priv->logind_proxy)
+-    g_warning ("Failed to get logind proxy: %s", error->message);
+-
+-  g_signal_connect_object (priv->logind_proxy,
+-                           "prepare-for-sleep",
+-                           G_CALLBACK (prepare_for_sleep_cb),
+-                           user_data,
+-                           G_CONNECT_SWAPPED);
++    {
++      g_warning ("Failed to get logind proxy: %s", error->message);
++    }
++  else
++    {
++      inhibit_sleep (backend);
++      g_signal_connect_object (priv->logind_proxy,
++                               "prepare-for-sleep",
++                               G_CALLBACK (prepare_for_sleep_cb),
++                               user_data,
++                               G_CONNECT_SWAPPED);
++    }
+ }
+ 
+ static gboolean
+ meta_backend_initable_init (GInitable     *initable,
+                             GCancellable  *cancellable,
+                             GError       **error)
+ {
+   MetaBackend *backend = META_BACKEND (initable);
+   MetaBackendPrivate *priv = meta_backend_get_instance_private (backend);
+ 
+   priv->settings = meta_settings_new (backend);
+ 
+   priv->egl = g_object_new (META_TYPE_EGL, NULL);
+ 
+   priv->orientation_manager = g_object_new (META_TYPE_ORIENTATION_MANAGER, NULL);
+ 
+   priv->monitor_manager = meta_backend_create_monitor_manager (backend, error);
+   if (!priv->monitor_manager)
+     return FALSE;
+ 
+   priv->renderer = meta_backend_create_renderer (backend, error);
+   if (!priv->renderer)
+     return FALSE;
+ 
+   priv->cursor_tracker = g_object_new (META_TYPE_CURSOR_TRACKER, NULL);
+ 
+   priv->dnd = g_object_new (META_TYPE_DND, NULL);
+ 
+   priv->up_client = up_client_new ();
+   g_signal_connect (priv->up_client, "notify::lid-is-closed",
+-- 
+2.18.1
+
diff --git a/SOURCES/0003-remote-desktop-Do-not-leak-the-virtual-touchscreen.patch b/SOURCES/0003-remote-desktop-Do-not-leak-the-virtual-touchscreen.patch
new file mode 100644
index 0000000..e63ce67
--- /dev/null
+++ b/SOURCES/0003-remote-desktop-Do-not-leak-the-virtual-touchscreen.patch
@@ -0,0 +1,29 @@
+From 22c109d082c5403902bcc1afb6661f951246c5bd Mon Sep 17 00:00:00 2001
+From: Olivier Fourdan <ofourdan@redhat.com>
+Date: Mon, 19 Nov 2018 11:49:41 +0100
+Subject: [PATCH 03/12] remote-desktop: Do not leak the virtual touchscreen
+
+Virtual keyboard and pointer are freed on session close, but the
+virtual touchscreen isn't.
+
+Avoid a leak by freeing the virtual touchscreen along with the rest of
+virtual devices.
+---
+ src/backends/meta-remote-desktop-session.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/backends/meta-remote-desktop-session.c b/src/backends/meta-remote-desktop-session.c
+index 73bcf66..181b7ea 100644
+--- a/src/backends/meta-remote-desktop-session.c
++++ b/src/backends/meta-remote-desktop-session.c
+@@ -160,6 +160,7 @@ meta_remote_desktop_session_close (MetaRemoteDesktopSession *session)
+ 
+   g_clear_object (&session->virtual_pointer);
+   g_clear_object (&session->virtual_keyboard);
++  g_clear_object (&session->virtual_touchscreen);
+ 
+   meta_dbus_session_notify_closed (META_DBUS_SESSION (session));
+   meta_dbus_remote_desktop_session_emit_closed (skeleton);
+-- 
+2.19.2
+
diff --git a/SOURCES/0003-tests-Add-closed-transient-test.patch b/SOURCES/0003-tests-Add-closed-transient-test.patch
new file mode 100644
index 0000000..c37787a
--- /dev/null
+++ b/SOURCES/0003-tests-Add-closed-transient-test.patch
@@ -0,0 +1,59 @@
+From 0a09a3c9063230d9a3f170371f93d2d2280d8e06 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
+Date: Thu, 19 Jul 2018 11:47:12 +0200
+Subject: [PATCH 3/3] tests: Add "closed-transient" test
+
+When a transient window is destroyed, the expected behavior is that
+focus is passed to the ancestor if possible. This was broken for
+quite a while until the previous commit, so add a test case to make
+sure it doesn't happen again.
+
+https://gitlab.gnome.org/GNOME/mutter/issues/15
+
+(cherry-picked from commit 137f22236cba5f2583d8db3d957e3926d1f7f668)
+---
+ src/Makefile-tests.am                        |  1 +
+ src/tests/stacking/closed-transient.metatest | 19 +++++++++++++++++++
+ 2 files changed, 20 insertions(+)
+ create mode 100644 src/tests/stacking/closed-transient.metatest
+
+diff --git a/src/Makefile-tests.am b/src/Makefile-tests.am
+index 72706e2bd..4be832cc9 100644
+--- a/src/Makefile-tests.am
++++ b/src/Makefile-tests.am
+@@ -14,6 +14,7 @@ stackingdir = $(pkgdatadir)/tests/stacking
+ dist_stacking_DATA =				\
+ 	tests/stacking/basic-x11.metatest	\
+ 	tests/stacking/basic-wayland.metatest	\
++	tests/stacking/closed-transient.metatest	\
+ 	tests/stacking/minimized.metatest   	\
+ 	tests/stacking/mixed-windows.metatest   \
+ 	tests/stacking/set-parent.metatest	\
+diff --git a/src/tests/stacking/closed-transient.metatest b/src/tests/stacking/closed-transient.metatest
+new file mode 100644
+index 000000000..5ca95ba0f
+--- /dev/null
++++ b/src/tests/stacking/closed-transient.metatest
+@@ -0,0 +1,19 @@
++new_client 1 wayland
++create 1/1
++show 1/1
++
++new_client 2 wayland
++create 2/1
++show 2/1
++
++create 1/2
++show 1/2
++set_parent 1/2 1
++
++wait
++assert_stacking 1/1 2/1 1/2
++
++destroy 1/2
++
++wait
++assert_stacking 2/1 1/1
+-- 
+2.19.0
+
diff --git a/SOURCES/0003-wayland-xdg-shell-Cache-pending-frame-callbacks-on-p.patch b/SOURCES/0003-wayland-xdg-shell-Cache-pending-frame-callbacks-on-p.patch
new file mode 100644
index 0000000..0b5abd3
--- /dev/null
+++ b/SOURCES/0003-wayland-xdg-shell-Cache-pending-frame-callbacks-on-p.patch
@@ -0,0 +1,38 @@
+From 0a85e6272e7d04edc6c754affeea8d66716cc9fb Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Wed, 25 Jul 2018 11:53:17 +0200
+Subject: [PATCH 3/8] wayland/xdg-shell: Cache pending frame callbacks on popup
+ reset
+
+A popup can be reset, and when that happens, window and actor are
+destroyed, and won't be created again unless it is reassigned the
+popup role.
+
+If a client queued frame callbacks when resetting a popup, the frame
+callbacks would be left in the pending state, as they were not queued on
+the actor, meaning we'd hit an assert about the frame callbacks not
+being handled. Fix this by caching them on the MetaWaylandSurface, so
+that they either are cleaned up on destruction, or queued on the actor
+would the surface be re-assigned the popup role.
+
+https://gitlab.gnome.org/GNOME/mutter/issues/240
+(cherry picked from commit 407d62943c1c0bbb34df5943b6b2d91c5723b6c4)
+---
+ src/wayland/meta-wayland-xdg-shell.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/wayland/meta-wayland-xdg-shell.c b/src/wayland/meta-wayland-xdg-shell.c
+index 4ab638f74..3cf6b5716 100644
+--- a/src/wayland/meta-wayland-xdg-shell.c
++++ b/src/wayland/meta-wayland-xdg-shell.c
+@@ -962,6 +962,7 @@ meta_wayland_xdg_popup_commit (MetaWaylandSurfaceRole  *surface_role,
+   if (!surface->buffer_ref.buffer && xdg_surface_priv->first_buffer_attached)
+     {
+       meta_wayland_xdg_surface_reset (xdg_surface);
++      meta_wayland_surface_cache_pending_frame_callbacks (surface, pending);
+       return;
+     }
+ 
+-- 
+2.19.0
+
diff --git a/SOURCES/0004-screen-cast-src-Add-VideoCrop-support.patch b/SOURCES/0004-screen-cast-src-Add-VideoCrop-support.patch
new file mode 100644
index 0000000..354286d
--- /dev/null
+++ b/SOURCES/0004-screen-cast-src-Add-VideoCrop-support.patch
@@ -0,0 +1,169 @@
+From 20dbf18596feafc17341c330d573807b4940a636 Mon Sep 17 00:00:00 2001
+From: Olivier Fourdan <ofourdan@redhat.com>
+Date: Thu, 22 Nov 2018 11:59:10 +0100
+Subject: [PATCH 04/12] screen-cast-src: Add VideoCrop support
+
+To be able to cast windows, which by definition can change in size
+dynamically, we need a way to specify the video crop meta to adjust to
+the window size whenever it changes.
+
+Add VideoCrop support with a new optional hook `get_videocrop()` in the
+`ScreenCastStreamSrcClass` which, if defined, can let the child specify
+a rectangle for the video cropping area.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/306
+(cherry picked from commit f64eba57ce3fb67a65a5c5755469d4b97067030f)
+---
+ src/backends/meta-screen-cast-stream-src.c | 56 ++++++++++++++++++++--
+ src/backends/meta-screen-cast-stream-src.h |  3 ++
+ 2 files changed, 55 insertions(+), 4 deletions(-)
+
+diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c
+index 0c4f33b..673a464 100644
+--- a/src/backends/meta-screen-cast-stream-src.c
++++ b/src/backends/meta-screen-cast-stream-src.c
+@@ -91,6 +91,9 @@ typedef struct _MetaScreenCastStreamSrcPrivate
+   struct spa_video_info_raw video_format;
+ 
+   uint64_t last_frame_timestamp_us;
++
++  int stream_width;
++  int stream_height;
+ } MetaScreenCastStreamSrcPrivate;
+ 
+ static void
+@@ -117,6 +120,19 @@ meta_screen_cast_stream_src_get_specs (MetaScreenCastStreamSrc *src,
+   klass->get_specs (src, width, height, frame_rate);
+ }
+ 
++static gboolean
++meta_screen_cast_stream_src_get_videocrop (MetaScreenCastStreamSrc *src,
++                                           MetaRectangle           *crop_rect)
++{
++  MetaScreenCastStreamSrcClass *klass =
++    META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src);
++
++  if (klass->get_videocrop)
++    return klass->get_videocrop (src, crop_rect);
++
++  return FALSE;
++}
++
+ static void
+ meta_screen_cast_stream_src_record_frame (MetaScreenCastStreamSrc *src,
+                                           uint8_t                 *data)
+@@ -132,8 +148,10 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src)
+ {
+   MetaScreenCastStreamSrcPrivate *priv =
+     meta_screen_cast_stream_src_get_instance_private (src);
++  MetaRectangle crop_rect;
+   struct pw_buffer *buffer;
+   struct spa_buffer *spa_buffer;
++  struct spa_meta_video_crop *spa_meta_video_crop;
+   uint8_t *map = NULL;
+   uint8_t *data;
+   uint64_t now_us;
+@@ -182,6 +200,27 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src)
+     }
+ 
+   meta_screen_cast_stream_src_record_frame (src, data);
++
++  /* Update VideoCrop if needed */
++  spa_meta_video_crop = spa_buffer_find_meta (spa_buffer, priv->pipewire_type->meta.VideoCrop);
++  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;
++        }
++      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;
++        }
++    }
++
+   priv->last_frame_timestamp_us = now_us;
+ 
+   if (map)
+@@ -275,7 +314,7 @@ on_stream_format_changed (void                 *data,
+   uint8_t params_buffer[1024];
+   int32_t width, height, stride, size;
+   struct spa_pod_builder pod_builder;
+-  const struct spa_pod *params[1];
++  const struct spa_pod *params[2];
+   const int bpp = 4;
+ 
+   if (!format)
+@@ -303,6 +342,12 @@ on_stream_format_changed (void                 *data,
+     ":", pipewire_type->param_buffers.buffers, "iru", 16, PROP_RANGE (2, 16),
+     ":", pipewire_type->param_buffers.align, "i", 16);
+ 
++  params[1] = spa_pod_builder_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));
++
+   pw_stream_finish_format (priv->pipewire_stream, 0,
+                            params, G_N_ELEMENTS (params));
+ }
+@@ -325,7 +370,6 @@ create_pipewire_stream (MetaScreenCastStreamSrc  *src,
+     SPA_POD_BUILDER_INIT (buffer, sizeof (buffer));
+   MetaSpaType *spa_type = &priv->spa_type;
+   struct pw_type *pipewire_type = priv->pipewire_type;
+-  int width, height;
+   float frame_rate;
+   MetaFraction frame_rate_fraction;
+   struct spa_fraction max_framerate;
+@@ -344,7 +388,10 @@ create_pipewire_stream (MetaScreenCastStreamSrc  *src,
+       return NULL;
+     }
+ 
+-  meta_screen_cast_stream_src_get_specs (src, &width, &height, &frame_rate);
++  meta_screen_cast_stream_src_get_specs (src,
++                                         &priv->stream_width,
++                                         &priv->stream_height,
++                                         &frame_rate);
+   frame_rate_fraction = meta_fraction_from_double (frame_rate);
+ 
+   min_framerate = SPA_FRACTION (1, 1);
+@@ -357,7 +404,8 @@ create_pipewire_stream (MetaScreenCastStreamSrc  *src,
+     "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 (width, height),
++    ":", 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,
+diff --git a/src/backends/meta-screen-cast-stream-src.h b/src/backends/meta-screen-cast-stream-src.h
+index c7ac57b..f3b3fd7 100644
+--- a/src/backends/meta-screen-cast-stream-src.h
++++ b/src/backends/meta-screen-cast-stream-src.h
+@@ -26,6 +26,7 @@
+ #include <glib-object.h>
+ 
+ #include "clutter/clutter.h"
++#include "meta/boxes.h"
+ 
+ typedef struct _MetaScreenCastStream MetaScreenCastStream;
+ 
+@@ -47,6 +48,8 @@ struct _MetaScreenCastStreamSrcClass
+   void (* disable) (MetaScreenCastStreamSrc *src);
+   void (* record_frame) (MetaScreenCastStreamSrc *src,
+                          uint8_t                 *data);
++  gboolean (* get_videocrop) (MetaScreenCastStreamSrc *src,
++                              MetaRectangle           *crop_rect);
+ };
+ 
+ void meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src);
+-- 
+2.19.2
+
diff --git a/SOURCES/0004-wayland-force-X-clients-to-redraw-on-resume.patch b/SOURCES/0004-wayland-force-X-clients-to-redraw-on-resume.patch
new file mode 100644
index 0000000..93a7bd2
--- /dev/null
+++ b/SOURCES/0004-wayland-force-X-clients-to-redraw-on-resume.patch
@@ -0,0 +1,307 @@
+From d20f20c9d4c52b7a96d5c879df5fe322a0ca356c Mon Sep 17 00:00:00 2001
+From: Ray Strode <rstrode@redhat.com>
+Date: Sat, 12 Jan 2019 12:38:01 -0500
+Subject: [PATCH 4/9] wayland: force X clients to redraw on resume
+
+On nvidia, the textures backing Xwayland client window contents get
+corrupted on suspend.  Xwayland currently doesn't handle this situation
+itself.
+
+For now, in order to work around this issue, send an empty output
+change event to Xwayland.  This will cause it to force Expose events
+to get sent to all clients and get them to redraw.
+---
+ .../native/meta-monitor-manager-kms.c         |  7 +++
+ src/wayland/meta-wayland-outputs.c            | 47 +++++++++++++++++++
+ src/wayland/meta-wayland-outputs.h            |  1 +
+ 3 files changed, 55 insertions(+)
+
+diff --git a/src/backends/native/meta-monitor-manager-kms.c b/src/backends/native/meta-monitor-manager-kms.c
+index 438069110..f9a9e1c6d 100644
+--- a/src/backends/native/meta-monitor-manager-kms.c
++++ b/src/backends/native/meta-monitor-manager-kms.c
+@@ -7,60 +7,61 @@
+  * modify it under the terms of the GNU General Public License as
+  * published by the Free Software Foundation; either version 2 of the
+  * License, or (at your option) any later version.
+  *
+  * This program 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
+  * General Public License for more details.
+  *
+  * You should have received a copy of the GNU General Public License
+  * along with this program; if not, write to the Free Software
+  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+  * 02111-1307, USA.
+  *
+  * Author: Giovanni Campagna <gcampagn@redhat.com>
+  */
+ 
+ #include "config.h"
+ 
+ #include "meta-monitor-manager-kms.h"
+ #include "meta-monitor-config-manager.h"
+ #include "meta-backend-native.h"
+ #include "meta-crtc.h"
+ #include "meta-launcher.h"
+ #include "meta-output.h"
+ #include "meta-backend-private.h"
+ #include "meta-renderer-native.h"
+ #include "meta-crtc-kms.h"
+ #include "meta-gpu-kms.h"
+ #include "meta-output-kms.h"
++#include "wayland/meta-wayland-outputs.h"
+ 
+ #include <string.h>
+ #include <stdlib.h>
+ #include <clutter/clutter.h>
+ 
+ #include <drm.h>
+ #include <errno.h>
+ #include <sys/ioctl.h>
+ #include <sys/mman.h>
+ #include <unistd.h>
+ 
+ #include <meta/main.h>
+ #include <meta/errors.h>
+ 
+ #include <gudev/gudev.h>
+ 
+ #define DRM_CARD_UDEV_DEVICE_TYPE "drm_minor"
+ 
+ typedef struct
+ {
+   GSource source;
+ 
+   gpointer fd_tag;
+   MetaMonitorManagerKms *manager_kms;
+ } MetaKmsSource;
+ 
+ struct _MetaMonitorManagerKms
+ {
+   MetaMonitorManager parent_instance;
+ 
+@@ -388,63 +389,69 @@ on_uevent (GUdevClient *client,
+   handle_hotplug_event (manager);
+ }
+ 
+ static void
+ meta_monitor_manager_kms_connect_uevent_handler (MetaMonitorManagerKms *manager_kms)
+ {
+   manager_kms->uevent_handler_id = g_signal_connect (manager_kms->udev,
+                                                      "uevent",
+                                                      G_CALLBACK (on_uevent),
+                                                      manager_kms);
+ }
+ 
+ static void
+ meta_monitor_manager_kms_disconnect_uevent_handler (MetaMonitorManagerKms *manager_kms)
+ {
+   g_signal_handler_disconnect (manager_kms->udev,
+                                manager_kms->uevent_handler_id);
+   manager_kms->uevent_handler_id = 0;
+ }
+ 
+ void
+ meta_monitor_manager_kms_pause (MetaMonitorManagerKms *manager_kms)
+ {
+   meta_monitor_manager_kms_disconnect_uevent_handler (manager_kms);
+ }
+ 
+ void
+ meta_monitor_manager_kms_resume (MetaMonitorManagerKms *manager_kms)
+ {
+   MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_kms);
++  ClutterBackend *clutter_backend = clutter_get_default_backend ();
++  CoglContext *cogl_context =
++    clutter_backend_get_cogl_context (clutter_backend);
+ 
+   meta_monitor_manager_kms_connect_uevent_handler (manager_kms);
+   handle_hotplug_event (manager);
++
++  if (cogl_has_feature (cogl_context, COGL_FEATURE_ID_UNSTABLE_TEXTURES))
++    meta_wayland_outputs_redraw (meta_wayland_compositor_get_default ());
+ }
+ 
+ static gboolean
+ meta_monitor_manager_kms_is_transform_handled (MetaMonitorManager  *manager,
+                                                MetaCrtc            *crtc,
+                                                MetaMonitorTransform transform)
+ {
+   return meta_crtc_kms_is_transform_handled (crtc, transform);
+ }
+ 
+ static float
+ meta_monitor_manager_kms_calculate_monitor_mode_scale (MetaMonitorManager *manager,
+                                                        MetaMonitor        *monitor,
+                                                        MetaMonitorMode    *monitor_mode)
+ {
+   return meta_monitor_calculate_mode_scale (monitor, monitor_mode);
+ }
+ 
+ static float *
+ meta_monitor_manager_kms_calculate_supported_scales (MetaMonitorManager          *manager,
+                                                      MetaLogicalMonitorLayoutMode layout_mode,
+                                                      MetaMonitor                 *monitor,
+                                                      MetaMonitorMode             *monitor_mode,
+                                                      int                         *n_supported_scales)
+ {
+   MetaMonitorScalesConstraint constraints =
+     META_MONITOR_SCALES_CONSTRAINT_NONE;
+ 
+   switch (layout_mode)
+     {
+diff --git a/src/wayland/meta-wayland-outputs.c b/src/wayland/meta-wayland-outputs.c
+index 1f99a163d..450f5e484 100644
+--- a/src/wayland/meta-wayland-outputs.c
++++ b/src/wayland/meta-wayland-outputs.c
+@@ -425,60 +425,107 @@ meta_wayland_compositor_update_outputs (MetaWaylandCompositor *compositor,
+ 
+       if (logical_monitor->winsys_id == 0)
+         continue;
+ 
+       wayland_output =
+         g_hash_table_lookup (compositor->outputs,
+                              GSIZE_TO_POINTER (logical_monitor->winsys_id));
+ 
+       if (wayland_output)
+         {
+           g_hash_table_steal (compositor->outputs,
+                               GSIZE_TO_POINTER (logical_monitor->winsys_id));
+         }
+       else
+         {
+           wayland_output = meta_wayland_output_new (compositor, logical_monitor);
+         }
+ 
+       wayland_output_update_for_output (wayland_output, logical_monitor);
+       g_hash_table_insert (new_table,
+                            GSIZE_TO_POINTER (logical_monitor->winsys_id),
+                            wayland_output);
+     }
+ 
+   g_hash_table_foreach (compositor->outputs, make_output_inert, NULL);
+   g_timeout_add_seconds (10, delayed_destroy_outputs, compositor->outputs);
+ 
+   return new_table;
+ }
+ 
++void
++meta_wayland_outputs_redraw (MetaWaylandCompositor *compositor)
++{
++  MetaMonitorManager *monitor_manager;
++  GList *logical_monitors, *l;
++
++  monitor_manager = meta_monitor_manager_get ();
++
++  logical_monitors =
++    meta_monitor_manager_get_logical_monitors (monitor_manager);
++
++  for (l = logical_monitors; l; l = l->next)
++    {
++      MetaLogicalMonitor *logical_monitor = l->data;
++      MetaWaylandOutput *wayland_output;
++      GList *iter;
++
++      if (logical_monitor->winsys_id == 0)
++        continue;
++
++      wayland_output =
++        g_hash_table_lookup (compositor->outputs,
++                             GSIZE_TO_POINTER (logical_monitor->winsys_id));
++
++      if (wayland_output == NULL)
++        continue;
++
++      /* Just output a "changes done" event for one of the outputs, with no actual changes.
++       * xwayland takes this as a cue to send expose events to all X clients.
++       */
++      for (iter = wayland_output->resources; iter; iter = iter->next)
++        {
++          struct wl_resource *resource = iter->data;
++          if (wl_resource_get_version (resource) >= WL_OUTPUT_DONE_SINCE_VERSION)
++            wl_output_send_done (resource);
++        }
++
++      for (iter = wayland_output->xdg_output_resources; iter; iter = iter->next)
++        {
++          struct wl_resource *xdg_output = iter->data;
++          zxdg_output_v1_send_done (xdg_output);
++        }
++
++      break;
++    }
++}
++
+ static void
+ on_monitors_changed (MetaMonitorManager    *monitors,
+                      MetaWaylandCompositor *compositor)
+ {
+   compositor->outputs = meta_wayland_compositor_update_outputs (compositor, monitors);
+ }
+ 
+ static void
+ meta_wayland_output_init (MetaWaylandOutput *wayland_output)
+ {
+ }
+ 
+ static void
+ meta_wayland_output_finalize (GObject *object)
+ {
+   MetaWaylandOutput *wayland_output = META_WAYLAND_OUTPUT (object);
+   GList *l;
+ 
+   wl_global_destroy (wayland_output->global);
+ 
+   /* Make sure the wl_output destructor doesn't try to access MetaWaylandOutput
+    * after we have freed it.
+    */
+   make_output_resources_inert (wayland_output);
+ 
+   G_OBJECT_CLASS (meta_wayland_output_parent_class)->finalize (object);
+ }
+ 
+ static void
+ meta_wayland_output_class_init (MetaWaylandOutputClass *klass)
+diff --git a/src/wayland/meta-wayland-outputs.h b/src/wayland/meta-wayland-outputs.h
+index e6b60d5fa..d8c648174 100644
+--- a/src/wayland/meta-wayland-outputs.h
++++ b/src/wayland/meta-wayland-outputs.h
+@@ -20,32 +20,33 @@
+  *
+  * Written by:
+  *     Jasper St. Pierre <jstpierre@mecheye.net>
+  */
+ 
+ #ifndef META_WAYLAND_OUTPUTS_H
+ #define META_WAYLAND_OUTPUTS_H
+ 
+ #include "backends/meta-monitor-manager-private.h"
+ #include "meta-wayland-private.h"
+ 
+ #define META_TYPE_WAYLAND_OUTPUT (meta_wayland_output_get_type ())
+ G_DECLARE_FINAL_TYPE (MetaWaylandOutput, meta_wayland_output,
+                       META, WAYLAND_OUTPUT, GObject)
+ 
+ struct _MetaWaylandOutput
+ {
+   GObject                   parent;
+ 
+   struct wl_global         *global;
+   MetaLogicalMonitor       *logical_monitor;
+   guint                     mode_flags;
+   float                     refresh_rate;
+   gint                      scale;
+ 
+   GList                    *resources;
+   GList                    *xdg_output_resources;
+ };
+ 
+ void meta_wayland_outputs_init (MetaWaylandCompositor *compositor);
++void meta_wayland_outputs_redraw (MetaWaylandCompositor *compositor);
+ 
+ #endif /* META_WAYLAND_OUTPUTS_H */
+-- 
+2.18.1
+
diff --git a/SOURCES/0004-wayland-xdg-shell-Cache-frame-callbacks-if-toplevel-.patch b/SOURCES/0004-wayland-xdg-shell-Cache-frame-callbacks-if-toplevel-.patch
new file mode 100644
index 0000000..bb9fed0
--- /dev/null
+++ b/SOURCES/0004-wayland-xdg-shell-Cache-frame-callbacks-if-toplevel-.patch
@@ -0,0 +1,49 @@
+From b5c2555601da7c8b347664e7ef34803e08a52697 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Mon, 27 Aug 2018 12:30:07 +0200
+Subject: [PATCH 4/8] wayland/xdg-shell: Cache frame callbacks if toplevel is
+ unmanaged
+
+A toplevel window can be unmanaged without the client knowing it (e.g. a
+modal dialog being unmapped together with its parent. When this has
+happened, take frame callbacks queued on a commit and cache them on the
+generic surface queue. If the toplevel is to be remapped, either because
+the surface was reassigned the toplevel role, or if it was reset and
+remapped, the cached frame callbacks will be queued on the surface actor
+and dispatched accordingly.
+
+https://gitlab.gnome.org/GNOME/mutter/issues/240
+(cherry picked from commit 80d420ff430e8e9495fd29d68084cb050600b26f)
+---
+ src/wayland/meta-wayland-xdg-shell.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/src/wayland/meta-wayland-xdg-shell.c b/src/wayland/meta-wayland-xdg-shell.c
+index 3cf6b5716..9e300df6b 100644
+--- a/src/wayland/meta-wayland-xdg-shell.c
++++ b/src/wayland/meta-wayland-xdg-shell.c
+@@ -624,6 +624,13 @@ meta_wayland_xdg_toplevel_commit (MetaWaylandSurfaceRole  *surface_role,
+   MetaRectangle old_geometry;
+   gboolean geometry_changed;
+ 
++  window = surface->window;
++  if (!window)
++    {
++      meta_wayland_surface_cache_pending_frame_callbacks (surface, pending);
++      return;
++    }
++
+   if (!surface->buffer_ref.buffer && xdg_surface_priv->first_buffer_attached)
+     {
+       MetaWaylandActorSurface *actor_surface =
+@@ -635,7 +642,6 @@ meta_wayland_xdg_toplevel_commit (MetaWaylandSurfaceRole  *surface_role,
+       return;
+     }
+ 
+-  window = surface->window;
+   old_geometry = xdg_surface_priv->geometry;
+ 
+   surface_role_class =
+-- 
+2.19.0
+
diff --git a/SOURCES/0005-backends-native-emit-gl-video-memory-purged-when-bec.patch b/SOURCES/0005-backends-native-emit-gl-video-memory-purged-when-bec.patch
new file mode 100644
index 0000000..3ab2dce
--- /dev/null
+++ b/SOURCES/0005-backends-native-emit-gl-video-memory-purged-when-bec.patch
@@ -0,0 +1,76 @@
+From 0af0c75dc60166d2755537b8499e9e4a1501e4a8 Mon Sep 17 00:00:00 2001
+From: Ray Strode <rstrode@redhat.com>
+Date: Tue, 15 Jan 2019 10:29:55 -0500
+Subject: [PATCH 5/9] backends/native: emit gl-video-memory-purged when
+ becoming active
+
+The proprietary NVIDIA driver garbles memory on suspend.  In order
+to work around that limitation, mutter needs to refresh all its
+textures on resuem.
+
+This commit lays the way toward doing that by emitting the
+"gl-video-memory-purged" signal when the compositor becomes active
+by logind (which happens on VT switch and on resume).
+---
+ src/backends/native/meta-backend-native.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/src/backends/native/meta-backend-native.c b/src/backends/native/meta-backend-native.c
+index 042d96ec6..f87c5a066 100644
+--- a/src/backends/native/meta-backend-native.c
++++ b/src/backends/native/meta-backend-native.c
+@@ -618,44 +618,51 @@ meta_activate_session (void)
+       g_warning ("Could not activate session: %s\n", error->message);
+       g_error_free (error);
+       return FALSE;
+     }
+ 
+   return TRUE;
+ }
+ 
+ void
+ meta_backend_native_pause (MetaBackendNative *native)
+ {
+   MetaBackend *backend = META_BACKEND (native);
+   MetaMonitorManager *monitor_manager =
+     meta_backend_get_monitor_manager (backend);
+   MetaMonitorManagerKms *monitor_manager_kms =
+     META_MONITOR_MANAGER_KMS (monitor_manager);
+ 
+   clutter_evdev_release_devices ();
+   clutter_egl_freeze_master_clock ();
+ 
+   meta_monitor_manager_kms_pause (monitor_manager_kms);
+ }
+ 
+ void meta_backend_native_resume (MetaBackendNative *native)
+ {
+   MetaBackend *backend = META_BACKEND (native);
+   MetaMonitorManager *monitor_manager =
+     meta_backend_get_monitor_manager (backend);
+   MetaMonitorManagerKms *monitor_manager_kms =
+     META_MONITOR_MANAGER_KMS (monitor_manager);
++  MetaDisplay *display = meta_get_display ();
++  ClutterBackend *clutter_backend = clutter_get_default_backend ();
++  CoglContext *cogl_context =
++    clutter_backend_get_cogl_context (clutter_backend);
+   ClutterActor *stage;
+   MetaIdleMonitor *idle_monitor;
+ 
++  if (cogl_has_feature (cogl_context, COGL_FEATURE_ID_UNSTABLE_TEXTURES))
++    g_signal_emit_by_name (display, "gl-video-memory-purged");
++
+   meta_monitor_manager_kms_resume (monitor_manager_kms);
+ 
+   clutter_evdev_reclaim_devices ();
+   clutter_egl_thaw_master_clock ();
+ 
+   stage = meta_backend_get_stage (backend);
+   clutter_actor_queue_redraw (stage);
+ 
+   idle_monitor = meta_backend_get_idle_monitor (backend, 0);
+   meta_idle_monitor_reset_idletime (idle_monitor);
+ }
+-- 
+2.18.1
+
diff --git a/SOURCES/0005-screen-cast-Add-screen-cast-window-interface.patch b/SOURCES/0005-screen-cast-Add-screen-cast-window-interface.patch
new file mode 100644
index 0000000..442e9e6
--- /dev/null
+++ b/SOURCES/0005-screen-cast-Add-screen-cast-window-interface.patch
@@ -0,0 +1,204 @@
+From be6ba9eb1e28cc339e691f02c7c1cb6ae627d87a Mon Sep 17 00:00:00 2001
+From: Olivier Fourdan <ofourdan@redhat.com>
+Date: Wed, 5 Dec 2018 08:48:41 +0100
+Subject: [PATCH 05/12] screen-cast: Add screen-cast-window interface
+
+Typically, to stream the content of a window, we need a way to copy the
+content of its window-actor into a buffer, transform relative input
+coordinates to relative position within the window-actor and a mean to
+get the window bounds within the buffer.
+
+For this purpose, add a new GType interface `MetaScreenCastWindow` with
+the methods needed for screen-cast window mode:
+
+ * meta_screen_cast_window_get_buffer_bounds()
+ * meta_screen_cast_window_get_frame_bounds()
+ * meta_screen_cast_window_transform_relative_position()
+ * meta_screen_cast_window_capture_into()
+
+This interface is meant to be implemented by `MetaWindowActor` which has
+access to all the necessary bits to implement them.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/306
+(cherry picked from commit 20c9ca25c01a9160d4a2ddfad29b48eb86a9142a)
+---
+ src/Makefile.am                        |  2 +
+ src/backends/meta-screen-cast-window.c | 71 ++++++++++++++++++++++++
+ src/backends/meta-screen-cast-window.h | 74 ++++++++++++++++++++++++++
+ 3 files changed, 147 insertions(+)
+ create mode 100644 src/backends/meta-screen-cast-window.c
+ create mode 100644 src/backends/meta-screen-cast-window.h
+
+diff --git a/src/Makefile.am b/src/Makefile.am
+index bd879f2..93586a2 100644
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -159,6 +159,8 @@ libmutter_@LIBMUTTER_API_VERSION@_la_SOURCES =	\
+ 	backends/meta-output.h			\
+ 	backends/meta-pointer-constraint.c	\
+ 	backends/meta-pointer-constraint.h	\
++	backends/meta-screen-cast-window.c	\
++	backends/meta-screen-cast-window.h	\
+ 	backends/meta-settings.c		\
+ 	backends/meta-settings-private.h	\
+ 	backends/meta-stage-private.h		\
+diff --git a/src/backends/meta-screen-cast-window.c b/src/backends/meta-screen-cast-window.c
+new file mode 100644
+index 0000000..21629a0
+--- /dev/null
++++ b/src/backends/meta-screen-cast-window.c
+@@ -0,0 +1,71 @@
++/*
++ * Copyright (C) 2018 Red Hat Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program 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
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
++ * 02111-1307, USA.
++ *
++ */
++
++#include "config.h"
++
++#include "backends/meta-screen-cast-window.h"
++
++G_DEFINE_INTERFACE (MetaScreenCastWindow, meta_screen_cast_window, G_TYPE_OBJECT)
++
++static void
++meta_screen_cast_window_default_init (MetaScreenCastWindowInterface *iface)
++{
++}
++
++void
++meta_screen_cast_window_get_buffer_bounds (MetaScreenCastWindow *screen_cast_window,
++                                           MetaRectangle        *bounds)
++{
++  META_SCREEN_CAST_WINDOW_GET_IFACE (screen_cast_window)->get_buffer_bounds (screen_cast_window,
++                                                                             bounds);
++}
++
++void
++meta_screen_cast_window_get_frame_bounds (MetaScreenCastWindow *screen_cast_window,
++                                          MetaRectangle        *bounds)
++{
++  META_SCREEN_CAST_WINDOW_GET_IFACE (screen_cast_window)->get_frame_bounds (screen_cast_window,
++                                                                            bounds);
++}
++
++void
++meta_screen_cast_window_transform_relative_position (MetaScreenCastWindow *screen_cast_window,
++                                                     double                x,
++                                                     double                y,
++                                                     double               *x_out,
++                                                     double               *y_out)
++{
++  META_SCREEN_CAST_WINDOW_GET_IFACE (screen_cast_window)->transform_relative_position (screen_cast_window,
++                                                                                       x,
++                                                                                       y,
++                                                                                       x_out,
++                                                                                       y_out);
++}
++
++
++void
++meta_screen_cast_window_capture_into (MetaScreenCastWindow *screen_cast_window,
++                                      MetaRectangle        *bounds,
++                                      uint8_t              *data)
++{
++  META_SCREEN_CAST_WINDOW_GET_IFACE (screen_cast_window)->capture_into (screen_cast_window,
++                                                                        bounds,
++                                                                        data);
++}
+diff --git a/src/backends/meta-screen-cast-window.h b/src/backends/meta-screen-cast-window.h
+new file mode 100644
+index 0000000..e023d3e
+--- /dev/null
++++ b/src/backends/meta-screen-cast-window.h
+@@ -0,0 +1,74 @@
++/*
++ * Copyright (C) 2018 Red Hat Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program 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
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
++ * 02111-1307, USA.
++ *
++ */
++
++#ifndef META_SCREEN_CAST_WINDOW_H
++#define META_SCREEN_CAST_WINDOW_H
++
++#include <stdint.h>
++#include <glib-object.h>
++
++#include "meta/boxes.h"
++
++G_BEGIN_DECLS
++
++#define META_TYPE_SCREEN_CAST_WINDOW (meta_screen_cast_window_get_type ())
++G_DECLARE_INTERFACE (MetaScreenCastWindow, meta_screen_cast_window,
++                     META, SCREEN_CAST_WINDOW, GObject)
++
++struct _MetaScreenCastWindowInterface
++{
++  GTypeInterface parent_iface;
++
++  void (*get_buffer_bounds) (MetaScreenCastWindow *screen_cast_window,
++                             MetaRectangle        *bounds);
++
++  void (*get_frame_bounds) (MetaScreenCastWindow *screen_cast_window,
++                            MetaRectangle        *bounds);
++
++  void (*transform_relative_position) (MetaScreenCastWindow *screen_cast_window,
++                                       double                x,
++                                       double                y,
++                                       double               *x_out,
++                                       double               *y_out);
++
++  void (*capture_into) (MetaScreenCastWindow *screen_cast_window,
++                        MetaRectangle        *bounds,
++                        uint8_t              *data);
++};
++
++void meta_screen_cast_window_get_buffer_bounds (MetaScreenCastWindow *screen_cast_window,
++                                                MetaRectangle        *bounds);
++
++void meta_screen_cast_window_get_frame_bounds (MetaScreenCastWindow *screen_cast_window,
++                                               MetaRectangle        *bounds);
++
++void meta_screen_cast_window_transform_relative_position (MetaScreenCastWindow *screen_cast_window,
++                                                          double                x,
++                                                          double                y,
++                                                          double               *x_out,
++                                                          double               *y_out);
++
++void meta_screen_cast_window_capture_into (MetaScreenCastWindow *screen_cast_window,
++                                           MetaRectangle        *bounds,
++                                           uint8_t              *data);
++
++G_END_DECLS
++
++#endif /* META_SCREEN_CAST_WINDOW_H */
+-- 
+2.19.2
+
diff --git a/SOURCES/0005-wayland-xdg-shell-Handle-requests-after-toplevel-was.patch b/SOURCES/0005-wayland-xdg-shell-Handle-requests-after-toplevel-was.patch
new file mode 100644
index 0000000..eb02b27
--- /dev/null
+++ b/SOURCES/0005-wayland-xdg-shell-Handle-requests-after-toplevel-was.patch
@@ -0,0 +1,234 @@
+From 80f942773a29889094dbf83aece8d210bd22c73e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Wed, 25 Jul 2018 11:56:14 +0200
+Subject: [PATCH 5/8] wayland/xdg-shell: Handle requests after toplevel was
+ unmanaged
+
+A window can be unmanaged without asking the client to do it, for
+example as a side effect of a parent window being unmanaged, if the
+child window was a attached dialog.
+
+This means that the client might still make requests post updates to it
+after that it was unmapped. Handle this gracefully by NULL-checking the
+surface's MetaWindow pointer. We're not loosing any state due to this,
+as if the client wants to map the same surface again, it needs to either
+reassign it the toplevel role, or reset the xdg-toplevel, both resulting
+in all state being lost anyway.
+
+https://gitlab.gnome.org/GNOME/mutter/issues/240
+(cherry picked from commit 5fd0f62a62a194ffd8e64d177f389912a582f8e1)
+---
+ src/wayland/meta-wayland-xdg-shell.c | 81 +++++++++++++++++++++++-----
+ 1 file changed, 68 insertions(+), 13 deletions(-)
+
+diff --git a/src/wayland/meta-wayland-xdg-shell.c b/src/wayland/meta-wayland-xdg-shell.c
+index 9e300df6b..bfd8163f7 100644
+--- a/src/wayland/meta-wayland-xdg-shell.c
++++ b/src/wayland/meta-wayland-xdg-shell.c
+@@ -191,6 +191,11 @@ xdg_toplevel_set_parent (struct wl_client   *client,
+ {
+   MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
+   MetaWindow *transient_for = NULL;
++  MetaWindow *window;
++
++  window = surface->window;
++  if (!window)
++    return;
+ 
+   if (parent_resource)
+     {
+@@ -200,7 +205,7 @@ xdg_toplevel_set_parent (struct wl_client   *client,
+       transient_for = parent_surface->window;
+     }
+ 
+-  meta_window_set_transient_for (surface->window, transient_for);
++  meta_window_set_transient_for (window, transient_for);
+ }
+ 
+ static void
+@@ -209,11 +214,16 @@ xdg_toplevel_set_title (struct wl_client   *client,
+                         const char         *title)
+ {
+   MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
++  MetaWindow *window;
++
++  window = surface->window;
++  if (!window)
++    return;
+ 
+   if (!g_utf8_validate (title, -1, NULL))
+     title = "";
+ 
+-  meta_window_set_title (surface->window, title);
++  meta_window_set_title (window, title);
+ }
+ 
+ static void
+@@ -222,11 +232,16 @@ xdg_toplevel_set_app_id (struct wl_client   *client,
+                          const char         *app_id)
+ {
+   MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
++  MetaWindow *window;
++
++  window = surface->window;
++  if (!window)
++    return;
+ 
+   if (!g_utf8_validate (app_id, -1, NULL))
+     app_id = "";
+ 
+-  meta_window_set_wm_class (surface->window, app_id, app_id);
++  meta_window_set_wm_class (window, app_id, app_id);
+ }
+ 
+ static void
+@@ -239,15 +254,20 @@ xdg_toplevel_show_window_menu (struct wl_client   *client,
+ {
+   MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource);
+   MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
++  MetaWindow *window;
+   int monitor_scale;
+ 
++  window = surface->window;
++  if (!window)
++    return;
++
+   if (!meta_wayland_seat_get_grab_info (seat, surface, serial, FALSE, NULL, NULL))
+     return;
+ 
+-  monitor_scale = surface->window->monitor->scale;
+-  meta_window_show_menu (surface->window, META_WINDOW_MENU_WM,
+-                         surface->window->buffer_rect.x + (x * monitor_scale),
+-                         surface->window->buffer_rect.y + (y * monitor_scale));
++  monitor_scale = window->monitor->scale;
++  meta_window_show_menu (window, META_WINDOW_MENU_WM,
++                         window->buffer_rect.x + (x * monitor_scale),
++                         window->buffer_rect.y + (y * monitor_scale));
+ }
+ 
+ static void
+@@ -258,8 +278,13 @@ xdg_toplevel_move (struct wl_client   *client,
+ {
+   MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource);
+   MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
++  MetaWindow *window;
+   float x, y;
+ 
++  window = surface->window;
++  if (!window)
++    return;
++
+   if (!meta_wayland_seat_get_grab_info (seat, surface, serial, TRUE, &x, &y))
+     return;
+ 
+@@ -298,9 +323,14 @@ xdg_toplevel_resize (struct wl_client   *client,
+ {
+   MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource);
+   MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
++  MetaWindow *window;
+   gfloat x, y;
+   MetaGrabOp grab_op;
+ 
++  window = surface->window;
++  if (!window)
++    return;
++
+   if (!meta_wayland_seat_get_grab_info (seat, surface, serial, TRUE, &x, &y))
+     return;
+ 
+@@ -357,7 +387,11 @@ xdg_toplevel_set_maximized (struct wl_client   *client,
+                             struct wl_resource *resource)
+ {
+   MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
+-  MetaWindow *window = surface->window;
++  MetaWindow *window;
++
++  window = surface->window;
++  if (!window)
++    return;
+ 
+   meta_window_force_placement (window, TRUE);
+   meta_window_maximize (window, META_MAXIMIZE_BOTH);
+@@ -368,8 +402,13 @@ xdg_toplevel_unset_maximized (struct wl_client   *client,
+                               struct wl_resource *resource)
+ {
+   MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
++  MetaWindow *window;
+ 
+-  meta_window_unmaximize (surface->window, META_MAXIMIZE_BOTH);
++  window = surface->window;
++  if (!window)
++    return;
++
++  meta_window_unmaximize (window, META_MAXIMIZE_BOTH);
+ }
+ 
+ static void
+@@ -378,6 +417,11 @@ xdg_toplevel_set_fullscreen (struct wl_client   *client,
+                              struct wl_resource *output_resource)
+ {
+   MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
++  MetaWindow *window;
++
++  window = surface->window;
++  if (!window)
++    return;
+ 
+   if (output_resource)
+     {
+@@ -385,12 +429,12 @@ xdg_toplevel_set_fullscreen (struct wl_client   *client,
+ 
+       if (output)
+         {
+-          meta_window_move_to_monitor (surface->window,
++          meta_window_move_to_monitor (window,
+                                        output->logical_monitor->number);
+         }
+     }
+ 
+-  meta_window_make_fullscreen (surface->window);
++  meta_window_make_fullscreen (window);
+ }
+ 
+ static void
+@@ -398,8 +442,13 @@ xdg_toplevel_unset_fullscreen (struct wl_client   *client,
+                                struct wl_resource *resource)
+ {
+   MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
++  MetaWindow *window;
++
++  window = surface->window;
++  if (!window)
++    return;
+ 
+-  meta_window_unmake_fullscreen (surface->window);
++  meta_window_unmake_fullscreen (window);
+ }
+ 
+ static void
+@@ -407,8 +456,13 @@ xdg_toplevel_set_minimized (struct wl_client   *client,
+                             struct wl_resource *resource)
+ {
+   MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
++  MetaWindow *window;
+ 
+-  meta_window_minimize (surface->window);
++  window = surface->window;
++  if (!window)
++    return;
++
++  meta_window_minimize (window);
+ }
+ 
+ static const struct xdg_toplevel_interface meta_wayland_xdg_toplevel_interface = {
+@@ -639,6 +693,7 @@ meta_wayland_xdg_toplevel_commit (MetaWaylandSurfaceRole  *surface_role,
+       meta_wayland_xdg_surface_reset (xdg_surface);
+       meta_wayland_actor_surface_queue_frame_callbacks (actor_surface,
+                                                         pending);
++
+       return;
+     }
+ 
+-- 
+2.19.0
+
diff --git a/SOURCES/0006-backends-native-update-glyph-cache-on-resume.patch b/SOURCES/0006-backends-native-update-glyph-cache-on-resume.patch
new file mode 100644
index 0000000..4c93e85
--- /dev/null
+++ b/SOURCES/0006-backends-native-update-glyph-cache-on-resume.patch
@@ -0,0 +1,71 @@
+From 5134dd47c161a47d45e7742cbfba8a0e8cb67be5 Mon Sep 17 00:00:00 2001
+From: Ray Strode <rstrode@redhat.com>
+Date: Tue, 15 Jan 2019 10:29:55 -0500
+Subject: [PATCH 6/9] backends/native: update glyph cache on resume
+
+As mentioned in a previous commit, the proprietary NVIDIA
+driver garbles memory on suspend. That behavior, means that
+clutter's glyph cache (which is stored in GPU memory) gets
+corrupted on suspend.
+
+This commit ensures the glyph cache is blown away when
+the logind session becomes active (on VT switch and resume).
+---
+ src/backends/native/meta-backend-native.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/src/backends/native/meta-backend-native.c b/src/backends/native/meta-backend-native.c
+index f87c5a066..0730a4acb 100644
+--- a/src/backends/native/meta-backend-native.c
++++ b/src/backends/native/meta-backend-native.c
+@@ -626,43 +626,46 @@ meta_activate_session (void)
+ void
+ meta_backend_native_pause (MetaBackendNative *native)
+ {
+   MetaBackend *backend = META_BACKEND (native);
+   MetaMonitorManager *monitor_manager =
+     meta_backend_get_monitor_manager (backend);
+   MetaMonitorManagerKms *monitor_manager_kms =
+     META_MONITOR_MANAGER_KMS (monitor_manager);
+ 
+   clutter_evdev_release_devices ();
+   clutter_egl_freeze_master_clock ();
+ 
+   meta_monitor_manager_kms_pause (monitor_manager_kms);
+ }
+ 
+ void meta_backend_native_resume (MetaBackendNative *native)
+ {
+   MetaBackend *backend = META_BACKEND (native);
+   MetaMonitorManager *monitor_manager =
+     meta_backend_get_monitor_manager (backend);
+   MetaMonitorManagerKms *monitor_manager_kms =
+     META_MONITOR_MANAGER_KMS (monitor_manager);
+   MetaDisplay *display = meta_get_display ();
+   ClutterBackend *clutter_backend = clutter_get_default_backend ();
+   CoglContext *cogl_context =
+     clutter_backend_get_cogl_context (clutter_backend);
+   ClutterActor *stage;
+   MetaIdleMonitor *idle_monitor;
+ 
+   if (cogl_has_feature (cogl_context, COGL_FEATURE_ID_UNSTABLE_TEXTURES))
+-    g_signal_emit_by_name (display, "gl-video-memory-purged");
++    {
++      clutter_clear_glyph_cache ();
++      g_signal_emit_by_name (display, "gl-video-memory-purged");
++    }
+ 
+   meta_monitor_manager_kms_resume (monitor_manager_kms);
+ 
+   clutter_evdev_reclaim_devices ();
+   clutter_egl_thaw_master_clock ();
+ 
+   stage = meta_backend_get_stage (backend);
+   clutter_actor_queue_redraw (stage);
+ 
+   idle_monitor = meta_backend_get_idle_monitor (backend, 0);
+   meta_idle_monitor_reset_idletime (idle_monitor);
+ }
+-- 
+2.18.1
+
diff --git a/SOURCES/0006-wayland-legacy-xdg-shell-Cache-frame-callbacks-if-to.patch b/SOURCES/0006-wayland-legacy-xdg-shell-Cache-frame-callbacks-if-to.patch
new file mode 100644
index 0000000..3d42e7f
--- /dev/null
+++ b/SOURCES/0006-wayland-legacy-xdg-shell-Cache-frame-callbacks-if-to.patch
@@ -0,0 +1,46 @@
+From 082efaca968aeefd4528ac6c8e28bf15e2f86ee5 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Mon, 27 Aug 2018 12:36:51 +0200
+Subject: [PATCH 6/8] wayland/legacy-xdg-shell: Cache frame callbacks if
+ toplevel is unmanaged
+
+A toplevel window can be unmanaged without the client knowing it (e.g. a
+modal dialog being unmapped together with its parent. When this has
+happened, take frame callbacks queued on a commit and cache them on the
+generic surface queue. If the toplevel is to be remapped because the
+surface was reassigned the toplevel role, the cached frame callbacks
+will be queued on the surface actor and dispatched accordingly.
+
+https://gitlab.gnome.org/GNOME/mutter/issues/240
+(cherry picked from commit a740f50cd7d05582a251c23a7025999e55e4aba1)
+---
+ src/wayland/meta-wayland-legacy-xdg-shell.c | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+diff --git a/src/wayland/meta-wayland-legacy-xdg-shell.c b/src/wayland/meta-wayland-legacy-xdg-shell.c
+index e871be972..861270193 100644
+--- a/src/wayland/meta-wayland-legacy-xdg-shell.c
++++ b/src/wayland/meta-wayland-legacy-xdg-shell.c
+@@ -598,11 +598,18 @@ meta_wayland_zxdg_toplevel_v6_commit (MetaWaylandSurfaceRole  *surface_role,
+   MetaWaylandSurfaceRoleClass *surface_role_class;
+   MetaWaylandSurface *surface =
+     meta_wayland_surface_role_get_surface (surface_role);
+-  MetaWindow *window = surface->window;
++  MetaWindow *window;
+   MetaRectangle window_geometry;
+   MetaRectangle old_geometry;
+   gboolean geometry_changed;
+ 
++  window = surface->window;
++  if (!window)
++    {
++      meta_wayland_surface_cache_pending_frame_callbacks (surface, pending);
++      return;
++    }
++
+   old_geometry = xdg_surface_priv->geometry;
+ 
+   surface_role_class =
+-- 
+2.19.0
+
diff --git a/SOURCES/0006-window-actor-Implement-MetaScreenCastWindow-interfac.patch b/SOURCES/0006-window-actor-Implement-MetaScreenCastWindow-interfac.patch
new file mode 100644
index 0000000..456aa3f
--- /dev/null
+++ b/SOURCES/0006-window-actor-Implement-MetaScreenCastWindow-interfac.patch
@@ -0,0 +1,178 @@
+From 5f2c181eda1b721b807e41130232714bacb823a9 Mon Sep 17 00:00:00 2001
+From: Olivier Fourdan <ofourdan@redhat.com>
+Date: Tue, 4 Dec 2018 16:29:47 +0100
+Subject: [PATCH 06/12] window-actor: Implement MetaScreenCastWindow interface
+
+Implements the `MetaScreenCastWindow` interface for screen-cast
+`RecordWindow` mode.
+
+`meta_window_actor_capture_into()` implementation is still pretty crude
+and doesn't take into account subsurfaces and O-R windows so menus,
+popups and other tooltips won't show in the capture.
+
+This is left as a future improvement for now.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/306
+(cherry picked from commit 931934511c8e885d5f607ba90187286a0a7ec3a3)
+---
+ src/compositor/meta-window-actor.c | 132 ++++++++++++++++++++++++++++-
+ 1 file changed, 131 insertions(+), 1 deletion(-)
+
+diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c
+index 5078624..396fef1 100644
+--- a/src/compositor/meta-window-actor.c
++++ b/src/compositor/meta-window-actor.c
+@@ -24,6 +24,7 @@
+ #include <meta/meta-shadow-factory.h>
+ 
+ #include "backends/meta-backend-private.h"
++#include "backends/meta-screen-cast-window.h"
+ #include "clutter/clutter-mutter.h"
+ #include "compositor-private.h"
+ #include "meta-shaped-texture-private.h"
+@@ -188,8 +189,11 @@ static void do_send_frame_timings (MetaWindowActor  *self,
+ 
+ static void cullable_iface_init (MetaCullableInterface *iface);
+ 
++static void screen_cast_window_iface_init (MetaScreenCastWindowInterface *iface);
++
+ G_DEFINE_TYPE_WITH_CODE (MetaWindowActor, meta_window_actor, CLUTTER_TYPE_ACTOR,
+-                         G_IMPLEMENT_INTERFACE (META_TYPE_CULLABLE, cullable_iface_init));
++                         G_IMPLEMENT_INTERFACE (META_TYPE_CULLABLE, cullable_iface_init)
++                         G_IMPLEMENT_INTERFACE (META_TYPE_SCREEN_CAST_WINDOW, screen_cast_window_iface_init));
+ 
+ static void
+ frame_data_free (FrameData *frame)
+@@ -2181,3 +2185,129 @@ meta_window_actor_from_window (MetaWindow *window)
+ {
+   return META_WINDOW_ACTOR (meta_window_get_compositor_private (window));
+ }
++
++static void
++meta_window_actor_get_buffer_bounds (MetaScreenCastWindow *screen_cast_window,
++                                     MetaRectangle        *bounds)
++{
++  MetaWindowActor *window_actor = META_WINDOW_ACTOR (screen_cast_window);
++  ClutterActor *clutter_actor;
++
++  clutter_actor = CLUTTER_ACTOR (meta_window_actor_get_texture (window_actor));
++  bounds->x = 0;
++  bounds->y = 0;
++  bounds->width = (int) clutter_actor_get_width (clutter_actor);
++  bounds->height = (int) clutter_actor_get_height (clutter_actor);
++}
++
++static void
++meta_window_actor_get_frame_bounds (MetaScreenCastWindow *screen_cast_window,
++                                    MetaRectangle        *bounds)
++{
++  MetaWindowActor *window_actor = META_WINDOW_ACTOR (screen_cast_window);
++  MetaWindow *window;
++  MetaShapedTexture *stex;
++  MetaRectangle buffer_rect;
++  MetaRectangle frame_rect;
++  double scale_x, scale_y;
++
++  stex = meta_surface_actor_get_texture (window_actor->priv->surface);
++  clutter_actor_get_scale (CLUTTER_ACTOR (stex), &scale_x, &scale_y);
++
++  window = window_actor->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);
++}
++
++static void
++meta_window_actor_transform_relative_position (MetaScreenCastWindow *screen_cast_window,
++                                               double                x,
++                                               double                y,
++                                               double               *x_out,
++                                               double               *y_out)
++
++{
++  MetaWindowActor *window_actor = META_WINDOW_ACTOR (screen_cast_window);
++  MetaShapedTexture *stex;
++  MetaRectangle bounds;
++  ClutterVertex v1 = { 0.f, }, v2 = { 0.f, };
++
++  meta_window_actor_get_frame_bounds (screen_cast_window, &bounds);
++
++  v1.x = CLAMP ((float) x,
++                bounds.x,
++                bounds.x + bounds.width);
++  v1.y = CLAMP ((float) y,
++                bounds.y,
++                bounds.y + bounds.height);
++
++  stex = meta_surface_actor_get_texture (window_actor->priv->surface);
++  clutter_actor_apply_transform_to_point (CLUTTER_ACTOR (stex), &v1, &v2);
++
++  *x_out = (double) v2.x;
++  *y_out = (double) v2.y;
++}
++
++static void
++meta_window_actor_capture_into (MetaScreenCastWindow *screen_cast_window,
++                                MetaRectangle        *bounds,
++                                uint8_t              *data)
++{
++  MetaWindowActor *window_actor = META_WINDOW_ACTOR (screen_cast_window);
++  cairo_surface_t *image;
++  MetaRectangle clip_rect;
++  uint8_t *cr_data;
++  int cr_stride;
++  int bpp = 4;
++
++  if (meta_window_actor_is_destroyed (window_actor))
++    return;
++
++  clip_rect = *bounds;
++  image = meta_surface_actor_get_image (window_actor->priv->surface, &clip_rect);
++  cr_data = cairo_image_surface_get_data (image);
++  cr_stride = cairo_image_surface_get_stride (image);
++
++  if (clip_rect.width < bounds->width || clip_rect.height < bounds->height)
++    {
++      uint8_t *src, *dst;
++      src = cr_data;
++      dst = data;
++
++      for (int i = 0; i < clip_rect.height; i++)
++        {
++          memcpy (dst, src, cr_stride);
++          if (clip_rect.width < bounds->width)
++            memset (dst + cr_stride, 0, (bounds->width * bpp) - cr_stride);
++
++          src += cr_stride;
++          dst += bounds->width * bpp;
++        }
++
++      for (int i = clip_rect.height; i < bounds->height; i++)
++        {
++          memset (dst, 0, bounds->width * bpp);
++          dst += bounds->width * bpp;
++        }
++    }
++  else
++    {
++      memcpy (data, cr_data, clip_rect.height * cr_stride);
++    }
++
++  cairo_surface_destroy (image);
++}
++
++static void
++screen_cast_window_iface_init (MetaScreenCastWindowInterface *iface)
++{
++  iface->get_buffer_bounds = meta_window_actor_get_buffer_bounds;
++  iface->get_frame_bounds = meta_window_actor_get_frame_bounds;
++  iface->transform_relative_position = meta_window_actor_transform_relative_position;
++  iface->capture_into = meta_window_actor_capture_into;
++}
+-- 
+2.19.2
+
diff --git a/SOURCES/0007-backends-native-update-cursor-on-resume.patch b/SOURCES/0007-backends-native-update-cursor-on-resume.patch
new file mode 100644
index 0000000..3f5b387
--- /dev/null
+++ b/SOURCES/0007-backends-native-update-cursor-on-resume.patch
@@ -0,0 +1,176 @@
+From b5ea81b0356884dca6d362d61df4f68e9e84e0b3 Mon Sep 17 00:00:00 2001
+From: Ray Strode <rstrode@redhat.com>
+Date: Tue, 15 Jan 2019 10:29:55 -0500
+Subject: [PATCH 7/9] backends/native: update cursor on resume
+
+As mentioned in a previous commit, the proprietary NVIDIA
+driver garbles memory on suspend. That behavior, means that
+the cursor gets corrupted on suspend.
+
+This commit forces the cursor to redraw itself when the
+logind session becomes active (on VT switch and resume).
+---
+ src/backends/native/meta-backend-native.c | 1 +
+ src/core/screen-private.h                 | 2 --
+ src/meta/screen.h                         | 3 +++
+ 3 files changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/src/backends/native/meta-backend-native.c b/src/backends/native/meta-backend-native.c
+index 0730a4acb..fd95dfc11 100644
+--- a/src/backends/native/meta-backend-native.c
++++ b/src/backends/native/meta-backend-native.c
+@@ -628,44 +628,45 @@ meta_backend_native_pause (MetaBackendNative *native)
+ {
+   MetaBackend *backend = META_BACKEND (native);
+   MetaMonitorManager *monitor_manager =
+     meta_backend_get_monitor_manager (backend);
+   MetaMonitorManagerKms *monitor_manager_kms =
+     META_MONITOR_MANAGER_KMS (monitor_manager);
+ 
+   clutter_evdev_release_devices ();
+   clutter_egl_freeze_master_clock ();
+ 
+   meta_monitor_manager_kms_pause (monitor_manager_kms);
+ }
+ 
+ void meta_backend_native_resume (MetaBackendNative *native)
+ {
+   MetaBackend *backend = META_BACKEND (native);
+   MetaMonitorManager *monitor_manager =
+     meta_backend_get_monitor_manager (backend);
+   MetaMonitorManagerKms *monitor_manager_kms =
+     META_MONITOR_MANAGER_KMS (monitor_manager);
+   MetaDisplay *display = meta_get_display ();
+   ClutterBackend *clutter_backend = clutter_get_default_backend ();
+   CoglContext *cogl_context =
+     clutter_backend_get_cogl_context (clutter_backend);
+   ClutterActor *stage;
+   MetaIdleMonitor *idle_monitor;
+ 
+   if (cogl_has_feature (cogl_context, COGL_FEATURE_ID_UNSTABLE_TEXTURES))
+     {
+       clutter_clear_glyph_cache ();
++      meta_screen_update_cursor (display->screen);
+       g_signal_emit_by_name (display, "gl-video-memory-purged");
+     }
+ 
+   meta_monitor_manager_kms_resume (monitor_manager_kms);
+ 
+   clutter_evdev_reclaim_devices ();
+   clutter_egl_thaw_master_clock ();
+ 
+   stage = meta_backend_get_stage (backend);
+   clutter_actor_queue_redraw (stage);
+ 
+   idle_monitor = meta_backend_get_idle_monitor (backend, 0);
+   meta_idle_monitor_reset_idletime (idle_monitor);
+ }
+diff --git a/src/core/screen-private.h b/src/core/screen-private.h
+index ff796f01a..37f2a01a1 100644
+--- a/src/core/screen-private.h
++++ b/src/core/screen-private.h
+@@ -95,62 +95,60 @@ struct _MetaScreen
+   int closing;
+ 
+   /* Instead of unmapping withdrawn windows we can leave them mapped
+    * and restack them below a guard window. When using a compositor
+    * this allows us to provide live previews of unmapped windows */
+   Window guard_window;
+ 
+   Window composite_overlay_window;
+ };
+ 
+ struct _MetaScreenClass
+ {
+   GObjectClass parent_class;
+ 
+   void (*restacked)         (MetaScreen *);
+   void (*workareas_changed) (MetaScreen *);
+   void (*monitors_changed)  (MetaScreen *);
+ };
+ 
+ MetaScreen*   meta_screen_new                 (MetaDisplay                *display,
+                                                guint32                     timestamp);
+ void          meta_screen_free                (MetaScreen                 *screen,
+                                                guint32                     timestamp);
+ void          meta_screen_init_workspaces     (MetaScreen                 *screen);
+ void          meta_screen_manage_all_windows  (MetaScreen                 *screen);
+ void          meta_screen_foreach_window      (MetaScreen                 *screen,
+                                                MetaListWindowsFlags        flags,
+                                                MetaScreenWindowFunc        func,
+                                                gpointer                    data);
+ 
+-void          meta_screen_update_cursor       (MetaScreen                 *screen);
+-
+ void          meta_screen_update_tile_preview          (MetaScreen    *screen,
+                                                         gboolean       delay);
+ void          meta_screen_hide_tile_preview            (MetaScreen    *screen);
+ 
+ MetaWindow*   meta_screen_get_mouse_window     (MetaScreen                 *screen,
+                                                 MetaWindow                 *not_this_one);
+ 
+ void          meta_screen_update_workspace_layout (MetaScreen             *screen);
+ void          meta_screen_update_workspace_names  (MetaScreen             *screen);
+ void          meta_screen_queue_workarea_recalc   (MetaScreen             *screen);
+ void          meta_screen_queue_check_fullscreen  (MetaScreen             *screen);
+ 
+ 
+ Window meta_create_offscreen_window (Display *xdisplay,
+                                      Window   parent,
+                                      long     valuemask);
+ 
+ typedef struct MetaWorkspaceLayout MetaWorkspaceLayout;
+ 
+ struct MetaWorkspaceLayout
+ {
+   int rows;
+   int cols;
+   int *grid;
+   int grid_area;
+   int current_row;
+   int current_col;
+ };
+ 
+ void meta_screen_calc_workspace_layout (MetaScreen          *screen,
+diff --git a/src/meta/screen.h b/src/meta/screen.h
+index 13c92516e..36c397418 100644
+--- a/src/meta/screen.h
++++ b/src/meta/screen.h
+@@ -98,31 +98,34 @@ int meta_screen_get_monitor_neighbor_index (MetaScreen *screen,
+                                             int         which_monitor,
+                                             MetaScreenDirection dir);
+ 
+ void meta_screen_focus_default_window (MetaScreen *screen,
+                                        guint32     timestamp);
+ 
+ /**
+  * MetaScreenCorner:
+  * @META_SCREEN_TOPLEFT: top-left corner
+  * @META_SCREEN_TOPRIGHT: top-right corner
+  * @META_SCREEN_BOTTOMLEFT: bottom-left corner
+  * @META_SCREEN_BOTTOMRIGHT: bottom-right corner
+  */
+ typedef enum
+ {
+   META_SCREEN_TOPLEFT,
+   META_SCREEN_TOPRIGHT,
+   META_SCREEN_BOTTOMLEFT,
+   META_SCREEN_BOTTOMRIGHT
+ } MetaScreenCorner;
+ 
+ void meta_screen_override_workspace_layout (MetaScreen      *screen,
+                                             MetaScreenCorner starting_corner,
+                                             gboolean         vertical_layout,
+                                             int              n_rows,
+                                             int              n_columns);
+ 
+ void          meta_screen_set_cursor          (MetaScreen                 *screen,
+                                                MetaCursor                  cursor);
+ 
++void          meta_screen_update_cursor       (MetaScreen                 *screen);
++
++
+ #endif
+-- 
+2.18.1
+
diff --git a/SOURCES/0007-screen-cast-session-Add-screen-cast-window-mode.patch b/SOURCES/0007-screen-cast-session-Add-screen-cast-window-mode.patch
new file mode 100644
index 0000000..cc63492
--- /dev/null
+++ b/SOURCES/0007-screen-cast-session-Add-screen-cast-window-mode.patch
@@ -0,0 +1,659 @@
+From fab59c0866bd13b944f0e9a3a251ba1a70ca31d4 Mon Sep 17 00:00:00 2001
+From: Olivier Fourdan <ofourdan@redhat.com>
+Date: Tue, 9 Oct 2018 15:35:14 +0200
+Subject: [PATCH 07/12] screen-cast-session: Add screen-cast window mode
+
+Window mode will cast the content of a single window using the
+`MetaScreenCastWindow` interface.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/306
+(cherry picked from commit dbe7279c7fe6fb793292cd6740c900e6f0c21975)
+---
+ src/Makefile.am                               |   4 +
+ .../meta-screen-cast-window-stream-src.c      | 245 ++++++++++++++++
+ .../meta-screen-cast-window-stream-src.h      |  37 +++
+ src/backends/meta-screen-cast-window-stream.c | 270 ++++++++++++++++++
+ src/backends/meta-screen-cast-window-stream.h |  43 +++
+ 5 files changed, 599 insertions(+)
+ create mode 100644 src/backends/meta-screen-cast-window-stream-src.c
+ create mode 100644 src/backends/meta-screen-cast-window-stream-src.h
+ create mode 100644 src/backends/meta-screen-cast-window-stream.c
+ create mode 100644 src/backends/meta-screen-cast-window-stream.h
+
+diff --git a/src/Makefile.am b/src/Makefile.am
+index 93586a2..0606efa 100644
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -372,6 +372,10 @@ libmutter_@LIBMUTTER_API_VERSION@_la_SOURCES +=	\
+ 	backends/meta-screen-cast-monitor-stream.h	\
+ 	backends/meta-screen-cast-monitor-stream-src.c	\
+ 	backends/meta-screen-cast-monitor-stream-src.h	\
++	backends/meta-screen-cast-window-stream-src.c	\
++	backends/meta-screen-cast-window-stream-src.h	\
++	backends/meta-screen-cast-window-stream.c	\
++	backends/meta-screen-cast-window-stream.h	\
+ 	backends/meta-screen-cast-session.c	\
+ 	backends/meta-screen-cast-session.h	\
+ 	backends/meta-screen-cast-stream.c	\
+diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c
+new file mode 100644
+index 0000000..c3f9cf5
+--- /dev/null
++++ b/src/backends/meta-screen-cast-window-stream-src.c
+@@ -0,0 +1,245 @@
++/*
++ * Copyright (C) 2018 Red Hat Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program 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
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
++ * 02111-1307, USA.
++ *
++ */
++
++#include "config.h"
++
++#include "backends/meta-screen-cast-window-stream-src.h"
++
++#include "backends/meta-backend-private.h"
++#include "backends/meta-screen-cast-window.h"
++#include "backends/meta-screen-cast-window-stream.h"
++#include "compositor/meta-window-actor-private.h"
++
++struct _MetaScreenCastWindowStreamSrc
++{
++  MetaScreenCastStreamSrc parent;
++
++  MetaWindowActor *window_actor;
++
++  unsigned long actor_painted_handler_id;
++  unsigned long actor_destroyed_handler_id;
++};
++
++G_DEFINE_TYPE (MetaScreenCastWindowStreamSrc,
++               meta_screen_cast_window_stream_src,
++               META_TYPE_SCREEN_CAST_STREAM_SRC)
++
++static MetaScreenCastWindowStream *
++get_window_stream (MetaScreenCastWindowStreamSrc *window_src)
++{
++  MetaScreenCastStreamSrc *src;
++  MetaScreenCastStream *stream;
++
++  src = META_SCREEN_CAST_STREAM_SRC (window_src);
++  stream = meta_screen_cast_stream_src_get_stream (src);
++
++  return META_SCREEN_CAST_WINDOW_STREAM (stream);
++}
++
++static MetaWindow *
++get_window (MetaScreenCastWindowStreamSrc *window_src)
++{
++  MetaScreenCastWindowStream *window_stream;
++
++  window_stream = get_window_stream (window_src);
++
++  return meta_screen_cast_window_stream_get_window (window_stream);
++}
++
++static int
++get_stream_width (MetaScreenCastWindowStreamSrc *window_src)
++{
++  MetaScreenCastWindowStream *window_stream;
++
++  window_stream = get_window_stream (window_src);
++
++  return meta_screen_cast_window_stream_get_width (window_stream);
++}
++
++static int
++get_stream_height (MetaScreenCastWindowStreamSrc *window_src)
++{
++  MetaScreenCastWindowStream *window_stream;
++
++  window_stream = get_window_stream (window_src);
++
++  return meta_screen_cast_window_stream_get_height (window_stream);
++}
++
++static gboolean
++capture_into (MetaScreenCastWindowStreamSrc *window_src,
++              uint8_t                       *data)
++{
++  MetaRectangle stream_rect;
++  MetaScreenCastWindow *screen_cast_window;
++
++  stream_rect.x = 0;
++  stream_rect.y = 0;
++  stream_rect.width = get_stream_width (window_src);
++  stream_rect.height = get_stream_height (window_src);
++
++  screen_cast_window = META_SCREEN_CAST_WINDOW (window_src->window_actor);
++  meta_screen_cast_window_capture_into (screen_cast_window, &stream_rect, data);
++
++  return TRUE;
++}
++
++static void
++meta_screen_cast_window_stream_src_get_specs (MetaScreenCastStreamSrc *src,
++                                              int                     *width,
++                                              int                     *height,
++                                              float                   *frame_rate)
++{
++  MetaScreenCastWindowStreamSrc *window_src =
++    META_SCREEN_CAST_WINDOW_STREAM_SRC (src);
++
++  *width = get_stream_width (window_src);
++  *height = get_stream_height (window_src);
++  *frame_rate = 60.0f;
++}
++
++static gboolean
++meta_screen_cast_window_stream_src_get_videocrop (MetaScreenCastStreamSrc *src,
++                                                  MetaRectangle           *crop_rect)
++{
++  MetaScreenCastWindowStreamSrc *window_src =
++    META_SCREEN_CAST_WINDOW_STREAM_SRC (src);
++  MetaScreenCastWindow *screen_cast_window;
++  MetaRectangle stream_rect;
++
++  screen_cast_window = META_SCREEN_CAST_WINDOW (window_src->window_actor);
++  meta_screen_cast_window_get_frame_bounds (screen_cast_window, crop_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);
++
++  meta_rectangle_intersect (crop_rect, &stream_rect, crop_rect);
++
++  return TRUE;
++}
++
++static void
++meta_screen_cast_window_stream_src_stop (MetaScreenCastWindowStreamSrc *window_src)
++
++{
++  if (!window_src->window_actor)
++    return;
++
++  if (window_src->actor_painted_handler_id)
++    g_signal_handler_disconnect (window_src->window_actor,
++                                 window_src->actor_painted_handler_id);
++  window_src->actor_painted_handler_id = 0;
++
++  if (window_src->actor_destroyed_handler_id)
++    g_signal_handler_disconnect (window_src->window_actor,
++                                 window_src->actor_destroyed_handler_id);
++  window_src->actor_destroyed_handler_id = 0;
++}
++
++static void
++window_actor_painted (MetaWindowActor               *actor,
++                      MetaScreenCastWindowStreamSrc *window_src)
++{
++  MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (window_src);
++
++  meta_screen_cast_stream_src_maybe_record_frame (src);
++}
++
++static void
++window_actor_destroyed (MetaWindowActor               *actor,
++                        MetaScreenCastWindowStreamSrc *window_src)
++{
++  meta_screen_cast_window_stream_src_stop (window_src);
++  window_src->window_actor = NULL;
++}
++
++static void
++meta_screen_cast_window_stream_src_enable (MetaScreenCastStreamSrc *src)
++{
++  MetaScreenCastWindowStreamSrc *window_src =
++    META_SCREEN_CAST_WINDOW_STREAM_SRC (src);
++  MetaWindowActor *window_actor;
++
++  window_actor = meta_window_actor_from_window (get_window (window_src));
++  if (!window_actor)
++    return;
++
++  window_src->window_actor = window_actor;
++
++  window_src->actor_painted_handler_id =
++    g_signal_connect_after (window_src->window_actor,
++                            "paint",
++                            G_CALLBACK (window_actor_painted),
++                            window_src);
++
++  window_src->actor_destroyed_handler_id =
++    g_signal_connect (window_src->window_actor,
++                      "destroy",
++                      G_CALLBACK (window_actor_destroyed),
++                      window_src);
++}
++
++static void
++meta_screen_cast_window_stream_src_disable (MetaScreenCastStreamSrc *src)
++{
++  MetaScreenCastWindowStreamSrc *window_src =
++    META_SCREEN_CAST_WINDOW_STREAM_SRC (src);
++
++  meta_screen_cast_window_stream_src_stop (window_src);
++}
++
++static void
++meta_screen_cast_window_stream_src_record_frame (MetaScreenCastStreamSrc *src,
++                                                 uint8_t                 *data)
++{
++  MetaScreenCastWindowStreamSrc *window_src =
++    META_SCREEN_CAST_WINDOW_STREAM_SRC (src);
++
++  capture_into (window_src, data);
++}
++
++MetaScreenCastWindowStreamSrc *
++meta_screen_cast_window_stream_src_new (MetaScreenCastWindowStream  *window_stream,
++                                        GError                     **error)
++{
++  return g_initable_new (META_TYPE_SCREEN_CAST_WINDOW_STREAM_SRC, NULL, error,
++                         "stream", window_stream,
++                         NULL);
++}
++
++static void
++meta_screen_cast_window_stream_src_init (MetaScreenCastWindowStreamSrc *window_src)
++{
++}
++
++static void
++meta_screen_cast_window_stream_src_class_init (MetaScreenCastWindowStreamSrcClass *klass)
++{
++  MetaScreenCastStreamSrcClass *src_class =
++    META_SCREEN_CAST_STREAM_SRC_CLASS (klass);
++
++  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->get_videocrop = meta_screen_cast_window_stream_src_get_videocrop;
++}
+diff --git a/src/backends/meta-screen-cast-window-stream-src.h b/src/backends/meta-screen-cast-window-stream-src.h
+new file mode 100644
+index 0000000..37f7869
+--- /dev/null
++++ b/src/backends/meta-screen-cast-window-stream-src.h
+@@ -0,0 +1,37 @@
++/*
++ * Copyright (C) 2018 Red Hat Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program 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
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
++ * 02111-1307, USA.
++ *
++ */
++
++#ifndef META_SCREEN_CAST_WINDOW_STREAM_SRC_H
++#define META_SCREEN_CAST_WINDOW_STREAM_SRC_H
++
++#include "backends/meta-screen-cast-stream-src.h"
++
++typedef struct _MetaScreenCastWindowStream MetaScreenCastWindowStream;
++
++#define META_TYPE_SCREEN_CAST_WINDOW_STREAM_SRC (meta_screen_cast_window_stream_src_get_type ())
++G_DECLARE_FINAL_TYPE (MetaScreenCastWindowStreamSrc,
++                      meta_screen_cast_window_stream_src,
++                      META, SCREEN_CAST_WINDOW_STREAM_SRC,
++                      MetaScreenCastStreamSrc)
++
++MetaScreenCastWindowStreamSrc * meta_screen_cast_window_stream_src_new (MetaScreenCastWindowStream  *window_stream,
++                                                                        GError                     **error);
++
++#endif /* META_SCREEN_CAST_WINDOW_STREAM_SRC_H */
+diff --git a/src/backends/meta-screen-cast-window-stream.c b/src/backends/meta-screen-cast-window-stream.c
+new file mode 100644
+index 0000000..1200a39
+--- /dev/null
++++ b/src/backends/meta-screen-cast-window-stream.c
+@@ -0,0 +1,270 @@
++/*
++ * Copyright (C) 2018 Red Hat Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program 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
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
++ * 02111-1307, USA.
++ *
++ */
++
++#include "config.h"
++
++#include "backends/meta-screen-cast-window-stream.h"
++
++#include "backends/meta-logical-monitor.h"
++#include "backends/meta-monitor-manager-private.h"
++#include "backends/meta-screen-cast-window.h"
++#include "backends/meta-screen-cast-window-stream-src.h"
++#include "compositor/meta-window-actor-private.h"
++#include "core/window-private.h"
++
++enum
++{
++  PROP_0,
++
++  PROP_WINDOW,
++};
++
++struct _MetaScreenCastWindowStream
++{
++  MetaScreenCastStream parent;
++
++  MetaWindow *window;
++
++  int stream_width;
++  int stream_height;
++
++  unsigned long window_unmanaged_handler_id;
++};
++
++G_DEFINE_TYPE (MetaScreenCastWindowStream,
++               meta_screen_cast_window_stream,
++               META_TYPE_SCREEN_CAST_STREAM)
++
++MetaWindow *
++meta_screen_cast_window_stream_get_window (MetaScreenCastWindowStream *window_stream)
++{
++  return window_stream->window;
++}
++
++int
++meta_screen_cast_window_stream_get_width (MetaScreenCastWindowStream *window_stream)
++{
++  return window_stream->stream_width;
++}
++
++int
++meta_screen_cast_window_stream_get_height (MetaScreenCastWindowStream *window_stream)
++{
++  return window_stream->stream_height;
++}
++
++MetaScreenCastWindowStream *
++meta_screen_cast_window_stream_new (GDBusConnection  *connection,
++                                    MetaWindow       *window,
++                                    GError          **error)
++{
++  MetaScreenCastWindowStream *window_stream;
++  MetaLogicalMonitor *logical_monitor;
++  int scale;
++
++  logical_monitor = meta_window_get_main_logical_monitor (window);
++  if (!logical_monitor)
++    {
++      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
++                   "Main logical monitor not found");
++      return NULL;
++    }
++
++  window_stream = g_initable_new (META_TYPE_SCREEN_CAST_WINDOW_STREAM,
++                                  NULL,
++                                  error,
++                                  "connection", connection,
++                                  "window", window,
++                                  NULL);
++  if (!window_stream)
++    return NULL;
++
++  window_stream->window = window;
++  /* 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->stream_width = logical_monitor->rect.width * scale;
++  window_stream->stream_height = logical_monitor->rect.height * scale;
++
++  return window_stream;
++}
++
++static MetaScreenCastStreamSrc *
++meta_screen_cast_window_stream_create_src (MetaScreenCastStream  *stream,
++                                           GError               **error)
++{
++  MetaScreenCastWindowStream *window_stream =
++    META_SCREEN_CAST_WINDOW_STREAM (stream);
++  MetaScreenCastWindowStreamSrc *window_stream_src;
++
++  window_stream_src = meta_screen_cast_window_stream_src_new (window_stream,
++                                                              error);
++  if (!window_stream_src)
++    return NULL;
++
++  return META_SCREEN_CAST_STREAM_SRC (window_stream_src);
++}
++
++static void
++meta_screen_cast_window_stream_set_parameters (MetaScreenCastStream *stream,
++                                               GVariantBuilder      *parameters_builder)
++{
++  MetaScreenCastWindowStream *window_stream =
++    META_SCREEN_CAST_WINDOW_STREAM (stream);
++  MetaScreenCastWindow *screen_cast_window =
++    META_SCREEN_CAST_WINDOW (meta_window_actor_from_window (window_stream->window));
++  MetaRectangle bounds;
++
++  meta_screen_cast_window_get_buffer_bounds (screen_cast_window, &bounds);
++
++  g_variant_builder_add (parameters_builder, "{sv}",
++                         "position",
++                         g_variant_new ("(ii)",
++                                        bounds.x, bounds.y));
++
++  g_variant_builder_add (parameters_builder, "{sv}",
++                         "size",
++                         g_variant_new ("(ii)",
++                                        bounds.width,
++                                        bounds.height));
++}
++
++static void
++meta_screen_cast_window_stream_transform_position (MetaScreenCastStream *stream,
++                                                   double                stream_x,
++                                                   double                stream_y,
++                                                   double               *x,
++                                                   double               *y)
++{
++  MetaScreenCastWindowStream *window_stream =
++    META_SCREEN_CAST_WINDOW_STREAM (stream);
++  MetaScreenCastWindow *screen_cast_window =
++    META_SCREEN_CAST_WINDOW (meta_window_actor_from_window (window_stream->window));
++
++  meta_screen_cast_window_transform_relative_position (screen_cast_window,
++                                                       stream_x,
++                                                       stream_y,
++                                                       x,
++                                                       y);
++}
++
++static void
++on_window_unmanaged (MetaScreenCastWindowStream *window_stream)
++{
++  meta_screen_cast_stream_close (META_SCREEN_CAST_STREAM (window_stream));
++}
++
++static void
++meta_screen_cast_window_stream_constructed (GObject *object)
++{
++  MetaScreenCastWindowStream *window_stream =
++    META_SCREEN_CAST_WINDOW_STREAM (object);
++
++  window_stream->window_unmanaged_handler_id =
++    g_signal_connect_swapped (window_stream->window, "unmanaged",
++                              G_CALLBACK (on_window_unmanaged),
++                              window_stream);
++
++  G_OBJECT_CLASS (meta_screen_cast_window_stream_parent_class)->constructed (object);
++}
++
++static void
++meta_screen_cast_window_stream_set_property (GObject      *object,
++                                             guint         prop_id,
++                                             const GValue *value,
++                                             GParamSpec   *pspec)
++{
++  MetaScreenCastWindowStream *window_stream =
++    META_SCREEN_CAST_WINDOW_STREAM (object);
++
++  switch (prop_id)
++    {
++    case PROP_WINDOW:
++      window_stream->window = g_value_get_object (value);
++      break;
++    default:
++      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
++    }
++}
++
++static void
++meta_screen_cast_window_stream_get_property (GObject    *object,
++                                             guint       prop_id,
++                                             GValue     *value,
++                                             GParamSpec *pspec)
++{
++  MetaScreenCastWindowStream *window_stream =
++    META_SCREEN_CAST_WINDOW_STREAM (object);
++
++  switch (prop_id)
++    {
++    case PROP_WINDOW:
++      g_value_set_object (value, window_stream->window);
++      break;
++    default:
++      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
++    }
++}
++
++static void
++meta_screen_cast_window_stream_finalize (GObject *object)
++{
++  MetaScreenCastWindowStream *window_stream =
++    META_SCREEN_CAST_WINDOW_STREAM (object);
++
++  g_signal_handler_disconnect (window_stream->window,
++                               window_stream->window_unmanaged_handler_id);
++
++  G_OBJECT_CLASS (meta_screen_cast_window_stream_parent_class)->finalize (object);
++}
++
++static void
++meta_screen_cast_window_stream_init (MetaScreenCastWindowStream *window_stream)
++{
++}
++
++static void
++meta_screen_cast_window_stream_class_init (MetaScreenCastWindowStreamClass *klass)
++{
++  GObjectClass *object_class = G_OBJECT_CLASS (klass);
++  MetaScreenCastStreamClass *stream_class =
++    META_SCREEN_CAST_STREAM_CLASS (klass);
++
++  object_class->constructed = meta_screen_cast_window_stream_constructed;
++  object_class->set_property = meta_screen_cast_window_stream_set_property;
++  object_class->get_property = meta_screen_cast_window_stream_get_property;
++  object_class->finalize = meta_screen_cast_window_stream_finalize;
++
++  stream_class->create_src = meta_screen_cast_window_stream_create_src;
++  stream_class->set_parameters = meta_screen_cast_window_stream_set_parameters;
++  stream_class->transform_position = meta_screen_cast_window_stream_transform_position;
++
++  g_object_class_install_property (object_class,
++                                   PROP_WINDOW,
++                                   g_param_spec_object ("window",
++                                                        "window",
++                                                        "MetaWindow",
++                                                        META_TYPE_WINDOW,
++                                                        G_PARAM_READWRITE |
++                                                        G_PARAM_CONSTRUCT_ONLY |
++                                                        G_PARAM_STATIC_STRINGS));
++}
+diff --git a/src/backends/meta-screen-cast-window-stream.h b/src/backends/meta-screen-cast-window-stream.h
+new file mode 100644
+index 0000000..6726ef8
+--- /dev/null
++++ b/src/backends/meta-screen-cast-window-stream.h
+@@ -0,0 +1,43 @@
++/*
++ * Copyright (C) 2018 Red Hat Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program 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
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
++ * 02111-1307, USA.
++ *
++ */
++
++#ifndef META_SCREEN_CAST_WINDOW_STREAM_H
++#define META_SCREEN_CAST_WINDOW_STREAM_H
++
++#include <glib-object.h>
++
++#include "backends/meta-screen-cast-stream.h"
++#include "meta/window.h"
++
++#define META_TYPE_SCREEN_CAST_WINDOW_STREAM (meta_screen_cast_window_stream_get_type ())
++G_DECLARE_FINAL_TYPE (MetaScreenCastWindowStream,
++                      meta_screen_cast_window_stream,
++                      META, SCREEN_CAST_WINDOW_STREAM,
++                      MetaScreenCastStream)
++
++MetaScreenCastWindowStream * meta_screen_cast_window_stream_new (GDBusConnection  *connection,
++                                                                 MetaWindow       *window,
++                                                                 GError          **error);
++
++MetaWindow  * meta_screen_cast_window_stream_get_window (MetaScreenCastWindowStream *window_stream);
++int           meta_screen_cast_window_stream_get_width  (MetaScreenCastWindowStream *window_stream);
++int           meta_screen_cast_window_stream_get_height (MetaScreenCastWindowStream *window_stream);
++
++#endif /* META_SCREEN_CAST_WINDOW_STREAM_H */
+-- 
+2.19.2
+
diff --git a/SOURCES/0007-wayland-legacy-xdg-shell-Handle-requests-after-tople.patch b/SOURCES/0007-wayland-legacy-xdg-shell-Handle-requests-after-tople.patch
new file mode 100644
index 0000000..aeb9517
--- /dev/null
+++ b/SOURCES/0007-wayland-legacy-xdg-shell-Handle-requests-after-tople.patch
@@ -0,0 +1,246 @@
+From a22cb8832de7ff7323f87888afd91a3b94629530 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Wed, 25 Jul 2018 13:24:20 +0200
+Subject: [PATCH 7/8] wayland/legacy-xdg-shell: Handle requests after toplevel
+ was unmanaged
+
+As with xdg-toplevel proper, a legacy xdg-toplevel can be unmanaged by
+the compositor without the client knowing about it, meaning the client
+may still send updates and make requests. Handle this gracefully by
+ignoring them. The client needs to reassign the surface the legacy
+xdg-toplevel role again, if it wants to remap the same surface, meaning
+all state would be reset anyway.
+
+Closes: https://gitlab.gnome.org/GNOME/mutter/issues/240
+(cherry picked from commit 64df6276881c5f42c6d2054d556d8cd391f7ee70)
+---
+ src/wayland/meta-wayland-legacy-xdg-shell.c | 93 +++++++++++++++++----
+ 1 file changed, 79 insertions(+), 14 deletions(-)
+
+diff --git a/src/wayland/meta-wayland-legacy-xdg-shell.c b/src/wayland/meta-wayland-legacy-xdg-shell.c
+index 861270193..addd0855f 100644
+--- a/src/wayland/meta-wayland-legacy-xdg-shell.c
++++ b/src/wayland/meta-wayland-legacy-xdg-shell.c
+@@ -185,6 +185,11 @@ zxdg_toplevel_v6_set_parent (struct wl_client   *client,
+ {
+   MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
+   MetaWindow *transient_for = NULL;
++  MetaWindow *window;
++
++  window = surface->window;
++  if (!window)
++    return;
+ 
+   if (parent_resource)
+     {
+@@ -194,7 +199,7 @@ zxdg_toplevel_v6_set_parent (struct wl_client   *client,
+       transient_for = parent_surface->window;
+     }
+ 
+-  meta_window_set_transient_for (surface->window, transient_for);
++  meta_window_set_transient_for (window, transient_for);
+ }
+ 
+ static void
+@@ -203,11 +208,16 @@ zxdg_toplevel_v6_set_title (struct wl_client   *client,
+                             const char         *title)
+ {
+   MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
++  MetaWindow *window;
++
++  window = surface->window;
++  if (!window)
++    return;
+ 
+   if (!g_utf8_validate (title, -1, NULL))
+     title = "";
+ 
+-  meta_window_set_title (surface->window, title);
++  meta_window_set_title (window, title);
+ }
+ 
+ static void
+@@ -216,11 +226,16 @@ zxdg_toplevel_v6_set_app_id (struct wl_client   *client,
+                              const char         *app_id)
+ {
+   MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
++  MetaWindow *window;
++
++  window = surface->window;
++  if (!window)
++    return;
+ 
+   if (!g_utf8_validate (app_id, -1, NULL))
+     app_id = "";
+ 
+-  meta_window_set_wm_class (surface->window, app_id, app_id);
++  meta_window_set_wm_class (window, app_id, app_id);
+ }
+ 
+ static void
+@@ -233,15 +248,20 @@ zxdg_toplevel_v6_show_window_menu (struct wl_client   *client,
+ {
+   MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource);
+   MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
++  MetaWindow *window;
+   int monitor_scale;
+ 
++  window = surface->window;
++  if (!window)
++    return;
++
+   if (!meta_wayland_seat_get_grab_info (seat, surface, serial, FALSE, NULL, NULL))
+     return;
+ 
+-  monitor_scale = surface->window->monitor->scale;
+-  meta_window_show_menu (surface->window, META_WINDOW_MENU_WM,
+-                         surface->window->buffer_rect.x + (x * monitor_scale),
+-                         surface->window->buffer_rect.y + (y * monitor_scale));
++  monitor_scale = window->monitor->scale;
++  meta_window_show_menu (window, META_WINDOW_MENU_WM,
++                         window->buffer_rect.x + (x * monitor_scale),
++                         window->buffer_rect.y + (y * monitor_scale));
+ }
+ 
+ static void
+@@ -252,8 +272,13 @@ zxdg_toplevel_v6_move (struct wl_client   *client,
+ {
+   MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource);
+   MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
++  MetaWindow *window;
+   gfloat x, y;
+ 
++  window = surface->window;
++  if (!window)
++    return;
++
+   if (!meta_wayland_seat_get_grab_info (seat, surface, serial, TRUE, &x, &y))
+     return;
+ 
+@@ -292,9 +317,14 @@ zxdg_toplevel_v6_resize (struct wl_client   *client,
+ {
+   MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource);
+   MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
++  MetaWindow *window;
+   gfloat x, y;
+   MetaGrabOp grab_op;
+ 
++  window = surface->window;
++  if (!window)
++    return;
++
+   if (!meta_wayland_seat_get_grab_info (seat, surface, serial, TRUE, &x, &y))
+     return;
+ 
+@@ -309,6 +339,11 @@ zxdg_toplevel_v6_set_max_size (struct wl_client   *client,
+                                int32_t             height)
+ {
+   MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
++  MetaWindow *window;
++
++  window = surface->window;
++  if (!window)
++    return;
+ 
+   if (width < 0 || height < 0)
+     {
+@@ -331,6 +366,11 @@ zxdg_toplevel_v6_set_min_size (struct wl_client   *client,
+                                int32_t             height)
+ {
+   MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
++  MetaWindow *window;
++
++  window = surface->window;
++  if (!window)
++    return;
+ 
+   if (width < 0 || height < 0)
+     {
+@@ -351,9 +391,14 @@ zxdg_toplevel_v6_set_maximized (struct wl_client   *client,
+                                 struct wl_resource *resource)
+ {
+   MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
++  MetaWindow *window;
+ 
+-  meta_window_force_placement (surface->window, TRUE);
+-  meta_window_maximize (surface->window, META_MAXIMIZE_BOTH);
++  window = surface->window;
++  if (!window)
++    return;
++
++  meta_window_force_placement (window, TRUE);
++  meta_window_maximize (window, META_MAXIMIZE_BOTH);
+ }
+ 
+ static void
+@@ -361,8 +406,13 @@ zxdg_toplevel_v6_unset_maximized (struct wl_client   *client,
+                                   struct wl_resource *resource)
+ {
+   MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
++  MetaWindow *window;
++
++  window = surface->window;
++  if (!window)
++    return;
+ 
+-  meta_window_unmaximize (surface->window, META_MAXIMIZE_BOTH);
++  meta_window_unmaximize (window, META_MAXIMIZE_BOTH);
+ }
+ 
+ static void
+@@ -371,15 +421,20 @@ zxdg_toplevel_v6_set_fullscreen (struct wl_client   *client,
+                                  struct wl_resource *output_resource)
+ {
+   MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
++  MetaWindow *window;
++
++  window = surface->window;
++  if (!window)
++    return;
+ 
+   if (output_resource)
+     {
+       MetaWaylandOutput *output = wl_resource_get_user_data (output_resource);
+       if (output)
+-        meta_window_move_to_monitor (surface->window, output->logical_monitor->number);
++        meta_window_move_to_monitor (window, output->logical_monitor->number);
+     }
+ 
+-  meta_window_make_fullscreen (surface->window);
++  meta_window_make_fullscreen (window);
+ }
+ 
+ static void
+@@ -387,8 +442,13 @@ zxdg_toplevel_v6_unset_fullscreen (struct wl_client   *client,
+                                    struct wl_resource *resource)
+ {
+   MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
++  MetaWindow *window;
++
++  window = surface->window;
++  if (!window)
++    return;
+ 
+-  meta_window_unmake_fullscreen (surface->window);
++  meta_window_unmake_fullscreen (window);
+ }
+ 
+ static void
+@@ -396,8 +456,13 @@ zxdg_toplevel_v6_set_minimized (struct wl_client   *client,
+                                 struct wl_resource *resource)
+ {
+   MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
++  MetaWindow *window;
++
++  window = surface->window;
++  if (!window)
++    return;
+ 
+-  meta_window_minimize (surface->window);
++  meta_window_minimize (window);
+ }
+ 
+ static const struct zxdg_toplevel_v6_interface meta_wayland_zxdg_toplevel_v6_interface = {
+-- 
+2.19.0
+
diff --git a/SOURCES/0008-background-purge-all-background-textures-on-suspend.patch b/SOURCES/0008-background-purge-all-background-textures-on-suspend.patch
new file mode 100644
index 0000000..03aeef8
--- /dev/null
+++ b/SOURCES/0008-background-purge-all-background-textures-on-suspend.patch
@@ -0,0 +1,276 @@
+From 6bf00273a995597c22b3e98d9f4752fbb238f5ea Mon Sep 17 00:00:00 2001
+From: Ray Strode <rstrode@redhat.com>
+Date: Wed, 9 Jan 2019 16:57:05 -0500
+Subject: [PATCH 8/9] background: purge all background textures on suspend
+
+This commit makes sure all background textures get purged
+on suspend, which is important for nvidia.
+---
+ src/compositor/meta-background-image.c | 28 ++++++++++++++++++++++++++
+ src/compositor/meta-background.c       | 19 ++++++++++++++++-
+ src/meta/meta-background-image.h       |  2 ++
+ 3 files changed, 48 insertions(+), 1 deletion(-)
+
+diff --git a/src/compositor/meta-background-image.c b/src/compositor/meta-background-image.c
+index b06066422..995e588fa 100644
+--- a/src/compositor/meta-background-image.c
++++ b/src/compositor/meta-background-image.c
+@@ -252,60 +252,88 @@ meta_background_image_cache_load (MetaBackgroundImageCache *cache,
+   g_object_unref (task);
+ 
+   return image;
+ }
+ 
+ /**
+  * meta_background_image_cache_purge:
+  * @cache: a #MetaBackgroundImageCache
+  * @file: file to remove from the cache
+  *
+  * Remove an entry from the cache; this would be used if monitoring
+  * showed that the file changed.
+  */
+ void
+ meta_background_image_cache_purge (MetaBackgroundImageCache *cache,
+                                    GFile                    *file)
+ {
+   MetaBackgroundImage *image;
+ 
+   g_return_if_fail (META_IS_BACKGROUND_IMAGE_CACHE (cache));
+   g_return_if_fail (file != NULL);
+ 
+   image = g_hash_table_lookup (cache->images, file);
+   if (image == NULL)
+     return;
+ 
+   g_hash_table_remove (cache->images, image->file);
+   image->in_cache = FALSE;
+ }
+ 
++/**
++ * meta_background_image_cache_unload_all:
++ * @cache: a #MetaBackgroundImageCache
++ *
++ * Remove all entries from the cache and unloads them; this would be used
++ * if textures in video memory have been invalidated.
++ */
++void
++meta_background_image_cache_unload_all (MetaBackgroundImageCache *cache)
++{
++  GHashTableIter iter;
++  gpointer key, value;
++
++  g_return_if_fail (META_IS_BACKGROUND_IMAGE_CACHE (cache));
++
++  g_hash_table_iter_init (&iter, cache->images);
++  while (g_hash_table_iter_next (&iter, &key, &value))
++    {
++      MetaBackgroundImage *image = value;
++
++      g_clear_pointer (&image->texture, cogl_object_unref);
++      image->in_cache = FALSE;
++      image->loaded = FALSE;
++    }
++
++  g_hash_table_remove_all (cache->images);
++}
++
+ G_DEFINE_TYPE (MetaBackgroundImage, meta_background_image, G_TYPE_OBJECT);
+ 
+ static void
+ meta_background_image_init (MetaBackgroundImage *image)
+ {
+ }
+ 
+ static void
+ meta_background_image_finalize (GObject *object)
+ {
+   MetaBackgroundImage *image = META_BACKGROUND_IMAGE (object);
+ 
+   if (image->in_cache)
+     g_hash_table_remove (image->cache->images, image->file);
+ 
+   if (image->texture)
+     cogl_object_unref (image->texture);
+   if (image->file)
+     g_object_unref (image->file);
+ 
+   G_OBJECT_CLASS (meta_background_image_parent_class)->finalize (object);
+ }
+ 
+ static void
+ meta_background_image_class_init (MetaBackgroundImageClass *klass)
+ {
+   GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ 
+   object_class->finalize = meta_background_image_finalize;
+ 
+diff --git a/src/compositor/meta-background.c b/src/compositor/meta-background.c
+index 61dd12095..3d23f1fdc 100644
+--- a/src/compositor/meta-background.c
++++ b/src/compositor/meta-background.c
+@@ -290,70 +290,84 @@ set_file (MetaBackground       *self,
+                             G_CALLBACK (on_background_loaded), self);
+         }
+     }
+ }
+ 
+ static void
+ meta_background_dispose (GObject *object)
+ {
+   MetaBackground        *self = META_BACKGROUND (object);
+   MetaBackgroundPrivate *priv = self->priv;
+ 
+   free_color_texture (self);
+   free_wallpaper_texture (self);
+ 
+   set_file (self, &priv->file1, &priv->background_image1, NULL);
+   set_file (self, &priv->file2, &priv->background_image2, NULL);
+ 
+   set_screen (self, NULL);
+ 
+   G_OBJECT_CLASS (meta_background_parent_class)->dispose (object);
+ }
+ 
+ static void
+ meta_background_finalize (GObject *object)
+ {
+   all_backgrounds = g_slist_remove (all_backgrounds, object);
+ 
+   G_OBJECT_CLASS (meta_background_parent_class)->finalize (object);
+ }
+ 
++static void
++free_textures (MetaBackground *self)
++{
++  MetaBackgroundPrivate *priv = self->priv;
++
++  free_color_texture (self);
++  free_wallpaper_texture (self);
++
++  set_file (self, &priv->file1, &priv->background_image1, NULL);
++  set_file (self, &priv->file2, &priv->background_image2, NULL);
++
++  mark_changed (self);
++}
++
+ static void
+ meta_background_constructed (GObject *object)
+ {
+   MetaBackground        *self = META_BACKGROUND (object);
+   MetaBackgroundPrivate *priv = self->priv;
+ 
+   G_OBJECT_CLASS (meta_background_parent_class)->constructed (object);
+ 
+   g_signal_connect_object (meta_screen_get_display (priv->screen), "gl-video-memory-purged",
+-                           G_CALLBACK (mark_changed), object, G_CONNECT_SWAPPED);
++                           G_CALLBACK (free_textures), object, G_CONNECT_SWAPPED);
+ }
+ 
+ static void
+ meta_background_class_init (MetaBackgroundClass *klass)
+ {
+   GObjectClass *object_class = G_OBJECT_CLASS (klass);
+   GParamSpec *param_spec;
+ 
+   g_type_class_add_private (klass, sizeof (MetaBackgroundPrivate));
+ 
+   object_class->dispose = meta_background_dispose;
+   object_class->finalize = meta_background_finalize;
+   object_class->constructed = meta_background_constructed;
+   object_class->set_property = meta_background_set_property;
+   object_class->get_property = meta_background_get_property;
+ 
+   signals[CHANGED] =
+     g_signal_new ("changed",
+                   G_TYPE_FROM_CLASS (object_class),
+                   G_SIGNAL_RUN_LAST,
+                   0,
+                   NULL, NULL, NULL,
+                   G_TYPE_NONE, 0);
+ 
+   param_spec = g_param_spec_object ("meta-screen",
+                                     "MetaScreen",
+                                     "MetaScreen",
+                                     META_TYPE_SCREEN,
+                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ 
+@@ -933,35 +947,38 @@ meta_background_set_file (MetaBackground            *self,
+   meta_background_set_blend (self, file, NULL, 0.0, style);
+ }
+ 
+ void
+ meta_background_set_blend (MetaBackground          *self,
+                            GFile                   *file1,
+                            GFile                   *file2,
+                            double                   blend_factor,
+                            GDesktopBackgroundStyle  style)
+ {
+   MetaBackgroundPrivate *priv;
+ 
+   g_return_if_fail (META_IS_BACKGROUND (self));
+   g_return_if_fail (blend_factor >= 0.0 && blend_factor <= 1.0);
+ 
+   priv = self->priv;
+ 
+   set_file (self, &priv->file1, &priv->background_image1, file1);
+   set_file (self, &priv->file2, &priv->background_image2, file2);
+ 
+   priv->blend_factor = blend_factor;
+   priv->style = style;
+ 
+   free_wallpaper_texture (self);
+   mark_changed (self);
+ }
+ 
+ void
+ meta_background_refresh_all (void)
+ {
++  MetaBackgroundImageCache *cache = meta_background_image_cache_get_default ();
+   GSList *l;
+ 
++  meta_background_image_cache_unload_all (cache);
++
+   for (l = all_backgrounds; l; l = l->next)
+     mark_changed (l->data);
+ }
+diff --git a/src/meta/meta-background-image.h b/src/meta/meta-background-image.h
+index fa67b42cf..5ecfb9753 100644
+--- a/src/meta/meta-background-image.h
++++ b/src/meta/meta-background-image.h
+@@ -46,31 +46,33 @@ GType meta_background_image_get_type (void);
+ gboolean     meta_background_image_is_loaded   (MetaBackgroundImage *image);
+ gboolean     meta_background_image_get_success (MetaBackgroundImage *image);
+ CoglTexture *meta_background_image_get_texture (MetaBackgroundImage *image);
+ 
+ #define META_TYPE_BACKGROUND_IMAGE_CACHE            (meta_background_image_cache_get_type ())
+ #define META_BACKGROUND_IMAGE_CACHE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_BACKGROUND_IMAGE_CACHE, MetaBackgroundImageCache))
+ #define META_BACKGROUND_IMAGE_CACHE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  META_TYPE_BACKGROUND_IMAGE_CACHE, MetaBackgroundImageCacheClass))
+ #define META_IS_BACKGROUND_IMAGE_CACHE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_BACKGROUND_IMAGE_CACHE))
+ #define META_IS_BACKGROUND_IMAGE_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  META_TYPE_BACKGROUND_IMAGE_CACHE))
+ #define META_BACKGROUND_IMAGE_CACHE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  META_TYPE_BACKGROUND_IMAGE_CACHE, MetaBackgroundImageCacheClass))
+ 
+ /**
+  * MetaBackgroundImageCache:
+  *
+  * #MetaBackgroundImageCache caches loading of textures for backgrounds; there's actually
+  * nothing background specific about it, other than it is tuned to work well for
+  * large images as typically are used for backgrounds.
+  */
+ typedef struct _MetaBackgroundImageCache      MetaBackgroundImageCache;
+ typedef struct _MetaBackgroundImageCacheClass MetaBackgroundImageCacheClass;
+ 
+ MetaBackgroundImageCache *meta_background_image_cache_get_default (void);
+ 
+ GType meta_background_image_cache_get_type (void);
+ 
+ MetaBackgroundImage *meta_background_image_cache_load  (MetaBackgroundImageCache *cache,
+                                                         GFile                    *file);
+ void                 meta_background_image_cache_purge (MetaBackgroundImageCache *cache,
+                                                         GFile                    *file);
+ 
++void                 meta_background_image_cache_unload_all (MetaBackgroundImageCache *cache);
++
+ #endif /* __META_BACKGROUND_IMAGE_H__ */
+-- 
+2.18.1
+
diff --git a/SOURCES/0008-screen-cast-session-Add-support-for-RecordWindow.patch b/SOURCES/0008-screen-cast-session-Add-support-for-RecordWindow.patch
new file mode 100644
index 0000000..9c5fbec
--- /dev/null
+++ b/SOURCES/0008-screen-cast-session-Add-support-for-RecordWindow.patch
@@ -0,0 +1,96 @@
+From c4e07c78940aa873a0258d45da7f417f483b8873 Mon Sep 17 00:00:00 2001
+From: Olivier Fourdan <ofourdan@redhat.com>
+Date: Fri, 28 Sep 2018 10:48:31 +0200
+Subject: [PATCH 08/12] screen-cast-session: Add support for RecordWindow
+
+Add support for the RecordWindow screencast method, casting the
+currently focused window.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/306
+(cherry picked from commit ec25f3a6b7923627484c6c1c65f8af0bebe56423)
+---
+ src/backends/meta-screen-cast-session.c | 52 +++++++++++++++++++++++--
+ 1 file changed, 48 insertions(+), 4 deletions(-)
+
+diff --git a/src/backends/meta-screen-cast-session.c b/src/backends/meta-screen-cast-session.c
+index e1b6393..3ee02c5 100644
+--- a/src/backends/meta-screen-cast-session.c
++++ b/src/backends/meta-screen-cast-session.c
+@@ -26,9 +26,11 @@
+ 
+ #include "backends/meta-backend-private.h"
+ #include "backends/meta-dbus-session-watcher.h"
++#include "backends/meta-remote-access-controller-private.h"
+ #include "backends/meta-screen-cast-monitor-stream.h"
+ #include "backends/meta-screen-cast-stream.h"
+-#include "backends/meta-remote-access-controller-private.h"
++#include "backends/meta-screen-cast-window-stream.h"
++#include "core/display-private.h"
+ 
+ #define META_SCREEN_CAST_SESSION_DBUS_PATH "/org/gnome/Mutter/ScreenCast/Session"
+ 
+@@ -333,6 +335,14 @@ handle_record_window (MetaDBusScreenCastSession *skeleton,
+                       GVariant                  *properties_variant)
+ {
+   MetaScreenCastSession *session = META_SCREEN_CAST_SESSION (skeleton);
++  GDBusInterfaceSkeleton *interface_skeleton;
++  GDBusConnection *connection;
++  MetaWindow *window;
++  GError *error = NULL;
++  MetaDisplay *display;
++  MetaScreenCastWindowStream *window_stream;
++  MetaScreenCastStream *stream;
++  char *stream_path;
+ 
+   if (!check_permission (session, invocation))
+     {
+@@ -342,9 +352,43 @@ handle_record_window (MetaDBusScreenCastSession *skeleton,
+       return TRUE;
+     }
+ 
+-  g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+-                                         G_DBUS_ERROR_FAILED,
+-                                         "Recording a window not yet supported");
++  display = meta_get_display ();
++  window = meta_display_get_focus_window (display);
++  if (!window)
++    {
++      g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
++                                             G_DBUS_ERROR_FAILED,
++                                             "Window not found");
++      return TRUE;
++    }
++
++  interface_skeleton = G_DBUS_INTERFACE_SKELETON (skeleton);
++  connection = g_dbus_interface_skeleton_get_connection (interface_skeleton);
++
++  window_stream = meta_screen_cast_window_stream_new (connection,
++                                                      window,
++                                                      &error);
++  if (!window_stream)
++    {
++      g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
++                                             G_DBUS_ERROR_FAILED,
++                                             "Failed to record window: %s",
++                                             error->message);
++      g_error_free (error);
++      return TRUE;
++    }
++
++  stream = META_SCREEN_CAST_STREAM (window_stream);
++  stream_path = meta_screen_cast_stream_get_object_path (stream);
++
++  session->streams = g_list_append (session->streams, stream);
++
++  g_signal_connect (stream, "closed", G_CALLBACK (on_stream_closed), session);
++
++  meta_dbus_screen_cast_session_complete_record_window (skeleton,
++                                                        invocation,
++                                                        stream_path);
++
+   return TRUE;
+ }
+ 
+-- 
+2.19.2
+
diff --git a/SOURCES/0008-wayland-gtk-shell-Handle-requests-after-toplevel-was.patch b/SOURCES/0008-wayland-gtk-shell-Handle-requests-after-toplevel-was.patch
new file mode 100644
index 0000000..ea648bd
--- /dev/null
+++ b/SOURCES/0008-wayland-gtk-shell-Handle-requests-after-toplevel-was.patch
@@ -0,0 +1,95 @@
+From 8ddbe9d98bb02145fea898a2a85bbb49f2e85f5b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Wed, 25 Jul 2018 12:20:57 +0200
+Subject: [PATCH 8/8] wayland/gtk-shell: Handle requests after toplevel was
+ unmanaged
+
+As with xdg-toplevel, a gtk-surface can be unmanaged by the compositor
+without the client knowing about it, meaning the client may still send
+updates and make requests. Handle this gracefully by ignoring them. The
+client needs to reset all the state anyway, if it wants to remap the
+same surface.
+
+https://gitlab.gnome.org/GNOME/mutter/issues/240
+(cherry picked from commit ca5b27baf517f00354ff8811ba204fd90f1ddb38)
+---
+ src/wayland/meta-wayland-gtk-shell.c | 29 ++++++++++++++++++----------
+ 1 file changed, 19 insertions(+), 10 deletions(-)
+
+diff --git a/src/wayland/meta-wayland-gtk-shell.c b/src/wayland/meta-wayland-gtk-shell.c
+index 0ef9b83ff..474595b18 100644
+--- a/src/wayland/meta-wayland-gtk-shell.c
++++ b/src/wayland/meta-wayland-gtk-shell.c
+@@ -71,15 +71,13 @@ gtk_surface_set_dbus_properties (struct wl_client   *client,
+ {
+   MetaWaylandGtkSurface *gtk_surface = wl_resource_get_user_data (resource);
+   MetaWaylandSurface *surface = gtk_surface->surface;
++  MetaWindow *window;
+ 
+-  /* Broken client, let it die instead of us */
+-  if (!surface->window)
+-    {
+-      meta_warning ("meta-wayland-surface: set_dbus_properties called with invalid window!\n");
+-      return;
+-    }
++  window = surface->window;
++  if (!window)
++    return;
+ 
+-  meta_window_set_gtk_dbus_properties (surface->window,
++  meta_window_set_gtk_dbus_properties (window,
+                                        application_id,
+                                        unique_bus_name,
+                                        app_menu_path,
+@@ -94,12 +92,17 @@ gtk_surface_set_modal (struct wl_client   *client,
+ {
+   MetaWaylandGtkSurface *gtk_surface = wl_resource_get_user_data (resource);
+   MetaWaylandSurface *surface = gtk_surface->surface;
++  MetaWindow *window;
++
++  window = surface->window;
++  if (!window)
++    return;
+ 
+   if (gtk_surface->is_modal)
+     return;
+ 
+   gtk_surface->is_modal = TRUE;
+-  meta_window_set_type (surface->window, META_WINDOW_MODAL_DIALOG);
++  meta_window_set_type (window, META_WINDOW_MODAL_DIALOG);
+ }
+ 
+ static void
+@@ -108,12 +111,17 @@ gtk_surface_unset_modal (struct wl_client   *client,
+ {
+   MetaWaylandGtkSurface *gtk_surface = wl_resource_get_user_data (resource);
+   MetaWaylandSurface *surface = gtk_surface->surface;
++  MetaWindow *window;
++
++  window = surface->window;
++  if (!window)
++    return;
+ 
+   if (!gtk_surface->is_modal)
+     return;
+ 
+   gtk_surface->is_modal = FALSE;
+-  meta_window_set_type (surface->window, META_WINDOW_NORMAL);
++  meta_window_set_type (window, META_WINDOW_NORMAL);
+ }
+ 
+ static void
+@@ -123,8 +131,9 @@ gtk_surface_present (struct wl_client   *client,
+ {
+   MetaWaylandGtkSurface *gtk_surface = wl_resource_get_user_data (resource);
+   MetaWaylandSurface *surface = gtk_surface->surface;
+-  MetaWindow *window = surface->window;
++  MetaWindow *window;
+ 
++  window = surface->window;
+   if (!window)
+     return;
+ 
+-- 
+2.19.0
+
diff --git a/SOURCES/0009-MetaShapedTexture-save-and-restore-textures-on-suspe.patch b/SOURCES/0009-MetaShapedTexture-save-and-restore-textures-on-suspe.patch
new file mode 100644
index 0000000..4d17828
--- /dev/null
+++ b/SOURCES/0009-MetaShapedTexture-save-and-restore-textures-on-suspe.patch
@@ -0,0 +1,1415 @@
+From 04b7954c7f04e7348f139fdf5c38e7702b387ee3 Mon Sep 17 00:00:00 2001
+From: Ray Strode <rstrode@redhat.com>
+Date: Thu, 10 Jan 2019 10:48:02 -0500
+Subject: [PATCH 9/9] MetaShapedTexture: save and restore textures on suspend
+
+The proprietary nvidia driver garbles GPU memory on suspend.
+
+In order to workaround that limitation, this commit copies all
+textures to host memory on suspend and restores them on resume.
+
+One complication comes from external textures (such as those
+given to us by Xwayland for X clients).  We can't just restore
+those textures, since they aren't writable.
+
+This commit addresses that complication by keeping a local texture
+around for those external textures, and using it instead for parts
+of the window that haven't been redrawn since resume.
+---
+ src/compositor/meta-shaped-texture.c | 494 +++++++++++++++++++++++++--
+ src/meta/meta-shaped-texture.h       |   2 +
+ 2 files changed, 475 insertions(+), 21 deletions(-)
+
+diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c
+index d8c250fc9..8de173bf7 100644
+--- a/src/compositor/meta-shaped-texture.c
++++ b/src/compositor/meta-shaped-texture.c
+@@ -2,166 +2,203 @@
+  * Authored By Neil Roberts  <neil@linux.intel.com>
+  * and Jasper St. Pierre <jstpierre@mecheye.net>
+  *
+  * Copyright (C) 2008 Intel Corporation
+  * Copyright (C) 2012 Red Hat, Inc.
+  *
+  * This program is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU General Public License as
+  * published by the Free Software Foundation; either version 2 of the
+  * License, or (at your option) any later version.
+  *
+  * This program 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
+  * General Public License for more details.
+  *
+  * You should have received a copy of the GNU General Public License
+  * along with this program; if not, see <http://www.gnu.org/licenses/>.
+  */
+ 
+ /**
+  * SECTION:meta-shaped-texture
+  * @title: MetaShapedTexture
+  * @short_description: An actor to draw a masked texture.
+  */
+ 
+ #include <config.h>
+ 
+ #include <meta/meta-shaped-texture.h>
+ #include "meta-shaped-texture-private.h"
++#include "meta-texture-rectangle.h"
+ 
+ #include <cogl/cogl.h>
+ #include <gdk/gdk.h> /* for gdk_rectangle_intersect() */
+ 
+ #include "clutter-utils.h"
+ #include "meta-texture-tower.h"
+ #include "core/boxes-private.h"
+ 
+ #include "meta-cullable.h"
++#include <meta/meta-backend.h>
+ 
+ static void meta_shaped_texture_dispose  (GObject    *object);
+ 
+ static void meta_shaped_texture_paint (ClutterActor       *actor);
+ 
+ static void meta_shaped_texture_get_preferred_width (ClutterActor *self,
+                                                      gfloat        for_height,
+                                                      gfloat       *min_width_p,
+                                                      gfloat       *natural_width_p);
+ 
+ static void meta_shaped_texture_get_preferred_height (ClutterActor *self,
+                                                       gfloat        for_width,
+                                                       gfloat       *min_height_p,
+                                                       gfloat       *natural_height_p);
+ 
+ static gboolean meta_shaped_texture_get_paint_volume (ClutterActor *self, ClutterPaintVolume *volume);
+ 
++static void disable_backing_store (MetaShapedTexture *stex);
++
+ static void cullable_iface_init (MetaCullableInterface *iface);
+ 
++static gboolean meta_debug_show_backing_store = FALSE;
++
+ G_DEFINE_TYPE_WITH_CODE (MetaShapedTexture, meta_shaped_texture, CLUTTER_TYPE_ACTOR,
+                          G_IMPLEMENT_INTERFACE (META_TYPE_CULLABLE, cullable_iface_init));
+ 
+ #define META_SHAPED_TEXTURE_GET_PRIVATE(obj) \
+   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), META_TYPE_SHAPED_TEXTURE, \
+                                 MetaShapedTexturePrivate))
+ 
+ enum {
+   SIZE_CHANGED,
+ 
+   LAST_SIGNAL,
+ };
+ 
+ static guint signals[LAST_SIGNAL];
+ 
++typedef struct
++{
++  CoglTexture *texture;
++  CoglTexture *mask_texture;
++  cairo_surface_t *mask_surface;
++  cairo_region_t *region;
++} MetaTextureBackingStore;
++
+ struct _MetaShapedTexturePrivate
+ {
+   MetaTextureTower *paint_tower;
+ 
+   CoglTexture *texture;
+   CoglTexture *mask_texture;
+   CoglSnippet *snippet;
+ 
+   CoglPipeline *base_pipeline;
+   CoglPipeline *masked_pipeline;
+   CoglPipeline *unblended_pipeline;
+ 
+   gboolean is_y_inverted;
+ 
+   /* The region containing only fully opaque pixels */
+   cairo_region_t *opaque_region;
+ 
+   /* MetaCullable regions, see that documentation for more details */
+   cairo_region_t *clip_region;
+   cairo_region_t *unobscured_region;
+ 
++  /* textures get corrupted on suspend, so save them */
++  cairo_surface_t *saved_base_surface;
++  cairo_surface_t *saved_mask_surface;
++
++  /* We can't just restore external textures, so we need to track
++   * which parts of the external texture are freshly drawn from
++   * the client after corruption, and fill in the rest from our
++   * saved snapshot */
++  MetaTextureBackingStore *backing_store;
++
+   guint tex_width, tex_height;
+   guint fallback_width, fallback_height;
+ 
+   guint create_mipmaps : 1;
+ };
+ 
+ static void
+ meta_shaped_texture_class_init (MetaShapedTextureClass *klass)
+ {
+   GObjectClass *gobject_class = (GObjectClass *) klass;
+   ClutterActorClass *actor_class = (ClutterActorClass *) klass;
+ 
+   gobject_class->dispose = meta_shaped_texture_dispose;
+ 
+   actor_class->get_preferred_width = meta_shaped_texture_get_preferred_width;
+   actor_class->get_preferred_height = meta_shaped_texture_get_preferred_height;
+   actor_class->paint = meta_shaped_texture_paint;
+   actor_class->get_paint_volume = meta_shaped_texture_get_paint_volume;
+ 
+   signals[SIZE_CHANGED] = g_signal_new ("size-changed",
+                                         G_TYPE_FROM_CLASS (gobject_class),
+                                         G_SIGNAL_RUN_LAST,
+                                         0,
+                                         NULL, NULL, NULL,
+                                         G_TYPE_NONE, 0);
+ 
+   g_type_class_add_private (klass, sizeof (MetaShapedTexturePrivate));
++
++  if (g_getenv ("MUTTER_DEBUG_BACKING_STORE"))
++    meta_debug_show_backing_store = TRUE;
+ }
+ 
+ static void
+ meta_shaped_texture_init (MetaShapedTexture *self)
+ {
+   MetaShapedTexturePrivate *priv;
++  MetaBackend *backend = meta_get_backend ();
++  ClutterBackend *clutter_backend = clutter_get_default_backend ();
++  CoglContext *cogl_context =
++    clutter_backend_get_cogl_context (clutter_backend);
+ 
+   priv = self->priv = META_SHAPED_TEXTURE_GET_PRIVATE (self);
+ 
+   priv->paint_tower = meta_texture_tower_new ();
+ 
+   priv->texture = NULL;
+   priv->mask_texture = NULL;
+   priv->create_mipmaps = TRUE;
+   priv->is_y_inverted = TRUE;
++
++  if (cogl_has_feature (cogl_context, COGL_FEATURE_ID_UNSTABLE_TEXTURES))
++    {
++      g_signal_connect_object (backend, "suspending", G_CALLBACK (meta_shaped_texture_save), self, G_CONNECT_SWAPPED);
++      g_signal_connect_object (backend, "resuming", G_CALLBACK (meta_shaped_texture_restore), self, G_CONNECT_SWAPPED);
++    }
+ }
+ 
+ static void
+ set_unobscured_region (MetaShapedTexture *self,
+                        cairo_region_t    *unobscured_region)
+ {
+   MetaShapedTexturePrivate *priv = self->priv;
+ 
+   g_clear_pointer (&priv->unobscured_region, (GDestroyNotify) cairo_region_destroy);
+   if (unobscured_region)
+     {
+       guint width, height;
+ 
+       if (priv->texture)
+         {
+           width = priv->tex_width;
+           height = priv->tex_height;
+         }
+       else
+         {
+           width = priv->fallback_width;
+           height = priv->fallback_height;
+         }
+ 
+       cairo_rectangle_int_t bounds = { 0, 0, width, height };
+       priv->unobscured_region = cairo_region_copy (unobscured_region);
+       cairo_region_intersect_rectangle (priv->unobscured_region, &bounds);
+     }
+ }
+ 
+@@ -183,116 +220,183 @@ meta_shaped_texture_reset_pipelines (MetaShapedTexture *stex)
+ 
+   g_clear_pointer (&priv->base_pipeline, cogl_object_unref);
+   g_clear_pointer (&priv->masked_pipeline, cogl_object_unref);
+   g_clear_pointer (&priv->unblended_pipeline, cogl_object_unref);
+ }
+ 
+ static void
+ meta_shaped_texture_dispose (GObject *object)
+ {
+   MetaShapedTexture *self = (MetaShapedTexture *) object;
+   MetaShapedTexturePrivate *priv = self->priv;
+ 
+   if (priv->paint_tower)
+     meta_texture_tower_free (priv->paint_tower);
+   priv->paint_tower = NULL;
+ 
+   g_clear_pointer (&priv->texture, cogl_object_unref);
+   g_clear_pointer (&priv->opaque_region, cairo_region_destroy);
+ 
+   meta_shaped_texture_set_mask_texture (self, NULL);
+   set_unobscured_region (self, NULL);
+   set_clip_region (self, NULL);
+ 
+   meta_shaped_texture_reset_pipelines (self);
+ 
+   g_clear_pointer (&priv->snippet, cogl_object_unref);
+ 
+   G_OBJECT_CLASS (meta_shaped_texture_parent_class)->dispose (object);
+ }
+ 
++static int
++get_layer_indices (MetaShapedTexture *stex,
++                   int               *main_layer_index,
++                   int               *backing_mask_layer_index,
++                   int               *backing_layer_index,
++                   int               *mask_layer_index)
++{
++  MetaShapedTexturePrivate *priv = stex->priv;
++  int next_layer_index = 0;
++
++  if (main_layer_index)
++    *main_layer_index = next_layer_index;
++
++  next_layer_index++;
++
++  if (priv->backing_store)
++    {
++      if (backing_mask_layer_index)
++        *backing_mask_layer_index = next_layer_index;
++      next_layer_index++;
++      if (backing_layer_index)
++        *backing_layer_index = next_layer_index;
++      next_layer_index++;
++    }
++  else
++    {
++      if (backing_mask_layer_index)
++        *backing_mask_layer_index = -1;
++      if (backing_layer_index)
++        *backing_layer_index = -1;
++    }
++
++  if (mask_layer_index)
++    *mask_layer_index = next_layer_index;
++
++  return next_layer_index;
++}
++
+ static CoglPipeline *
+ get_base_pipeline (MetaShapedTexture *stex,
+                    CoglContext       *ctx)
+ {
+   MetaShapedTexturePrivate *priv = stex->priv;
+   CoglPipeline *pipeline;
++  int main_layer_index;
++  int backing_layer_index;
++  int backing_mask_layer_index;
++  int i, number_of_layers;
+ 
+   if (priv->base_pipeline)
+     return priv->base_pipeline;
+ 
+   pipeline = cogl_pipeline_new (ctx);
+-  cogl_pipeline_set_layer_wrap_mode_s (pipeline, 0,
+-                                       COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
+-  cogl_pipeline_set_layer_wrap_mode_t (pipeline, 0,
+-                                       COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
+-  cogl_pipeline_set_layer_wrap_mode_s (pipeline, 1,
+-                                       COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
+-  cogl_pipeline_set_layer_wrap_mode_t (pipeline, 1,
+-                                       COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
++
++  number_of_layers = get_layer_indices (stex,
++                                        &main_layer_index,
++                                        &backing_mask_layer_index,
++                                        &backing_layer_index,
++                                        NULL);
++
++  for (i = 0; i < number_of_layers; i++)
++    {
++      cogl_pipeline_set_layer_wrap_mode_s (pipeline, i,
++                                           COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
++      cogl_pipeline_set_layer_wrap_mode_t (pipeline, i,
++                                           COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
++    }
++
+   if (!priv->is_y_inverted)
+     {
+       CoglMatrix matrix;
+ 
+       cogl_matrix_init_identity (&matrix);
+       cogl_matrix_scale (&matrix, 1, -1, 1);
+       cogl_matrix_translate (&matrix, 0, -1, 0);
+-      cogl_pipeline_set_layer_matrix (pipeline, 0, &matrix);
++      cogl_pipeline_set_layer_matrix (pipeline, main_layer_index, &matrix);
++    }
++
++  if (priv->backing_store)
++    {
++      g_autofree char *backing_description = NULL;
++      cogl_pipeline_set_layer_combine (pipeline, backing_mask_layer_index,
++                                       "RGBA = REPLACE(PREVIOUS)",
++                                       NULL);
++      backing_description = g_strdup_printf ("RGBA = INTERPOLATE(PREVIOUS, TEXTURE_%d, TEXTURE_%d[A])",
++                                             backing_layer_index,
++                                             backing_mask_layer_index);
++      cogl_pipeline_set_layer_combine (pipeline,
++                                       backing_layer_index,
++                                       backing_description,
++                                       NULL);
+     }
+ 
+   if (priv->snippet)
+-    cogl_pipeline_add_layer_snippet (pipeline, 0, priv->snippet);
++    cogl_pipeline_add_layer_snippet (pipeline, main_layer_index, priv->snippet);
+ 
+   priv->base_pipeline = pipeline;
+ 
+   return priv->base_pipeline;
+ }
+ 
+ static CoglPipeline *
+ get_unmasked_pipeline (MetaShapedTexture *stex,
+                        CoglContext       *ctx)
+ {
+   return get_base_pipeline (stex, ctx);
+ }
+ 
+ static CoglPipeline *
+ get_masked_pipeline (MetaShapedTexture *stex,
+                      CoglContext       *ctx)
+ {
+   MetaShapedTexturePrivate *priv = stex->priv;
+   CoglPipeline *pipeline;
++  int mask_layer_index;
+ 
+   if (priv->masked_pipeline)
+     return priv->masked_pipeline;
+ 
++  get_layer_indices (stex, NULL, NULL, NULL, &mask_layer_index);
++
+   pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx));
+-  cogl_pipeline_set_layer_combine (pipeline, 1,
++  cogl_pipeline_set_layer_combine (pipeline, mask_layer_index,
+                                    "RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
+                                    NULL);
+ 
+   priv->masked_pipeline = pipeline;
+ 
+   return pipeline;
+ }
+ 
+ static CoglPipeline *
+ get_unblended_pipeline (MetaShapedTexture *stex,
+                         CoglContext       *ctx)
+ {
+   MetaShapedTexturePrivate *priv = stex->priv;
+   CoglPipeline *pipeline;
+   CoglColor color;
+ 
+   if (priv->unblended_pipeline)
+     return priv->unblended_pipeline;
+ 
+   pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx));
+   cogl_color_init_from_4ub (&color, 255, 255, 255, 255);
+   cogl_pipeline_set_blend (pipeline,
+                            "RGBA = ADD (SRC_COLOR, 0)",
+                            NULL);
+   cogl_pipeline_set_color (pipeline, &color);
+ 
+   priv->unblended_pipeline = pipeline;
+ 
+   return pipeline;
+ }
+@@ -313,105 +417,111 @@ paint_clipped_rectangle (CoglFramebuffer       *fb,
+ 
+   coords[0] = rect->x / (alloc->x2 - alloc->x1);
+   coords[1] = rect->y / (alloc->y2 - alloc->y1);
+   coords[2] = (rect->x + rect->width) / (alloc->x2 - alloc->x1);
+   coords[3] = (rect->y + rect->height) / (alloc->y2 - alloc->y1);
+ 
+   coords[4] = coords[0];
+   coords[5] = coords[1];
+   coords[6] = coords[2];
+   coords[7] = coords[3];
+ 
+   cogl_framebuffer_draw_multitextured_rectangle (fb, pipeline,
+                                                  x1, y1, x2, y2,
+                                                  &coords[0], 8);
+ }
+ 
+ static void
+ set_cogl_texture (MetaShapedTexture *stex,
+                   CoglTexture       *cogl_tex)
+ {
+   MetaShapedTexturePrivate *priv;
+   guint width, height;
+ 
+   g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
+ 
+   priv = stex->priv;
+ 
+   if (priv->texture)
+     cogl_object_unref (priv->texture);
+ 
++  g_clear_pointer (&priv->saved_base_surface, cairo_surface_destroy);
++
+   priv->texture = cogl_tex;
+ 
+   if (cogl_tex != NULL)
+     {
+       cogl_object_ref (cogl_tex);
+       width = cogl_texture_get_width (COGL_TEXTURE (cogl_tex));
+       height = cogl_texture_get_height (COGL_TEXTURE (cogl_tex));
+     }
+   else
+     {
+       width = 0;
+       height = 0;
+     }
+ 
+   if (priv->tex_width != width ||
+       priv->tex_height != height)
+     {
+       priv->tex_width = width;
+       priv->tex_height = height;
+       meta_shaped_texture_set_mask_texture (stex, NULL);
+       clutter_actor_queue_relayout (CLUTTER_ACTOR (stex));
+       g_signal_emit (stex, signals[SIZE_CHANGED], 0);
+     }
+ 
+   /* NB: We don't queue a redraw of the actor here because we don't
+    * know how much of the buffer has changed with respect to the
+    * previous buffer. We only queue a redraw in response to surface
+    * damage. */
+ 
+   if (priv->create_mipmaps)
+     meta_texture_tower_set_base_texture (priv->paint_tower, cogl_tex);
+ }
+ 
+ static void
+ do_paint (MetaShapedTexture *stex,
+           CoglFramebuffer   *fb,
+           CoglTexture       *paint_tex,
+           cairo_region_t    *clip_region)
+ {
+   MetaShapedTexturePrivate *priv = stex->priv;
+   guint tex_width, tex_height;
+   guchar opacity;
+   CoglContext *ctx;
+   ClutterActorBox alloc;
+   CoglPipelineFilter filter;
++  int main_layer_index;
++  int backing_mask_layer_index;
++  int backing_layer_index;
++  int mask_layer_index;
+ 
+   tex_width = priv->tex_width;
+   tex_height = priv->tex_height;
+ 
+   if (tex_width == 0 || tex_height == 0) /* no contents yet */
+     return;
+ 
+   cairo_rectangle_int_t tex_rect = { 0, 0, tex_width, tex_height };
+ 
+   /* Use nearest-pixel interpolation if the texture is unscaled. This
+    * improves performance, especially with software rendering.
+    */
+ 
+   filter = COGL_PIPELINE_FILTER_LINEAR;
+ 
+   if (meta_actor_painting_untransformed (fb,
+                                          tex_width, tex_height,
+                                          NULL, NULL))
+     filter = COGL_PIPELINE_FILTER_NEAREST;
+ 
+   ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
+ 
+   opacity = clutter_actor_get_paint_opacity (CLUTTER_ACTOR (stex));
+   clutter_actor_get_allocation_box (CLUTTER_ACTOR (stex), &alloc);
+ 
+   cairo_region_t *blended_region;
+   gboolean use_opaque_region = (priv->opaque_region != NULL && opacity == 255);
+ 
+   if (use_opaque_region)
+     {
+@@ -420,123 +530,161 @@ do_paint (MetaShapedTexture *stex,
+       else
+         blended_region = cairo_region_create_rectangle (&tex_rect);
+ 
+       cairo_region_subtract (blended_region, priv->opaque_region);
+     }
+   else
+     {
+       if (priv->clip_region != NULL)
+         blended_region = cairo_region_reference (priv->clip_region);
+       else
+         blended_region = NULL;
+     }
+ 
+   /* Limit to how many separate rectangles we'll draw; beyond this just
+    * fall back and draw the whole thing */
+ #define MAX_RECTS 16
+ 
+   if (blended_region != NULL)
+     {
+       int n_rects = cairo_region_num_rectangles (blended_region);
+       if (n_rects > MAX_RECTS)
+         {
+           /* Fall back to taking the fully blended path. */
+           use_opaque_region = FALSE;
+ 
+           cairo_region_destroy (blended_region);
+           blended_region = NULL;
+         }
+     }
+ 
++  get_layer_indices (stex,
++                     &main_layer_index,
++                     &backing_mask_layer_index,
++                     &backing_layer_index,
++                     &mask_layer_index);
++
+   /* First, paint the unblended parts, which are part of the opaque region. */
+   if (use_opaque_region)
+     {
+       CoglPipeline *opaque_pipeline;
+       cairo_region_t *region;
+       int n_rects;
+       int i;
+ 
+       if (priv->clip_region != NULL)
+         {
+           region = cairo_region_copy (priv->clip_region);
+           cairo_region_intersect (region, priv->opaque_region);
+         }
+       else
+         {
+           region = cairo_region_reference (priv->opaque_region);
+         }
+ 
+       if (!cairo_region_is_empty (region))
+         {
+           opaque_pipeline = get_unblended_pipeline (stex, ctx);
+-          cogl_pipeline_set_layer_texture (opaque_pipeline, 0, paint_tex);
+-          cogl_pipeline_set_layer_filters (opaque_pipeline, 0, filter, filter);
++          cogl_pipeline_set_layer_texture (opaque_pipeline, main_layer_index, paint_tex);
++          cogl_pipeline_set_layer_filters (opaque_pipeline, main_layer_index, filter, filter);
++
++          if (priv->backing_store)
++            {
++              cogl_pipeline_set_layer_texture (opaque_pipeline,
++                                               backing_mask_layer_index,
++                                               priv->backing_store->mask_texture);
++              cogl_pipeline_set_layer_filters (opaque_pipeline,
++                                               backing_mask_layer_index,
++                                               filter, filter);
++              cogl_pipeline_set_layer_texture (opaque_pipeline,
++                                               backing_layer_index,
++                                               priv->backing_store->texture);
++              cogl_pipeline_set_layer_filters (opaque_pipeline,
++                                               backing_layer_index,
++                                               filter, filter);
++            }
+ 
+           n_rects = cairo_region_num_rectangles (region);
+           for (i = 0; i < n_rects; i++)
+             {
+               cairo_rectangle_int_t rect;
+               cairo_region_get_rectangle (region, i, &rect);
+               paint_clipped_rectangle (fb, opaque_pipeline, &rect, &alloc);
+             }
+         }
+ 
+       cairo_region_destroy (region);
+     }
+ 
+   /* Now, go ahead and paint the blended parts. */
+ 
+   /* We have three cases:
+    *   1) blended_region has rectangles - paint the rectangles.
+    *   2) blended_region is empty - don't paint anything
+    *   3) blended_region is NULL - paint fully-blended.
+    *
+    *   1) and 3) are the times where we have to paint stuff. This tests
+    *   for 1) and 3).
+    */
+   if (blended_region == NULL || !cairo_region_is_empty (blended_region))
+     {
+       CoglPipeline *blended_pipeline;
+ 
+       if (priv->mask_texture == NULL)
+         {
+           blended_pipeline = get_unmasked_pipeline (stex, ctx);
+         }
+       else
+         {
+           blended_pipeline = get_masked_pipeline (stex, ctx);
+-          cogl_pipeline_set_layer_texture (blended_pipeline, 1, priv->mask_texture);
+-          cogl_pipeline_set_layer_filters (blended_pipeline, 1, filter, filter);
++          cogl_pipeline_set_layer_texture (blended_pipeline, mask_layer_index, priv->mask_texture);
++          cogl_pipeline_set_layer_filters (blended_pipeline, mask_layer_index, filter, filter);
+         }
+ 
+-      cogl_pipeline_set_layer_texture (blended_pipeline, 0, paint_tex);
+-      cogl_pipeline_set_layer_filters (blended_pipeline, 0, filter, filter);
++      cogl_pipeline_set_layer_texture (blended_pipeline, main_layer_index, paint_tex);
++      cogl_pipeline_set_layer_filters (blended_pipeline, main_layer_index, filter, filter);
++
++      if (priv->backing_store)
++        {
++          cogl_pipeline_set_layer_texture (blended_pipeline,
++                                           backing_mask_layer_index,
++                                           priv->backing_store->mask_texture);
++          cogl_pipeline_set_layer_filters (blended_pipeline,
++                                           backing_mask_layer_index,
++                                           filter, filter);
++          cogl_pipeline_set_layer_texture (blended_pipeline,
++                                           backing_layer_index,
++                                           priv->backing_store->texture);
++          cogl_pipeline_set_layer_filters (blended_pipeline,
++                                           backing_layer_index,
++                                           filter, filter);
++        }
+ 
+       CoglColor color;
+       cogl_color_init_from_4ub (&color, opacity, opacity, opacity, opacity);
+       cogl_pipeline_set_color (blended_pipeline, &color);
+ 
+       if (blended_region != NULL)
+         {
+           /* 1) blended_region is not empty. Paint the rectangles. */
+           int i;
+           int n_rects = cairo_region_num_rectangles (blended_region);
+ 
+           for (i = 0; i < n_rects; i++)
+             {
+               cairo_rectangle_int_t rect;
+               cairo_region_get_rectangle (blended_region, i, &rect);
+ 
+               if (!gdk_rectangle_intersect (&tex_rect, &rect, &rect))
+                 continue;
+ 
+               paint_clipped_rectangle (fb, blended_pipeline, &rect, &alloc);
+             }
+         }
+       else
+         {
+           /* 3) blended_region is NULL. Do a full paint. */
+           cogl_framebuffer_draw_rectangle (fb, blended_pipeline,
+                                            0, 0,
+                                            alloc.x2 - alloc.x1,
+                                            alloc.y2 - alloc.y1);
+         }
+@@ -698,110 +846,173 @@ meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex,
+ 					gboolean           create_mipmaps)
+ {
+   MetaShapedTexturePrivate *priv;
+ 
+   g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
+ 
+   priv = stex->priv;
+ 
+   create_mipmaps = create_mipmaps != FALSE;
+ 
+   if (create_mipmaps != priv->create_mipmaps)
+     {
+       CoglTexture *base_texture;
+       priv->create_mipmaps = create_mipmaps;
+       base_texture = create_mipmaps ? priv->texture : NULL;
+       meta_texture_tower_set_base_texture (priv->paint_tower, base_texture);
+     }
+ }
+ 
+ void
+ meta_shaped_texture_set_mask_texture (MetaShapedTexture *stex,
+                                       CoglTexture       *mask_texture)
+ {
+   MetaShapedTexturePrivate *priv;
+ 
+   g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
+ 
+   priv = stex->priv;
+ 
+   g_clear_pointer (&priv->mask_texture, cogl_object_unref);
++  g_clear_pointer (&priv->saved_mask_surface, cairo_surface_destroy);
+ 
+   if (mask_texture != NULL)
+     {
+       priv->mask_texture = mask_texture;
+       cogl_object_ref (priv->mask_texture);
+     }
+ 
+   clutter_actor_queue_redraw (CLUTTER_ACTOR (stex));
+ }
+ 
+ gboolean
+ meta_shaped_texture_is_obscured (MetaShapedTexture *self)
+ {
+   cairo_region_t *unobscured_region = effective_unobscured_region (self);
+ 
+   if (unobscured_region)
+     return cairo_region_is_empty (unobscured_region);
+   else
+     return FALSE;
+ }
+ 
++static void
++meta_texture_backing_store_redraw_mask (MetaTextureBackingStore *backing_store)
++{
++  CoglError *error = NULL;
++
++  if (!cogl_texture_set_data (backing_store->mask_texture, COGL_PIXEL_FORMAT_A_8,
++                              cairo_image_surface_get_stride (backing_store->mask_surface),
++                              cairo_image_surface_get_data (backing_store->mask_surface), 0,
++                              &error))
++    {
++
++      g_warning ("Failed to update backing mask texture");
++      g_clear_pointer (&error, cogl_error_free);
++    }
++}
++
++static gboolean
++meta_texture_backing_store_shrink (MetaTextureBackingStore     *backing_store,
++                                   const cairo_rectangle_int_t *area)
++{
++  cairo_t *cr;
++
++  cairo_region_subtract_rectangle (backing_store->region, area);
++
++  /* If the client has finally redrawn the entire surface, we can
++   * ditch our snapshot
++   */
++  if (cairo_region_is_empty (backing_store->region))
++    return FALSE;
++
++  cr = cairo_create (backing_store->mask_surface);
++  cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
++  cairo_paint (cr);
++  gdk_cairo_region (cr, backing_store->region);
++  cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
++  cairo_fill (cr);
++  cairo_destroy (cr);
++
++  meta_texture_backing_store_redraw_mask (backing_store);
++
++  return TRUE;
++}
++
++static void
++shrink_backing_region (MetaShapedTexture           *stex,
++                       const cairo_rectangle_int_t *area)
++{
++  MetaShapedTexturePrivate *priv = stex->priv;
++  gboolean still_backing_texture;
++
++  if (!priv->backing_store)
++    return;
++
++  still_backing_texture =
++      meta_texture_backing_store_shrink (priv->backing_store, area);
++
++  if (!still_backing_texture)
++    disable_backing_store (stex);
++}
++
+ /**
+  * meta_shaped_texture_update_area:
+  * @stex: #MetaShapedTexture
+  * @x: the x coordinate of the damaged area
+  * @y: the y coordinate of the damaged area
+  * @width: the width of the damaged area
+  * @height: the height of the damaged area
+  *
+  * Repairs the damaged area indicated by @x, @y, @width and @height
+  * and potentially queues a redraw.
+  *
+  * Return value: Whether a redraw have been queued or not
+  */
+ gboolean
+ meta_shaped_texture_update_area (MetaShapedTexture *stex,
+ 				 int                x,
+ 				 int                y,
+ 				 int                width,
+ 				 int                height)
+ {
+   MetaShapedTexturePrivate *priv;
+   cairo_region_t *unobscured_region;
+   const cairo_rectangle_int_t clip = { x, y, width, height };
+ 
+   priv = stex->priv;
+ 
+   if (priv->texture == NULL)
+     return FALSE;
+ 
++  shrink_backing_region (stex, &clip);
++
+   meta_texture_tower_update_area (priv->paint_tower, x, y, width, height);
+ 
+   unobscured_region = effective_unobscured_region (stex);
+   if (unobscured_region)
+     {
+       cairo_region_t *intersection;
+ 
+       if (cairo_region_is_empty (unobscured_region))
+         return FALSE;
+ 
+       intersection = cairo_region_copy (unobscured_region);
+       cairo_region_intersect_rectangle (intersection, &clip);
+ 
+       if (!cairo_region_is_empty (intersection))
+         {
+           cairo_rectangle_int_t damage_rect;
+           cairo_region_get_extents (intersection, &damage_rect);
+           clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stex), &damage_rect);
+           cairo_region_destroy (intersection);
+           return TRUE;
+         }
+ 
+       cairo_region_destroy (intersection);
+       return FALSE;
+     }
+   else
+     {
+       clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stex), &clip);
+       return TRUE;
+     }
+@@ -892,62 +1103,63 @@ meta_shaped_texture_set_opaque_region (MetaShapedTexture *stex,
+   priv = stex->priv;
+ 
+   if (priv->opaque_region)
+     cairo_region_destroy (priv->opaque_region);
+ 
+   if (opaque_region)
+     priv->opaque_region = cairo_region_reference (opaque_region);
+   else
+     priv->opaque_region = NULL;
+ }
+ 
+ cairo_region_t *
+ meta_shaped_texture_get_opaque_region (MetaShapedTexture *stex)
+ {
+   MetaShapedTexturePrivate *priv = stex->priv;
+   return priv->opaque_region;
+ }
+ 
+ static gboolean
+ should_get_via_offscreen (MetaShapedTexture *stex)
+ {
+   MetaShapedTexturePrivate *priv = stex->priv;
+ 
+   if (!cogl_texture_is_get_data_supported (priv->texture))
+     return TRUE;
+ 
+   return FALSE;
+ }
+ 
+ static cairo_surface_t *
+-get_image_via_offscreen (MetaShapedTexture     *stex,
+-                         cairo_rectangle_int_t *clip)
++get_image_via_offscreen (MetaShapedTexture      *stex,
++                         cairo_rectangle_int_t  *clip,
++                         CoglTexture           **texture)
+ {
+   MetaShapedTexturePrivate *priv = stex->priv;
+   ClutterBackend *clutter_backend = clutter_get_default_backend ();
+   CoglContext *cogl_context =
+     clutter_backend_get_cogl_context (clutter_backend);
+   CoglTexture *image_texture;
+   GError *error = NULL;
+   CoglOffscreen *offscreen;
+   CoglFramebuffer *fb;
+   CoglMatrix projection_matrix;
+   unsigned int fb_width, fb_height;
+   cairo_rectangle_int_t fallback_clip;
+   CoglColor clear_color;
+   cairo_surface_t *surface;
+ 
+   if (cogl_has_feature (cogl_context, COGL_FEATURE_ID_TEXTURE_NPOT))
+     {
+       fb_width = priv->tex_width;
+       fb_height = priv->tex_height;
+     }
+   else
+     {
+       fb_width = clutter_util_next_p2 (priv->tex_width);
+       fb_height = clutter_util_next_p2 (priv->tex_height);
+     }
+ 
+   if (!clip)
+     {
+       fallback_clip = (cairo_rectangle_int_t) {
+         .width = priv->tex_width,
+@@ -988,184 +1200,424 @@ get_image_via_offscreen (MetaShapedTexture     *stex,
+       g_error_free (error);
+       cogl_object_unref (fb);
+       return FALSE;
+     }
+ 
+   cogl_framebuffer_push_matrix (fb);
+   cogl_matrix_init_identity (&projection_matrix);
+   cogl_matrix_scale (&projection_matrix,
+                      1.0 / (priv->tex_width / 2.0),
+                      -1.0 / (priv->tex_height / 2.0), 0);
+   cogl_matrix_translate (&projection_matrix,
+                          -(priv->tex_width / 2.0),
+                          -(priv->tex_height / 2.0), 0);
+ 
+   cogl_framebuffer_set_projection_matrix (fb, &projection_matrix);
+ 
+   cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 0);
+   cogl_framebuffer_clear (fb, COGL_BUFFER_BIT_COLOR, &clear_color);
+ 
+   do_paint (stex, fb, priv->texture, NULL);
+ 
+   cogl_framebuffer_pop_matrix (fb);
+ 
+   surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                         clip->width, clip->height);
+   cogl_framebuffer_read_pixels (fb,
+                                 clip->x, clip->y,
+                                 clip->width, clip->height,
+                                 CLUTTER_CAIRO_FORMAT_ARGB32,
+                                 cairo_image_surface_get_data (surface));
++  cairo_surface_mark_dirty (surface);
++
++  if (texture)
++    {
++      *texture = cogl_object_ref (image_texture);
++
++      if (G_UNLIKELY (meta_debug_show_backing_store))
++        {
++          cairo_t *cr;
++
++          cr = cairo_create (surface);
++          cairo_set_source_rgba (cr, 1.0, 0.0, 0.0, 0.75);
++          cairo_paint (cr);
++          cairo_destroy (cr);
++        }
++
++      cogl_texture_set_data (*texture, CLUTTER_CAIRO_FORMAT_ARGB32,
++                             cairo_image_surface_get_stride (surface),
++                             cairo_image_surface_get_data (surface), 0, NULL);
++    }
++
+   cogl_object_unref (fb);
+ 
+-  cairo_surface_mark_dirty (surface);
+ 
+   return surface;
+ }
+ 
+ /**
+  * meta_shaped_texture_get_image:
+  * @stex: A #MetaShapedTexture
+  * @clip: A clipping rectangle, to help prevent extra processing.
+  * In the case that the clipping rectangle is partially or fully
+  * outside the bounds of the texture, the rectangle will be clipped.
+  *
+  * Flattens the two layers of the shaped texture into one ARGB32
+  * image by alpha blending the two images, and returns the flattened
+  * image.
+  *
+  * Returns: (transfer full): a new cairo surface to be freed with
+  * cairo_surface_destroy().
+  */
+ cairo_surface_t *
+ meta_shaped_texture_get_image (MetaShapedTexture     *stex,
+                                cairo_rectangle_int_t *clip)
+ {
+   MetaShapedTexturePrivate *priv = stex->priv;
+   cairo_rectangle_int_t *transformed_clip = NULL;
+   CoglTexture *texture, *mask_texture;
+   cairo_surface_t *surface;
+ 
+   g_return_val_if_fail (META_IS_SHAPED_TEXTURE (stex), NULL);
+ 
+   texture = COGL_TEXTURE (priv->texture);
+ 
+   if (texture == NULL)
+     return NULL;
+ 
+   if (priv->tex_width == 0 || priv->tex_height == 0)
+     return NULL;
+ 
+   if (clip != NULL)
+     {
+       double tex_scale;
+       cairo_rectangle_int_t tex_rect;
+ 
+       transformed_clip = alloca (sizeof (cairo_rectangle_int_t));
+       *transformed_clip = *clip;
+ 
+       clutter_actor_get_scale (CLUTTER_ACTOR (stex), &tex_scale, NULL);
+       meta_rectangle_scale_double (transformed_clip, 1.0 / tex_scale,
+                                    META_ROUNDING_STRATEGY_GROW);
+ 
+       tex_rect = (cairo_rectangle_int_t) {
+         .width = priv->tex_width,
+         .height = priv->tex_height,
+       };
+ 
+       if (!meta_rectangle_intersect (&tex_rect, transformed_clip,
+                                      transformed_clip))
+         return NULL;
+     }
+ 
+   if (should_get_via_offscreen (stex))
+-    return get_image_via_offscreen (stex, transformed_clip);
++    return get_image_via_offscreen (stex, transformed_clip, NULL);
+ 
+   if (transformed_clip)
+     texture = cogl_texture_new_from_sub_texture (texture,
+                                                  transformed_clip->x,
+                                                  transformed_clip->y,
+                                                  transformed_clip->width,
+                                                  transformed_clip->height);
+ 
+   surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                         cogl_texture_get_width (texture),
+                                         cogl_texture_get_height (texture));
+ 
+   cogl_texture_get_data (texture, CLUTTER_CAIRO_FORMAT_ARGB32,
+                          cairo_image_surface_get_stride (surface),
+                          cairo_image_surface_get_data (surface));
+ 
+   cairo_surface_mark_dirty (surface);
+ 
+   if (transformed_clip)
+     cogl_object_unref (texture);
+ 
+   mask_texture = priv->mask_texture;
+   if (mask_texture != NULL)
+     {
+       cairo_t *cr;
+       cairo_surface_t *mask_surface;
+ 
+       if (transformed_clip)
+         mask_texture =
+           cogl_texture_new_from_sub_texture (mask_texture,
+                                              transformed_clip->x,
+                                              transformed_clip->y,
+                                              transformed_clip->width,
+                                              transformed_clip->height);
+ 
+       mask_surface = cairo_image_surface_create (CAIRO_FORMAT_A8,
+                                                  cogl_texture_get_width (mask_texture),
+                                                  cogl_texture_get_height (mask_texture));
+ 
+       cogl_texture_get_data (mask_texture, COGL_PIXEL_FORMAT_A_8,
+                              cairo_image_surface_get_stride (mask_surface),
+                              cairo_image_surface_get_data (mask_surface));
+ 
+       cairo_surface_mark_dirty (mask_surface);
+ 
+       cr = cairo_create (surface);
+       cairo_set_source_surface (cr, mask_surface, 0, 0);
+       cairo_set_operator (cr, CAIRO_OPERATOR_DEST_IN);
+       cairo_paint (cr);
+       cairo_destroy (cr);
+ 
+       cairo_surface_destroy (mask_surface);
+ 
+       if (transformed_clip)
+         cogl_object_unref (mask_texture);
+     }
+ 
+   return surface;
+ }
+ 
++static void
++meta_texture_backing_store_free (MetaTextureBackingStore *backing_store)
++{
++  g_clear_pointer (&backing_store->texture, cogl_object_unref);
++  g_clear_pointer (&backing_store->mask_texture, cogl_object_unref);
++  g_clear_pointer (&backing_store->mask_surface, cairo_surface_destroy);
++  g_clear_pointer (&backing_store->region, cairo_region_destroy);
++
++  g_slice_free (MetaTextureBackingStore, backing_store);
++}
++
++static MetaTextureBackingStore *
++meta_texture_backing_store_new (CoglTexture *texture)
++{
++  MetaTextureBackingStore *backing_store = NULL;
++  ClutterBackend *backend = clutter_get_default_backend ();
++  CoglContext *context = clutter_backend_get_cogl_context (backend);
++  CoglTexture *mask_texture = NULL;
++  guchar *mask_data;
++  int width, height, stride;
++  cairo_surface_t *surface;
++  cairo_region_t *region;
++  cairo_rectangle_int_t backing_rectangle;
++
++  width = cogl_texture_get_width (texture);
++  height = cogl_texture_get_height (texture);
++  stride = cairo_format_stride_for_width (CAIRO_FORMAT_A8, width);
++
++  /* we start off by only letting the backing texture through, and none of the real texture */
++  backing_rectangle.x = 0;
++  backing_rectangle.y = 0;
++  backing_rectangle.width = width;
++  backing_rectangle.height = height;
++
++  region = cairo_region_create_rectangle (&backing_rectangle);
++
++  /* initialize mask to transparent, so the entire backing store shows through
++   * up front
++   */
++  mask_data = g_malloc0 (stride * height);
++  surface = cairo_image_surface_create_for_data (mask_data,
++                                                 CAIRO_FORMAT_A8,
++                                                 width,
++                                                 height,
++                                                 stride);
++
++  if (meta_texture_rectangle_check (texture))
++    {
++      mask_texture = COGL_TEXTURE (cogl_texture_rectangle_new_with_size (context,
++                                                                         width,
++                                                                         height));
++      cogl_texture_set_components (mask_texture, COGL_TEXTURE_COMPONENTS_A);
++      cogl_texture_set_region (mask_texture,
++                               0, 0,
++                               0, 0,
++                               width, height,
++                               width, height,
++                               COGL_PIXEL_FORMAT_A_8,
++                               stride, mask_data);
++    }
++  else
++    {
++      CoglError *error = NULL;
++
++      mask_texture = COGL_TEXTURE (cogl_texture_2d_new_from_data (context, width, height,
++                                                                  COGL_PIXEL_FORMAT_A_8,
++                                                                  stride, mask_data, &error));
++
++      if (error)
++        {
++          g_warning ("Failed to allocate mask texture: %s", error->message);
++          cogl_error_free (error);
++        }
++    }
++
++  if (mask_texture)
++    {
++      backing_store = g_slice_new0 (MetaTextureBackingStore);
++      backing_store->texture = cogl_object_ref (texture);
++      backing_store->mask_texture = mask_texture;
++      backing_store->mask_surface = surface;
++      backing_store->region = region;
++    }
++
++  return backing_store;
++}
++
++static void
++enable_backing_store (MetaShapedTexture *stex,
++                      CoglTexture       *texture)
++{
++  MetaShapedTexturePrivate *priv = stex->priv;
++
++  g_clear_pointer (&priv->backing_store, meta_texture_backing_store_free);
++
++  priv->backing_store = meta_texture_backing_store_new (texture);
++
++  meta_shaped_texture_reset_pipelines (stex);
++}
++
++static void
++disable_backing_store (MetaShapedTexture *stex)
++{
++  MetaShapedTexturePrivate *priv = stex->priv;
++
++  g_clear_pointer (&priv->backing_store, meta_texture_backing_store_free);
++
++  meta_shaped_texture_reset_pipelines (stex);
++}
++
++void
++meta_shaped_texture_save (MetaShapedTexture *stex)
++{
++
++  CoglTexture *texture, *mask_texture;
++  MetaShapedTexturePrivate *priv = stex->priv;
++  cairo_surface_t *surface;
++
++  g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
++
++  texture = COGL_TEXTURE (priv->texture);
++
++  if (texture == NULL)
++    return;
++
++  g_clear_pointer (&priv->saved_base_surface, cairo_surface_destroy);
++  g_clear_pointer (&priv->saved_mask_surface, cairo_surface_destroy);
++  g_clear_pointer (&priv->backing_store, meta_texture_backing_store_free);
++
++  if (should_get_via_offscreen (stex))
++    {
++      CoglTexture *backing_texture;
++
++      meta_shaped_texture_reset_pipelines (stex);
++
++      surface = get_image_via_offscreen (stex, NULL, &backing_texture);
++
++      enable_backing_store (stex, backing_texture);
++      cogl_object_unref (backing_texture);
++    }
++  else
++    {
++      surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
++                                            cogl_texture_get_width (texture),
++                                            cogl_texture_get_height (texture));
++
++      cogl_texture_get_data (texture, CLUTTER_CAIRO_FORMAT_ARGB32,
++                             cairo_image_surface_get_stride (surface),
++                             cairo_image_surface_get_data (surface));
++    }
++
++  priv->saved_base_surface = surface;
++
++  mask_texture = stex->priv->mask_texture;
++  if (mask_texture != NULL)
++    {
++      cairo_surface_t *mask_surface;
++
++      mask_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
++                                                 cogl_texture_get_width (mask_texture),
++                                                 cogl_texture_get_height (mask_texture));
++
++      cogl_texture_get_data (mask_texture, CLUTTER_CAIRO_FORMAT_ARGB32,
++                             cairo_image_surface_get_stride (mask_surface),
++                             cairo_image_surface_get_data (mask_surface));
++
++      cairo_surface_mark_dirty (mask_surface);
++
++      priv->saved_mask_surface = mask_surface;
++    }
++}
++
++void
++meta_shaped_texture_restore (MetaShapedTexture *stex)
++{
++  MetaShapedTexturePrivate *priv = stex->priv;
++  CoglTexture *texture;
++  CoglError *error = NULL;
++
++  texture = meta_shaped_texture_get_texture (stex);
++
++  if (texture == NULL)
++    return;
++
++  if (priv->mask_texture)
++    {
++      if (!cogl_texture_set_data (priv->mask_texture, CLUTTER_CAIRO_FORMAT_ARGB32,
++                                  cairo_image_surface_get_stride (priv->saved_mask_surface),
++                                  cairo_image_surface_get_data (priv->saved_mask_surface), 0,
++                                  &error))
++        {
++          g_warning ("Failed to restore mask texture");
++          g_clear_pointer (&error, cogl_error_free);
++        }
++      g_clear_pointer (&priv->saved_mask_surface, cairo_surface_destroy);
++    }
++
++  /* if the main texture doesn't support direct writes, then
++   * write to the local backing texture instead, and blend old
++   * versus new at paint time.
++   */
++  if (priv->backing_store)
++    {
++      meta_texture_backing_store_redraw_mask (priv->backing_store);
++      texture = priv->backing_store->texture;
++    }
++
++  if (!cogl_texture_set_data (texture, CLUTTER_CAIRO_FORMAT_ARGB32,
++                              cairo_image_surface_get_stride (priv->saved_base_surface),
++                              cairo_image_surface_get_data (priv->saved_base_surface), 0,
++                              &error))
++    {
++      g_warning ("Failed to restore texture");
++      g_clear_pointer (&error, cogl_error_free);
++    }
++  g_clear_pointer (&priv->saved_base_surface, cairo_surface_destroy);
++
++  clutter_actor_queue_redraw (CLUTTER_ACTOR (stex));
++}
++
+ void
+ meta_shaped_texture_set_fallback_size (MetaShapedTexture *self,
+                                        guint              fallback_width,
+                                        guint              fallback_height)
+ {
+   MetaShapedTexturePrivate *priv = self->priv;
+ 
+   priv->fallback_width = fallback_width;
+   priv->fallback_height = fallback_height;
+ }
+ 
+ static void
+ meta_shaped_texture_cull_out (MetaCullable   *cullable,
+                               cairo_region_t *unobscured_region,
+                               cairo_region_t *clip_region)
+ {
+   MetaShapedTexture *self = META_SHAPED_TEXTURE (cullable);
+   MetaShapedTexturePrivate *priv = self->priv;
+ 
+   set_unobscured_region (self, unobscured_region);
+   set_clip_region (self, clip_region);
+ 
+   if (clutter_actor_get_paint_opacity (CLUTTER_ACTOR (self)) == 0xff)
+     {
+       if (priv->opaque_region)
+         {
+           if (unobscured_region)
+             cairo_region_subtract (unobscured_region, priv->opaque_region);
+           if (clip_region)
+             cairo_region_subtract (clip_region, priv->opaque_region);
+diff --git a/src/meta/meta-shaped-texture.h b/src/meta/meta-shaped-texture.h
+index 80b23f2ea..fc0567c9f 100644
+--- a/src/meta/meta-shaped-texture.h
++++ b/src/meta/meta-shaped-texture.h
+@@ -54,33 +54,35 @@ struct _MetaShapedTextureClass
+  */
+ struct _MetaShapedTexture
+ {
+   /*< private >*/
+   ClutterActor parent;
+ 
+   MetaShapedTexturePrivate *priv;
+ };
+ 
+ GType meta_shaped_texture_get_type (void) G_GNUC_CONST;
+ 
+ void meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex,
+ 					     gboolean           create_mipmaps);
+ 
+ gboolean meta_shaped_texture_update_area (MetaShapedTexture *stex,
+                                           int                x,
+                                           int                y,
+                                           int                width,
+                                           int                height);
+ 
+ CoglTexture * meta_shaped_texture_get_texture (MetaShapedTexture *stex);
+ 
+ void meta_shaped_texture_set_mask_texture (MetaShapedTexture *stex,
+                                            CoglTexture       *mask_texture);
+ void meta_shaped_texture_set_opaque_region (MetaShapedTexture *stex,
+                                             cairo_region_t    *opaque_region);
+ 
+ cairo_surface_t * meta_shaped_texture_get_image (MetaShapedTexture     *stex,
+                                                  cairo_rectangle_int_t *clip);
+ 
++void meta_shaped_texture_save (MetaShapedTexture *self);
++void meta_shaped_texture_restore (MetaShapedTexture *self);
+ G_END_DECLS
+ 
+ #endif /* __META_SHAPED_TEXTURE_H__ */
+-- 
+2.18.1
+
diff --git a/SOURCES/0009-window-Add-window-id.patch b/SOURCES/0009-window-Add-window-id.patch
new file mode 100644
index 0000000..aa3fceb
--- /dev/null
+++ b/SOURCES/0009-window-Add-window-id.patch
@@ -0,0 +1,128 @@
+From ca78984a92c84e8684455c80efb5a86c65061f96 Mon Sep 17 00:00:00 2001
+From: Olivier Fourdan <ofourdan@redhat.com>
+Date: Fri, 21 Sep 2018 17:49:36 +0200
+Subject: [PATCH 09/12] window: Add window id
+
+Generate a unique 64bit window-id which is unrelated to any windowing
+backend.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/306
+(cherry picked from commit bbcb66ddf4f5eeff379f250b361de0366459b4a3)
+---
+ src/core/display-private.h |  4 ++++
+ src/core/display.c         | 32 ++++++++++++++++++++++++++++++++
+ src/core/window-private.h  |  1 +
+ src/core/window.c          | 16 ++++++++++++++++
+ src/meta/window.h          |  2 ++
+ 5 files changed, 55 insertions(+)
+
+diff --git a/src/core/display-private.h b/src/core/display-private.h
+index 9e4518b..d24ee00 100644
+--- a/src/core/display-private.h
++++ b/src/core/display-private.h
+@@ -507,4 +507,8 @@ void meta_display_notify_pad_group_switch (MetaDisplay        *display,
+                                            guint               n_mode,
+                                            guint               n_modes);
+ 
++MetaWindow *meta_display_get_window_from_id (MetaDisplay *display,
++                                             uint64_t     window_id);
++uint64_t    meta_display_generate_window_id (MetaDisplay *display);
++
+ #endif
+diff --git a/src/core/display.c b/src/core/display.c
+index e7dd453..d09f119 100644
+--- a/src/core/display.c
++++ b/src/core/display.c
+@@ -3269,3 +3269,35 @@ meta_display_notify_pad_group_switch (MetaDisplay        *display,
+ 
+   g_string_free (message, TRUE);
+ }
++
++MetaWindow *
++meta_display_get_window_from_id (MetaDisplay *display,
++                                 uint64_t     window_id)
++{
++  g_autoptr (GSList) windows = NULL;
++  GSList *l;
++
++  windows = meta_display_list_windows (display, META_LIST_DEFAULT);
++  for (l = windows; l; l = l->next)
++    {
++      MetaWindow *window = l->data;
++
++      if (window->id == window_id)
++        return window;
++    }
++
++  return NULL;
++}
++
++uint64_t
++meta_display_generate_window_id (MetaDisplay *display)
++{
++  static uint64_t base_window_id;
++  static uint64_t last_window_id;
++
++  if (!base_window_id)
++    base_window_id = g_random_int () + 1;
++
++  /* We can overflow here, that's fine */
++  return (base_window_id + last_window_id++);
++}
+diff --git a/src/core/window-private.h b/src/core/window-private.h
+index 8552829..0f652b9 100644
+--- a/src/core/window-private.h
++++ b/src/core/window-private.h
+@@ -153,6 +153,7 @@ struct _MetaWindow
+ 
+   MetaDisplay *display;
+   MetaScreen *screen;
++  uint64_t id;
+   guint64 stamp;
+   MetaLogicalMonitor *monitor;
+   MetaWorkspace *workspace;
+diff --git a/src/core/window.c b/src/core/window.c
+index a1f4aa4..00b2e4d 100644
+--- a/src/core/window.c
++++ b/src/core/window.c
+@@ -1142,6 +1142,8 @@ _meta_window_shared_new (MetaDisplay         *display,
+       window->has_resize_func = FALSE;
+     }
+ 
++  window->id = meta_display_generate_window_id (display);
++
+   META_WINDOW_GET_CLASS (window)->manage (window);
+ 
+   if (!window->override_redirect)
+@@ -8466,3 +8468,17 @@ meta_window_is_stackable (MetaWindow *window)
+ {
+   return META_WINDOW_GET_CLASS (window)->is_stackable (window);
+ }
++
++/**
++ * meta_window_get_id:
++ * @window: a #MetaWindow
++ *
++ * Returns the window id associated with window.
++ *
++ * Returns: (transfer none): The window id
++ */
++uint64_t
++meta_window_get_id (MetaWindow *window)
++{
++  return window->id;
++}
+diff --git a/src/meta/window.h b/src/meta/window.h
+index 09317e6..364d541 100644
+--- a/src/meta/window.h
++++ b/src/meta/window.h
+@@ -260,4 +260,6 @@ gboolean meta_window_is_client_decorated (MetaWindow *window);
+ gboolean meta_window_titlebar_is_onscreen    (MetaWindow *window);
+ void     meta_window_shove_titlebar_onscreen (MetaWindow *window);
+ 
++uint64_t meta_window_get_id (MetaWindow *window);
++
+ #endif
+-- 
+2.19.2
+
diff --git a/SOURCES/0010-screen-cast-session-Add-window-id-support.patch b/SOURCES/0010-screen-cast-session-Add-window-id-support.patch
new file mode 100644
index 0000000..4324078
--- /dev/null
+++ b/SOURCES/0010-screen-cast-session-Add-window-id-support.patch
@@ -0,0 +1,72 @@
+From 4487b25d277e9ef73b464eb6e2e8971e4e5b13ae Mon Sep 17 00:00:00 2001
+From: Olivier Fourdan <ofourdan@redhat.com>
+Date: Mon, 15 Oct 2018 12:08:29 +0200
+Subject: [PATCH 10/12] screen-cast-session: Add window-id support
+
+Use the "window-id" property to select the window to cast using
+RecordWindow.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/306
+(cherry picked from commit c786b6c13cf9cb1bb9d7f7d2ca0ead0bf28a5fcd)
+---
+ src/backends/meta-screen-cast-session.c | 19 ++++++++++++++++++-
+ src/org.gnome.Mutter.ScreenCast.xml     |  5 ++++-
+ 2 files changed, 22 insertions(+), 2 deletions(-)
+
+diff --git a/src/backends/meta-screen-cast-session.c b/src/backends/meta-screen-cast-session.c
+index 3ee02c5..d0f5a79 100644
+--- a/src/backends/meta-screen-cast-session.c
++++ b/src/backends/meta-screen-cast-session.c
+@@ -340,6 +340,7 @@ handle_record_window (MetaDBusScreenCastSession *skeleton,
+   MetaWindow *window;
+   GError *error = NULL;
+   MetaDisplay *display;
++  GVariant *window_id_variant = NULL;
+   MetaScreenCastWindowStream *window_stream;
+   MetaScreenCastStream *stream;
+   char *stream_path;
+@@ -352,8 +353,24 @@ handle_record_window (MetaDBusScreenCastSession *skeleton,
+       return TRUE;
+     }
+ 
++  if (properties_variant)
++    window_id_variant = g_variant_lookup_value (properties_variant,
++                                                "window-id",
++                                                G_VARIANT_TYPE ("t"));
++
+   display = meta_get_display ();
+-  window = meta_display_get_focus_window (display);
++  if (window_id_variant)
++    {
++      uint64_t window_id;
++
++      g_variant_get (window_id_variant, "t", &window_id);
++      window = meta_display_get_window_from_id (display, window_id);
++    }
++  else
++    {
++      window = meta_display_get_focus_window (display);
++    }
++
+   if (!window)
+     {
+       g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+diff --git a/src/org.gnome.Mutter.ScreenCast.xml b/src/org.gnome.Mutter.ScreenCast.xml
+index 6134556..3cd02b6 100644
+--- a/src/org.gnome.Mutter.ScreenCast.xml
++++ b/src/org.gnome.Mutter.ScreenCast.xml
+@@ -86,7 +86,10 @@
+ 
+ 	Record a single window.
+ 
+-	Available @properties include: (none)
++	Available @properties include:
++
++	* "window-id" (t): Id of the window to record.
++
+     -->
+     <method name="RecordWindow">
+       <arg name="properties" type="a{sv}" direction="in" />
+-- 
+2.19.2
+
diff --git a/SOURCES/0011-window-Expose-the-client-type-in-the-API.patch b/SOURCES/0011-window-Expose-the-client-type-in-the-API.patch
new file mode 100644
index 0000000..33a3216
--- /dev/null
+++ b/SOURCES/0011-window-Expose-the-client-type-in-the-API.patch
@@ -0,0 +1,53 @@
+From bc7d794d3a605342f384e2ff13ab241a5eb2f468 Mon Sep 17 00:00:00 2001
+From: Olivier Fourdan <ofourdan@redhat.com>
+Date: Wed, 12 Dec 2018 13:05:21 +0100
+Subject: [PATCH 11/12] window: Expose the client type in the API
+
+We already have the enum exposed, but no accessor function.
+
+Add `meta_window_get_client_type()` which returns the
+`MetaWindowClientType` of a window.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/306
+(cherry picked from commit 7a5e0c78244b31052edfe3ea4ce1a1f48c94f93f)
+---
+ src/core/window.c | 14 ++++++++++++++
+ src/meta/window.h |  2 ++
+ 2 files changed, 16 insertions(+)
+
+diff --git a/src/core/window.c b/src/core/window.c
+index 00b2e4d..4dd8bd6 100644
+--- a/src/core/window.c
++++ b/src/core/window.c
+@@ -8482,3 +8482,17 @@ meta_window_get_id (MetaWindow *window)
+ {
+   return window->id;
+ }
++
++/**
++ * meta_window_get_client_type:
++ * @window: a #MetaWindow
++ *
++ * Returns the #MetaWindowClientType of the window.
++ *
++ * Returns: (transfer none): The root ancestor window
++ */
++MetaWindowClientType
++meta_window_get_client_type (MetaWindow *window)
++{
++  return window->client_type;
++}
+diff --git a/src/meta/window.h b/src/meta/window.h
+index 364d541..8a56afc 100644
+--- a/src/meta/window.h
++++ b/src/meta/window.h
+@@ -262,4 +262,6 @@ void     meta_window_shove_titlebar_onscreen (MetaWindow *window);
+ 
+ uint64_t meta_window_get_id (MetaWindow *window);
+ 
++MetaWindowClientType meta_window_get_client_type (MetaWindow *window);
++
+ #endif
+-- 
+2.19.2
+
diff --git a/SOURCES/0012-window-Fix-introspection-warnings.patch b/SOURCES/0012-window-Fix-introspection-warnings.patch
new file mode 100644
index 0000000..845931f
--- /dev/null
+++ b/SOURCES/0012-window-Fix-introspection-warnings.patch
@@ -0,0 +1,39 @@
+From 527f6e642a0ece477ae3a2c43421ad4101e34c57 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Fri, 14 Dec 2018 15:55:36 +0100
+Subject: [PATCH 12/12] window: Fix introspection warnings
+
+(transfer none) was added for fundamental types, which can't be
+transfered.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/356
+(cherry picked from commit 781ec74fd2bb505de7abde6e0641a62ab6ef728a)
+---
+ src/core/window.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/core/window.c b/src/core/window.c
+index 4dd8bd6..c79d266 100644
+--- a/src/core/window.c
++++ b/src/core/window.c
+@@ -8475,7 +8475,7 @@ meta_window_is_stackable (MetaWindow *window)
+  *
+  * Returns the window id associated with window.
+  *
+- * Returns: (transfer none): The window id
++ * Returns: The window id
+  */
+ uint64_t
+ meta_window_get_id (MetaWindow *window)
+@@ -8489,7 +8489,7 @@ meta_window_get_id (MetaWindow *window)
+  *
+  * Returns the #MetaWindowClientType of the window.
+  *
+- * Returns: (transfer none): The root ancestor window
++ * Returns: The root ancestor window
+  */
+ MetaWindowClientType
+ meta_window_get_client_type (MetaWindow *window)
+-- 
+2.19.2
+
diff --git a/SOURCES/add-support-for-plain-old-x-device-configuration.patch b/SOURCES/add-support-for-plain-old-x-device-configuration.patch
new file mode 100644
index 0000000..003fe08
--- /dev/null
+++ b/SOURCES/add-support-for-plain-old-x-device-configuration.patch
@@ -0,0 +1,377 @@
+From 210dfadcf0a2ae99bf4e2846118e4b22887551b8 Mon Sep 17 00:00:00 2001
+From: Rui Matos <tiagomatos@gmail.com>
+Date: Mon, 9 Oct 2017 18:39:52 +0200
+Subject: [PATCH 1/3] backends/x11: Add a synaptics check for two finger scroll
+ availability
+
+Commit "backends/x11: Support synaptics configuration" added support
+for synaptics two finger scrolling but didn't add the code to check
+that it is available resulting in the upper layer always assuming it
+isn't.
+---
+ src/backends/x11/meta-input-settings-x11.c | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/src/backends/x11/meta-input-settings-x11.c b/src/backends/x11/meta-input-settings-x11.c
+index 1d84dfccf..5a82edbe6 100644
+--- a/src/backends/x11/meta-input-settings-x11.c
++++ b/src/backends/x11/meta-input-settings-x11.c
+@@ -625,6 +625,17 @@ meta_input_settings_x11_has_two_finger_scroll (MetaInputSettings  *settings,
+   guchar *available = NULL;
+   gboolean has_two_finger = TRUE;
+ 
++  if (is_device_synaptics (device))
++    {
++      available = get_property (device, "Synaptics Capabilities",
++                                XA_INTEGER, 8, 4);
++      if (!available || !available[3])
++          has_two_finger = FALSE;
++
++      meta_XFree (available);
++      return has_two_finger;
++    }
++
+   available = get_property (device, "libinput Scroll Methods Available",
+                             XA_INTEGER, 8, SCROLL_METHOD_NUM_FIELDS);
+   if (!available || !available[SCROLL_METHOD_FIELD_2FG])
+-- 
+2.20.1
+
+
+From e2b5d9c65699ace1f00273bb9860a0fe66f662ee Mon Sep 17 00:00:00 2001
+From: Rui Matos <tiagomatos@gmail.com>
+Date: Mon, 9 Oct 2017 18:55:56 +0200
+Subject: [PATCH 2/3] backends/x11: Add disable while typing support for
+ synaptics
+
+This is basically a copy of the old g-s-d mouse plugin code to manage
+syndaemon when the synaptics driver is being used.
+---
+ src/backends/x11/meta-input-settings-x11.c | 112 +++++++++++++++++++++
+ 1 file changed, 112 insertions(+)
+
+diff --git a/src/backends/x11/meta-input-settings-x11.c b/src/backends/x11/meta-input-settings-x11.c
+index 5a82edbe6..33abd0c72 100644
+--- a/src/backends/x11/meta-input-settings-x11.c
++++ b/src/backends/x11/meta-input-settings-x11.c
+@@ -35,6 +35,9 @@
+ #ifdef HAVE_LIBGUDEV
+ #include <gudev/gudev.h>
+ #endif
++#ifdef __linux
++#include <sys/prctl.h>
++#endif
+ 
+ #include <meta/errors.h>
+ #include "backends/meta-logical-monitor.h"
+@@ -44,6 +47,8 @@ typedef struct _MetaInputSettingsX11Private
+ #ifdef HAVE_LIBGUDEV
+   GUdevClient *udev_client;
+ #endif
++  gboolean syndaemon_spawned;
++  GPid syndaemon_pid;
+ } MetaInputSettingsX11Private;
+ 
+ G_DEFINE_TYPE_WITH_PRIVATE (MetaInputSettingsX11, meta_input_settings_x11,
+@@ -332,6 +337,107 @@ change_synaptics_speed (ClutterInputDevice *device,
+   XCloseDevice (xdisplay, xdevice);
+ }
+ 
++/* Ensure that syndaemon dies together with us, to avoid running several of
++ * them */
++static void
++setup_syndaemon (gpointer user_data)
++{
++#ifdef __linux
++  prctl (PR_SET_PDEATHSIG, SIGHUP);
++#endif
++}
++
++static gboolean
++have_program_in_path (const char *name)
++{
++  gchar *path;
++  gboolean result;
++
++  path = g_find_program_in_path (name);
++  result = (path != NULL);
++  g_free (path);
++  return result;
++}
++
++static void
++syndaemon_died (GPid     pid,
++                gint     status,
++                gpointer user_data)
++{
++  MetaInputSettingsX11 *settings_x11 = META_INPUT_SETTINGS_X11 (user_data);
++  MetaInputSettingsX11Private *priv =
++    meta_input_settings_x11_get_instance_private (settings_x11);
++  GError *error = NULL;
++
++  if (!g_spawn_check_exit_status (status, &error))
++    {
++      if ((WIFSIGNALED (status) && WTERMSIG (status) != SIGHUP) ||
++          error->domain == G_SPAWN_EXIT_ERROR)
++        g_warning ("Syndaemon exited unexpectedly: %s", error->message);
++      g_error_free (error);
++    }
++
++  g_spawn_close_pid (pid);
++  priv->syndaemon_spawned = FALSE;
++}
++
++static void
++set_synaptics_disable_w_typing (MetaInputSettings *settings,
++                                gboolean           state)
++{
++  MetaInputSettingsX11 *settings_x11 = META_INPUT_SETTINGS_X11 (settings);
++  MetaInputSettingsX11Private *priv =
++    meta_input_settings_x11_get_instance_private (settings_x11);
++
++  if (state)
++    {
++      GError *error = NULL;
++      GPtrArray *args;
++
++      if (priv->syndaemon_spawned)
++        return;
++
++      if (!have_program_in_path ("syndaemon"))
++        return;
++
++      args = g_ptr_array_new ();
++
++      g_ptr_array_add (args, "syndaemon");
++      g_ptr_array_add (args, "-i");
++      g_ptr_array_add (args, "1.0");
++      g_ptr_array_add (args, "-t");
++      g_ptr_array_add (args, "-K");
++      g_ptr_array_add (args, "-R");
++      g_ptr_array_add (args, NULL);
++
++      /* we must use G_SPAWN_DO_NOT_REAP_CHILD to avoid
++       * double-forking, otherwise syndaemon will immediately get
++       * killed again through (PR_SET_PDEATHSIG when the intermediate
++       * process dies */
++      g_spawn_async (g_get_home_dir (), (char **) args->pdata, NULL,
++                     G_SPAWN_SEARCH_PATH|G_SPAWN_DO_NOT_REAP_CHILD, setup_syndaemon, NULL,
++                     &priv->syndaemon_pid, &error);
++
++      priv->syndaemon_spawned = (error == NULL);
++      g_ptr_array_free (args, TRUE);
++
++      if (error)
++        {
++          g_warning ("Failed to launch syndaemon: %s", error->message);
++          g_error_free (error);
++        }
++      else
++        {
++          g_child_watch_add (priv->syndaemon_pid, syndaemon_died, settings);
++        }
++    }
++  else if (priv->syndaemon_spawned)
++    {
++      kill (priv->syndaemon_pid, SIGHUP);
++      priv->syndaemon_spawned = FALSE;
++    }
++}
++
+ static void
+ meta_input_settings_x11_set_send_events (MetaInputSettings        *settings,
+                                          ClutterInputDevice       *device,
+@@ -456,6 +562,12 @@ meta_input_settings_x11_set_disable_while_typing (MetaInputSettings  *settings,
+ {
+   guchar value = (enabled) ? 1 : 0;
+ 
++  if (is_device_synaptics (device))
++    {
++      set_synaptics_disable_w_typing (settings, enabled);
++      return;
++    }
++
+   change_property (device, "libinput Disable While Typing Enabled",
+                    XA_INTEGER, 8, &value, 1);
+ }
+-- 
+2.20.1
+
+
+From 8c1d608c499a2038b0715f9cbf37e1bba87ef6e1 Mon Sep 17 00:00:00 2001
+From: Rui Matos <tiagomatos@gmail.com>
+Date: Tue, 10 Oct 2017 19:07:27 +0200
+Subject: [PATCH 3/3] backends/x11: Support plain old X device configuration
+
+We re-use part of the code added to support synaptics and add a few
+bits specific for xorg-x11-drv-evdev devices.
+---
+ src/backends/x11/meta-input-settings-x11.c | 97 +++++++++++++++++-----
+ 1 file changed, 74 insertions(+), 23 deletions(-)
+
+diff --git a/src/backends/x11/meta-input-settings-x11.c b/src/backends/x11/meta-input-settings-x11.c
+index 33abd0c72..906f504f8 100644
+--- a/src/backends/x11/meta-input-settings-x11.c
++++ b/src/backends/x11/meta-input-settings-x11.c
+@@ -179,35 +179,35 @@ is_device_synaptics (ClutterInputDevice *device)
+   return TRUE;
+ }
+ 
++static gboolean
++is_device_libinput (ClutterInputDevice *device)
++{
++  guchar *has_setting;
++
++  /* We just need looking for a synaptics-specific property */
++  has_setting = get_property (device, "libinput Send Events Modes Available", XA_INTEGER, 8, 2);
++  if (!has_setting)
++    return FALSE;
++
++  meta_XFree (has_setting);
++  return TRUE;
++}
++
+ static void
+-change_synaptics_tap_left_handed (ClutterInputDevice *device,
+-                                  gboolean            tap_enabled,
+-                                  gboolean            left_handed)
++change_x_device_left_handed (ClutterInputDevice *device,
++                             gboolean            left_handed)
+ {
+   MetaDisplay *display = meta_get_display ();
+   MetaBackend *backend = meta_get_backend ();
+   Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
+   XDevice *xdevice;
+-  guchar *tap_action, *buttons;
++  guchar *buttons;
+   guint buttons_capacity = 16, n_buttons;
+ 
+   xdevice = XOpenDevice(xdisplay, clutter_input_device_get_device_id (device));
+   if (!xdevice)
+     return;
+ 
+-  tap_action = get_property (device, "Synaptics Tap Action",
+-                             XA_INTEGER, 8, 7);
+-  if (!tap_action)
+-    goto out;
+-
+-  tap_action[4] = tap_enabled ? (left_handed ? 3 : 1) : 0;
+-  tap_action[5] = tap_enabled ? (left_handed ? 1 : 3) : 0;
+-  tap_action[6] = tap_enabled ? 2 : 0;
+-
+-  change_property (device, "Synaptics Tap Action",
+-                   XA_INTEGER, 8, tap_action, 7);
+-  meta_XFree (tap_action);
+-
+   if (display)
+     meta_error_trap_push (display);
+   buttons = g_new (guchar, buttons_capacity);
+@@ -231,17 +231,39 @@ change_synaptics_tap_left_handed (ClutterInputDevice *device,
+ 
+   if (display && meta_error_trap_pop_with_return (display))
+     {
+-      g_warning ("Could not set synaptics touchpad left-handed for %s",
++      g_warning ("Could not set left-handed for %s",
+                  clutter_input_device_get_device_name (device));
+     }
+ 
+- out:
+   XCloseDevice (xdisplay, xdevice);
+ }
+ 
+ static void
+-change_synaptics_speed (ClutterInputDevice *device,
+-                        gdouble             speed)
++change_synaptics_tap_left_handed (ClutterInputDevice *device,
++                                  gboolean            tap_enabled,
++                                  gboolean            left_handed)
++{
++  guchar *tap_action;
++
++  tap_action = get_property (device, "Synaptics Tap Action",
++                             XA_INTEGER, 8, 7);
++  if (!tap_action)
++    return;
++
++  tap_action[4] = tap_enabled ? (left_handed ? 3 : 1) : 0;
++  tap_action[5] = tap_enabled ? (left_handed ? 1 : 3) : 0;
++  tap_action[6] = tap_enabled ? 2 : 0;
++
++  change_property (device, "Synaptics Tap Action",
++                   XA_INTEGER, 8, tap_action, 7);
++  meta_XFree (tap_action);
++
++  change_x_device_left_handed (device, left_handed);
++}
++
++static void
++change_x_device_speed (ClutterInputDevice *device,
++                       gdouble             speed)
+ {
+   MetaDisplay *display = meta_get_display ();
+   MetaBackend *backend = meta_get_backend ();
+@@ -337,6 +359,23 @@ change_synaptics_speed (ClutterInputDevice *device,
+   XCloseDevice (xdisplay, xdevice);
+ }
+ 
++static void
++change_x_device_scroll_button (ClutterInputDevice *device,
++                               guint               button)
++{
++  guchar value;
++
++  value = button > 0 ? 1 : 0;
++  change_property (device, "Evdev Wheel Emulation",
++                   XA_INTEGER, 8, &value, 1);
++  if (button > 0)
++    {
++      value = button;
++      change_property (device, "Evdev Wheel Emulation Button",
++                       XA_INTEGER, 8, &value, 1);
++    }
++}
++
+ /* Ensure that syndaemon dies together with us, to avoid running several of
+  * them */
+ static void
+@@ -505,9 +544,10 @@ meta_input_settings_x11_set_speed (MetaInputSettings  *settings,
+   Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
+   gfloat value = speed;
+ 
+-  if (is_device_synaptics (device))
++  if (is_device_synaptics (device) ||
++      !is_device_libinput (device))
+     {
+-      change_synaptics_speed (device, speed);
++      change_x_device_speed (device, speed);
+       return;
+     }
+ 
+@@ -549,6 +589,11 @@ meta_input_settings_x11_set_left_handed (MetaInputSettings  *settings,
+           g_object_unref (settings);
+           return;
+         }
++      else if (!is_device_libinput (device))
++        {
++          change_x_device_left_handed (device, enabled);
++          return;
++        }
+ 
+       change_property (device, "libinput Left Handed Enabled",
+                        XA_INTEGER, 8, &value, 1);
+@@ -762,6 +807,12 @@ meta_input_settings_x11_set_scroll_button (MetaInputSettings  *settings,
+                                            ClutterInputDevice *device,
+                                            guint               button)
+ {
++  if (!is_device_libinput (device))
++    {
++      change_x_device_scroll_button (device, button);
++      return;
++    }
++
+   change_property (device, "libinput Button Scrolling Button",
+                    XA_INTEGER, 32, &button, 1);
+ }
+-- 
+2.20.1
+
diff --git a/SOURCES/deal-more-gracefully-with-oversized-windows.patch b/SOURCES/deal-more-gracefully-with-oversized-windows.patch
new file mode 100644
index 0000000..0e8807a
--- /dev/null
+++ b/SOURCES/deal-more-gracefully-with-oversized-windows.patch
@@ -0,0 +1,85 @@
+From af23f59757579b26296f967395c6e0ae1547a6c7 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
+Date: Wed, 12 Mar 2014 02:04:13 +0100
+Subject: [PATCH] constraints: Enforce X11 size limits
+
+X11 limits windows to a maximum of 32767x32767, enforce that restriction
+to keep insanely huge windows from crashing the WM.
+---
+ src/core/constraints.c | 42 ++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 42 insertions(+)
+
+diff --git a/src/core/constraints.c b/src/core/constraints.c
+index 57cfb73ed..026dd82c0 100644
+--- a/src/core/constraints.c
++++ b/src/core/constraints.c
+@@ -106,6 +106,7 @@ typedef enum
+   PRIORITY_TITLEBAR_VISIBLE = 4,
+   PRIORITY_PARTIALLY_VISIBLE_ON_WORKAREA = 4,
+   PRIORITY_CUSTOM_RULE = 4,
++  PRIORITY_XLIMITS = 4,
+   PRIORITY_MAXIMUM = 4 /* Dummy value used for loop end = max(all priorities) */
+ } ConstraintPriority;
+ 
+@@ -196,6 +197,10 @@ static gboolean constrain_partially_onscreen (MetaWindow         *window,
+                                               ConstraintInfo     *info,
+                                               ConstraintPriority  priority,
+                                               gboolean            check_only);
++static gboolean constrain_xlimits            (MetaWindow         *window,
++                                              ConstraintInfo     *info,
++                                              ConstraintPriority  priority,
++                                              gboolean            check_only);
+ 
+ static void setup_constraint_info        (ConstraintInfo      *info,
+                                           MetaWindow          *window,
+@@ -231,6 +236,7 @@ static const Constraint all_constraints[] = {
+   {constrain_fully_onscreen,     "constrain_fully_onscreen"},
+   {constrain_titlebar_visible,   "constrain_titlebar_visible"},
+   {constrain_partially_onscreen, "constrain_partially_onscreen"},
++  {constrain_xlimits,            "constrain_xlimits"},
+   {NULL,                         NULL}
+ };
+ 
+@@ -1675,3 +1681,39 @@ constrain_partially_onscreen (MetaWindow         *window,
+ 
+   return retval;
+ }
++
++
++#define MAX_WINDOW_SIZE 32767
++
++static gboolean
++constrain_xlimits (MetaWindow         *window,
++                   ConstraintInfo     *info,
++                   ConstraintPriority  priority,
++                   gboolean            check_only)
++{
++  int max_w, max_h;
++  gboolean constraint_already_satisfied;
++
++  if (priority > PRIORITY_XLIMITS)
++    return TRUE;
++
++  max_w = max_h = MAX_WINDOW_SIZE;
++
++  if (window->frame)
++    {
++      MetaFrameBorders borders;
++      meta_frame_calc_borders (window->frame, &borders);
++
++      max_w -= (borders.total.left + borders.total.right);
++      max_h -= (borders.total.top + borders.total.bottom);
++    }
++
++  constraint_already_satisfied = info->current.width < max_w && info->current.height < max_h;
++  if (check_only || constraint_already_satisfied)
++    return constraint_already_satisfied;
++
++  info->current.width = MIN (info->current.width, max_w);
++  info->current.height = MIN (info->current.height, max_h);
++
++  return TRUE;
++}
+-- 
+2.20.1
+
diff --git a/SOURCES/eglstream-mailbox-mode.patch b/SOURCES/eglstream-mailbox-mode.patch
new file mode 100644
index 0000000..3c48d20
--- /dev/null
+++ b/SOURCES/eglstream-mailbox-mode.patch
@@ -0,0 +1,77 @@
+From 6cfab9d2140aecff2f7e267bfb97c810c38823f2 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Fri, 11 Jan 2019 16:05:09 +0100
+Subject: [PATCH 1/2] renderer/native: Make EGLStream page flip errors
+ non-fatal
+
+Just continue rendering; we don't care if we were busy once, as it'll most
+likely work when we flip the next time.
+---
+ src/backends/native/meta-renderer-native.c | 8 +-------
+ 1 file changed, 1 insertion(+), 7 deletions(-)
+
+diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c
+index 470da8845..130ca5268 100644
+--- a/src/backends/native/meta-renderer-native.c
++++ b/src/backends/native/meta-renderer-native.c
+@@ -96,8 +96,6 @@ typedef struct _MetaRendererNativeGpuData
+ #ifdef HAVE_EGL_DEVICE
+   struct {
+     EGLDeviceEXT device;
+-
+-    gboolean no_egl_output_drm_flip_event;
+   } egl;
+ #endif
+ 
+@@ -1293,8 +1291,6 @@ flip_egl_stream (MetaOnscreenNative *onscreen_native,
+   renderer_gpu_data =
+     meta_renderer_native_get_gpu_data (onscreen_native->renderer_native,
+                                        onscreen_native->render_gpu);
+-  if (renderer_gpu_data->egl.no_egl_output_drm_flip_event)
+-    return FALSE;
+ 
+   closure_container =
+     meta_gpu_kms_wrap_flip_closure (onscreen_native->render_gpu, flip_closure);
+@@ -1315,9 +1311,7 @@ flip_egl_stream (MetaOnscreenNative *onscreen_native,
+       if (error->domain != META_EGL_ERROR ||
+           error->code != EGL_RESOURCE_BUSY_EXT)
+         {
+-          g_warning ("Failed to flip EGL stream (%s), relying on clock from "
+-                     "now on", error->message);
+-          renderer_gpu_data->egl.no_egl_output_drm_flip_event = TRUE;
++          g_warning ("Failed to flip EGL stream: %s", error->message);
+         }
+       g_error_free (error);
+       meta_gpu_kms_flip_closure_container_free (closure_container);
+-- 
+2.19.2
+
+
+From 7c22e8434348b16ff433494026a69d2892f3e48d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Fri, 11 Jan 2019 16:03:37 +0100
+Subject: [PATCH 2/2] renderer/native: Make the EGLStreams operate in mailbox
+ mode
+
+This means eglSwapBuffers() wont dead lock if there is an old buffer pending
+page flip. This could happen after e.g. mode changes or for other reasons.
+---
+ src/backends/native/meta-renderer-native.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c
+index 130ca5268..af5693b6e 100644
+--- a/src/backends/native/meta-renderer-native.c
++++ b/src/backends/native/meta-renderer-native.c
+@@ -2122,7 +2122,7 @@ meta_renderer_native_create_surface_egl_device (CoglOnscreen       *onscreen,
+   EGLOutputLayerEXT output_layer;
+   EGLAttrib output_attribs[3];
+   EGLint stream_attribs[] = {
+-    EGL_STREAM_FIFO_LENGTH_KHR, 1,
++    EGL_STREAM_FIFO_LENGTH_KHR, 0,
+     EGL_CONSUMER_AUTO_ACQUIRE_EXT, EGL_FALSE,
+     EGL_NONE
+   };
+-- 
+2.19.2
+
diff --git a/SOURCES/hw-cursor-on-demand-gnome-3-28.patch b/SOURCES/hw-cursor-on-demand-gnome-3-28.patch
new file mode 100644
index 0000000..ae3db81
--- /dev/null
+++ b/SOURCES/hw-cursor-on-demand-gnome-3-28.patch
@@ -0,0 +1,3006 @@
+diff --git a/src/Makefile.am b/src/Makefile.am
+index bcb3505c7..5bbac70e8 100644
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -114,6 +114,8 @@ libmutter_@LIBMUTTER_API_VERSION@_la_SOURCES =	\
+ 	backends/meta-cursor-tracker-private.h	\
+ 	backends/meta-cursor-renderer.c		\
+ 	backends/meta-cursor-renderer.h		\
++	backends/meta-cursor-sprite-xcursor.c	\
++	backends/meta-cursor-sprite-xcursor.h	\
+ 	backends/meta-dnd-private.h		\
+ 	backends/meta-egl.c			\
+ 	backends/meta-egl.h			\
+@@ -176,6 +178,8 @@ libmutter_@LIBMUTTER_API_VERSION@_la_SOURCES =	\
+ 	backends/x11/meta-gpu-xrandr.h			\
+ 	backends/x11/cm/meta-backend-x11-cm.c		\
+ 	backends/x11/cm/meta-backend-x11-cm.h		\
++	backends/x11/cm/meta-cursor-sprite-xfixes.c	\
++	backends/x11/cm/meta-cursor-sprite-xfixes.h	\
+ 	backends/x11/cm/meta-renderer-x11-cm.c		\
+ 	backends/x11/cm/meta-renderer-x11-cm.h		\
+ 	backends/x11/nested/meta-backend-x11-nested.c	\
+@@ -370,6 +374,8 @@ if HAVE_WAYLAND
+ libmutter_@LIBMUTTER_API_VERSION@_la_SOURCES +=	\
+ 	compositor/meta-surface-actor-wayland.c	\
+ 	compositor/meta-surface-actor-wayland.h	\
++	wayland/meta-cursor-sprite-wayland.c	\
++	wayland/meta-cursor-sprite-wayland.h	\
+ 	wayland/meta-wayland.c			\
+ 	wayland/meta-wayland.h			\
+ 	wayland/meta-wayland-private.h		\
+@@ -431,10 +437,10 @@ libmutter_@LIBMUTTER_API_VERSION@_la_SOURCES +=	\
+ 	wayland/meta-wayland-touch.h		\
+ 	wayland/meta-wayland-surface.c		\
+ 	wayland/meta-wayland-surface.h		\
+-	wayland/meta-wayland-surface-role-cursor.c	\
+-	wayland/meta-wayland-surface-role-cursor.h	\
+-	wayland/meta-wayland-surface-role-tablet-cursor.c	\
+-	wayland/meta-wayland-surface-role-tablet-cursor.h	\
++	wayland/meta-wayland-cursor-surface.c	\
++	wayland/meta-wayland-cursor-surface.h	\
++	wayland/meta-wayland-tablet-cursor-surface.c	\
++	wayland/meta-wayland-tablet-cursor-surface.h	\
+ 	wayland/meta-wayland-actor-surface.c	\
+ 	wayland/meta-wayland-actor-surface.h	\
+ 	wayland/meta-wayland-subsurface.c	\
+diff --git a/src/backends/meta-cursor-renderer.c b/src/backends/meta-cursor-renderer.c
+index f6470e66a..eb79737f1 100644
+--- a/src/backends/meta-cursor-renderer.c
++++ b/src/backends/meta-cursor-renderer.c
+@@ -193,8 +193,8 @@ meta_cursor_renderer_calculate_rect (MetaCursorRenderer *renderer,
+ }
+ 
+ static void
+-update_cursor (MetaCursorRenderer *renderer,
+-               MetaCursorSprite   *cursor_sprite)
++meta_cursor_renderer_update_cursor (MetaCursorRenderer *renderer,
++                                    MetaCursorSprite   *cursor_sprite)
+ {
+   MetaCursorRendererPrivate *priv = meta_cursor_renderer_get_instance_private (renderer);
+   gboolean handled_by_backend;
+@@ -237,7 +237,7 @@ meta_cursor_renderer_set_cursor (MetaCursorRenderer *renderer,
+     return;
+   priv->displayed_cursor = cursor_sprite;
+ 
+-  update_cursor (renderer, cursor_sprite);
++  meta_cursor_renderer_update_cursor (renderer, cursor_sprite);
+ }
+ 
+ void
+@@ -246,7 +246,7 @@ meta_cursor_renderer_force_update (MetaCursorRenderer *renderer)
+   MetaCursorRendererPrivate *priv =
+     meta_cursor_renderer_get_instance_private (renderer);
+ 
+-  update_cursor (renderer, priv->displayed_cursor);
++  meta_cursor_renderer_update_cursor (renderer, priv->displayed_cursor);
+ }
+ 
+ void
+@@ -261,7 +261,7 @@ meta_cursor_renderer_set_position (MetaCursorRenderer *renderer,
+   priv->current_x = x;
+   priv->current_y = y;
+ 
+-  update_cursor (renderer, priv->displayed_cursor);
++  meta_cursor_renderer_update_cursor (renderer, priv->displayed_cursor);
+ }
+ 
+ ClutterPoint
+@@ -283,28 +283,3 @@ meta_cursor_renderer_get_cursor (MetaCursorRenderer *renderer)
+ 
+   return priv->displayed_cursor;
+ }
+-
+-#ifdef HAVE_WAYLAND
+-void
+-meta_cursor_renderer_realize_cursor_from_wl_buffer (MetaCursorRenderer *renderer,
+-                                                    MetaCursorSprite   *cursor_sprite,
+-                                                    struct wl_resource *buffer)
+-{
+-
+-  MetaCursorRendererClass *renderer_class = META_CURSOR_RENDERER_GET_CLASS (renderer);
+-
+-  if (renderer_class->realize_cursor_from_wl_buffer)
+-    renderer_class->realize_cursor_from_wl_buffer (renderer, cursor_sprite, buffer);
+-}
+-#endif
+-
+-void
+-meta_cursor_renderer_realize_cursor_from_xcursor (MetaCursorRenderer *renderer,
+-                                                  MetaCursorSprite   *cursor_sprite,
+-                                                  XcursorImage       *xc_image)
+-{
+-  MetaCursorRendererClass *renderer_class = META_CURSOR_RENDERER_GET_CLASS (renderer);
+-
+-  if (renderer_class->realize_cursor_from_xcursor)
+-    renderer_class->realize_cursor_from_xcursor (renderer, cursor_sprite, xc_image);
+-}
+diff --git a/src/backends/meta-cursor-renderer.h b/src/backends/meta-cursor-renderer.h
+index 1691f4471..830d16ef6 100644
+--- a/src/backends/meta-cursor-renderer.h
++++ b/src/backends/meta-cursor-renderer.h
+@@ -26,10 +26,6 @@
+ #define META_CURSOR_RENDERER_H
+ 
+ #include <glib-object.h>
+-#include <X11/Xcursor/Xcursor.h>
+-#ifdef HAVE_WAYLAND
+-#include <wayland-server.h>
+-#endif
+ 
+ #include <meta/screen.h>
+ #include "meta-cursor.h"
+@@ -44,14 +40,6 @@ struct _MetaCursorRendererClass
+ 
+   gboolean (* update_cursor) (MetaCursorRenderer *renderer,
+                               MetaCursorSprite   *cursor_sprite);
+-#ifdef HAVE_WAYLAND
+-  void (* realize_cursor_from_wl_buffer) (MetaCursorRenderer *renderer,
+-                                          MetaCursorSprite *cursor_sprite,
+-                                          struct wl_resource *buffer);
+-#endif
+-  void (* realize_cursor_from_xcursor) (MetaCursorRenderer *renderer,
+-                                        MetaCursorSprite *cursor_sprite,
+-                                        XcursorImage *xc_image);
+ };
+ 
+ MetaCursorRenderer * meta_cursor_renderer_new (void);
+@@ -70,16 +58,6 @@ MetaCursorSprite * meta_cursor_renderer_get_cursor (MetaCursorRenderer *renderer
+ ClutterRect meta_cursor_renderer_calculate_rect (MetaCursorRenderer *renderer,
+                                                  MetaCursorSprite   *cursor_sprite);
+ 
+-#ifdef HAVE_WAYLAND
+-void meta_cursor_renderer_realize_cursor_from_wl_buffer (MetaCursorRenderer *renderer,
+-                                                         MetaCursorSprite   *cursor_sprite,
+-                                                         struct wl_resource *buffer);
+-#endif
+-
+-void meta_cursor_renderer_realize_cursor_from_xcursor (MetaCursorRenderer *renderer,
+-                                                       MetaCursorSprite   *cursor_sprite,
+-                                                       XcursorImage       *xc_image);
+-
+ void meta_cursor_renderer_emit_painted (MetaCursorRenderer *renderer,
+                                         MetaCursorSprite   *cursor_sprite);
+ 
+diff --git a/src/backends/meta-cursor-sprite-xcursor.c b/src/backends/meta-cursor-sprite-xcursor.c
+new file mode 100644
+index 000000000..657c1dae8
+--- /dev/null
++++ b/src/backends/meta-cursor-sprite-xcursor.c
+@@ -0,0 +1,292 @@
++/*
++ * Copyright 2013, 2018 Red Hat, Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program 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
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, see <http://www.gnu.org/licenses/>.
++ *
++ */
++
++#include "config.h"
++
++#include "backends/meta-cursor-sprite-xcursor.h"
++
++#include "backends/meta-cursor.h"
++#include "backends/meta-cursor-renderer.h"
++#include "clutter/clutter.h"
++#include "cogl/cogl.h"
++#include "meta/prefs.h"
++
++struct _MetaCursorSpriteXcursor
++{
++  MetaCursorSprite parent;
++
++  MetaCursor cursor;
++
++  int current_frame;
++  XcursorImages *xcursor_images;
++
++  int theme_scale;
++  gboolean theme_dirty;
++};
++
++G_DEFINE_TYPE (MetaCursorSpriteXcursor, meta_cursor_sprite_xcursor,
++               META_TYPE_CURSOR_SPRITE)
++
++static const char *
++translate_meta_cursor (MetaCursor cursor)
++{
++  switch (cursor)
++    {
++    case META_CURSOR_DEFAULT:
++      return "left_ptr";
++    case META_CURSOR_NORTH_RESIZE:
++      return "top_side";
++    case META_CURSOR_SOUTH_RESIZE:
++      return "bottom_side";
++    case META_CURSOR_WEST_RESIZE:
++      return "left_side";
++    case META_CURSOR_EAST_RESIZE:
++      return "right_side";
++    case META_CURSOR_SE_RESIZE:
++      return "bottom_right_corner";
++    case META_CURSOR_SW_RESIZE:
++      return "bottom_left_corner";
++    case META_CURSOR_NE_RESIZE:
++      return "top_right_corner";
++    case META_CURSOR_NW_RESIZE:
++      return "top_left_corner";
++    case META_CURSOR_MOVE_OR_RESIZE_WINDOW:
++      return "fleur";
++    case META_CURSOR_BUSY:
++      return "watch";
++    case META_CURSOR_DND_IN_DRAG:
++      return "dnd-none";
++    case META_CURSOR_DND_MOVE:
++      return "dnd-move";
++    case META_CURSOR_DND_COPY:
++      return "dnd-copy";
++    case META_CURSOR_DND_UNSUPPORTED_TARGET:
++      return "dnd-none";
++    case META_CURSOR_POINTING_HAND:
++      return "hand2";
++    case META_CURSOR_CROSSHAIR:
++      return "crosshair";
++    case META_CURSOR_IBEAM:
++      return "xterm";
++    default:
++      break;
++    }
++
++  g_assert_not_reached ();
++}
++
++MetaCursor
++meta_cursor_sprite_xcursor_get_cursor (MetaCursorSpriteXcursor *sprite_xcursor)
++{
++  return sprite_xcursor->cursor;
++}
++
++Cursor
++meta_create_x_cursor (Display    *xdisplay,
++                      MetaCursor  cursor)
++{
++  return XcursorLibraryLoadCursor (xdisplay, translate_meta_cursor (cursor));
++}
++
++static XcursorImages *
++load_cursor_on_client (MetaCursor cursor, int scale)
++{
++  return XcursorLibraryLoadImages (translate_meta_cursor (cursor),
++                                   meta_prefs_get_cursor_theme (),
++                                   meta_prefs_get_cursor_size () * scale);
++}
++
++static void
++load_from_current_xcursor_image (MetaCursorSpriteXcursor *sprite_xcursor)
++{
++  MetaCursorSprite *sprite = META_CURSOR_SPRITE (sprite_xcursor);
++  XcursorImage *xc_image;
++  int width, height, rowstride;
++  CoglPixelFormat cogl_format;
++  ClutterBackend *clutter_backend;
++  CoglContext *cogl_context;
++  CoglTexture2D *texture;
++  CoglError *error = NULL;
++
++  g_assert (!meta_cursor_sprite_get_cogl_texture (sprite));
++
++  xc_image = meta_cursor_sprite_xcursor_get_current_image (sprite_xcursor);
++  width = (int) xc_image->width;
++  height = (int) xc_image->height;
++  rowstride = width * 4;
++
++#if G_BYTE_ORDER == G_LITTLE_ENDIAN
++  cogl_format = COGL_PIXEL_FORMAT_BGRA_8888;
++#else
++  cogl_format = COGL_PIXEL_FORMAT_ARGB_8888;
++#endif
++
++  clutter_backend = clutter_get_default_backend ();
++  cogl_context = clutter_backend_get_cogl_context (clutter_backend);
++  texture = cogl_texture_2d_new_from_data (cogl_context,
++                                           width, height,
++                                           cogl_format,
++                                           rowstride,
++                                           (uint8_t *) xc_image->pixels,
++                                           &error);
++  if (!texture)
++    {
++      g_warning ("Failed to allocate cursor texture: %s\n", error->message);
++      cogl_error_free (error);
++    }
++
++  meta_cursor_sprite_set_texture (sprite,
++                                  COGL_TEXTURE (texture),
++                                  xc_image->xhot, xc_image->yhot);
++
++  if (texture)
++    cogl_object_unref (texture);
++}
++
++void
++meta_cursor_sprite_xcursor_set_theme_scale (MetaCursorSpriteXcursor *sprite_xcursor,
++                                            int                      theme_scale)
++{
++  if (sprite_xcursor->theme_scale != theme_scale)
++    sprite_xcursor->theme_dirty = TRUE;
++  sprite_xcursor->theme_scale = theme_scale;
++}
++
++
++static gboolean
++meta_cursor_sprite_xcursor_is_animated (MetaCursorSprite *sprite)
++{
++  MetaCursorSpriteXcursor *sprite_xcursor = META_CURSOR_SPRITE_XCURSOR (sprite);
++
++  return (sprite_xcursor->xcursor_images &&
++          sprite_xcursor->xcursor_images->nimage > 1);
++}
++
++XcursorImage *
++meta_cursor_sprite_xcursor_get_current_image (MetaCursorSpriteXcursor *sprite_xcursor)
++{
++  return sprite_xcursor->xcursor_images->images[sprite_xcursor->current_frame];
++}
++
++static void
++meta_cursor_sprite_xcursor_tick_frame (MetaCursorSprite *sprite)
++{
++  MetaCursorSpriteXcursor *sprite_xcursor = META_CURSOR_SPRITE_XCURSOR (sprite);
++
++  if (!meta_cursor_sprite_is_animated (sprite))
++    return;
++
++  sprite_xcursor->current_frame++;
++
++  if (sprite_xcursor->current_frame >= sprite_xcursor->xcursor_images->nimage)
++    sprite_xcursor->current_frame = 0;
++
++  meta_cursor_sprite_clear_texture (sprite);
++  load_from_current_xcursor_image (sprite_xcursor);
++}
++
++static unsigned int
++meta_cursor_sprite_xcursor_get_current_frame_time (MetaCursorSprite *sprite)
++{
++  MetaCursorSpriteXcursor *sprite_xcursor = META_CURSOR_SPRITE_XCURSOR (sprite);
++  XcursorImages *xcursor_images;
++
++  g_return_val_if_fail (meta_cursor_sprite_is_animated (sprite), 0);
++
++  xcursor_images = sprite_xcursor->xcursor_images;
++  return xcursor_images->images[sprite_xcursor->current_frame]->delay;
++}
++
++static void
++load_cursor_from_theme (MetaCursorSprite *sprite)
++{
++  MetaCursorSpriteXcursor *sprite_xcursor = META_CURSOR_SPRITE_XCURSOR (sprite);
++
++  g_assert (sprite_xcursor->cursor != META_CURSOR_NONE);
++
++  sprite_xcursor->theme_dirty = FALSE;
++
++  /* We might be reloading with a different scale. If so clear the old data. */
++  if (sprite_xcursor->xcursor_images)
++    {
++      meta_cursor_sprite_clear_texture (sprite);
++      XcursorImagesDestroy (sprite_xcursor->xcursor_images);
++    }
++
++  sprite_xcursor->current_frame = 0;
++  sprite_xcursor->xcursor_images =
++    load_cursor_on_client (sprite_xcursor->cursor,
++                           sprite_xcursor->theme_scale);
++  if (!sprite_xcursor->xcursor_images)
++    g_error ("Could not find cursor. Perhaps set XCURSOR_PATH?");
++
++  load_from_current_xcursor_image (sprite_xcursor);
++}
++
++static void
++meta_cursor_sprite_xcursor_realize_texture (MetaCursorSprite *sprite)
++{
++  MetaCursorSpriteXcursor *sprite_xcursor = META_CURSOR_SPRITE_XCURSOR (sprite);
++
++  if (sprite_xcursor->theme_dirty)
++    load_cursor_from_theme (sprite);
++}
++
++MetaCursorSpriteXcursor *
++meta_cursor_sprite_xcursor_new (MetaCursor cursor)
++{
++  MetaCursorSpriteXcursor *sprite_xcursor;
++
++  sprite_xcursor = g_object_new (META_TYPE_CURSOR_SPRITE_XCURSOR, NULL);
++  sprite_xcursor->cursor = cursor;
++
++  return sprite_xcursor;
++}
++
++static void
++meta_cursor_sprite_xcursor_finalize (GObject *object)
++{
++  MetaCursorSpriteXcursor *sprite_xcursor = META_CURSOR_SPRITE_XCURSOR (object);
++
++  g_clear_pointer (&sprite_xcursor->xcursor_images,
++                   XcursorImagesDestroy);
++
++  G_OBJECT_CLASS (meta_cursor_sprite_xcursor_parent_class)->finalize (object);
++}
++
++static void
++meta_cursor_sprite_xcursor_init (MetaCursorSpriteXcursor *sprite_xcursor)
++{
++  sprite_xcursor->theme_dirty = TRUE;
++}
++
++static void
++meta_cursor_sprite_xcursor_class_init (MetaCursorSpriteXcursorClass *klass)
++{
++  GObjectClass *object_class = G_OBJECT_CLASS (klass);
++  MetaCursorSpriteClass *cursor_sprite_class = META_CURSOR_SPRITE_CLASS (klass);
++
++  object_class->finalize = meta_cursor_sprite_xcursor_finalize;
++
++  cursor_sprite_class->realize_texture =
++    meta_cursor_sprite_xcursor_realize_texture;
++  cursor_sprite_class->is_animated = meta_cursor_sprite_xcursor_is_animated;
++  cursor_sprite_class->tick_frame = meta_cursor_sprite_xcursor_tick_frame;
++  cursor_sprite_class->get_current_frame_time =
++    meta_cursor_sprite_xcursor_get_current_frame_time;
++}
+diff --git a/src/backends/meta-cursor-sprite-xcursor.h b/src/backends/meta-cursor-sprite-xcursor.h
+new file mode 100644
+index 000000000..dbc927484
+--- /dev/null
++++ b/src/backends/meta-cursor-sprite-xcursor.h
+@@ -0,0 +1,43 @@
++/*
++ * Copyright 2013, 2018 Red Hat, Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program 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
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, see <http://www.gnu.org/licenses/>.
++ *
++ */
++
++#ifndef META_CURSOR_SPRITE_XCURSOR_H
++#define META_CURSOR_SPRITE_XCURSOR_H
++
++#include <glib-object.h>
++#include <X11/Xcursor/Xcursor.h>
++
++#include "backends/meta-cursor.h"
++
++#define META_TYPE_CURSOR_SPRITE_XCURSOR meta_cursor_sprite_xcursor_get_type ()
++G_DECLARE_FINAL_TYPE (MetaCursorSpriteXcursor, meta_cursor_sprite_xcursor,
++                      META, CURSOR_SPRITE_XCURSOR, MetaCursorSprite)
++
++MetaCursorSpriteXcursor * meta_cursor_sprite_xcursor_new (MetaCursor cursor);
++
++void meta_cursor_sprite_xcursor_set_theme_scale (MetaCursorSpriteXcursor *sprite_xcursor,
++                                                 int                      scale);
++
++MetaCursor meta_cursor_sprite_xcursor_get_cursor (MetaCursorSpriteXcursor *sprite_xcusror);
++
++XcursorImage * meta_cursor_sprite_xcursor_get_current_image (MetaCursorSpriteXcursor *sprite_xcursor);
++
++Cursor meta_create_x_cursor (Display    *xdisplay,
++                             MetaCursor  cursor);
++
++#endif /* META_CURSOR_SPRITE_XCURSOR_H */
+diff --git a/src/backends/meta-cursor-tracker-private.h b/src/backends/meta-cursor-tracker-private.h
+index 2ec946847..6f4f84b83 100644
+--- a/src/backends/meta-cursor-tracker-private.h
++++ b/src/backends/meta-cursor-tracker-private.h
+@@ -26,6 +26,7 @@
+ 
+ #include "meta-cursor.h"
+ #include "meta-cursor-renderer.h"
++#include "backends/x11/cm/meta-cursor-sprite-xfixes.h"
+ 
+ struct _MetaCursorTracker {
+   GObject parent_instance;
+@@ -46,7 +47,7 @@ struct _MetaCursorTracker {
+   MetaCursorSprite *root_cursor;
+ 
+   /* The cursor from the X11 server. */
+-  MetaCursorSprite *xfixes_cursor;
++  MetaCursorSpriteXfixes *xfixes_cursor;
+ };
+ 
+ struct _MetaCursorTrackerClass {
+diff --git a/src/backends/meta-cursor-tracker.c b/src/backends/meta-cursor-tracker.c
+index 74fa4351d..6244f11ee 100644
+--- a/src/backends/meta-cursor-tracker.c
++++ b/src/backends/meta-cursor-tracker.c
+@@ -40,9 +40,9 @@
+ 
+ #include <gdk/gdk.h>
+ #include <gdk/gdkx.h>
+-#include <X11/extensions/Xfixes.h>
+ 
+ #include "meta-backend-private.h"
++#include "backends/x11/cm/meta-cursor-sprite-xfixes.h"
+ 
+ G_DEFINE_TYPE (MetaCursorTracker, meta_cursor_tracker, G_TYPE_OBJECT);
+ 
+@@ -218,75 +218,14 @@ static void
+ ensure_xfixes_cursor (MetaCursorTracker *tracker)
+ {
+   MetaDisplay *display = meta_get_display ();
+-  XFixesCursorImage *cursor_image;
+-  CoglTexture2D *sprite;
+-  guint8 *cursor_data;
+-  gboolean free_cursor_data;
+-  CoglContext *ctx;
+-  CoglError *error = NULL;
++  g_autoptr (GError) error = NULL;
+ 
+   if (tracker->xfixes_cursor)
+     return;
+ 
+-  cursor_image = XFixesGetCursorImage (display->xdisplay);
+-  if (!cursor_image)
+-    return;
+-
+-  /* Like all X APIs, XFixesGetCursorImage() returns arrays of 32-bit
+-   * quantities as arrays of long; we need to convert on 64 bit */
+-  if (sizeof(long) == 4)
+-    {
+-      cursor_data = (guint8 *)cursor_image->pixels;
+-      free_cursor_data = FALSE;
+-    }
+-  else
+-    {
+-      int i, j;
+-      guint32 *cursor_words;
+-      gulong *p;
+-      guint32 *q;
+-
+-      cursor_words = g_new (guint32, cursor_image->width * cursor_image->height);
+-      cursor_data = (guint8 *)cursor_words;
+-
+-      p = cursor_image->pixels;
+-      q = cursor_words;
+-      for (j = 0; j < cursor_image->height; j++)
+-        for (i = 0; i < cursor_image->width; i++)
+-          *(q++) = *(p++);
+-
+-      free_cursor_data = TRUE;
+-    }
+-
+-  ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
+-  sprite = cogl_texture_2d_new_from_data (ctx,
+-                                          cursor_image->width,
+-                                          cursor_image->height,
+-                                          CLUTTER_CAIRO_FORMAT_ARGB32,
+-                                          cursor_image->width * 4, /* stride */
+-                                          cursor_data,
+-                                          &error);
+-
+-  if (free_cursor_data)
+-    g_free (cursor_data);
+-
+-  if (error != NULL)
+-    {
+-      meta_warning ("Failed to allocate cursor sprite texture: %s\n", error->message);
+-      cogl_error_free (error);
+-    }
+-
+-  if (sprite != NULL)
+-    {
+-      MetaCursorSprite *cursor_sprite = meta_cursor_sprite_new ();
+-      meta_cursor_sprite_set_texture (cursor_sprite,
+-                                      COGL_TEXTURE (sprite),
+-                                      cursor_image->xhot,
+-                                      cursor_image->yhot);
+-      cogl_object_unref (sprite);
+-      tracker->xfixes_cursor = cursor_sprite;
+-    }
+-  XFree (cursor_image);
++  tracker->xfixes_cursor = meta_cursor_sprite_xfixes_new (display, &error);
++  if (!tracker->xfixes_cursor)
++    g_warning ("Failed to create XFIXES cursor: %s", error->message);
+ }
+ 
+ /**
+@@ -308,7 +247,7 @@ meta_cursor_tracker_get_sprite (MetaCursorTracker *tracker)
+   else
+     {
+       ensure_xfixes_cursor (tracker);
+-      cursor_sprite = tracker->xfixes_cursor;
++      cursor_sprite = META_CURSOR_SPRITE (tracker->xfixes_cursor);
+     }
+ 
+   if (cursor_sprite)
+@@ -345,7 +284,7 @@ meta_cursor_tracker_get_hot (MetaCursorTracker *tracker,
+   else
+     {
+       ensure_xfixes_cursor (tracker);
+-      cursor_sprite = tracker->xfixes_cursor;
++      cursor_sprite = META_CURSOR_SPRITE (tracker->xfixes_cursor);
+     }
+ 
+   if (cursor_sprite)
+diff --git a/src/backends/meta-cursor.c b/src/backends/meta-cursor.c
+index beeee765b..9750dc00b 100644
+--- a/src/backends/meta-cursor.c
++++ b/src/backends/meta-cursor.c
+@@ -23,19 +23,12 @@
+ 
+ #include "meta-cursor.h"
+ 
+-#include <meta/errors.h>
++#include "backends/meta-backend-private.h"
++#include "cogl/cogl.h"
++#include "meta/common.h"
+ 
+-#include "display-private.h"
+-#include "screen-private.h"
+-#include "meta-backend-private.h"
+-
+-#include <string.h>
+-
+-#include <X11/cursorfont.h>
+-#include <X11/extensions/Xfixes.h>
+-#include <X11/Xcursor/Xcursor.h>
+-
+-enum {
++enum
++{
+   PREPARE_AT,
+   TEXTURE_CHANGED,
+ 
+@@ -44,316 +37,148 @@ enum {
+ 
+ static guint signals[LAST_SIGNAL];
+ 
+-struct _MetaCursorSprite
++typedef struct _MetaCursorSpritePrivate
+ {
+   GObject parent;
+ 
+-  MetaCursor cursor;
+-
+   CoglTexture2D *texture;
+   float texture_scale;
+   int hot_x, hot_y;
++} MetaCursorSpritePrivate;
+ 
+-  int current_frame;
+-  XcursorImages *xcursor_images;
+-
+-  int theme_scale;
+-  gboolean theme_dirty;
+-};
+-
+-G_DEFINE_TYPE (MetaCursorSprite, meta_cursor_sprite, G_TYPE_OBJECT)
++G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (MetaCursorSprite,
++                                     meta_cursor_sprite,
++                                     G_TYPE_OBJECT)
+ 
+-static const char *
+-translate_meta_cursor (MetaCursor cursor)
+-{
+-  switch (cursor)
+-    {
+-    case META_CURSOR_DEFAULT:
+-      return "left_ptr";
+-    case META_CURSOR_NORTH_RESIZE:
+-      return "top_side";
+-    case META_CURSOR_SOUTH_RESIZE:
+-      return "bottom_side";
+-    case META_CURSOR_WEST_RESIZE:
+-      return "left_side";
+-    case META_CURSOR_EAST_RESIZE:
+-      return "right_side";
+-    case META_CURSOR_SE_RESIZE:
+-      return "bottom_right_corner";
+-    case META_CURSOR_SW_RESIZE:
+-      return "bottom_left_corner";
+-    case META_CURSOR_NE_RESIZE:
+-      return "top_right_corner";
+-    case META_CURSOR_NW_RESIZE:
+-      return "top_left_corner";
+-    case META_CURSOR_MOVE_OR_RESIZE_WINDOW:
+-      return "fleur";
+-    case META_CURSOR_BUSY:
+-      return "watch";
+-    case META_CURSOR_DND_IN_DRAG:
+-      return "dnd-none";
+-    case META_CURSOR_DND_MOVE:
+-      return "dnd-move";
+-    case META_CURSOR_DND_COPY:
+-      return "dnd-copy";
+-    case META_CURSOR_DND_UNSUPPORTED_TARGET:
+-      return "dnd-none";
+-    case META_CURSOR_POINTING_HAND:
+-      return "hand2";
+-    case META_CURSOR_CROSSHAIR:
+-      return "crosshair";
+-    case META_CURSOR_IBEAM:
+-      return "xterm";
+-    default:
+-      break;
+-    }
+-
+-  g_assert_not_reached ();
+-}
+-
+-Cursor
+-meta_cursor_create_x_cursor (Display    *xdisplay,
+-                             MetaCursor  cursor)
+-{
+-  return XcursorLibraryLoadCursor (xdisplay, translate_meta_cursor (cursor));
+-}
+-
+-static XcursorImages *
+-load_cursor_on_client (MetaCursor cursor, int scale)
+-{
+-  return XcursorLibraryLoadImages (translate_meta_cursor (cursor),
+-                                   meta_prefs_get_cursor_theme (),
+-                                   meta_prefs_get_cursor_size () * scale);
+-}
+-
+-static void
+-meta_cursor_sprite_load_from_xcursor_image (MetaCursorSprite *self,
+-                                            XcursorImage     *xc_image)
++gboolean
++meta_cursor_sprite_is_animated (MetaCursorSprite *sprite)
+ {
+-  MetaBackend *meta_backend = meta_get_backend ();
+-  MetaCursorRenderer *renderer = meta_backend_get_cursor_renderer (meta_backend);
+-  uint width, height, rowstride;
+-  CoglPixelFormat cogl_format;
+-  ClutterBackend *clutter_backend;
+-  CoglContext *cogl_context;
+-  CoglTexture2D *texture;
+-  CoglError *error = NULL;
+-
+-  g_assert (self->texture == NULL);
+-
+-  width           = xc_image->width;
+-  height          = xc_image->height;
+-  rowstride       = width * 4;
+-
+-#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+-  cogl_format = COGL_PIXEL_FORMAT_BGRA_8888;
+-#else
+-  cogl_format = COGL_PIXEL_FORMAT_ARGB_8888;
+-#endif
+-
+-  clutter_backend = clutter_get_default_backend ();
+-  cogl_context = clutter_backend_get_cogl_context (clutter_backend);
+-  texture = cogl_texture_2d_new_from_data (cogl_context,
+-                                           width, height,
+-                                           cogl_format,
+-                                           rowstride,
+-                                           (uint8_t *) xc_image->pixels,
+-                                           &error);
+-
+-  if (error)
+-    {
+-      meta_warning ("Failed to allocate cursor texture: %s\n", error->message);
+-      cogl_error_free (error);
+-    }
+-
+-  meta_cursor_sprite_set_texture (self, COGL_TEXTURE (texture),
+-                                  xc_image->xhot, xc_image->yhot);
++  MetaCursorSpriteClass *klass = META_CURSOR_SPRITE_GET_CLASS (sprite);
+ 
+-  if (texture)
+-    cogl_object_unref (texture);
+-
+-  meta_cursor_renderer_realize_cursor_from_xcursor (renderer, self, xc_image);
+-}
+-
+-static XcursorImage *
+-meta_cursor_sprite_get_current_frame_image (MetaCursorSprite *self)
+-{
+-  return self->xcursor_images->images[self->current_frame];
++  if (klass->is_animated)
++    return klass->is_animated (sprite);
++  else
++    return FALSE;
+ }
+ 
+ void
+-meta_cursor_sprite_tick_frame (MetaCursorSprite *self)
+-{
+-  XcursorImage *image;
+-
+-  if (!meta_cursor_sprite_is_animated (self))
+-    return;
+-
+-  self->current_frame++;
+-
+-  if (self->current_frame >= self->xcursor_images->nimage)
+-    self->current_frame = 0;
+-
+-  image = meta_cursor_sprite_get_current_frame_image (self);
+-
+-  g_clear_pointer (&self->texture, cogl_object_unref);
+-  meta_cursor_sprite_load_from_xcursor_image (self, image);
+-}
+-
+-guint
+-meta_cursor_sprite_get_current_frame_time (MetaCursorSprite *self)
+-{
+-  if (!meta_cursor_sprite_is_animated (self))
+-    return 0;
+-
+-  return self->xcursor_images->images[self->current_frame]->delay;
+-}
+-
+-gboolean
+-meta_cursor_sprite_is_animated (MetaCursorSprite *self)
++meta_cursor_sprite_tick_frame (MetaCursorSprite *sprite)
+ {
+-  return (self->xcursor_images &&
+-          self->xcursor_images->nimage > 1);
++  return META_CURSOR_SPRITE_GET_CLASS (sprite)->tick_frame (sprite);
+ }
+ 
+-MetaCursorSprite *
+-meta_cursor_sprite_new (void)
++unsigned int
++meta_cursor_sprite_get_current_frame_time (MetaCursorSprite *sprite)
+ {
+-  return g_object_new (META_TYPE_CURSOR_SPRITE, NULL);
++  return META_CURSOR_SPRITE_GET_CLASS (sprite)->get_current_frame_time (sprite);
+ }
+ 
+-static void
+-meta_cursor_sprite_load_from_theme (MetaCursorSprite *self)
+-{
+-  XcursorImage *image;
+-
+-  g_assert (self->cursor != META_CURSOR_NONE);
+-
+-  self->theme_dirty = FALSE;
+-
+-  /* We might be reloading with a different scale. If so clear the old data. */
+-  if (self->xcursor_images)
+-    {
+-      g_clear_pointer (&self->texture, cogl_object_unref);
+-      XcursorImagesDestroy (self->xcursor_images);
+-    }
+-
+-  self->current_frame = 0;
+-  self->xcursor_images = load_cursor_on_client (self->cursor,
+-                                                self->theme_scale);
+-  if (!self->xcursor_images)
+-    meta_fatal ("Could not find cursor. Perhaps set XCURSOR_PATH?");
+-
+-  image = meta_cursor_sprite_get_current_frame_image (self);
+-  meta_cursor_sprite_load_from_xcursor_image (self, image);
+-}
+-
+-MetaCursorSprite *
+-meta_cursor_sprite_from_theme (MetaCursor cursor)
++void
++meta_cursor_sprite_clear_texture (MetaCursorSprite *sprite)
+ {
+-  MetaCursorSprite *self;
+-
+-  self = meta_cursor_sprite_new ();
+-
+-  self->cursor = cursor;
+-  self->theme_dirty = TRUE;
++  MetaCursorSpritePrivate *priv =
++    meta_cursor_sprite_get_instance_private (sprite);
+ 
+-  return self;
++  g_clear_pointer (&priv->texture, cogl_object_unref);
+ }
+ 
+ void
+-meta_cursor_sprite_set_texture (MetaCursorSprite *self,
++meta_cursor_sprite_set_texture (MetaCursorSprite *sprite,
+                                 CoglTexture      *texture,
+                                 int               hot_x,
+                                 int               hot_y)
+ {
+-  if (self->texture == COGL_TEXTURE_2D (texture) &&
+-      self->hot_x == hot_x &&
+-      self->hot_y == hot_y)
++  MetaCursorSpritePrivate *priv =
++    meta_cursor_sprite_get_instance_private (sprite);
++
++  if (priv->texture == COGL_TEXTURE_2D (texture) &&
++      priv->hot_x == hot_x &&
++      priv->hot_y == hot_y)
+     return;
+ 
+-  g_clear_pointer (&self->texture, cogl_object_unref);
++  g_clear_pointer (&priv->texture, cogl_object_unref);
+   if (texture)
+-    self->texture = cogl_object_ref (texture);
+-  self->hot_x = hot_x;
+-  self->hot_y = hot_y;
++    priv->texture = cogl_object_ref (texture);
++  priv->hot_x = hot_x;
++  priv->hot_y = hot_y;
+ 
+-  g_signal_emit (self, signals[TEXTURE_CHANGED], 0);
++  g_signal_emit (sprite, signals[TEXTURE_CHANGED], 0);
+ }
+ 
+ void
+-meta_cursor_sprite_set_texture_scale (MetaCursorSprite *self,
++meta_cursor_sprite_set_texture_scale (MetaCursorSprite *sprite,
+                                       float             scale)
+ {
+-  self->texture_scale = scale;
+-}
++  MetaCursorSpritePrivate *priv =
++    meta_cursor_sprite_get_instance_private (sprite);
+ 
+-void
+-meta_cursor_sprite_set_theme_scale (MetaCursorSprite *self,
+-                                    int               theme_scale)
+-{
+-  if (self->theme_scale != theme_scale)
+-    self->theme_dirty = TRUE;
+-  self->theme_scale = theme_scale;
++  priv->texture_scale = scale;
+ }
+ 
+ CoglTexture *
+-meta_cursor_sprite_get_cogl_texture (MetaCursorSprite *self)
++meta_cursor_sprite_get_cogl_texture (MetaCursorSprite *sprite)
+ {
+-  return COGL_TEXTURE (self->texture);
+-}
++  MetaCursorSpritePrivate *priv =
++    meta_cursor_sprite_get_instance_private (sprite);
+ 
+-MetaCursor
+-meta_cursor_sprite_get_meta_cursor (MetaCursorSprite *self)
+-{
+-  return self->cursor;
++  return COGL_TEXTURE (priv->texture);
+ }
+ 
+ void
+-meta_cursor_sprite_get_hotspot (MetaCursorSprite *self,
++meta_cursor_sprite_get_hotspot (MetaCursorSprite *sprite,
+                                 int              *hot_x,
+                                 int              *hot_y)
+ {
+-  *hot_x = self->hot_x;
+-  *hot_y = self->hot_y;
++  MetaCursorSpritePrivate *priv =
++    meta_cursor_sprite_get_instance_private (sprite);
++
++  *hot_x = priv->hot_x;
++  *hot_y = priv->hot_y;
+ }
+ 
+ float
+-meta_cursor_sprite_get_texture_scale (MetaCursorSprite *self)
++meta_cursor_sprite_get_texture_scale (MetaCursorSprite *sprite)
+ {
+-  return self->texture_scale;
++  MetaCursorSpritePrivate *priv =
++    meta_cursor_sprite_get_instance_private (sprite);
++
++  return priv->texture_scale;
+ }
+ 
+ void
+-meta_cursor_sprite_prepare_at (MetaCursorSprite *self,
++meta_cursor_sprite_prepare_at (MetaCursorSprite *sprite,
+                                int               x,
+                                int               y)
+ {
+-  g_signal_emit (self, signals[PREPARE_AT], 0, x, y);
++  g_signal_emit (sprite, signals[PREPARE_AT], 0, x, y);
+ }
+ 
+ void
+-meta_cursor_sprite_realize_texture (MetaCursorSprite *self)
++meta_cursor_sprite_realize_texture (MetaCursorSprite *sprite)
+ {
+-  if (self->theme_dirty)
+-    meta_cursor_sprite_load_from_theme (self);
++  MetaCursorSpriteClass *klass = META_CURSOR_SPRITE_GET_CLASS (sprite);
++
++  if (klass->realize_texture)
++    klass->realize_texture (sprite);
+ }
+ 
+ static void
+-meta_cursor_sprite_init (MetaCursorSprite *self)
++meta_cursor_sprite_init (MetaCursorSprite *sprite)
+ {
+-  self->texture_scale = 1.0f;
++  MetaCursorSpritePrivate *priv =
++    meta_cursor_sprite_get_instance_private (sprite);
++
++  priv->texture_scale = 1.0f;
+ }
+ 
+ static void
+ meta_cursor_sprite_finalize (GObject *object)
+ {
+-  MetaCursorSprite *self = META_CURSOR_SPRITE (object);
+-
+-  if (self->xcursor_images)
+-    XcursorImagesDestroy (self->xcursor_images);
++  MetaCursorSprite *sprite = META_CURSOR_SPRITE (object);
++  MetaCursorSpritePrivate *priv =
++    meta_cursor_sprite_get_instance_private (sprite);
+ 
+-  g_clear_pointer (&self->texture, cogl_object_unref);
++  g_clear_pointer (&priv->texture, cogl_object_unref);
+ 
+   G_OBJECT_CLASS (meta_cursor_sprite_parent_class)->finalize (object);
+ }
+diff --git a/src/backends/meta-cursor.h b/src/backends/meta-cursor.h
+index 6087df69c..3051fdee6 100644
+--- a/src/backends/meta-cursor.h
++++ b/src/backends/meta-cursor.h
+@@ -25,51 +25,50 @@
+ #include <meta/common.h>
+ #include <meta/boxes.h>
+ 
+-typedef struct _MetaCursorSprite MetaCursorSprite;
+-
+ #define META_TYPE_CURSOR_SPRITE (meta_cursor_sprite_get_type ())
+-G_DECLARE_FINAL_TYPE (MetaCursorSprite,
+-                      meta_cursor_sprite,
+-                      META, CURSOR_SPRITE,
+-                      GObject);
+-
+-MetaCursorSprite * meta_cursor_sprite_new (void);
+-
+-MetaCursorSprite * meta_cursor_sprite_from_theme  (MetaCursor cursor);
+-
+-
+-void meta_cursor_sprite_set_theme_scale (MetaCursorSprite *self,
+-                                         int               scale);
+-
+-MetaCursor meta_cursor_sprite_get_meta_cursor (MetaCursorSprite *self);
+-
+-Cursor meta_cursor_create_x_cursor (Display    *xdisplay,
+-                                    MetaCursor  cursor);
+-
+-void meta_cursor_sprite_prepare_at (MetaCursorSprite *self,
++G_DECLARE_DERIVABLE_TYPE (MetaCursorSprite,
++                          meta_cursor_sprite,
++                          META, CURSOR_SPRITE,
++                          GObject)
++
++struct _MetaCursorSpriteClass
++{
++  GObjectClass parent_class;
++
++  void (* realize_texture) (MetaCursorSprite *sprite);
++  gboolean (* is_animated) (MetaCursorSprite *sprite);
++  void (* tick_frame) (MetaCursorSprite *sprite);
++  unsigned int (* get_current_frame_time) (MetaCursorSprite *sprite);
++};
++
++void meta_cursor_sprite_prepare_at (MetaCursorSprite *sprite,
+                                     int               x,
+                                     int               y);
+ 
+-void meta_cursor_sprite_realize_texture (MetaCursorSprite *self);
++void meta_cursor_sprite_realize_texture (MetaCursorSprite *sprite);
++
++void meta_cursor_sprite_clear_texture (MetaCursorSprite *sprite);
+ 
+-void meta_cursor_sprite_set_texture (MetaCursorSprite *self,
++void meta_cursor_sprite_set_texture (MetaCursorSprite *sprite,
+                                      CoglTexture      *texture,
+                                      int               hot_x,
+                                      int               hot_y);
+ 
+-void meta_cursor_sprite_set_texture_scale (MetaCursorSprite *self,
++void meta_cursor_sprite_set_texture_scale (MetaCursorSprite *sprite,
+                                            float             scale);
+ 
+-CoglTexture *meta_cursor_sprite_get_cogl_texture (MetaCursorSprite *self);
++CoglTexture *meta_cursor_sprite_get_cogl_texture (MetaCursorSprite *sprite);
+ 
+-void meta_cursor_sprite_get_hotspot (MetaCursorSprite *self,
++void meta_cursor_sprite_get_hotspot (MetaCursorSprite *sprite,
+                                      int              *hot_x,
+                                      int              *hot_y);
+ 
+-float meta_cursor_sprite_get_texture_scale (MetaCursorSprite *self);
++float meta_cursor_sprite_get_texture_scale (MetaCursorSprite *sprite);
++
++gboolean meta_cursor_sprite_is_animated (MetaCursorSprite *sprite);
++
++void meta_cursor_sprite_tick_frame (MetaCursorSprite *sprite);
+ 
+-gboolean meta_cursor_sprite_is_animated            (MetaCursorSprite *self);
+-void     meta_cursor_sprite_tick_frame             (MetaCursorSprite *self);
+-guint    meta_cursor_sprite_get_current_frame_time (MetaCursorSprite *self);
++unsigned int meta_cursor_sprite_get_current_frame_time (MetaCursorSprite *sprite);
+ 
+ #endif /* META_CURSOR_H */
+diff --git a/src/backends/native/meta-backend-native.c b/src/backends/native/meta-backend-native.c
+index a29f593ea..042d96ec6 100644
+--- a/src/backends/native/meta-backend-native.c
++++ b/src/backends/native/meta-backend-native.c
+@@ -645,8 +645,6 @@ void meta_backend_native_resume (MetaBackendNative *native)
+     meta_backend_get_monitor_manager (backend);
+   MetaMonitorManagerKms *monitor_manager_kms =
+     META_MONITOR_MANAGER_KMS (monitor_manager);
+-  MetaCursorRenderer *cursor_renderer;
+-  MetaCursorRendererNative *cursor_renderer_native;
+   ClutterActor *stage;
+   MetaIdleMonitor *idle_monitor;
+ 
+@@ -658,10 +656,6 @@ void meta_backend_native_resume (MetaBackendNative *native)
+   stage = meta_backend_get_stage (backend);
+   clutter_actor_queue_redraw (stage);
+ 
+-  cursor_renderer = meta_backend_get_cursor_renderer (backend);
+-  cursor_renderer_native = META_CURSOR_RENDERER_NATIVE (cursor_renderer);
+-  meta_cursor_renderer_native_force_update (cursor_renderer_native);
+-
+   idle_monitor = meta_backend_get_idle_monitor (backend, 0);
+   meta_idle_monitor_reset_idletime (idle_monitor);
+ }
+diff --git a/src/backends/native/meta-cursor-renderer-native.c b/src/backends/native/meta-cursor-renderer-native.c
+index c7326af42..29800953b 100644
+--- a/src/backends/native/meta-cursor-renderer-native.c
++++ b/src/backends/native/meta-cursor-renderer-native.c
+@@ -35,6 +35,7 @@
+ #include <meta/meta-backend.h>
+ 
+ #include "backends/meta-backend-private.h"
++#include "backends/meta-cursor-sprite-xcursor.h"
+ #include "backends/meta-logical-monitor.h"
+ #include "backends/meta-monitor.h"
+ #include "backends/meta-monitor-manager-private.h"
+@@ -43,6 +44,11 @@
+ #include "core/boxes-private.h"
+ #include "meta/boxes.h"
+ 
++#ifdef HAVE_WAYLAND
++#include "wayland/meta-cursor-sprite-wayland.h"
++#include "wayland/meta-wayland-buffer.h"
++#endif
++
+ #ifndef DRM_CAP_CURSOR_WIDTH
+ #define DRM_CAP_CURSOR_WIDTH 0x8
+ #endif
+@@ -113,6 +119,11 @@ static GQuark quark_cursor_renderer_native_gpu_data = 0;
+ 
+ G_DEFINE_TYPE_WITH_PRIVATE (MetaCursorRendererNative, meta_cursor_renderer_native, META_TYPE_CURSOR_RENDERER);
+ 
++static void
++realize_cursor_sprite (MetaCursorRenderer *renderer,
++                       MetaCursorSprite   *cursor_sprite,
++                       GList              *gpus);
++
+ static MetaCursorNativeGpuState *
+ get_cursor_gpu_state (MetaCursorNativePrivate *cursor_priv,
+                       MetaGpuKms              *gpu_kms);
+@@ -152,7 +163,8 @@ static void
+ meta_cursor_renderer_native_finalize (GObject *object)
+ {
+   MetaCursorRendererNative *renderer = META_CURSOR_RENDERER_NATIVE (object);
+-  MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (renderer);
++  MetaCursorRendererNativePrivate *priv =
++    meta_cursor_renderer_native_get_instance_private (renderer);
+ 
+   if (priv->animation_timeout_id)
+     g_source_remove (priv->animation_timeout_id);
+@@ -203,7 +215,8 @@ set_crtc_cursor (MetaCursorRendererNative *native,
+                  MetaCrtc                 *crtc,
+                  MetaCursorSprite         *cursor_sprite)
+ {
+-  MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native);
++  MetaCursorRendererNativePrivate *priv =
++    meta_cursor_renderer_native_get_instance_private (native);
+   MetaCursorRendererNativeGpuData *cursor_renderer_gpu_data;
+   MetaGpuKms *gpu_kms;
+   int kms_fd;
+@@ -371,7 +384,8 @@ static void
+ update_hw_cursor (MetaCursorRendererNative *native,
+                   MetaCursorSprite         *cursor_sprite)
+ {
+-  MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native);
++  MetaCursorRendererNativePrivate *priv =
++    meta_cursor_renderer_native_get_instance_private (native);
+   MetaCursorRenderer *renderer = META_CURSOR_RENDERER (native);
+   MetaMonitorManager *monitor_manager = priv->monitor_manager;
+   GList *logical_monitors;
+@@ -564,18 +578,15 @@ can_draw_cursor_unscaled (MetaCursorRenderer *renderer,
+ 
+ static gboolean
+ should_have_hw_cursor (MetaCursorRenderer *renderer,
+-                       MetaCursorSprite   *cursor_sprite)
++                       MetaCursorSprite   *cursor_sprite,
++                       GList              *gpus)
+ {
+-  MetaCursorRendererNative *native = META_CURSOR_RENDERER_NATIVE (renderer);
+-  MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native);
+-  GList *gpus;
+   GList *l;
+   CoglTexture *texture;
+ 
+   if (!cursor_sprite)
+     return FALSE;
+ 
+-  gpus = meta_monitor_manager_get_gpus (priv->monitor_manager);
+   for (l = gpus; l; l = l->next)
+     {
+       MetaGpuKms *gpu_kms = l->data;
+@@ -609,7 +620,8 @@ should_have_hw_cursor (MetaCursorRenderer *renderer,
+ static gboolean
+ meta_cursor_renderer_native_update_animation (MetaCursorRendererNative *native)
+ {
+-  MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native);
++  MetaCursorRendererNativePrivate *priv =
++    meta_cursor_renderer_native_get_instance_private (native);
+   MetaCursorRenderer *renderer = META_CURSOR_RENDERER (native);
+   MetaCursorSprite *cursor_sprite = meta_cursor_renderer_get_cursor (renderer);
+ 
+@@ -621,10 +633,11 @@ meta_cursor_renderer_native_update_animation (MetaCursorRendererNative *native)
+ }
+ 
+ static void
+-meta_cursor_renderer_native_trigger_frame (MetaCursorRendererNative *native,
+-                                           MetaCursorSprite         *cursor_sprite)
++maybe_schedule_cursor_sprite_animation_frame (MetaCursorRendererNative *native,
++                                              MetaCursorSprite         *cursor_sprite)
+ {
+-  MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native);
++  MetaCursorRendererNativePrivate *priv =
++    meta_cursor_renderer_native_get_instance_private (native);
+   gboolean cursor_change;
+   guint delay;
+ 
+@@ -656,21 +669,78 @@ meta_cursor_renderer_native_trigger_frame (MetaCursorRendererNative *native,
+     }
+ }
+ 
++static GList *
++calculate_cursor_sprite_gpus (MetaCursorRenderer *renderer,
++                              MetaCursorSprite   *cursor_sprite)
++{
++  MetaCursorRendererNative *native = META_CURSOR_RENDERER_NATIVE (renderer);
++  MetaCursorRendererNativePrivate *priv =
++    meta_cursor_renderer_native_get_instance_private (native);
++  MetaMonitorManager *monitor_manager = priv->monitor_manager;
++  GList *gpus = NULL;
++  GList *logical_monitors;
++  GList *l;
++  ClutterRect cursor_rect;
++
++  cursor_rect = meta_cursor_renderer_calculate_rect (renderer, cursor_sprite);
++
++  logical_monitors =
++    meta_monitor_manager_get_logical_monitors (monitor_manager);
++  for (l = logical_monitors; l; l = l->next)
++    {
++      MetaLogicalMonitor *logical_monitor = l->data;
++      MetaRectangle logical_monitor_layout;
++      ClutterRect logical_monitor_rect;
++      GList *monitors, *l_mon;
++
++      logical_monitor_layout =
++        meta_logical_monitor_get_layout (logical_monitor);
++      logical_monitor_rect =
++        meta_rectangle_to_clutter_rect (&logical_monitor_layout);
++
++      if (!clutter_rect_intersection (&cursor_rect, &logical_monitor_rect,
++                                      NULL))
++        continue;
++
++      monitors = meta_logical_monitor_get_monitors (logical_monitor);
++      for (l_mon = monitors; l_mon; l_mon = l_mon->next)
++        {
++          MetaMonitor *monitor = l_mon->data;
++          MetaGpu *gpu;
++
++          gpu = meta_monitor_get_gpu (monitor);
++          if (!g_list_find (gpus, gpu))
++            gpus = g_list_prepend (gpus, gpu);
++        }
++    }
++
++  return gpus;
++}
++
+ static gboolean
+ meta_cursor_renderer_native_update_cursor (MetaCursorRenderer *renderer,
+                                            MetaCursorSprite   *cursor_sprite)
+ {
+   MetaCursorRendererNative *native = META_CURSOR_RENDERER_NATIVE (renderer);
+-  MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native);
++  MetaCursorRendererNativePrivate *priv =
++    meta_cursor_renderer_native_get_instance_private (native);
++  g_autoptr (GList) gpus = NULL;
+ 
+   if (cursor_sprite)
+-    meta_cursor_sprite_realize_texture (cursor_sprite);
++    {
++      meta_cursor_sprite_realize_texture (cursor_sprite);
++      gpus = calculate_cursor_sprite_gpus (renderer, cursor_sprite);
++      realize_cursor_sprite (renderer, cursor_sprite, gpus);
++    }
+ 
+-  meta_cursor_renderer_native_trigger_frame (native, cursor_sprite);
++  maybe_schedule_cursor_sprite_animation_frame (native, cursor_sprite);
+ 
+-  priv->has_hw_cursor = should_have_hw_cursor (renderer, cursor_sprite);
++  priv->has_hw_cursor = should_have_hw_cursor (renderer, cursor_sprite, gpus);
+   update_hw_cursor (native, cursor_sprite);
+-  return priv->has_hw_cursor;
++
++  return (priv->has_hw_cursor ||
++          !cursor_sprite ||
++          !meta_cursor_sprite_get_cogl_texture (cursor_sprite));
+ }
+ 
+ static void
+@@ -706,6 +776,24 @@ ensure_cursor_gpu_state (MetaCursorNativePrivate *cursor_priv,
+   return cursor_gpu_state;
+ }
+ 
++static void
++on_cursor_sprite_texture_changed (MetaCursorSprite *cursor_sprite)
++{
++  MetaCursorNativePrivate *cursor_priv = get_cursor_priv (cursor_sprite);
++  GHashTableIter iter;
++  MetaCursorNativeGpuState *cursor_gpu_state;
++
++  g_hash_table_iter_init (&iter, cursor_priv->gpu_states);
++  while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &cursor_gpu_state))
++    {
++      guint pending_bo;
++      pending_bo = get_pending_cursor_sprite_gbm_bo_index (cursor_gpu_state);
++      g_clear_pointer (&cursor_gpu_state->bos[pending_bo],
++                       (GDestroyNotify) gbm_bo_destroy);
++      cursor_gpu_state->pending_bo_state = META_CURSOR_GBM_BO_STATE_INVALIDATED;
++    }
++}
++
+ static void
+ cursor_priv_free (MetaCursorNativePrivate *cursor_priv)
+ {
+@@ -738,6 +826,9 @@ ensure_cursor_priv (MetaCursorSprite *cursor_sprite)
+                            cursor_priv,
+                            (GDestroyNotify) cursor_priv_free);
+ 
++  g_signal_connect (cursor_sprite, "texture-changed",
++                    G_CALLBACK (on_cursor_sprite_texture_changed), NULL);
++
+   return cursor_priv;
+ }
+ 
+@@ -805,57 +896,71 @@ load_cursor_sprite_gbm_buffer_for_gpu (MetaCursorRendererNative *native,
+     }
+ }
+ 
+-static void
+-invalidate_pending_cursor_sprite_gbm_bo (MetaCursorSprite *cursor_sprite,
+-                                         MetaGpuKms       *gpu_kms)
++static gboolean
++is_cursor_hw_state_valid (MetaCursorSprite *cursor_sprite,
++                          MetaGpuKms       *gpu_kms)
+ {
+   MetaCursorNativePrivate *cursor_priv;
+   MetaCursorNativeGpuState *cursor_gpu_state;
+-  guint pending_bo;
+ 
+   cursor_priv = get_cursor_priv (cursor_sprite);
+   if (!cursor_priv)
+-    return;
++    return FALSE;
+ 
+   cursor_gpu_state = get_cursor_gpu_state (cursor_priv, gpu_kms);
+   if (!cursor_gpu_state)
+-    return;
++    return FALSE;
+ 
+-  pending_bo = get_pending_cursor_sprite_gbm_bo_index (cursor_gpu_state);
+-  g_clear_pointer (&cursor_gpu_state->bos[pending_bo],
+-                   (GDestroyNotify) gbm_bo_destroy);
+-  cursor_gpu_state->pending_bo_state = META_CURSOR_GBM_BO_STATE_INVALIDATED;
++  switch (cursor_gpu_state->pending_bo_state)
++    {
++    case META_CURSOR_GBM_BO_STATE_SET:
++    case META_CURSOR_GBM_BO_STATE_NONE:
++      return TRUE;
++    case META_CURSOR_GBM_BO_STATE_INVALIDATED:
++      return FALSE;
++    }
++
++  g_assert_not_reached ();
+ }
+ 
+ #ifdef HAVE_WAYLAND
+ static void
+-meta_cursor_renderer_native_realize_cursor_from_wl_buffer_for_gpu (MetaCursorRenderer *renderer,
+-                                                                   MetaGpuKms         *gpu_kms,
+-                                                                   MetaCursorSprite   *cursor_sprite,
+-                                                                   struct wl_resource *buffer)
++realize_cursor_sprite_from_wl_buffer_for_gpu (MetaCursorRenderer      *renderer,
++                                              MetaGpuKms              *gpu_kms,
++                                              MetaCursorSpriteWayland *sprite_wayland)
+ {
+   MetaCursorRendererNative *native = META_CURSOR_RENDERER_NATIVE (renderer);
++  MetaCursorSprite *cursor_sprite = META_CURSOR_SPRITE (sprite_wayland);
+   MetaCursorRendererNativeGpuData *cursor_renderer_gpu_data;
+   uint32_t gbm_format;
+   uint64_t cursor_width, cursor_height;
+   CoglTexture *texture;
+   uint width, height;
++  MetaWaylandBuffer *buffer;
++  struct wl_resource *buffer_resource;
++  struct wl_shm_buffer *shm_buffer;
+ 
+   cursor_renderer_gpu_data =
+     meta_cursor_renderer_native_gpu_data_from_gpu (gpu_kms);
+   if (!cursor_renderer_gpu_data || cursor_renderer_gpu_data->hw_cursor_broken)
+     return;
+ 
+-  /* Destroy any previous pending cursor buffer; we'll always either fail (which
+-   * should unset, or succeed, which will set new buffer.
+-   */
+-  invalidate_pending_cursor_sprite_gbm_bo (cursor_sprite, gpu_kms);
++  if (is_cursor_hw_state_valid (cursor_sprite, gpu_kms))
++    return;
+ 
+   texture = meta_cursor_sprite_get_cogl_texture (cursor_sprite);
+   width = cogl_texture_get_width (texture);
+   height = cogl_texture_get_height (texture);
+ 
+-  struct wl_shm_buffer *shm_buffer = wl_shm_buffer_get (buffer);
++  buffer = meta_cursor_sprite_wayland_get_buffer (sprite_wayland);
++  if (!buffer)
++    return;
++
++  buffer_resource = meta_wayland_buffer_get_resource (buffer);
++  if (!buffer_resource)
++    return;
++
++  shm_buffer = wl_shm_buffer_get (buffer_resource);
+   if (shm_buffer)
+     {
+       int rowstride = wl_shm_buffer_get_stride (shm_buffer);
+@@ -929,47 +1034,27 @@ meta_cursor_renderer_native_realize_cursor_from_wl_buffer_for_gpu (MetaCursorRen
+       set_pending_cursor_sprite_gbm_bo (cursor_sprite, gpu_kms, bo);
+     }
+ }
+-
+-static void
+-meta_cursor_renderer_native_realize_cursor_from_wl_buffer (MetaCursorRenderer *renderer,
+-                                                           MetaCursorSprite *cursor_sprite,
+-                                                           struct wl_resource *buffer)
+-{
+-  MetaCursorRendererNative *native = META_CURSOR_RENDERER_NATIVE (renderer);
+-  MetaCursorRendererNativePrivate *priv =
+-	  meta_cursor_renderer_native_get_instance_private (native);
+-  GList *gpus;
+-  GList *l;
+-
+-  gpus = meta_monitor_manager_get_gpus (priv->monitor_manager);
+-  for (l = gpus; l; l = l->next)
+-    {
+-      MetaGpuKms *gpu_kms = l->data;
+-
+-      meta_cursor_renderer_native_realize_cursor_from_wl_buffer_for_gpu (
+-        renderer,
+-        gpu_kms,
+-        cursor_sprite,
+-        buffer);
+-    }
+-}
+ #endif
+ 
+ static void
+-meta_cursor_renderer_native_realize_cursor_from_xcursor_for_gpu (MetaCursorRenderer *renderer,
+-                                                                 MetaGpuKms         *gpu_kms,
+-                                                                 MetaCursorSprite   *cursor_sprite,
+-                                                                 XcursorImage       *xc_image)
++realize_cursor_sprite_from_xcursor_for_gpu (MetaCursorRenderer      *renderer,
++                                            MetaGpuKms              *gpu_kms,
++                                            MetaCursorSpriteXcursor *sprite_xcursor)
+ {
+   MetaCursorRendererNative *native = META_CURSOR_RENDERER_NATIVE (renderer);
+   MetaCursorRendererNativeGpuData *cursor_renderer_gpu_data;
++  MetaCursorSprite *cursor_sprite = META_CURSOR_SPRITE (sprite_xcursor);
++  XcursorImage *xc_image;
+ 
+   cursor_renderer_gpu_data =
+     meta_cursor_renderer_native_gpu_data_from_gpu (gpu_kms);
+   if (!cursor_renderer_gpu_data || cursor_renderer_gpu_data->hw_cursor_broken)
+     return;
+ 
+-  invalidate_pending_cursor_sprite_gbm_bo (cursor_sprite, gpu_kms);
++  if (is_cursor_hw_state_valid (cursor_sprite, gpu_kms))
++    return;
++
++  xc_image = meta_cursor_sprite_xcursor_get_current_image (sprite_xcursor);
+ 
+   load_cursor_sprite_gbm_buffer_for_gpu (native,
+                                          gpu_kms,
+@@ -982,26 +1067,45 @@ meta_cursor_renderer_native_realize_cursor_from_xcursor_for_gpu (MetaCursorRende
+ }
+ 
+ static void
+-meta_cursor_renderer_native_realize_cursor_from_xcursor (MetaCursorRenderer *renderer,
+-                                                         MetaCursorSprite   *cursor_sprite,
+-                                                         XcursorImage       *xc_image)
++realize_cursor_sprite_for_gpu (MetaCursorRenderer *renderer,
++                               MetaGpuKms         *gpu_kms,
++                               MetaCursorSprite   *cursor_sprite)
++{
++#ifdef HAVE_WAYLAND
++  if (META_IS_CURSOR_SPRITE_WAYLAND (cursor_sprite))
++    {
++      MetaCursorSpriteWayland *sprite_wayland =
++        META_CURSOR_SPRITE_WAYLAND (cursor_sprite);
++
++      realize_cursor_sprite_from_wl_buffer_for_gpu (renderer,
++                                                    gpu_kms,
++                                                    sprite_wayland);
++    }
++  else
++#endif
++  if (META_IS_CURSOR_SPRITE_XCURSOR (cursor_sprite))
++    {
++      MetaCursorSpriteXcursor *sprite_xcursor =
++        META_CURSOR_SPRITE_XCURSOR (cursor_sprite);
++
++      realize_cursor_sprite_from_xcursor_for_gpu (renderer,
++                                                  gpu_kms,
++                                                  sprite_xcursor);
++    }
++}
++
++static void
++realize_cursor_sprite (MetaCursorRenderer *renderer,
++                       MetaCursorSprite   *cursor_sprite,
++                       GList              *gpus)
+ {
+-  MetaCursorRendererNative *native = META_CURSOR_RENDERER_NATIVE (renderer);
+-  MetaCursorRendererNativePrivate *priv =
+-	  meta_cursor_renderer_native_get_instance_private (native);
+-  GList *gpus;
+   GList *l;
+ 
+-  gpus = meta_monitor_manager_get_gpus (priv->monitor_manager);
+   for (l = gpus; l; l = l->next)
+     {
+       MetaGpuKms *gpu_kms = l->data;
+ 
+-      meta_cursor_renderer_native_realize_cursor_from_xcursor_for_gpu (
+-        renderer,
+-        gpu_kms,
+-        cursor_sprite,
+-        xc_image);
++      realize_cursor_sprite_for_gpu (renderer, gpu_kms, cursor_sprite);
+     }
+ }
+ 
+@@ -1013,12 +1117,6 @@ meta_cursor_renderer_native_class_init (MetaCursorRendererNativeClass *klass)
+ 
+   object_class->finalize = meta_cursor_renderer_native_finalize;
+   renderer_class->update_cursor = meta_cursor_renderer_native_update_cursor;
+-#ifdef HAVE_WAYLAND
+-  renderer_class->realize_cursor_from_wl_buffer =
+-    meta_cursor_renderer_native_realize_cursor_from_wl_buffer;
+-#endif
+-  renderer_class->realize_cursor_from_xcursor =
+-    meta_cursor_renderer_native_realize_cursor_from_xcursor;
+ 
+   quark_cursor_sprite = g_quark_from_static_string ("-meta-cursor-native");
+   quark_cursor_renderer_native_gpu_data =
+@@ -1033,14 +1131,13 @@ force_update_hw_cursor (MetaCursorRendererNative *native)
+     meta_cursor_renderer_native_get_instance_private (native);
+ 
+   priv->hw_state_invalidated = TRUE;
+-  update_hw_cursor (native, meta_cursor_renderer_get_cursor (renderer));
++  meta_cursor_renderer_force_update (renderer);
+ }
+ 
+ static void
+ on_monitors_changed (MetaMonitorManager       *monitors,
+                      MetaCursorRendererNative *native)
+ {
+-  /* Our tracking is all messed up, so force an update. */
+   force_update_hw_cursor (native);
+ }
+ 
+@@ -1112,9 +1209,3 @@ static void
+ meta_cursor_renderer_native_init (MetaCursorRendererNative *native)
+ {
+ }
+-
+-void
+-meta_cursor_renderer_native_force_update (MetaCursorRendererNative *native)
+-{
+-  force_update_hw_cursor (native);
+-}
+diff --git a/src/backends/native/meta-cursor-renderer-native.h b/src/backends/native/meta-cursor-renderer-native.h
+index 09203a5f7..fb4c8edc7 100644
+--- a/src/backends/native/meta-cursor-renderer-native.h
++++ b/src/backends/native/meta-cursor-renderer-native.h
+@@ -32,8 +32,6 @@ G_DECLARE_FINAL_TYPE (MetaCursorRendererNative, meta_cursor_renderer_native,
+                       META, CURSOR_RENDERER_NATIVE,
+                       MetaCursorRenderer)
+ 
+-void meta_cursor_renderer_native_force_update (MetaCursorRendererNative *renderer);
+-
+ MetaCursorRendererNative * meta_cursor_renderer_native_new (MetaBackend *backend);
+ 
+ #endif /* META_CURSOR_RENDERER_NATIVE_H */
+diff --git a/src/backends/x11/cm/meta-cursor-sprite-xfixes.c b/src/backends/x11/cm/meta-cursor-sprite-xfixes.c
+new file mode 100644
+index 000000000..143ebb791
+--- /dev/null
++++ b/src/backends/x11/cm/meta-cursor-sprite-xfixes.c
+@@ -0,0 +1,226 @@
++/*
++ * Copyright 2013, 2018 Red Hat, Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program 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
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, see <http://www.gnu.org/licenses/>.
++ *
++ */
++
++#include "config.h"
++
++#include "backends/x11/cm/meta-cursor-sprite-xfixes.h"
++
++#include <X11/extensions/Xfixes.h>
++
++#include "core/display-private.h"
++
++enum
++{
++  PROP_0,
++
++  PROP_DISPLAY,
++
++  N_PROPS
++};
++
++static GParamSpec *obj_props[N_PROPS];
++
++struct _MetaCursorSpriteXfixes
++{
++  MetaCursorSprite parent;
++
++  MetaDisplay *display;
++};
++
++static void
++meta_screen_cast_xfixes_init_initable_iface (GInitableIface *iface);
++
++G_DEFINE_TYPE_WITH_CODE (MetaCursorSpriteXfixes,
++                         meta_cursor_sprite_xfixes,
++                         META_TYPE_CURSOR_SPRITE,
++                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
++                                                meta_screen_cast_xfixes_init_initable_iface))
++
++static void
++meta_cursor_sprite_xfixes_realize_texture (MetaCursorSprite *sprite)
++{
++}
++
++static gboolean
++meta_cursor_sprite_xfixes_is_animated (MetaCursorSprite *sprite)
++{
++  return FALSE;
++}
++
++static void
++meta_cursor_sprite_xfixes_get_property (GObject    *object,
++                                        guint       prop_id,
++                                        GValue     *value,
++                                        GParamSpec *pspec)
++{
++  MetaCursorSpriteXfixes *sprite_xfixes = META_CURSOR_SPRITE_XFIXES (object);
++
++  switch (prop_id)
++    {
++    case PROP_DISPLAY:
++      g_value_set_object (value, sprite_xfixes->display);
++      break;
++    default:
++      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
++      break;
++    }
++}
++
++static void
++meta_cursor_sprite_xfixes_set_property (GObject      *object,
++                                        guint         prop_id,
++                                        const GValue *value,
++                                        GParamSpec   *pspec)
++{
++  MetaCursorSpriteXfixes *sprite_xfixes = META_CURSOR_SPRITE_XFIXES (object);
++
++  switch (prop_id)
++    {
++    case PROP_DISPLAY:
++      sprite_xfixes->display = g_value_get_object (value);
++      break;
++    default:
++      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
++      break;
++    }
++}
++
++MetaCursorSpriteXfixes *
++meta_cursor_sprite_xfixes_new (MetaDisplay  *display,
++                               GError      **error)
++{
++  return g_initable_new (META_TYPE_CURSOR_SPRITE_XFIXES,
++                         NULL, error,
++                         "display", display,
++                         NULL);
++}
++
++static gboolean
++meta_cursor_sprite_xfixes_initable_init (GInitable     *initable,
++                                         GCancellable  *cancellable,
++                                         GError       **error)
++{
++  MetaCursorSpriteXfixes *sprite_xfixes =
++    META_CURSOR_SPRITE_XFIXES (initable);
++  MetaCursorSprite *sprite = META_CURSOR_SPRITE (sprite_xfixes);
++  XFixesCursorImage *cursor_image;
++  CoglTexture2D *texture;
++  uint8_t *cursor_data;
++  gboolean free_cursor_data;
++  ClutterBackend *clutter_backend;
++  CoglContext *cogl_context;
++
++  cursor_image = XFixesGetCursorImage (sprite_xfixes->display->xdisplay);
++  if (!cursor_image)
++    {
++      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
++                   "Failed to get cursor image");
++      return FALSE;
++    }
++
++  /*
++   * Like all X APIs, XFixesGetCursorImage() returns arrays of 32-bit
++   * quantities as arrays of long; we need to convert on 64 bit
++   */
++  if (sizeof (long) == 4)
++    {
++      cursor_data = (uint8_t *) cursor_image->pixels;
++      free_cursor_data = FALSE;
++    }
++  else
++    {
++      int i, j;
++      uint32_t *cursor_words;
++      unsigned long *p;
++      uint32_t *q;
++
++      cursor_words = g_new (uint32_t,
++                            cursor_image->width * cursor_image->height);
++      cursor_data = (uint8_t *) cursor_words;
++
++      p = cursor_image->pixels;
++      q = cursor_words;
++      for (j = 0; j < cursor_image->height; j++)
++        {
++          for (i = 0; i < cursor_image->width; i++)
++            *(q++) = *(p++);
++        }
++
++      free_cursor_data = TRUE;
++    }
++
++  clutter_backend = clutter_get_default_backend ();
++  cogl_context = clutter_backend_get_cogl_context (clutter_backend);
++  texture = cogl_texture_2d_new_from_data (cogl_context,
++                                          cursor_image->width,
++                                          cursor_image->height,
++                                          CLUTTER_CAIRO_FORMAT_ARGB32,
++                                          cursor_image->width * 4, /* stride */
++                                          cursor_data,
++                                          error);
++
++  if (free_cursor_data)
++    g_free (cursor_data);
++
++  if (!sprite)
++    return FALSE;
++
++  meta_cursor_sprite_set_texture (sprite,
++                                  COGL_TEXTURE (texture),
++                                  cursor_image->xhot,
++                                  cursor_image->yhot);
++  cogl_object_unref (texture);
++  XFree (cursor_image);
++
++  return TRUE;
++}
++
++static void
++meta_screen_cast_xfixes_init_initable_iface (GInitableIface *iface)
++{
++  iface->init = meta_cursor_sprite_xfixes_initable_init;
++}
++
++static void
++meta_cursor_sprite_xfixes_init (MetaCursorSpriteXfixes *sprite_xfixes)
++{
++}
++
++static void
++meta_cursor_sprite_xfixes_class_init (MetaCursorSpriteXfixesClass *klass)
++{
++  GObjectClass *object_class = G_OBJECT_CLASS (klass);
++  MetaCursorSpriteClass *cursor_sprite_class = META_CURSOR_SPRITE_CLASS (klass);
++
++  object_class->get_property = meta_cursor_sprite_xfixes_get_property;
++  object_class->set_property = meta_cursor_sprite_xfixes_set_property;
++
++  cursor_sprite_class->realize_texture =
++    meta_cursor_sprite_xfixes_realize_texture;
++  cursor_sprite_class->is_animated = meta_cursor_sprite_xfixes_is_animated;
++
++  obj_props[PROP_DISPLAY] =
++    g_param_spec_object ("display",
++                         "display",
++                         "MetaDisplay",
++                         META_TYPE_DISPLAY,
++                         G_PARAM_READWRITE |
++                         G_PARAM_CONSTRUCT_ONLY |
++                         G_PARAM_STATIC_STRINGS);
++  g_object_class_install_properties (object_class, N_PROPS, obj_props);
++}
+diff --git a/src/backends/x11/cm/meta-cursor-sprite-xfixes.h b/src/backends/x11/cm/meta-cursor-sprite-xfixes.h
+new file mode 100644
+index 000000000..c7073fc2c
+--- /dev/null
++++ b/src/backends/x11/cm/meta-cursor-sprite-xfixes.h
+@@ -0,0 +1,36 @@
++/*
++ * Copyright 2013, 2018 Red Hat, Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program 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
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, see <http://www.gnu.org/licenses/>.
++ *
++ */
++
++#ifndef META_CURSOR_SPRITE_XFIXES_H
++#define META_CURSOR_SPRITE_XFIXES_H
++
++#include <glib-object.h>
++
++#include "backends/meta-cursor.h"
++#include "meta/types.h"
++
++#define META_TYPE_CURSOR_SPRITE_XFIXES (meta_cursor_sprite_xfixes_get_type ())
++G_DECLARE_FINAL_TYPE (MetaCursorSpriteXfixes,
++                      meta_cursor_sprite_xfixes,
++                      META, CURSOR_SPRITE_XFIXES,
++                      MetaCursorSprite)
++
++MetaCursorSpriteXfixes * meta_cursor_sprite_xfixes_new (MetaDisplay  *display,
++                                                        GError      **error);
++
++#endif /* META_CURSOR_SPRITE_XFIXES_H */
+diff --git a/src/backends/x11/meta-cursor-renderer-x11.c b/src/backends/x11/meta-cursor-renderer-x11.c
+index 82109f1f3..bb3100a91 100644
+--- a/src/backends/x11/meta-cursor-renderer-x11.c
++++ b/src/backends/x11/meta-cursor-renderer-x11.c
+@@ -30,6 +30,7 @@
+ 
+ #include "meta-backend-x11.h"
+ #include "meta-stage-private.h"
++#include "backends/meta-cursor-sprite-xcursor.h"
+ 
+ struct _MetaCursorRendererX11Private
+ {
+@@ -59,13 +60,18 @@ meta_cursor_renderer_x11_update_cursor (MetaCursorRenderer *renderer,
+ 
+   gboolean has_server_cursor = FALSE;
+ 
+-  if (cursor_sprite)
++  if (cursor_sprite && META_IS_CURSOR_SPRITE_XCURSOR (cursor_sprite))
+     {
+-      MetaCursor cursor = meta_cursor_sprite_get_meta_cursor (cursor_sprite);
++      MetaCursorSpriteXcursor *sprite_xcursor =
++        META_CURSOR_SPRITE_XCURSOR (cursor_sprite);
++      MetaCursor cursor;
+ 
++      cursor = meta_cursor_sprite_xcursor_get_cursor (sprite_xcursor);
+       if (cursor != META_CURSOR_NONE)
+         {
+-          Cursor xcursor = meta_cursor_create_x_cursor (xdisplay, cursor);
++          Cursor xcursor;
++
++          xcursor = meta_create_x_cursor (xdisplay, cursor);
+           XDefineCursor (xdisplay, xwindow, xcursor);
+           XFlush (xdisplay);
+           XFreeCursor (xdisplay, xcursor);
+diff --git a/src/backends/x11/nested/meta-cursor-renderer-x11-nested.c b/src/backends/x11/nested/meta-cursor-renderer-x11-nested.c
+index da1a56038..0daae683c 100644
+--- a/src/backends/x11/nested/meta-cursor-renderer-x11-nested.c
++++ b/src/backends/x11/nested/meta-cursor-renderer-x11-nested.c
+@@ -26,6 +26,8 @@
+ 
+ #include "backends/x11/nested/meta-cursor-renderer-x11-nested.h"
+ 
++#include <X11/Xcursor/Xcursor.h>
++
+ #include "backends/x11/meta-backend-x11.h"
+ 
+ struct _MetaCursorRendererX11Nested
+diff --git a/src/core/display.c b/src/core/display.c
+index d6da84b30..e7dd4534b 100644
+--- a/src/core/display.c
++++ b/src/core/display.c
+@@ -3018,7 +3018,7 @@ Cursor
+ meta_display_create_x_cursor (MetaDisplay *display,
+                               MetaCursor   cursor)
+ {
+-  return meta_cursor_create_x_cursor (display->xdisplay, cursor);
++  return meta_create_x_cursor (display->xdisplay, cursor);
+ }
+ 
+ MetaGestureTracker *
+diff --git a/src/core/screen.c b/src/core/screen.c
+index c14bba0cf..048104150 100644
+--- a/src/core/screen.c
++++ b/src/core/screen.c
+@@ -60,6 +60,7 @@
+ #include "x11/xprops.h"
+ 
+ #include "backends/x11/meta-backend-x11.h"
++#include "backends/meta-cursor-sprite-xcursor.h"
+ 
+ static char* get_screen_name (MetaDisplay *display,
+                               int          number);
+@@ -1323,12 +1324,13 @@ find_highest_logical_monitor_scale (MetaBackend      *backend,
+ }
+ 
+ static void
+-root_cursor_prepare_at (MetaCursorSprite *cursor_sprite,
+-                        int               x,
+-                        int               y,
+-                        MetaScreen       *screen)
++root_cursor_prepare_at (MetaCursorSpriteXcursor *sprite_xcursor,
++                        int                      x,
++                        int                      y,
++                        MetaScreen              *screen)
+ {
+   MetaBackend *backend = meta_get_backend ();
++  MetaCursorSprite *cursor_sprite = META_CURSOR_SPRITE (sprite_xcursor);
+ 
+   if (meta_is_stage_views_scaled ())
+     {
+@@ -1337,7 +1339,7 @@ root_cursor_prepare_at (MetaCursorSprite *cursor_sprite,
+       scale = find_highest_logical_monitor_scale (backend, cursor_sprite);
+       if (scale != 0.0)
+         {
+-          meta_cursor_sprite_set_theme_scale (cursor_sprite, scale);
++          meta_cursor_sprite_xcursor_set_theme_scale (sprite_xcursor, scale);
+           meta_cursor_sprite_set_texture_scale (cursor_sprite, 1.0 / scale);
+         }
+     }
+@@ -1353,18 +1355,18 @@ root_cursor_prepare_at (MetaCursorSprite *cursor_sprite,
+       /* Reload the cursor texture if the scale has changed. */
+       if (logical_monitor)
+         {
+-          meta_cursor_sprite_set_theme_scale (cursor_sprite,
+-                                              logical_monitor->scale);
++          meta_cursor_sprite_xcursor_set_theme_scale (sprite_xcursor,
++                                                      logical_monitor->scale);
+           meta_cursor_sprite_set_texture_scale (cursor_sprite, 1.0);
+         }
+     }
+ }
+ 
+ static void
+-manage_root_cursor_sprite_scale (MetaScreen       *screen,
+-                                 MetaCursorSprite *cursor_sprite)
++manage_root_cursor_sprite_scale (MetaScreen              *screen,
++                                 MetaCursorSpriteXcursor *sprite_xcursor)
+ {
+-  g_signal_connect_object (cursor_sprite,
++  g_signal_connect_object (sprite_xcursor,
+                            "prepare-at",
+                            G_CALLBACK (root_cursor_prepare_at),
+                            screen,
+@@ -1377,17 +1379,18 @@ meta_screen_update_cursor (MetaScreen *screen)
+   MetaDisplay *display = screen->display;
+   MetaCursor cursor = screen->current_cursor;
+   Cursor xcursor;
+-  MetaCursorSprite *cursor_sprite;
++  MetaCursorSpriteXcursor *sprite_xcursor;
+   MetaBackend *backend = meta_get_backend ();
+   MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend);
+ 
+-  cursor_sprite = meta_cursor_sprite_from_theme (cursor);
++  sprite_xcursor = meta_cursor_sprite_xcursor_new (cursor);
+ 
+   if (meta_is_wayland_compositor ())
+-    manage_root_cursor_sprite_scale (screen, cursor_sprite);
++    manage_root_cursor_sprite_scale (screen, sprite_xcursor);
+ 
+-  meta_cursor_tracker_set_root_cursor (cursor_tracker, cursor_sprite);
+-  g_object_unref (cursor_sprite);
++  meta_cursor_tracker_set_root_cursor (cursor_tracker,
++                                       META_CURSOR_SPRITE (sprite_xcursor));
++  g_object_unref (sprite_xcursor);
+ 
+   /* Set a cursor for X11 applications that don't specify their own */
+   xcursor = meta_display_create_x_cursor (display, cursor);
+diff --git a/src/wayland/meta-cursor-sprite-wayland.c b/src/wayland/meta-cursor-sprite-wayland.c
+new file mode 100644
+index 000000000..7c14960ff
+--- /dev/null
++++ b/src/wayland/meta-cursor-sprite-wayland.c
+@@ -0,0 +1,75 @@
++/*
++ * Copyright 2015, 2018 Red Hat, Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program 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
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, see <http://www.gnu.org/licenses/>.
++ *
++ */
++
++#include "config.h"
++
++#include "wayland/meta-cursor-sprite-wayland.h"
++
++struct _MetaCursorSpriteWayland
++{
++  MetaCursorSprite parent;
++
++  MetaWaylandSurface *surface;
++};
++
++G_DEFINE_TYPE (MetaCursorSpriteWayland,
++               meta_cursor_sprite_wayland,
++               META_TYPE_CURSOR_SPRITE)
++
++static void
++meta_cursor_sprite_wayland_realize_texture (MetaCursorSprite *sprite)
++{
++}
++
++static gboolean
++meta_cursor_sprite_wayland_is_animated (MetaCursorSprite *sprite)
++{
++  return FALSE;
++}
++
++MetaCursorSpriteWayland *
++meta_cursor_sprite_wayland_new (MetaWaylandSurface *surface)
++{
++  MetaCursorSpriteWayland *sprite_wayland;
++
++  sprite_wayland = g_object_new (META_TYPE_CURSOR_SPRITE_WAYLAND, NULL);
++  sprite_wayland->surface = surface;
++
++  return sprite_wayland;
++}
++
++MetaWaylandBuffer *
++meta_cursor_sprite_wayland_get_buffer (MetaCursorSpriteWayland *sprite_wayland)
++{
++  return meta_wayland_surface_get_buffer (sprite_wayland->surface);
++}
++
++static void
++meta_cursor_sprite_wayland_init (MetaCursorSpriteWayland *sprite_wayland)
++{
++}
++
++static void
++meta_cursor_sprite_wayland_class_init (MetaCursorSpriteWaylandClass *klass)
++{
++  MetaCursorSpriteClass *cursor_sprite_class = META_CURSOR_SPRITE_CLASS (klass);
++
++  cursor_sprite_class->realize_texture =
++    meta_cursor_sprite_wayland_realize_texture;
++  cursor_sprite_class->is_animated = meta_cursor_sprite_wayland_is_animated;
++}
+diff --git a/src/wayland/meta-cursor-sprite-wayland.h b/src/wayland/meta-cursor-sprite-wayland.h
+new file mode 100644
+index 000000000..107698f3f
+--- /dev/null
++++ b/src/wayland/meta-cursor-sprite-wayland.h
+@@ -0,0 +1,35 @@
++/*
++ * Copyright 2013, 2018 Red Hat, Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program 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
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, see <http://www.gnu.org/licenses/>.
++ *
++ */
++
++#ifndef META_CURSOR_SPRITE_WAYLAND_H
++#define META_CURSOR_SPRITE_WAYLAND_H
++
++#include <glib-object.h>
++
++#include "backends/meta-cursor.h"
++#include "wayland/meta-wayland-surface.h"
++
++#define META_TYPE_CURSOR_SPRITE_WAYLAND meta_cursor_sprite_wayland_get_type ()
++G_DECLARE_FINAL_TYPE (MetaCursorSpriteWayland, meta_cursor_sprite_wayland,
++                      META, CURSOR_SPRITE_WAYLAND, MetaCursorSprite)
++
++MetaCursorSpriteWayland * meta_cursor_sprite_wayland_new (MetaWaylandSurface *surface);
++
++MetaWaylandBuffer * meta_cursor_sprite_wayland_get_buffer (MetaCursorSpriteWayland *sprite_wayland);
++
++#endif /* META_CURSOR_SPRITE_WAYLAND_H */
+diff --git a/src/wayland/meta-wayland-buffer.c b/src/wayland/meta-wayland-buffer.c
+index 55564492a..c759eefc1 100644
+--- a/src/wayland/meta-wayland-buffer.c
++++ b/src/wayland/meta-wayland-buffer.c
+@@ -88,6 +88,12 @@ meta_wayland_buffer_from_resource (struct wl_resource *resource)
+   return buffer;
+ }
+ 
++struct wl_resource *
++meta_wayland_buffer_get_resource (MetaWaylandBuffer *buffer)
++{
++  return buffer->resource;
++}
++
+ static gboolean
+ meta_wayland_buffer_is_realized (MetaWaylandBuffer *buffer)
+ {
+diff --git a/src/wayland/meta-wayland-buffer.h b/src/wayland/meta-wayland-buffer.h
+index 5345033c2..e00a41e09 100644
+--- a/src/wayland/meta-wayland-buffer.h
++++ b/src/wayland/meta-wayland-buffer.h
+@@ -68,6 +68,7 @@ G_DECLARE_FINAL_TYPE (MetaWaylandBuffer, meta_wayland_buffer,
+                       META, WAYLAND_BUFFER, GObject);
+ 
+ MetaWaylandBuffer *     meta_wayland_buffer_from_resource       (struct wl_resource    *resource);
++struct wl_resource *    meta_wayland_buffer_get_resource        (MetaWaylandBuffer     *buffer);
+ gboolean                meta_wayland_buffer_attach              (MetaWaylandBuffer     *buffer,
+                                                                  GError               **error);
+ CoglTexture *           meta_wayland_buffer_get_texture         (MetaWaylandBuffer     *buffer);
+diff --git a/src/wayland/meta-wayland-surface-role-cursor.c b/src/wayland/meta-wayland-cursor-surface.c
+similarity index 52%
+rename from src/wayland/meta-wayland-surface-role-cursor.c
+rename to src/wayland/meta-wayland-cursor-surface.c
+index d118a8917..d08af9e8c 100644
+--- a/src/wayland/meta-wayland-surface-role-cursor.c
++++ b/src/wayland/meta-wayland-cursor-surface.c
+@@ -23,7 +23,7 @@
+ 
+ #include <cogl/cogl.h>
+ #include <cogl/cogl-wayland-server.h>
+-#include "meta-wayland-surface-role-cursor.h"
++#include "meta-wayland-cursor-surface.h"
+ #include "meta-wayland-buffer.h"
+ #include "meta-xwayland.h"
+ #include "screen-private.h"
+@@ -31,35 +31,38 @@
+ #include "backends/meta-backend-private.h"
+ #include "backends/meta-logical-monitor.h"
+ #include "core/boxes-private.h"
++#include "wayland/meta-cursor-sprite-wayland.h"
+ 
+-typedef struct _MetaWaylandSurfaceRoleCursorPrivate MetaWaylandSurfaceRoleCursorPrivate;
++typedef struct _MetaWaylandCursorSurfacePrivate MetaWaylandCursorSurfacePrivate;
+ 
+-struct _MetaWaylandSurfaceRoleCursorPrivate
++struct _MetaWaylandCursorSurfacePrivate
+ {
+   int hot_x;
+   int hot_y;
+-  MetaCursorSprite *cursor_sprite;
++  MetaCursorSpriteWayland *cursor_sprite;
+   MetaCursorRenderer *cursor_renderer;
+   MetaWaylandBuffer *buffer;
+   struct wl_list frame_callbacks;
+   gulong cursor_painted_handler_id;
+ };
+ 
+-G_DEFINE_TYPE_WITH_PRIVATE (MetaWaylandSurfaceRoleCursor,
+-                            meta_wayland_surface_role_cursor,
++G_DEFINE_TYPE_WITH_PRIVATE (MetaWaylandCursorSurface,
++                            meta_wayland_cursor_surface,
+                             META_TYPE_WAYLAND_SURFACE_ROLE)
+ 
+ static void
+-update_cursor_sprite_texture (MetaWaylandSurfaceRoleCursor *cursor_role)
++update_cursor_sprite_texture (MetaWaylandCursorSurface *cursor_surface)
+ {
+-  MetaWaylandSurfaceRoleCursorPrivate *priv = meta_wayland_surface_role_cursor_get_instance_private (cursor_role);
+-  MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (META_WAYLAND_SURFACE_ROLE (cursor_role));
++  MetaWaylandCursorSurfacePrivate *priv =
++    meta_wayland_cursor_surface_get_instance_private (cursor_surface);
++  MetaWaylandSurface *surface =
++    meta_wayland_surface_role_get_surface (META_WAYLAND_SURFACE_ROLE (cursor_surface));
+   MetaWaylandBuffer *buffer = meta_wayland_surface_get_buffer (surface);
+-  MetaCursorSprite *cursor_sprite = priv->cursor_sprite;
++  MetaCursorSprite *cursor_sprite = META_CURSOR_SPRITE (priv->cursor_sprite);
+ 
+   g_return_if_fail (!buffer || buffer->texture);
+ 
+-  if (!priv->cursor_renderer || !cursor_sprite)
++  if (!priv->cursor_renderer)
+     return;
+ 
+   if (buffer)
+@@ -68,20 +71,6 @@ update_cursor_sprite_texture (MetaWaylandSurfaceRoleCursor *cursor_role)
+                                       buffer->texture,
+                                       priv->hot_x * surface->scale,
+                                       priv->hot_y * surface->scale);
+-
+-      if (priv->buffer)
+-        {
+-          struct wl_resource *buffer_resource;
+-
+-          g_assert (priv->buffer == buffer);
+-          buffer_resource = buffer->resource;
+-          meta_cursor_renderer_realize_cursor_from_wl_buffer (priv->cursor_renderer,
+-                                                              cursor_sprite,
+-                                                              buffer_resource);
+-
+-          meta_wayland_surface_unref_buffer_use_count (surface);
+-          g_clear_object (&priv->buffer);
+-        }
+     }
+   else
+     {
+@@ -92,12 +81,12 @@ update_cursor_sprite_texture (MetaWaylandSurfaceRoleCursor *cursor_role)
+ }
+ 
+ static void
+-cursor_sprite_prepare_at (MetaCursorSprite             *cursor_sprite,
+-                          int                           x,
+-                          int                           y,
+-                          MetaWaylandSurfaceRoleCursor *cursor_role)
++cursor_sprite_prepare_at (MetaCursorSprite         *cursor_sprite,
++                          int                       x,
++                          int                       y,
++                          MetaWaylandCursorSurface *cursor_surface)
+ {
+-  MetaWaylandSurfaceRole *role = META_WAYLAND_SURFACE_ROLE (cursor_role);
++  MetaWaylandSurfaceRole *role = META_WAYLAND_SURFACE_ROLE (cursor_surface);
+   MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (role);
+ 
+   if (!meta_xwayland_is_xwayland_surface (surface))
+@@ -126,14 +115,14 @@ cursor_sprite_prepare_at (MetaCursorSprite             *cursor_sprite,
+ }
+ 
+ static void
+-cursor_surface_role_assigned (MetaWaylandSurfaceRole *surface_role)
++meta_wayland_cursor_surface_assigned (MetaWaylandSurfaceRole *surface_role)
+ {
+   MetaWaylandSurface *surface =
+     meta_wayland_surface_role_get_surface (surface_role);
+-  MetaWaylandSurfaceRoleCursor *cursor_role =
+-    META_WAYLAND_SURFACE_ROLE_CURSOR (surface_role);
+-  MetaWaylandSurfaceRoleCursorPrivate *priv =
+-    meta_wayland_surface_role_cursor_get_instance_private (cursor_role);
++  MetaWaylandCursorSurface *cursor_surface =
++    META_WAYLAND_CURSOR_SURFACE (surface_role);
++  MetaWaylandCursorSurfacePrivate *priv =
++    meta_wayland_cursor_surface_get_instance_private (cursor_surface);
+ 
+   wl_list_insert_list (&priv->frame_callbacks,
+                        &surface->pending_frame_callback_list);
+@@ -141,13 +130,13 @@ cursor_surface_role_assigned (MetaWaylandSurfaceRole *surface_role)
+ }
+ 
+ static void
+-cursor_surface_role_pre_commit (MetaWaylandSurfaceRole  *surface_role,
+-                                MetaWaylandPendingState *pending)
++meta_wayland_cursor_surface_pre_commit (MetaWaylandSurfaceRole  *surface_role,
++                                        MetaWaylandPendingState *pending)
+ {
+-  MetaWaylandSurfaceRoleCursor *cursor_role =
+-    META_WAYLAND_SURFACE_ROLE_CURSOR (surface_role);
+-  MetaWaylandSurfaceRoleCursorPrivate *priv =
+-    meta_wayland_surface_role_cursor_get_instance_private (cursor_role);
++  MetaWaylandCursorSurface *cursor_surface =
++    META_WAYLAND_CURSOR_SURFACE (surface_role);
++  MetaWaylandCursorSurfacePrivate *priv =
++    meta_wayland_cursor_surface_get_instance_private (cursor_surface);
+   MetaWaylandSurface *surface =
+     meta_wayland_surface_role_get_surface (surface_role);
+ 
+@@ -159,13 +148,13 @@ cursor_surface_role_pre_commit (MetaWaylandSurfaceRole  *surface_role,
+ }
+ 
+ static void
+-cursor_surface_role_commit (MetaWaylandSurfaceRole  *surface_role,
+-                            MetaWaylandPendingState *pending)
++meta_wayland_cursor_surface_commit (MetaWaylandSurfaceRole  *surface_role,
++                                    MetaWaylandPendingState *pending)
+ {
+-  MetaWaylandSurfaceRoleCursor *cursor_role =
+-    META_WAYLAND_SURFACE_ROLE_CURSOR (surface_role);
+-  MetaWaylandSurfaceRoleCursorPrivate *priv =
+-    meta_wayland_surface_role_cursor_get_instance_private (cursor_role);
++  MetaWaylandCursorSurface *cursor_surface =
++    META_WAYLAND_CURSOR_SURFACE (surface_role);
++  MetaWaylandCursorSurfacePrivate *priv =
++    meta_wayland_cursor_surface_get_instance_private (cursor_surface);
+   MetaWaylandSurface *surface =
+     meta_wayland_surface_role_get_surface (surface_role);
+   MetaWaylandBuffer *buffer = meta_wayland_surface_get_buffer (surface);
+@@ -182,19 +171,19 @@ cursor_surface_role_commit (MetaWaylandSurfaceRole  *surface_role,
+   wl_list_init (&pending->frame_callback_list);
+ 
+   if (pending->newly_attached)
+-    update_cursor_sprite_texture (META_WAYLAND_SURFACE_ROLE_CURSOR (surface_role));
++    update_cursor_sprite_texture (META_WAYLAND_CURSOR_SURFACE (surface_role));
+ }
+ 
+ static gboolean
+-cursor_surface_role_is_on_logical_monitor (MetaWaylandSurfaceRole *role,
+-                                           MetaLogicalMonitor     *logical_monitor)
++meta_wayland_cursor_surface_is_on_logical_monitor (MetaWaylandSurfaceRole *role,
++                                                   MetaLogicalMonitor     *logical_monitor)
+ {
+   MetaWaylandSurface *surface =
+     meta_wayland_surface_role_get_surface (role);
+-  MetaWaylandSurfaceRoleCursor *cursor_role =
+-    META_WAYLAND_SURFACE_ROLE_CURSOR (surface->role);
+-  MetaWaylandSurfaceRoleCursorPrivate *priv =
+-    meta_wayland_surface_role_cursor_get_instance_private (cursor_role);
++  MetaWaylandCursorSurface *cursor_surface =
++    META_WAYLAND_CURSOR_SURFACE (surface->role);
++  MetaWaylandCursorSurfacePrivate *priv =
++    meta_wayland_cursor_surface_get_instance_private (cursor_surface);
+   ClutterPoint point;
+   ClutterRect logical_monitor_rect;
+ 
+@@ -207,12 +196,12 @@ cursor_surface_role_is_on_logical_monitor (MetaWaylandSurfaceRole *role,
+ }
+ 
+ static void
+-cursor_surface_role_dispose (GObject *object)
++meta_wayland_cursor_surface_dispose (GObject *object)
+ {
+-  MetaWaylandSurfaceRoleCursor *cursor_role =
+-    META_WAYLAND_SURFACE_ROLE_CURSOR (object);
+-  MetaWaylandSurfaceRoleCursorPrivate *priv =
+-    meta_wayland_surface_role_cursor_get_instance_private (cursor_role);
++  MetaWaylandCursorSurface *cursor_surface =
++    META_WAYLAND_CURSOR_SURFACE (object);
++  MetaWaylandCursorSurfacePrivate *priv =
++    meta_wayland_cursor_surface_get_instance_private (cursor_surface);
+   MetaWaylandSurface *surface =
+     meta_wayland_surface_role_get_surface (META_WAYLAND_SURFACE_ROLE (object));
+   MetaWaylandFrameCallback *cb, *next;
+@@ -221,7 +210,7 @@ cursor_surface_role_dispose (GObject *object)
+     wl_resource_destroy (cb->resource);
+ 
+   g_signal_handlers_disconnect_by_func (priv->cursor_sprite,
+-                                        cursor_sprite_prepare_at, cursor_role);
++                                        cursor_sprite_prepare_at, cursor_surface);
+ 
+   g_clear_object (&priv->cursor_renderer);
+   g_clear_object (&priv->cursor_sprite);
+@@ -232,18 +221,18 @@ cursor_surface_role_dispose (GObject *object)
+       g_clear_object (&priv->buffer);
+     }
+ 
+-  G_OBJECT_CLASS (meta_wayland_surface_role_cursor_parent_class)->dispose (object);
++  G_OBJECT_CLASS (meta_wayland_cursor_surface_parent_class)->dispose (object);
+ }
+ 
+ static void
+-cursor_surface_role_constructed (GObject *object)
++meta_wayland_cursor_surface_constructed (GObject *object)
+ {
+-  MetaWaylandSurfaceRoleCursor *cursor_role =
+-    META_WAYLAND_SURFACE_ROLE_CURSOR (object);
+-  MetaWaylandSurfaceRoleCursorPrivate *priv =
+-    meta_wayland_surface_role_cursor_get_instance_private (cursor_role);
++  MetaWaylandCursorSurface *cursor_surface =
++    META_WAYLAND_CURSOR_SURFACE (object);
++  MetaWaylandCursorSurfacePrivate *priv =
++    meta_wayland_cursor_surface_get_instance_private (cursor_surface);
+   MetaWaylandSurfaceRole *surface_role =
+-    META_WAYLAND_SURFACE_ROLE (cursor_role);
++    META_WAYLAND_SURFACE_ROLE (cursor_surface);
+   MetaWaylandSurface *surface =
+     meta_wayland_surface_role_get_surface (surface_role);
+   MetaWaylandBuffer *buffer;
+@@ -257,55 +246,57 @@ cursor_surface_role_constructed (GObject *object)
+       g_set_object (&priv->buffer, buffer);
+       meta_wayland_surface_ref_buffer_use_count (surface);
+     }
+-}
+ 
+-static void
+-meta_wayland_surface_role_cursor_init (MetaWaylandSurfaceRoleCursor *role)
+-{
+-  MetaWaylandSurfaceRoleCursorPrivate *priv =
+-    meta_wayland_surface_role_cursor_get_instance_private (role);
+-
+-  priv->cursor_sprite = meta_cursor_sprite_new ();
++  priv->cursor_sprite = meta_cursor_sprite_wayland_new (surface);
+   g_signal_connect_object (priv->cursor_sprite,
+                            "prepare-at",
+                            G_CALLBACK (cursor_sprite_prepare_at),
+-                           role,
++                           cursor_surface,
+                            0);
++}
++
++static void
++meta_wayland_cursor_surface_init (MetaWaylandCursorSurface *role)
++{
++  MetaWaylandCursorSurfacePrivate *priv =
++    meta_wayland_cursor_surface_get_instance_private (role);
++
+   wl_list_init (&priv->frame_callbacks);
+ }
+ 
+ static void
+-meta_wayland_surface_role_cursor_class_init (MetaWaylandSurfaceRoleCursorClass *klass)
++meta_wayland_cursor_surface_class_init (MetaWaylandCursorSurfaceClass *klass)
+ {
+   MetaWaylandSurfaceRoleClass *surface_role_class =
+     META_WAYLAND_SURFACE_ROLE_CLASS (klass);
+   GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ 
+-  surface_role_class->assigned = cursor_surface_role_assigned;
+-  surface_role_class->pre_commit = cursor_surface_role_pre_commit;
+-  surface_role_class->commit = cursor_surface_role_commit;
+-  surface_role_class->is_on_logical_monitor = cursor_surface_role_is_on_logical_monitor;
++  surface_role_class->assigned = meta_wayland_cursor_surface_assigned;
++  surface_role_class->pre_commit = meta_wayland_cursor_surface_pre_commit;
++  surface_role_class->commit = meta_wayland_cursor_surface_commit;
++  surface_role_class->is_on_logical_monitor =
++    meta_wayland_cursor_surface_is_on_logical_monitor;
+ 
+-  object_class->constructed = cursor_surface_role_constructed;
+-  object_class->dispose = cursor_surface_role_dispose;
++  object_class->constructed = meta_wayland_cursor_surface_constructed;
++  object_class->dispose = meta_wayland_cursor_surface_dispose;
+ }
+ 
+ MetaCursorSprite *
+-meta_wayland_surface_role_cursor_get_sprite (MetaWaylandSurfaceRoleCursor *cursor_role)
++meta_wayland_cursor_surface_get_sprite (MetaWaylandCursorSurface *cursor_surface)
+ {
+-  MetaWaylandSurfaceRoleCursorPrivate *priv =
+-    meta_wayland_surface_role_cursor_get_instance_private (cursor_role);
++  MetaWaylandCursorSurfacePrivate *priv =
++    meta_wayland_cursor_surface_get_instance_private (cursor_surface);
+ 
+-  return priv->cursor_sprite;
++  return META_CURSOR_SPRITE (priv->cursor_sprite);
+ }
+ 
+ void
+-meta_wayland_surface_role_cursor_set_hotspot (MetaWaylandSurfaceRoleCursor *cursor_role,
+-                                              gint                          hotspot_x,
+-                                              gint                          hotspot_y)
++meta_wayland_cursor_surface_set_hotspot (MetaWaylandCursorSurface *cursor_surface,
++                                         int                       hotspot_x,
++                                         int                       hotspot_y)
+ {
+-  MetaWaylandSurfaceRoleCursorPrivate *priv =
+-    meta_wayland_surface_role_cursor_get_instance_private (cursor_role);
++  MetaWaylandCursorSurfacePrivate *priv =
++    meta_wayland_cursor_surface_get_instance_private (cursor_surface);
+ 
+   if (priv->hot_x == hotspot_x &&
+       priv->hot_y == hotspot_y)
+@@ -313,16 +304,16 @@ meta_wayland_surface_role_cursor_set_hotspot (MetaWaylandSurfaceRoleCursor *curs
+ 
+   priv->hot_x = hotspot_x;
+   priv->hot_y = hotspot_y;
+-  update_cursor_sprite_texture (cursor_role);
++  update_cursor_sprite_texture (cursor_surface);
+ }
+ 
+ void
+-meta_wayland_surface_role_cursor_get_hotspot (MetaWaylandSurfaceRoleCursor *cursor_role,
+-                                              gint                         *hotspot_x,
+-                                              gint                         *hotspot_y)
++meta_wayland_cursor_surface_get_hotspot (MetaWaylandCursorSurface *cursor_surface,
++                                         int                      *hotspot_x,
++                                         int                      *hotspot_y)
+ {
+-  MetaWaylandSurfaceRoleCursorPrivate *priv =
+-    meta_wayland_surface_role_cursor_get_instance_private (cursor_role);
++  MetaWaylandCursorSurfacePrivate *priv =
++    meta_wayland_cursor_surface_get_instance_private (cursor_surface);
+ 
+   if (hotspot_x)
+     *hotspot_x = priv->hot_x;
+@@ -331,15 +322,15 @@ meta_wayland_surface_role_cursor_get_hotspot (MetaWaylandSurfaceRoleCursor *curs
+ }
+ 
+ static void
+-on_cursor_painted (MetaCursorRenderer           *renderer,
+-                   MetaCursorSprite             *displayed_sprite,
+-                   MetaWaylandSurfaceRoleCursor *cursor_role)
++on_cursor_painted (MetaCursorRenderer       *renderer,
++                   MetaCursorSprite         *displayed_sprite,
++                   MetaWaylandCursorSurface *cursor_surface)
+ {
+-  MetaWaylandSurfaceRoleCursorPrivate *priv =
+-    meta_wayland_surface_role_cursor_get_instance_private (cursor_role);
++  MetaWaylandCursorSurfacePrivate *priv =
++    meta_wayland_cursor_surface_get_instance_private (cursor_surface);
+   guint32 time = (guint32) (g_get_monotonic_time () / 1000);
+ 
+-  if (displayed_sprite != priv->cursor_sprite)
++  if (displayed_sprite != META_CURSOR_SPRITE (priv->cursor_sprite))
+     return;
+ 
+   while (!wl_list_empty (&priv->frame_callbacks))
+@@ -353,11 +344,11 @@ on_cursor_painted (MetaCursorRenderer           *renderer,
+ }
+ 
+ void
+-meta_wayland_surface_role_cursor_set_renderer (MetaWaylandSurfaceRoleCursor *cursor_role,
+-                                               MetaCursorRenderer           *renderer)
++meta_wayland_cursor_surface_set_renderer (MetaWaylandCursorSurface *cursor_surface,
++                                          MetaCursorRenderer       *renderer)
+ {
+-  MetaWaylandSurfaceRoleCursorPrivate *priv =
+-    meta_wayland_surface_role_cursor_get_instance_private (cursor_role);
++  MetaWaylandCursorSurfacePrivate *priv =
++    meta_wayland_cursor_surface_get_instance_private (cursor_surface);
+ 
+   if (priv->cursor_renderer == renderer)
+     return;
+@@ -373,19 +364,19 @@ meta_wayland_surface_role_cursor_set_renderer (MetaWaylandSurfaceRoleCursor *cur
+     {
+       priv->cursor_painted_handler_id =
+         g_signal_connect_object (renderer, "cursor-painted",
+-                                 G_CALLBACK (on_cursor_painted), cursor_role, 0);
++                                 G_CALLBACK (on_cursor_painted), cursor_surface, 0);
+       g_object_ref (renderer);
+     }
+ 
+   priv->cursor_renderer = renderer;
+-  update_cursor_sprite_texture (cursor_role);
++  update_cursor_sprite_texture (cursor_surface);
+ }
+ 
+ MetaCursorRenderer *
+-meta_wayland_surface_role_cursor_get_renderer (MetaWaylandSurfaceRoleCursor *cursor_role)
++meta_wayland_cursor_surface_get_renderer (MetaWaylandCursorSurface *cursor_surface)
+ {
+-  MetaWaylandSurfaceRoleCursorPrivate *priv =
+-    meta_wayland_surface_role_cursor_get_instance_private (cursor_role);
++  MetaWaylandCursorSurfacePrivate *priv =
++    meta_wayland_cursor_surface_get_instance_private (cursor_surface);
+ 
+   return priv->cursor_renderer;
+ }
+diff --git a/src/wayland/meta-wayland-cursor-surface.h b/src/wayland/meta-wayland-cursor-surface.h
+new file mode 100644
+index 000000000..2461a85b3
+--- /dev/null
++++ b/src/wayland/meta-wayland-cursor-surface.h
+@@ -0,0 +1,52 @@
++/*
++ * Wayland Support
++ *
++ * Copyright (C) 2015 Red Hat, Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program 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
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
++ * 02111-1307, USA.
++ */
++
++#ifndef META_WAYLAND_CURSOR_SURFACE_H
++#define META_WAYLAND_CURSOR_SURFACE_H
++
++#include "meta-wayland-surface.h"
++#include "backends/meta-cursor-renderer.h"
++
++struct _MetaWaylandCursorSurfaceClass
++{
++  MetaWaylandSurfaceRoleClass parent_class;
++};
++
++#define META_TYPE_WAYLAND_CURSOR_SURFACE (meta_wayland_cursor_surface_get_type ())
++G_DECLARE_DERIVABLE_TYPE (MetaWaylandCursorSurface,
++                          meta_wayland_cursor_surface,
++                          META, WAYLAND_CURSOR_SURFACE,
++                          MetaWaylandSurfaceRole);
++
++MetaCursorSprite *   meta_wayland_cursor_surface_get_sprite   (MetaWaylandCursorSurface *cursor_surface);
++
++void                 meta_wayland_cursor_surface_set_hotspot  (MetaWaylandCursorSurface *cursor_surface,
++                                                               int                       hotspot_x,
++                                                               int                       hotspot_y);
++void                 meta_wayland_cursor_surface_get_hotspot  (MetaWaylandCursorSurface *cursor_surface,
++                                                               int                       *hotspot_x,
++                                                               int                       *hotspot_y);
++void                 meta_wayland_cursor_surface_set_renderer (MetaWaylandCursorSurface *cursor_surface,
++                                                               MetaCursorRenderer       *renderer);
++MetaCursorRenderer * meta_wayland_cursor_surface_get_renderer (MetaWaylandCursorSurface *cursor_surface);
++
++
++#endif /* META_WAYLAND_CURSOR_SURFACE_H */
+diff --git a/src/wayland/meta-wayland-pointer.c b/src/wayland/meta-wayland-pointer.c
+index d5c90c169..e8138576e 100644
+--- a/src/wayland/meta-wayland-pointer.c
++++ b/src/wayland/meta-wayland-pointer.c
+@@ -55,7 +55,7 @@
+ #include "meta-wayland-seat.h"
+ #include "meta-wayland-surface.h"
+ #include "meta-wayland-buffer.h"
+-#include "meta-wayland-surface-role-cursor.h"
++#include "meta-wayland-cursor-surface.h"
+ #include "meta-xwayland.h"
+ #include "meta-cursor.h"
+ #include "meta-cursor-tracker-private.h"
+@@ -1025,10 +1025,10 @@ meta_wayland_pointer_update_cursor_surface (MetaWaylandPointer *pointer)
+ 
+       if (pointer->cursor_surface)
+         {
+-          MetaWaylandSurfaceRoleCursor *cursor_role =
+-            META_WAYLAND_SURFACE_ROLE_CURSOR (pointer->cursor_surface->role);
++          MetaWaylandCursorSurface *cursor_surface =
++            META_WAYLAND_CURSOR_SURFACE (pointer->cursor_surface->role);
+ 
+-          cursor_sprite = meta_wayland_surface_role_cursor_get_sprite (cursor_role);
++          cursor_sprite = meta_wayland_cursor_surface_get_sprite (cursor_surface);
+         }
+ 
+       meta_cursor_tracker_set_window_cursor (cursor_tracker, cursor_sprite);
+@@ -1102,7 +1102,7 @@ pointer_set_cursor (struct wl_client *client,
+ 
+   if (surface &&
+       !meta_wayland_surface_assign_role (surface,
+-                                         META_TYPE_WAYLAND_SURFACE_ROLE_CURSOR,
++                                         META_TYPE_WAYLAND_CURSOR_SURFACE,
+                                          NULL))
+     {
+       wl_resource_post_error (resource, WL_POINTER_ERROR_ROLE,
+@@ -1115,13 +1115,13 @@ pointer_set_cursor (struct wl_client *client,
+     {
+       MetaCursorRenderer *cursor_renderer =
+         meta_backend_get_cursor_renderer (meta_get_backend ());
+-      MetaWaylandSurfaceRoleCursor *cursor_role;
++      MetaWaylandCursorSurface *cursor_surface;
+ 
+-      cursor_role = META_WAYLAND_SURFACE_ROLE_CURSOR (surface->role);
+-      meta_wayland_surface_role_cursor_set_renderer (cursor_role,
+-                                                     cursor_renderer);
+-      meta_wayland_surface_role_cursor_set_hotspot (cursor_role,
+-                                                    hot_x, hot_y);
++      cursor_surface = META_WAYLAND_CURSOR_SURFACE (surface->role);
++      meta_wayland_cursor_surface_set_renderer (cursor_surface,
++                                                cursor_renderer);
++      meta_wayland_cursor_surface_set_hotspot (cursor_surface,
++                                               hot_x, hot_y);
+     }
+ 
+   meta_wayland_pointer_set_cursor_surface (pointer, surface);
+diff --git a/src/wayland/meta-wayland-surface-role-cursor.h b/src/wayland/meta-wayland-surface-role-cursor.h
+deleted file mode 100644
+index b6d6d4a6a..000000000
+--- a/src/wayland/meta-wayland-surface-role-cursor.h
++++ /dev/null
+@@ -1,52 +0,0 @@
+-/*
+- * Wayland Support
+- *
+- * Copyright (C) 2015 Red Hat, Inc.
+- *
+- * This program is free software; you can redistribute it and/or
+- * modify it under the terms of the GNU General Public License as
+- * published by the Free Software Foundation; either version 2 of the
+- * License, or (at your option) any later version.
+- *
+- * This program 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
+- * General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program; if not, write to the Free Software
+- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+- * 02111-1307, USA.
+- */
+-
+-#ifndef META_WAYLAND_SURFACE_ROLE_CURSOR_H
+-#define META_WAYLAND_SURFACE_ROLE_CURSOR_H
+-
+-#include "meta-wayland-surface.h"
+-#include "backends/meta-cursor-renderer.h"
+-
+-struct _MetaWaylandSurfaceRoleCursorClass
+-{
+-  MetaWaylandSurfaceRoleClass parent_class;
+-};
+-
+-#define META_TYPE_WAYLAND_SURFACE_ROLE_CURSOR (meta_wayland_surface_role_cursor_get_type ())
+-G_DECLARE_DERIVABLE_TYPE (MetaWaylandSurfaceRoleCursor,
+-                          meta_wayland_surface_role_cursor,
+-                          META, WAYLAND_SURFACE_ROLE_CURSOR,
+-                          MetaWaylandSurfaceRole);
+-
+-MetaCursorSprite *   meta_wayland_surface_role_cursor_get_sprite   (MetaWaylandSurfaceRoleCursor *cursor_role);
+-
+-void                 meta_wayland_surface_role_cursor_set_hotspot  (MetaWaylandSurfaceRoleCursor *cursor_role,
+-                                                                    gint                          hotspot_x,
+-                                                                    gint                          hotspot_y);
+-void                 meta_wayland_surface_role_cursor_get_hotspot  (MetaWaylandSurfaceRoleCursor *cursor_role,
+-                                                                    gint                         *hotspot_x,
+-                                                                    gint                         *hotspot_y);
+-void                 meta_wayland_surface_role_cursor_set_renderer (MetaWaylandSurfaceRoleCursor *cursor_role,
+-                                                                    MetaCursorRenderer           *renderer);
+-MetaCursorRenderer * meta_wayland_surface_role_cursor_get_renderer (MetaWaylandSurfaceRoleCursor *cursor_role);
+-
+-
+-#endif /* META_WAYLAND_SURFACE_ROLE_CURSOR_H */
+diff --git a/src/wayland/meta-wayland-surface-role-tablet-cursor.c b/src/wayland/meta-wayland-tablet-cursor-surface.c
+similarity index 63%
+rename from src/wayland/meta-wayland-surface-role-tablet-cursor.c
+rename to src/wayland/meta-wayland-tablet-cursor-surface.c
+index 075a5e4f6..808bf2820 100644
+--- a/src/wayland/meta-wayland-surface-role-tablet-cursor.c
++++ b/src/wayland/meta-wayland-tablet-cursor-surface.c
+@@ -20,23 +20,24 @@
+  */
+ 
+ #include "config.h"
+-#include "meta-wayland-surface-role-tablet-cursor.h"
+ 
+-struct _MetaWaylandSurfaceRoleTabletCursor
++#include "meta-wayland-tablet-cursor-surface.h"
++
++struct _MetaWaylandTabletCursorSurface
+ {
+-  MetaWaylandSurfaceRoleCursor parent;
++  MetaWaylandCursorSurface parent;
+ };
+ 
+-G_DEFINE_TYPE (MetaWaylandSurfaceRoleTabletCursor,
+-               meta_wayland_surface_role_tablet_cursor,
+-               META_TYPE_WAYLAND_SURFACE_ROLE_CURSOR)
++G_DEFINE_TYPE (MetaWaylandTabletCursorSurface,
++               meta_wayland_tablet_cursor_surface,
++               META_TYPE_WAYLAND_CURSOR_SURFACE)
+ 
+ static void
+-meta_wayland_surface_role_tablet_cursor_init (MetaWaylandSurfaceRoleTabletCursor *role)
++meta_wayland_tablet_cursor_surface_init (MetaWaylandTabletCursorSurface *role)
+ {
+ }
+ 
+ static void
+-meta_wayland_surface_role_tablet_cursor_class_init (MetaWaylandSurfaceRoleTabletCursorClass *klass)
++meta_wayland_tablet_cursor_surface_class_init (MetaWaylandTabletCursorSurfaceClass *klass)
+ {
+ }
+diff --git a/src/wayland/meta-wayland-surface-role-tablet-cursor.h b/src/wayland/meta-wayland-tablet-cursor-surface.h
+similarity index 59%
+rename from src/wayland/meta-wayland-surface-role-tablet-cursor.h
+rename to src/wayland/meta-wayland-tablet-cursor-surface.h
+index 69fc6cf0f..5c5c198f5 100644
+--- a/src/wayland/meta-wayland-surface-role-tablet-cursor.h
++++ b/src/wayland/meta-wayland-tablet-cursor-surface.h
+@@ -19,15 +19,15 @@
+  * 02111-1307, USA.
+  */
+ 
+-#ifndef META_WAYLAND_SURFACE_ROLE_TABLET_CURSOR_H
+-#define META_WAYLAND_SURFACE_ROLE_TABLET_CURSOR_H
++#ifndef META_WAYLAND_TABLET_CURSOR_SURFACE_H
++#define META_WAYLAND_TABLET_CURSOR_SURFACE_H
+ 
+-#include "meta-wayland-surface-role-cursor.h"
++#include "meta-wayland-cursor-surface.h"
+ 
+-#define META_TYPE_WAYLAND_SURFACE_ROLE_TABLET_CURSOR (meta_wayland_surface_role_tablet_cursor_get_type ())
+-G_DECLARE_FINAL_TYPE (MetaWaylandSurfaceRoleTabletCursor,
+-                      meta_wayland_surface_role_tablet_cursor,
+-                      META, WAYLAND_SURFACE_ROLE_TABLET_CURSOR,
+-                      MetaWaylandSurfaceRoleCursor);
++#define META_TYPE_WAYLAND_TABLET_CURSOR_SURFACE (meta_wayland_tablet_cursor_surface_get_type ())
++G_DECLARE_FINAL_TYPE (MetaWaylandTabletCursorSurface,
++                      meta_wayland_tablet_cursor_surface,
++                      META, WAYLAND_TABLET_CURSOR_SURFACE,
++                      MetaWaylandCursorSurface)
+ 
+-#endif /* META_WAYLAND_SURFACE_ROLE_TABLET_CURSOR_H */
++#endif /* META_WAYLAND_TABLET_CURSOR_SURFACE_H */
+diff --git a/src/wayland/meta-wayland-tablet-tool.c b/src/wayland/meta-wayland-tablet-tool.c
+index 4b57d4156..d373f8d25 100644
+--- a/src/wayland/meta-wayland-tablet-tool.c
++++ b/src/wayland/meta-wayland-tablet-tool.c
+@@ -31,7 +31,7 @@
+ #include <wayland-server.h>
+ #include "tablet-unstable-v2-server-protocol.h"
+ #include "meta-wayland-private.h"
+-#include "meta-wayland-surface-role-tablet-cursor.h"
++#include "meta-wayland-tablet-cursor-surface.h"
+ #include "meta-surface-actor-wayland.h"
+ #include "meta-wayland-tablet.h"
+ #include "meta-wayland-tablet-seat.h"
+@@ -90,16 +90,16 @@ meta_wayland_tablet_tool_update_cursor_surface (MetaWaylandTabletTool *tool)
+       if (tool->cursor_surface &&
+           meta_wayland_surface_get_buffer (tool->cursor_surface))
+         {
+-          MetaWaylandSurfaceRoleCursor *cursor_role =
+-            META_WAYLAND_SURFACE_ROLE_CURSOR (tool->cursor_surface->role);
++          MetaWaylandCursorSurface *cursor_surface =
++            META_WAYLAND_CURSOR_SURFACE (tool->cursor_surface->role);
+ 
+-          cursor = meta_wayland_surface_role_cursor_get_sprite (cursor_role);
++          cursor = meta_wayland_cursor_surface_get_sprite (cursor_surface);
+         }
+       else
+         cursor = NULL;
+     }
+   else if (tool->current_tablet)
+-    cursor = tool->default_sprite;
++    cursor = META_CURSOR_SPRITE (tool->default_sprite);
+   else
+     cursor = NULL;
+ 
+@@ -382,10 +382,10 @@ tablet_tool_handle_cursor_surface_destroy (struct wl_listener *listener,
+ }
+ 
+ static void
+-tool_cursor_prepare_at (MetaCursorSprite      *cursor_sprite,
+-                        int                    x,
+-                        int                    y,
+-                        MetaWaylandTabletTool *tool)
++tool_cursor_prepare_at (MetaCursorSpriteXcursor *sprite_xcursor,
++                        int                      x,
++                        int                      y,
++                        MetaWaylandTabletTool   *tool)
+ {
+   MetaBackend *backend = meta_get_backend ();
+   MetaMonitorManager *monitor_manager =
+@@ -397,7 +397,8 @@ tool_cursor_prepare_at (MetaCursorSprite      *cursor_sprite,
+ 
+   /* Reload the cursor texture if the scale has changed. */
+   if (logical_monitor)
+-    meta_cursor_sprite_set_theme_scale (cursor_sprite, logical_monitor->scale);
++    meta_cursor_sprite_xcursor_set_theme_scale (sprite_xcursor,
++                                                logical_monitor->scale);
+ }
+ 
+ MetaWaylandTabletTool *
+@@ -417,7 +418,7 @@ meta_wayland_tablet_tool_new (MetaWaylandTabletSeat  *seat,
+   tool->focus_surface_destroy_listener.notify = tablet_tool_handle_focus_surface_destroy;
+   tool->cursor_surface_destroy_listener.notify = tablet_tool_handle_cursor_surface_destroy;
+ 
+-  tool->default_sprite = meta_cursor_sprite_from_theme (META_CURSOR_CROSSHAIR);
++  tool->default_sprite = meta_cursor_sprite_xcursor_new (META_CURSOR_CROSSHAIR);
+   tool->prepare_at_signal_id =
+     g_signal_connect (tool->default_sprite, "prepare-at",
+                       G_CALLBACK (tool_cursor_prepare_at), tool);
+@@ -471,7 +472,7 @@ tool_set_cursor (struct wl_client   *client,
+ 
+   if (surface &&
+       !meta_wayland_surface_assign_role (surface,
+-                                         META_TYPE_WAYLAND_SURFACE_ROLE_TABLET_CURSOR,
++                                         META_TYPE_WAYLAND_TABLET_CURSOR_SURFACE,
+                                          NULL))
+     {
+       wl_resource_post_error (resource, WL_POINTER_ERROR_ROLE,
+@@ -482,13 +483,13 @@ tool_set_cursor (struct wl_client   *client,
+ 
+   if (surface)
+     {
+-      MetaWaylandSurfaceRoleCursor *cursor_role;
++      MetaWaylandCursorSurface *cursor_surface;
+ 
+-      cursor_role = META_WAYLAND_SURFACE_ROLE_CURSOR (surface->role);
+-      meta_wayland_surface_role_cursor_set_renderer (cursor_role,
+-                                                     tool->cursor_renderer);
+-      meta_wayland_surface_role_cursor_set_hotspot (cursor_role,
+-                                                    hotspot_x, hotspot_y);
++      cursor_surface = META_WAYLAND_CURSOR_SURFACE (surface->role);
++      meta_wayland_cursor_surface_set_renderer (cursor_surface,
++                                                tool->cursor_renderer);
++      meta_wayland_cursor_surface_set_hotspot (cursor_surface,
++                                               hotspot_x, hotspot_y);
+     }
+ 
+   meta_wayland_tablet_tool_set_cursor_surface (tool, surface);
+diff --git a/src/wayland/meta-wayland-tablet-tool.h b/src/wayland/meta-wayland-tablet-tool.h
+index 8cd930086..011972fc2 100644
+--- a/src/wayland/meta-wayland-tablet-tool.h
++++ b/src/wayland/meta-wayland-tablet-tool.h
+@@ -28,6 +28,7 @@
+ 
+ #include "meta-wayland-types.h"
+ #include "meta-cursor-renderer.h"
++#include "backends/meta-cursor-sprite-xcursor.h"
+ 
+ struct _MetaWaylandTabletTool
+ {
+@@ -43,7 +44,7 @@ struct _MetaWaylandTabletTool
+   MetaWaylandSurface *cursor_surface;
+   struct wl_listener cursor_surface_destroy_listener;
+   MetaCursorRenderer *cursor_renderer;
+-  MetaCursorSprite *default_sprite;
++  MetaCursorSpriteXcursor *default_sprite;
+   guint prepare_at_signal_id;
+ 
+   MetaWaylandSurface *current;
diff --git a/SOURCES/mutter-pipewire-0_2-API.patch b/SOURCES/mutter-pipewire-0_2-API.patch
new file mode 100644
index 0000000..c98d8d0
--- /dev/null
+++ b/SOURCES/mutter-pipewire-0_2-API.patch
@@ -0,0 +1,111 @@
+diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c
+index 457c058..0c4f33b 100644
+--- a/src/backends/meta-screen-cast-stream-src.c
++++ b/src/backends/meta-screen-cast-stream-src.c
+@@ -132,8 +132,8 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src)
+ {
+   MetaScreenCastStreamSrcPrivate *priv =
+     meta_screen_cast_stream_src_get_instance_private (src);
+-  uint32_t buffer_id;
+-  struct spa_buffer *buffer;
++  struct pw_buffer *buffer;
++  struct spa_buffer *spa_buffer;
+   uint8_t *map = NULL;
+   uint8_t *data;
+   uint64_t now_us;
+@@ -148,22 +148,24 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src)
+   if (!priv->pipewire_stream)
+     return;
+ 
+-  buffer_id = pw_stream_get_empty_buffer (priv->pipewire_stream);
+-  if (buffer_id == SPA_ID_INVALID)
+-    return;
+-
+-  buffer = pw_stream_peek_buffer (priv->pipewire_stream, buffer_id);
++  buffer = pw_stream_dequeue_buffer (priv->pipewire_stream);
+   if (!buffer)
+     {
+-      g_warning ("Failed to peek at PipeWire buffer");
++      g_warning ("Failed to dequeue at PipeWire buffer");
+       return;
+     }
+ 
+-  if (buffer->datas[0].type == priv->pipewire_type->data.MemFd)
++  spa_buffer = buffer->buffer;
++
++  if (spa_buffer->datas[0].data)
++    {
++      data = spa_buffer->datas[0].data;
++    }
++  else if (spa_buffer->datas[0].type == priv->pipewire_type->data.MemFd)
+     {
+-      map = mmap (NULL, buffer->datas[0].maxsize + buffer->datas[0].mapoffset,
++      map = mmap (NULL, spa_buffer->datas[0].maxsize + spa_buffer->datas[0].mapoffset,
+                   PROT_READ | PROT_WRITE, MAP_SHARED,
+-                  buffer->datas[0].fd, 0);
++                  spa_buffer->datas[0].fd, 0);
+       if (map == MAP_FAILED)
+         {
+           g_warning ("Failed to mmap pipewire stream buffer: %s\n",
+@@ -171,14 +173,11 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src)
+           return;
+         }
+ 
+-      data = SPA_MEMBER (map, buffer->datas[0].mapoffset, uint8_t);
+-    }
+-  else if (buffer->datas[0].type == priv->pipewire_type->data.MemPtr)
+-    {
+-      data = buffer->datas[0].data;
++      data = SPA_MEMBER (map, spa_buffer->datas[0].mapoffset, uint8_t);
+     }
+   else
+     {
++      g_warning ("Unhandled spa buffer type: %d", spa_buffer->datas[0].type);
+       return;
+     }
+ 
+@@ -186,11 +185,11 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src)
+   priv->last_frame_timestamp_us = now_us;
+ 
+   if (map)
+-    munmap (map, buffer->datas[0].maxsize + buffer->datas[0].mapoffset);
++    munmap (map, spa_buffer->datas[0].maxsize + spa_buffer->datas[0].mapoffset);
+ 
+-  buffer->datas[0].chunk->size = buffer->datas[0].maxsize;
++  spa_buffer->datas[0].chunk->size = spa_buffer->datas[0].maxsize;
+ 
+-  pw_stream_send_buffer (priv->pipewire_stream, buffer_id);
++  pw_stream_queue_buffer (priv->pipewire_stream, buffer);
+ }
+ 
+ static gboolean
+@@ -266,8 +265,8 @@ on_stream_state_changed (void                 *data,
+ }
+ 
+ static void
+-on_stream_format_changed (void           *data,
+-                          struct spa_pod *format)
++on_stream_format_changed (void                 *data,
++                          const struct spa_pod *format)
+ {
+   MetaScreenCastStreamSrc *src = data;
+   MetaScreenCastStreamSrcPrivate *priv =
+@@ -276,7 +275,7 @@ on_stream_format_changed (void           *data,
+   uint8_t params_buffer[1024];
+   int32_t width, height, stride, size;
+   struct spa_pod_builder pod_builder;
+-  struct spa_pod *params[1];
++  const struct spa_pod *params[1];
+   const int bpp = 4;
+ 
+   if (!format)
+@@ -372,7 +371,8 @@ create_pipewire_stream (MetaScreenCastStreamSrc  *src,
+   result = pw_stream_connect (pipewire_stream,
+                               PW_DIRECTION_OUTPUT,
+                               NULL,
+-                              PW_STREAM_FLAG_NONE,
++                              (PW_STREAM_FLAG_DRIVER |
++                               PW_STREAM_FLAG_MAP_BUFFERS),
+                               params, G_N_ELEMENTS (params));
+   if (result != 0)
+     {
diff --git a/SOURCES/mutter-search-for-libpipewire-0_2.patch b/SOURCES/mutter-search-for-libpipewire-0_2.patch
new file mode 100644
index 0000000..b278eaf
--- /dev/null
+++ b/SOURCES/mutter-search-for-libpipewire-0_2.patch
@@ -0,0 +1,26 @@
+diff --git a/configure b/configure
+index cfe46d0..6a06043 100755
+--- a/configure
++++ b/configure
+@@ -16590,7 +16590,7 @@ fi
+ 
+ if test "$enable_remote_desktop" = "yes"; then :
+ 
+-  MUTTER_PC_MODULES="$MUTTER_PC_MODULES libpipewire-0.1 >= 0.1.8"
++  MUTTER_PC_MODULES="$MUTTER_PC_MODULES libpipewire-0.2 >= 0.2.2"
+ 
+ $as_echo "#define HAVE_REMOTE_DESKTOP 1" >>confdefs.h
+ 
+diff --git a/configure.ac b/configure.ac
+index e795159..9adb14b 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -245,7 +245,7 @@ AC_ARG_ENABLE(remote-desktop,
+   enable_remote_desktop=no
+ )
+ AS_IF([test "$enable_remote_desktop" = "yes"], [
+-  MUTTER_PC_MODULES="$MUTTER_PC_MODULES libpipewire-0.1 >= 0.1.8"
++  MUTTER_PC_MODULES="$MUTTER_PC_MODULES libpipewire-0.2 >= 0.2.2"
+   AC_DEFINE([HAVE_REMOTE_DESKTOP],[1], [Defined if screen cast and remote desktop support is enabled])
+ ])
+ AM_CONDITIONAL([HAVE_REMOTE_DESKTOP],[test "$enable_remote_desktop" = "yes"])
diff --git a/SOURCES/screen-cast-cursor-side-channel.patch b/SOURCES/screen-cast-cursor-side-channel.patch
new file mode 100644
index 0000000..12ee90b
--- /dev/null
+++ b/SOURCES/screen-cast-cursor-side-channel.patch
@@ -0,0 +1,2098 @@
+From 5f428c5aa9f3e66cd23e4e75d54506960bd5b66c Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Wed, 12 Dec 2018 11:27:16 +0100
+Subject: [PATCH 01/12] cursor-tracker: Add 'cursor-moved' signal
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/357
+---
+ src/backends/meta-cursor-tracker.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+diff --git a/src/backends/meta-cursor-tracker.c b/src/backends/meta-cursor-tracker.c
+index 6244f11ee..060b6af3b 100644
+--- a/src/backends/meta-cursor-tracker.c
++++ b/src/backends/meta-cursor-tracker.c
+@@ -48,6 +48,7 @@ G_DEFINE_TYPE (MetaCursorTracker, meta_cursor_tracker, G_TYPE_OBJECT);
+ 
+ enum {
+   CURSOR_CHANGED,
++  CURSOR_MOVED,
+   LAST_SIGNAL
+ };
+ 
+@@ -158,6 +159,15 @@ meta_cursor_tracker_class_init (MetaCursorTrackerClass *klass)
+                                           0,
+                                           NULL, NULL, NULL,
+                                           G_TYPE_NONE, 0);
++
++  signals[CURSOR_MOVED] = g_signal_new ("cursor-moved",
++                                        G_TYPE_FROM_CLASS (klass),
++                                        G_SIGNAL_RUN_LAST,
++                                        0,
++                                        NULL, NULL, NULL,
++                                        G_TYPE_NONE, 2,
++                                        G_TYPE_FLOAT,
++                                        G_TYPE_FLOAT);
+ }
+ 
+ /**
+@@ -334,6 +344,8 @@ meta_cursor_tracker_update_position (MetaCursorTracker *tracker,
+   g_assert (meta_is_wayland_compositor ());
+ 
+   meta_cursor_renderer_set_position (cursor_renderer, new_x, new_y);
++
++  g_signal_emit (tracker, signals[CURSOR_MOVED], 0, new_x, new_y);
+ }
+ 
+ static void
+-- 
+2.20.1
+
+
+From 54c5034f6dcdaf382a8355460710ced47c05d91e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Fri, 21 Dec 2018 17:28:33 +0100
+Subject: [PATCH 02/12] clutter/stage: Add clutter_stage_is_redraw_queued() API
+
+This will be used by the screen casting code to check whether it should
+wait for a frame before reading cursor state, or send only the cursor
+update, if no redraw is queued.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/357
+---
+ clutter/clutter/clutter-stage.c | 11 +++++++++++
+ clutter/clutter/clutter-stage.h |  3 +++
+ 2 files changed, 14 insertions(+)
+
+diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c
+index 9352f49e8..763ec96ee 100644
+--- a/clutter/clutter/clutter-stage.c
++++ b/clutter/clutter/clutter-stage.c
+@@ -3722,6 +3722,17 @@ clutter_stage_ensure_redraw (ClutterStage *stage)
+   _clutter_master_clock_start_running (master_clock);
+ }
+ 
++/**
++ * clutter_stage_is_redraw_queued: (skip)
++ */
++gboolean
++clutter_stage_is_redraw_queued (ClutterStage *stage)
++{
++  ClutterStagePrivate *priv = stage->priv;
++
++  return priv->redraw_pending;
++}
++
+ /**
+  * clutter_stage_queue_redraw:
+  * @stage: the #ClutterStage
+diff --git a/clutter/clutter/clutter-stage.h b/clutter/clutter/clutter-stage.h
+index e8fbda84c..ddf08fde4 100644
+--- a/clutter/clutter/clutter-stage.h
++++ b/clutter/clutter/clutter-stage.h
+@@ -250,6 +250,9 @@ void            clutter_stage_ensure_viewport                   (ClutterStage
+ CLUTTER_AVAILABLE_IN_ALL
+ void            clutter_stage_ensure_redraw                     (ClutterStage          *stage);
+ 
++CLUTTER_AVAILABLE_IN_ALL
++gboolean        clutter_stage_is_redraw_queued                  (ClutterStage          *stage);
++
+ #ifdef CLUTTER_ENABLE_EXPERIMENTAL_API
+ CLUTTER_AVAILABLE_IN_1_14
+ void            clutter_stage_set_sync_delay                    (ClutterStage          *stage,
+-- 
+2.20.1
+
+
+From 59e1c68143d9090e238198448fa400bb0776d38a Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Wed, 12 Dec 2018 11:32:11 +0100
+Subject: [PATCH 03/12] screen-cast-monitor-stream: Don't pass monitor manager
+ when creating
+
+It can be fetched indirectly from the monitor already.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/357
+---
+ src/backends/meta-screen-cast-monitor-stream.c | 11 ++++++-----
+ src/backends/meta-screen-cast-monitor-stream.h |  9 ++++-----
+ src/backends/meta-screen-cast-session.c        |  1 -
+ 3 files changed, 10 insertions(+), 11 deletions(-)
+
+diff --git a/src/backends/meta-screen-cast-monitor-stream.c b/src/backends/meta-screen-cast-monitor-stream.c
+index df43f977c..5a816e4df 100644
+--- a/src/backends/meta-screen-cast-monitor-stream.c
++++ b/src/backends/meta-screen-cast-monitor-stream.c
+@@ -105,12 +105,13 @@ meta_screen_cast_monitor_stream_get_monitor (MetaScreenCastMonitorStream *monito
+ }
+ 
+ MetaScreenCastMonitorStream *
+-meta_screen_cast_monitor_stream_new (GDBusConnection     *connection,
+-                                     MetaMonitorManager  *monitor_manager,
+-                                     MetaMonitor         *monitor,
+-                                     ClutterStage        *stage,
+-                                     GError             **error)
++meta_screen_cast_monitor_stream_new (GDBusConnection  *connection,
++                                     MetaMonitor      *monitor,
++                                     ClutterStage     *stage,
++                                     GError          **error)
+ {
++  MetaGpu *gpu = meta_monitor_get_gpu (monitor);
++  MetaMonitorManager *monitor_manager = meta_gpu_get_monitor_manager (gpu);
+   MetaScreenCastMonitorStream *monitor_stream;
+ 
+   if (!meta_monitor_is_active (monitor))
+diff --git a/src/backends/meta-screen-cast-monitor-stream.h b/src/backends/meta-screen-cast-monitor-stream.h
+index fbf3c77c3..1d24de93a 100644
+--- a/src/backends/meta-screen-cast-monitor-stream.h
++++ b/src/backends/meta-screen-cast-monitor-stream.h
+@@ -34,11 +34,10 @@ G_DECLARE_FINAL_TYPE (MetaScreenCastMonitorStream,
+                       META, SCREEN_CAST_MONITOR_STREAM,
+                       MetaScreenCastStream)
+ 
+-MetaScreenCastMonitorStream * meta_screen_cast_monitor_stream_new (GDBusConnection    *connection,
+-								   MetaMonitorManager *monitor_manager,
+-                                                                   MetaMonitor        *monitor,
+-                                                                   ClutterStage       *stage,
+-                                                                   GError            **error);
++MetaScreenCastMonitorStream * meta_screen_cast_monitor_stream_new (GDBusConnection  *connection,
++                                                                   MetaMonitor      *monitor,
++                                                                   ClutterStage     *stage,
++                                                                   GError          **error);
+ 
+ ClutterStage * meta_screen_cast_monitor_stream_get_stage (MetaScreenCastMonitorStream *monitor_stream);
+ 
+diff --git a/src/backends/meta-screen-cast-session.c b/src/backends/meta-screen-cast-session.c
+index d0f5a79d9..3ba59037f 100644
+--- a/src/backends/meta-screen-cast-session.c
++++ b/src/backends/meta-screen-cast-session.c
+@@ -301,7 +301,6 @@ handle_record_monitor (MetaDBusScreenCastSession *skeleton,
+   stage = CLUTTER_STAGE (meta_backend_get_stage (backend));
+ 
+   monitor_stream = meta_screen_cast_monitor_stream_new (connection,
+-                                                        monitor_manager,
+                                                         monitor,
+                                                         stage,
+                                                         &error);
+-- 
+2.20.1
+
+
+From 45100944033987a946672fda6de309aa41a6139f Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Wed, 12 Dec 2018 11:35:58 +0100
+Subject: [PATCH 04/12] screen-cast: Add getters to fetch object owners
+
+MetaBackend owns MetaScreenCast which owns MetaScreenCastSession which
+owns MetaScreenCastStream. Make it possible to fetch objects in the
+oppositev direction too.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/357
+---
+ src/backends/meta-backend.c                   |  3 +-
+ .../meta-screen-cast-monitor-stream.c         | 10 ++++---
+ .../meta-screen-cast-monitor-stream.h         | 10 ++++---
+ src/backends/meta-screen-cast-session.c       | 15 ++++++++--
+ src/backends/meta-screen-cast-session.h       |  2 ++
+ src/backends/meta-screen-cast-stream.c        | 30 +++++++++++++++++++
+ src/backends/meta-screen-cast-stream.h        |  6 ++++
+ src/backends/meta-screen-cast-window-stream.c |  8 +++--
+ src/backends/meta-screen-cast-window-stream.h |  7 +++--
+ src/backends/meta-screen-cast.c               | 15 ++++++++--
+ src/backends/meta-screen-cast.h               |  6 +++-
+ 11 files changed, 92 insertions(+), 20 deletions(-)
+
+diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c
+index 888e51cd9..28f1cd92f 100644
+--- a/src/backends/meta-backend.c
++++ b/src/backends/meta-backend.c
+@@ -456,7 +456,8 @@ meta_backend_real_post_init (MetaBackend *backend)
+   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 (priv->dbus_session_watcher);
++  priv->screen_cast = meta_screen_cast_new (backend,
++                                            priv->dbus_session_watcher);
+   priv->remote_desktop = meta_remote_desktop_new (priv->dbus_session_watcher);
+ #endif /* HAVE_REMOTE_DESKTOP */
+ 
+diff --git a/src/backends/meta-screen-cast-monitor-stream.c b/src/backends/meta-screen-cast-monitor-stream.c
+index 5a816e4df..a6bed1b52 100644
+--- a/src/backends/meta-screen-cast-monitor-stream.c
++++ b/src/backends/meta-screen-cast-monitor-stream.c
+@@ -105,10 +105,11 @@ meta_screen_cast_monitor_stream_get_monitor (MetaScreenCastMonitorStream *monito
+ }
+ 
+ MetaScreenCastMonitorStream *
+-meta_screen_cast_monitor_stream_new (GDBusConnection  *connection,
+-                                     MetaMonitor      *monitor,
+-                                     ClutterStage     *stage,
+-                                     GError          **error)
++meta_screen_cast_monitor_stream_new (MetaScreenCastSession  *session,
++                                     GDBusConnection        *connection,
++                                     MetaMonitor            *monitor,
++                                     ClutterStage           *stage,
++                                     GError                **error)
+ {
+   MetaGpu *gpu = meta_monitor_get_gpu (monitor);
+   MetaMonitorManager *monitor_manager = meta_gpu_get_monitor_manager (gpu);
+@@ -123,6 +124,7 @@ meta_screen_cast_monitor_stream_new (GDBusConnection  *connection,
+   monitor_stream = g_initable_new (META_TYPE_SCREEN_CAST_MONITOR_STREAM,
+                                    NULL,
+                                    error,
++                                   "session", session,
+                                    "connection", connection,
+                                    "monitor", monitor,
+                                    NULL);
+diff --git a/src/backends/meta-screen-cast-monitor-stream.h b/src/backends/meta-screen-cast-monitor-stream.h
+index 1d24de93a..98f160c88 100644
+--- a/src/backends/meta-screen-cast-monitor-stream.h
++++ b/src/backends/meta-screen-cast-monitor-stream.h
+@@ -27,6 +27,7 @@
+ 
+ #include "backends/meta-monitor-manager-private.h"
+ #include "backends/meta-screen-cast-stream.h"
++#include "backends/meta-screen-cast.h"
+ 
+ #define META_TYPE_SCREEN_CAST_MONITOR_STREAM (meta_screen_cast_monitor_stream_get_type ())
+ G_DECLARE_FINAL_TYPE (MetaScreenCastMonitorStream,
+@@ -34,10 +35,11 @@ G_DECLARE_FINAL_TYPE (MetaScreenCastMonitorStream,
+                       META, SCREEN_CAST_MONITOR_STREAM,
+                       MetaScreenCastStream)
+ 
+-MetaScreenCastMonitorStream * meta_screen_cast_monitor_stream_new (GDBusConnection  *connection,
+-                                                                   MetaMonitor      *monitor,
+-                                                                   ClutterStage     *stage,
+-                                                                   GError          **error);
++MetaScreenCastMonitorStream * meta_screen_cast_monitor_stream_new (MetaScreenCastSession  *session,
++                                                                   GDBusConnection        *connection,
++                                                                   MetaMonitor            *monitor,
++                                                                   ClutterStage           *stage,
++                                                                   GError                **error);
+ 
+ ClutterStage * meta_screen_cast_monitor_stream_get_stage (MetaScreenCastMonitorStream *monitor_stream);
+ 
+diff --git a/src/backends/meta-screen-cast-session.c b/src/backends/meta-screen-cast-session.c
+index 3ba59037f..6a8f2d328 100644
+--- a/src/backends/meta-screen-cast-session.c
++++ b/src/backends/meta-screen-cast-session.c
+@@ -38,6 +38,8 @@ struct _MetaScreenCastSession
+ {
+   MetaDBusScreenCastSessionSkeleton parent;
+ 
++  MetaScreenCast *screen_cast;
++
+   char *peer_name;
+ 
+   MetaScreenCastSessionType session_type;
+@@ -159,6 +161,12 @@ meta_screen_cast_session_get_stream (MetaScreenCastSession *session,
+   return NULL;
+ }
+ 
++MetaScreenCast *
++meta_screen_cast_session_get_screen_cast (MetaScreenCastSession *session)
++{
++  return session->screen_cast;
++}
++
+ char *
+ meta_screen_cast_session_get_object_path (MetaScreenCastSession *session)
+ {
+@@ -300,7 +308,8 @@ handle_record_monitor (MetaDBusScreenCastSession *skeleton,
+ 
+   stage = CLUTTER_STAGE (meta_backend_get_stage (backend));
+ 
+-  monitor_stream = meta_screen_cast_monitor_stream_new (connection,
++  monitor_stream = meta_screen_cast_monitor_stream_new (session,
++                                                        connection,
+                                                         monitor,
+                                                         stage,
+                                                         &error);
+@@ -381,7 +390,8 @@ handle_record_window (MetaDBusScreenCastSession *skeleton,
+   interface_skeleton = G_DBUS_INTERFACE_SKELETON (skeleton);
+   connection = g_dbus_interface_skeleton_get_connection (interface_skeleton);
+ 
+-  window_stream = meta_screen_cast_window_stream_new (connection,
++  window_stream = meta_screen_cast_window_stream_new (session,
++                                                      connection,
+                                                       window,
+                                                       &error);
+   if (!window_stream)
+@@ -441,6 +451,7 @@ meta_screen_cast_session_new (MetaScreenCast             *screen_cast,
+   static unsigned int global_session_number = 0;
+ 
+   session = g_object_new (META_TYPE_SCREEN_CAST_SESSION, NULL);
++  session->screen_cast = screen_cast;
+   session->session_type = session_type;
+   session->peer_name = g_strdup (peer_name);
+   session->object_path =
+diff --git a/src/backends/meta-screen-cast-session.h b/src/backends/meta-screen-cast-session.h
+index ac0a31a16..105a65098 100644
+--- a/src/backends/meta-screen-cast-session.h
++++ b/src/backends/meta-screen-cast-session.h
+@@ -60,4 +60,6 @@ void meta_screen_cast_session_close (MetaScreenCastSession *session);
+ MetaScreenCastStream * meta_screen_cast_session_get_stream (MetaScreenCastSession *session,
+                                                             const char            *path);
+ 
++MetaScreenCast * meta_screen_cast_session_get_screen_cast (MetaScreenCastSession *session);
++
+ #endif /* META_SCREEN_CAST_SESSION_H */
+diff --git a/src/backends/meta-screen-cast-stream.c b/src/backends/meta-screen-cast-stream.c
+index 97ee4bfcf..875ada01a 100644
+--- a/src/backends/meta-screen-cast-stream.c
++++ b/src/backends/meta-screen-cast-stream.c
+@@ -24,12 +24,15 @@
+ 
+ #include "backends/meta-screen-cast-stream.h"
+ 
++#include "backends/meta-screen-cast-session.h"
++
+ #define META_SCREEN_CAST_STREAM_DBUS_PATH "/org/gnome/Mutter/ScreenCast/Stream"
+ 
+ enum
+ {
+   PROP_0,
+ 
++  PROP_SESSION,
+   PROP_CONNECTION,
+ };
+ 
+@@ -44,6 +47,8 @@ static guint signals[N_SIGNALS];
+ 
+ typedef struct _MetaScreenCastStreamPrivate
+ {
++  MetaScreenCastSession *session;
++
+   GDBusConnection *connection;
+   char *object_path;
+ 
+@@ -97,6 +102,15 @@ on_stream_src_ready (MetaScreenCastStreamSrc *src,
+   meta_dbus_screen_cast_stream_emit_pipewire_stream_added (skeleton, node_id);
+ }
+ 
++MetaScreenCastSession *
++meta_screen_cast_stream_get_session (MetaScreenCastStream *stream)
++{
++  MetaScreenCastStreamPrivate *priv =
++    meta_screen_cast_stream_get_instance_private (stream);
++
++  return priv->session;
++}
++
+ gboolean
+ meta_screen_cast_stream_start (MetaScreenCastStream  *stream,
+                                GError               **error)
+@@ -162,6 +176,9 @@ meta_screen_cast_stream_set_property (GObject      *object,
+ 
+   switch (prop_id)
+     {
++    case PROP_SESSION:
++      priv->session = g_value_get_object (value);
++      break;
+     case PROP_CONNECTION:
+       priv->connection = g_value_get_object (value);
+       break;
+@@ -182,6 +199,9 @@ meta_screen_cast_stream_get_property (GObject    *object,
+ 
+   switch (prop_id)
+     {
++    case PROP_SESSION:
++      g_value_set_object (value, priv->session);
++      break;
+     case PROP_CONNECTION:
+       g_value_set_object (value, priv->connection);
+       break;
+@@ -256,6 +276,16 @@ meta_screen_cast_stream_class_init (MetaScreenCastStreamClass *klass)
+   object_class->set_property = meta_screen_cast_stream_set_property;
+   object_class->get_property = meta_screen_cast_stream_get_property;
+ 
++  g_object_class_install_property (object_class,
++                                   PROP_SESSION,
++                                   g_param_spec_object ("session",
++                                                        "session",
++                                                        "MetaScreenSession",
++                                                        META_TYPE_SCREEN_CAST_SESSION,
++                                                        G_PARAM_READWRITE |
++                                                        G_PARAM_CONSTRUCT_ONLY |
++                                                        G_PARAM_STATIC_STRINGS));
++
+   g_object_class_install_property (object_class,
+                                    PROP_CONNECTION,
+                                    g_param_spec_object ("connection",
+diff --git a/src/backends/meta-screen-cast-stream.h b/src/backends/meta-screen-cast-stream.h
+index fd7930c4c..dcc280da6 100644
+--- a/src/backends/meta-screen-cast-stream.h
++++ b/src/backends/meta-screen-cast-stream.h
+@@ -26,8 +26,12 @@
+ #include <glib-object.h>
+ 
+ #include "backends/meta-screen-cast-stream-src.h"
++#include "backends/meta-screen-cast.h"
++
+ #include "meta-dbus-screen-cast.h"
+ 
++typedef struct _MetaScreenCastSession MetaScreenCastSession;
++
+ #define META_TYPE_SCREEN_CAST_STREAM (meta_screen_cast_stream_get_type ())
+ G_DECLARE_DERIVABLE_TYPE (MetaScreenCastStream, meta_screen_cast_stream,
+                           META, SCREEN_CAST_STREAM,
+@@ -48,6 +52,8 @@ struct _MetaScreenCastStreamClass
+                                double               *y);
+ };
+ 
++MetaScreenCastSession * meta_screen_cast_stream_get_session (MetaScreenCastStream *stream);
++
+ gboolean meta_screen_cast_stream_start (MetaScreenCastStream *stream,
+                                         GError              **error);
+ 
+diff --git a/src/backends/meta-screen-cast-window-stream.c b/src/backends/meta-screen-cast-window-stream.c
+index 1200a39ef..4c9227116 100644
+--- a/src/backends/meta-screen-cast-window-stream.c
++++ b/src/backends/meta-screen-cast-window-stream.c
+@@ -71,9 +71,10 @@ meta_screen_cast_window_stream_get_height (MetaScreenCastWindowStream *window_st
+ }
+ 
+ MetaScreenCastWindowStream *
+-meta_screen_cast_window_stream_new (GDBusConnection  *connection,
+-                                    MetaWindow       *window,
+-                                    GError          **error)
++meta_screen_cast_window_stream_new (MetaScreenCastSession  *session,
++                                    GDBusConnection        *connection,
++                                    MetaWindow             *window,
++                                    GError                **error)
+ {
+   MetaScreenCastWindowStream *window_stream;
+   MetaLogicalMonitor *logical_monitor;
+@@ -90,6 +91,7 @@ meta_screen_cast_window_stream_new (GDBusConnection  *connection,
+   window_stream = g_initable_new (META_TYPE_SCREEN_CAST_WINDOW_STREAM,
+                                   NULL,
+                                   error,
++                                  "session", session,
+                                   "connection", connection,
+                                   "window", window,
+                                   NULL);
+diff --git a/src/backends/meta-screen-cast-window-stream.h b/src/backends/meta-screen-cast-window-stream.h
+index 6726ef873..3799be98a 100644
+--- a/src/backends/meta-screen-cast-window-stream.h
++++ b/src/backends/meta-screen-cast-window-stream.h
+@@ -32,9 +32,10 @@ G_DECLARE_FINAL_TYPE (MetaScreenCastWindowStream,
+                       META, SCREEN_CAST_WINDOW_STREAM,
+                       MetaScreenCastStream)
+ 
+-MetaScreenCastWindowStream * meta_screen_cast_window_stream_new (GDBusConnection  *connection,
+-                                                                 MetaWindow       *window,
+-                                                                 GError          **error);
++MetaScreenCastWindowStream * meta_screen_cast_window_stream_new (MetaScreenCastSession  *session,
++                                                                 GDBusConnection        *connection,
++                                                                 MetaWindow             *window,
++                                                                 GError                **error);
+ 
+ MetaWindow  * meta_screen_cast_window_stream_get_window (MetaScreenCastWindowStream *window_stream);
+ int           meta_screen_cast_window_stream_get_width  (MetaScreenCastWindowStream *window_stream);
+diff --git a/src/backends/meta-screen-cast.c b/src/backends/meta-screen-cast.c
+index 4a67683e7..ceea9cbe1 100644
+--- a/src/backends/meta-screen-cast.c
++++ b/src/backends/meta-screen-cast.c
+@@ -43,6 +43,7 @@ struct _MetaScreenCast
+   GList *sessions;
+ 
+   MetaDbusSessionWatcher *session_watcher;
++  MetaBackend *backend;
+ };
+ 
+ static void
+@@ -62,12 +63,20 @@ meta_screen_cast_get_connection (MetaScreenCast *screen_cast)
+   return g_dbus_interface_skeleton_get_connection (interface_skeleton);
+ }
+ 
++MetaBackend *
++meta_screen_cast_get_backend (MetaScreenCast *screen_cast)
++{
++  return screen_cast->backend;
++}
++
+ static gboolean
+ register_remote_desktop_screen_cast_session (MetaScreenCastSession  *session,
+                                              const char             *remote_desktop_session_id,
+                                              GError                **error)
+ {
+-  MetaBackend *backend = meta_get_backend ();
++  MetaScreenCast *screen_cast =
++    meta_screen_cast_session_get_screen_cast (session);
++  MetaBackend *backend = meta_screen_cast_get_backend (screen_cast);
+   MetaRemoteDesktop *remote_desktop = meta_backend_get_remote_desktop (backend);
+   MetaRemoteDesktopSession *remote_desktop_session;
+ 
+@@ -244,11 +253,13 @@ meta_screen_cast_finalize (GObject *object)
+ }
+ 
+ MetaScreenCast *
+-meta_screen_cast_new (MetaDbusSessionWatcher *session_watcher)
++meta_screen_cast_new (MetaBackend            *backend,
++                      MetaDbusSessionWatcher *session_watcher)
+ {
+   MetaScreenCast *screen_cast;
+ 
+   screen_cast = g_object_new (META_TYPE_SCREEN_CAST, NULL);
++  screen_cast->backend = backend;
+   screen_cast->session_watcher = session_watcher;
+ 
+   return screen_cast;
+diff --git a/src/backends/meta-screen-cast.h b/src/backends/meta-screen-cast.h
+index a4fb8dd45..7e32b67b7 100644
+--- a/src/backends/meta-screen-cast.h
++++ b/src/backends/meta-screen-cast.h
+@@ -25,6 +25,7 @@
+ 
+ #include <glib-object.h>
+ 
++#include "backends/meta-backend-private.h"
+ #include "backends/meta-dbus-session-watcher.h"
+ #include "meta-dbus-screen-cast.h"
+ 
+@@ -35,6 +36,9 @@ G_DECLARE_FINAL_TYPE (MetaScreenCast, meta_screen_cast,
+ 
+ GDBusConnection * meta_screen_cast_get_connection (MetaScreenCast *screen_cast);
+ 
+-MetaScreenCast * meta_screen_cast_new (MetaDbusSessionWatcher *session_watcher);
++MetaBackend * meta_screen_cast_get_backend (MetaScreenCast *screen_cast);
++
++MetaScreenCast * meta_screen_cast_new (MetaBackend            *backend,
++                                       MetaDbusSessionWatcher *session_watcher);
+ 
+ #endif /* META_SCREEN_CAST_H */
+-- 
+2.20.1
+
+
+From 0bda3f694c9ea14239dd002856302e7d3007ae1c Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Wed, 12 Dec 2018 11:37:13 +0100
+Subject: [PATCH 05/12] renderer: Add API to get view from logical monitor
+
+Will be used to get the view scale for a logical monitor, which is
+necessary for passing cursor sprites via PipeWire.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/357
+---
+ src/backends/meta-renderer.c | 18 ++++++++++++++++++
+ src/backends/meta-renderer.h |  3 +++
+ 2 files changed, 21 insertions(+)
+
+diff --git a/src/backends/meta-renderer.c b/src/backends/meta-renderer.c
+index ceac7df57..ea93dc99e 100644
+--- a/src/backends/meta-renderer.c
++++ b/src/backends/meta-renderer.c
+@@ -94,6 +94,24 @@ meta_renderer_get_views (MetaRenderer *renderer)
+   return priv->views;
+ }
+ 
++MetaRendererView *
++meta_renderer_get_view_from_logical_monitor (MetaRenderer       *renderer,
++                                             MetaLogicalMonitor *logical_monitor)
++{
++  GList *l;
++
++  for (l = meta_renderer_get_views (renderer); l; l = l->next)
++    {
++      MetaRendererView *view = l->data;
++
++      if (meta_renderer_view_get_logical_monitor (view) ==
++          logical_monitor)
++        return view;
++    }
++
++  return NULL;
++}
++
+ static void
+ meta_renderer_finalize (GObject *object)
+ {
+diff --git a/src/backends/meta-renderer.h b/src/backends/meta-renderer.h
+index bf51b51ab..1c617214b 100644
+--- a/src/backends/meta-renderer.h
++++ b/src/backends/meta-renderer.h
+@@ -53,4 +53,7 @@ void meta_renderer_set_legacy_view (MetaRenderer     *renderer,
+ 
+ GList * meta_renderer_get_views (MetaRenderer *renderer);
+ 
++MetaRendererView * meta_renderer_get_view_from_logical_monitor (MetaRenderer       *renderer,
++                                                                MetaLogicalMonitor *logical_monitor);
++
+ #endif /* META_RENDERER_H */
+-- 
+2.20.1
+
+
+From 1a928d060cccd344004a06dacc008ae0654e8f3e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Wed, 12 Dec 2018 11:39:00 +0100
+Subject: [PATCH 06/12] backends/stage: Fix minor style issue
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/357
+---
+ src/backends/meta-stage.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/backends/meta-stage.c b/src/backends/meta-stage.c
+index 73ca70118..d1b7185e8 100644
+--- a/src/backends/meta-stage.c
++++ b/src/backends/meta-stage.c
+@@ -30,7 +30,8 @@
+ #include "backends/meta-backend-private.h"
+ #include "clutter/clutter-mutter.h"
+ 
+-struct _MetaOverlay {
++struct _MetaOverlay
++{
+   gboolean enabled;
+ 
+   CoglPipeline *pipeline;
+-- 
+2.20.1
+
+
+From 44655d12468c26bf6949e4b5137d324f9ec3b651 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Wed, 12 Dec 2018 11:39:18 +0100
+Subject: [PATCH 07/12] backends/stage: Emit signal between painting actors and
+ overlays
+
+Will be used by screen casting for embedding the cursor separately, or
+not including at all.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/357
+---
+ src/backends/meta-stage.c | 18 ++++++++++++++++++
+ 1 file changed, 18 insertions(+)
+
+diff --git a/src/backends/meta-stage.c b/src/backends/meta-stage.c
+index d1b7185e8..4ed56c04b 100644
+--- a/src/backends/meta-stage.c
++++ b/src/backends/meta-stage.c
+@@ -30,6 +30,15 @@
+ #include "backends/meta-backend-private.h"
+ #include "clutter/clutter-mutter.h"
+ 
++enum
++{
++  ACTORS_PAINTED,
++
++  N_SIGNALS
++};
++
++static guint signals[N_SIGNALS];
++
+ struct _MetaOverlay
+ {
+   gboolean enabled;
+@@ -141,6 +150,8 @@ meta_stage_paint (ClutterActor *actor)
+ 
+   CLUTTER_ACTOR_CLASS (meta_stage_parent_class)->paint (actor);
+ 
++  g_signal_emit (stage, signals[ACTORS_PAINTED], 0);
++
+   for (l = priv->overlays; l; l = l->next)
+     meta_overlay_paint (l->data);
+ }
+@@ -180,6 +191,13 @@ meta_stage_class_init (MetaStageClass *klass)
+ 
+   stage_class->activate = meta_stage_activate;
+   stage_class->deactivate = meta_stage_deactivate;
++
++  signals[ACTORS_PAINTED] = g_signal_new ("actors-painted",
++                                          G_TYPE_FROM_CLASS (klass),
++                                          G_SIGNAL_RUN_LAST,
++                                          0,
++                                          NULL, NULL, NULL,
++                                          G_TYPE_NONE, 0);
+ }
+ 
+ static void
+-- 
+2.20.1
+
+
+From 6afa7372f3c8104fb5670130b1cd54980e9b6446 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Wed, 12 Dec 2018 15:29:21 +0100
+Subject: [PATCH 08/12] screen-cast/monitor-stream-src: Copy content before
+ cursor is drawn
+
+To get a consistent behaviour no matter whether HW cursors are in use or
+not, make sure to copy the framebuffer content before the stage overlays
+(cursor sprite textures) are painted.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/357
+---
+ src/backends/meta-screen-cast-monitor-stream-src.c | 10 +++++-----
+ 1 file changed, 5 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 382d7d4a2..b240744a8 100644
+--- a/src/backends/meta-screen-cast-monitor-stream-src.c
++++ b/src/backends/meta-screen-cast-monitor-stream-src.c
+@@ -35,7 +35,7 @@ struct _MetaScreenCastMonitorStreamSrc
+ {
+   MetaScreenCastStreamSrc parent;
+ 
+-  gulong stage_painted_handler_id;
++  gulong actors_painted_handler_id;
+ };
+ 
+ G_DEFINE_TYPE (MetaScreenCastMonitorStreamSrc,
+@@ -110,8 +110,8 @@ meta_screen_cast_monitor_stream_src_enable (MetaScreenCastStreamSrc *src)
+   ClutterStage *stage;
+ 
+   stage = get_stage (monitor_src);
+-  monitor_src->stage_painted_handler_id =
+-    g_signal_connect_after (stage, "paint",
++  monitor_src->actors_painted_handler_id =
++    g_signal_connect_after (stage, "actors-painted",
+                             G_CALLBACK (stage_painted),
+                             monitor_src);
+   clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+@@ -125,8 +125,8 @@ meta_screen_cast_monitor_stream_src_disable (MetaScreenCastStreamSrc *src)
+   ClutterStage *stage;
+ 
+   stage = get_stage (monitor_src);
+-  g_signal_handler_disconnect (stage, monitor_src->stage_painted_handler_id);
+-  monitor_src->stage_painted_handler_id = 0;
++  g_signal_handler_disconnect (stage, monitor_src->actors_painted_handler_id);
++  monitor_src->actors_painted_handler_id = 0;
+ }
+ 
+ static void
+-- 
+2.20.1
+
+
+From 2e03a842ce2447f9d070a410d1a80c2e77b480a5 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Thu, 13 Sep 2018 11:28:51 +0200
+Subject: [PATCH 09/12] cursor-renderer: Add API to allow inhibiting HW cursor
+
+There may be reasons to temporarly inhibit the HW cursor under certain
+circumstances. Allow adding such inhibitations by adding API to the
+cursor renderer to allow API users to add generic inhibitors with
+whatever logic is deemed necessary.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/357
+---
+ src/backends/meta-cursor-renderer.c           | 62 +++++++++++++++++++
+ src/backends/meta-cursor-renderer.h           | 21 +++++++
+ .../native/meta-cursor-renderer-native.c      |  4 ++
+ 3 files changed, 87 insertions(+)
+
+diff --git a/src/backends/meta-cursor-renderer.c b/src/backends/meta-cursor-renderer.c
+index eb79737f1..0a456bee6 100644
+--- a/src/backends/meta-cursor-renderer.c
++++ b/src/backends/meta-cursor-renderer.c
+@@ -35,6 +35,9 @@
+ 
+ #include "meta-stage-private.h"
+ 
++G_DEFINE_INTERFACE (MetaHwCursorInhibitor, meta_hw_cursor_inhibitor,
++                    G_TYPE_OBJECT)
++
+ struct _MetaCursorRendererPrivate
+ {
+   float current_x;
+@@ -44,6 +47,8 @@ struct _MetaCursorRendererPrivate
+   MetaOverlay *stage_overlay;
+   gboolean handled_by_backend;
+   guint post_paint_func_id;
++
++  GList *hw_cursor_inhibitors;
+ };
+ typedef struct _MetaCursorRendererPrivate MetaCursorRendererPrivate;
+ 
+@@ -55,6 +60,21 @@ static guint signals[LAST_SIGNAL];
+ 
+ G_DEFINE_TYPE_WITH_PRIVATE (MetaCursorRenderer, meta_cursor_renderer, G_TYPE_OBJECT);
+ 
++static gboolean
++meta_hw_cursor_inhibitor_is_cursor_sprite_inhibited (MetaHwCursorInhibitor *inhibitor,
++                                                     MetaCursorSprite      *cursor_sprite)
++{
++  MetaHwCursorInhibitorInterface *iface =
++    META_HW_CURSOR_INHIBITOR_GET_IFACE (inhibitor);
++
++  return iface->is_cursor_sprite_inhibited (inhibitor, cursor_sprite);
++}
++
++static void
++meta_hw_cursor_inhibitor_default_init (MetaHwCursorInhibitorInterface *iface)
++{
++}
++
+ void
+ meta_cursor_renderer_emit_painted (MetaCursorRenderer *renderer,
+                                    MetaCursorSprite   *cursor_sprite)
+@@ -283,3 +303,45 @@ meta_cursor_renderer_get_cursor (MetaCursorRenderer *renderer)
+ 
+   return priv->displayed_cursor;
+ }
++
++void
++meta_cursor_renderer_add_hw_cursor_inhibitor (MetaCursorRenderer    *renderer,
++                                              MetaHwCursorInhibitor *inhibitor)
++{
++  MetaCursorRendererPrivate *priv =
++    meta_cursor_renderer_get_instance_private (renderer);
++
++  priv->hw_cursor_inhibitors = g_list_prepend (priv->hw_cursor_inhibitors,
++                                               inhibitor);
++}
++
++void
++meta_cursor_renderer_remove_hw_cursor_inhibitor (MetaCursorRenderer    *renderer,
++                                                 MetaHwCursorInhibitor *inhibitor)
++{
++  MetaCursorRendererPrivate *priv =
++    meta_cursor_renderer_get_instance_private (renderer);
++
++  priv->hw_cursor_inhibitors = g_list_remove (priv->hw_cursor_inhibitors,
++                                              inhibitor);
++}
++
++gboolean
++meta_cursor_renderer_is_hw_cursors_inhibited (MetaCursorRenderer *renderer,
++                                              MetaCursorSprite   *cursor_sprite)
++{
++  MetaCursorRendererPrivate *priv =
++    meta_cursor_renderer_get_instance_private (renderer);
++  GList *l;
++
++  for (l = priv->hw_cursor_inhibitors; l; l = l->next)
++    {
++      MetaHwCursorInhibitor *inhibitor = l->data;
++
++      if (meta_hw_cursor_inhibitor_is_cursor_sprite_inhibited (inhibitor,
++                                                               cursor_sprite))
++        return TRUE;
++    }
++
++  return FALSE;
++}
+diff --git a/src/backends/meta-cursor-renderer.h b/src/backends/meta-cursor-renderer.h
+index 830d16ef6..092f17f1e 100644
+--- a/src/backends/meta-cursor-renderer.h
++++ b/src/backends/meta-cursor-renderer.h
+@@ -30,6 +30,18 @@
+ #include <meta/screen.h>
+ #include "meta-cursor.h"
+ 
++#define META_TYPE_HW_CURSOR_INHIBITOR (meta_hw_cursor_inhibitor_get_type ())
++G_DECLARE_INTERFACE (MetaHwCursorInhibitor, meta_hw_cursor_inhibitor,
++                     META, HW_CURSOR_INHIBITOR, GObject)
++
++struct _MetaHwCursorInhibitorInterface
++{
++  GTypeInterface parent_iface;
++
++  gboolean (* is_cursor_sprite_inhibited) (MetaHwCursorInhibitor *inhibitor,
++                                           MetaCursorSprite      *cursor_sprite);
++};
++
+ #define META_TYPE_CURSOR_RENDERER (meta_cursor_renderer_get_type ())
+ G_DECLARE_DERIVABLE_TYPE (MetaCursorRenderer, meta_cursor_renderer,
+                           META, CURSOR_RENDERER, GObject);
+@@ -55,6 +67,15 @@ void meta_cursor_renderer_force_update (MetaCursorRenderer *renderer);
+ 
+ MetaCursorSprite * meta_cursor_renderer_get_cursor (MetaCursorRenderer *renderer);
+ 
++void meta_cursor_renderer_add_hw_cursor_inhibitor (MetaCursorRenderer    *renderer,
++                                                   MetaHwCursorInhibitor *inhibitor);
++
++void meta_cursor_renderer_remove_hw_cursor_inhibitor (MetaCursorRenderer    *renderer,
++                                                      MetaHwCursorInhibitor *inhibitor);
++
++gboolean meta_cursor_renderer_is_hw_cursors_inhibited (MetaCursorRenderer *renderer,
++                                                       MetaCursorSprite   *cursor_sprite);
++
+ ClutterRect meta_cursor_renderer_calculate_rect (MetaCursorRenderer *renderer,
+                                                  MetaCursorSprite   *cursor_sprite);
+ 
+diff --git a/src/backends/native/meta-cursor-renderer-native.c b/src/backends/native/meta-cursor-renderer-native.c
+index 29800953b..3ff4e81fb 100644
+--- a/src/backends/native/meta-cursor-renderer-native.c
++++ b/src/backends/native/meta-cursor-renderer-native.c
+@@ -587,6 +587,10 @@ should_have_hw_cursor (MetaCursorRenderer *renderer,
+   if (!cursor_sprite)
+     return FALSE;
+ 
++  if (meta_cursor_renderer_is_hw_cursors_inhibited (renderer,
++                                                    cursor_sprite))
++    return FALSE;
++
+   for (l = gpus; l; l = l->next)
+     {
+       MetaGpuKms *gpu_kms = l->data;
+-- 
+2.20.1
+
+
+From 65eaf0c6a12ca48fc92fbe4492726cd145549924 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Fri, 14 Dec 2018 17:47:57 +0100
+Subject: [PATCH 10/12] screen-cast: Add 'cursor-mode' to allow decoupled
+ cursor updates
+
+The 'cursor-mode', which currently is limited to RecordMonitor(), allows
+the user to either do screen casts where the cursor is hidden, embedded
+in the framebuffer, or sent as PipeWire stream metadata.
+
+The latter allows the user to get cursor updates sent, including the
+cursor sprite, without requiring a stage paint each frame. Currently
+this is done by using the cursor sprite texture, and either reading
+directly from, or drawing to an offscreen framebuffer which is read from
+instead, in case the texture is scaled.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/357
+---
+ configure.ac                                  |   2 +-
+ .../meta-screen-cast-monitor-stream-src.c     | 416 +++++++++++++++++-
+ .../meta-screen-cast-monitor-stream.c         |  12 +-
+ .../meta-screen-cast-monitor-stream.h         |  11 +-
+ src/backends/meta-screen-cast-session.c       |  31 ++
+ src/backends/meta-screen-cast-stream-src.c    | 128 ++++--
+ src/backends/meta-screen-cast-stream-src.h    |  24 +-
+ src/backends/meta-screen-cast-stream.c        |  30 ++
+ src/backends/meta-screen-cast-stream.h        |   2 +
+ .../meta-screen-cast-window-stream-src.c      |   4 +-
+ src/backends/meta-screen-cast.h               |   7 +
+ src/org.gnome.Mutter.ScreenCast.xml           |  12 +-
+ 12 files changed, 622 insertions(+), 57 deletions(-)
+
+diff --git a/configure.ac b/configure.ac
+index 2aa9c4352..449f3d693 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -245,7 +245,7 @@ AC_ARG_ENABLE(remote-desktop,
+   enable_remote_desktop=no
+ )
+ AS_IF([test "$enable_remote_desktop" = "yes"], [
+-  MUTTER_PC_MODULES="$MUTTER_PC_MODULES libpipewire-0.2 >= 0.2.2"
++  MUTTER_PC_MODULES="$MUTTER_PC_MODULES libpipewire-0.2 >= 0.2.5"
+   AC_DEFINE([HAVE_REMOTE_DESKTOP],[1], [Defined if screen cast and remote desktop support is enabled])
+ ])
+ AM_CONDITIONAL([HAVE_REMOTE_DESKTOP],[test "$enable_remote_desktop" = "yes"])
+diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c b/src/backends/meta-screen-cast-monitor-stream-src.c
+index b240744a8..6be477989 100644
+--- a/src/backends/meta-screen-cast-monitor-stream-src.c
++++ b/src/backends/meta-screen-cast-monitor-stream-src.c
+@@ -24,23 +24,36 @@
+ 
+ #include "backends/meta-screen-cast-monitor-stream-src.h"
+ 
++#include <spa/buffer/meta.h>
++
+ #include "backends/meta-backend-private.h"
++#include "backends/meta-cursor-tracker-private.h"
+ #include "backends/meta-screen-cast-monitor-stream.h"
++#include "backends/meta-screen-cast-session.h"
+ #include "backends/meta-logical-monitor.h"
+ #include "backends/meta-monitor.h"
+ #include "clutter/clutter.h"
+ #include "clutter/clutter-mutter.h"
++#include "core/boxes-private.h"
+ 
+ struct _MetaScreenCastMonitorStreamSrc
+ {
+   MetaScreenCastStreamSrc parent;
+ 
+   gulong actors_painted_handler_id;
++  gulong paint_handler_id;
++  gulong cursor_moved_handler_id;
++  gulong cursor_changed_handler_id;
+ };
+ 
+-G_DEFINE_TYPE (MetaScreenCastMonitorStreamSrc,
+-               meta_screen_cast_monitor_stream_src,
+-               META_TYPE_SCREEN_CAST_STREAM_SRC)
++static void
++hw_cursor_inhibitor_iface_init (MetaHwCursorInhibitorInterface *iface);
++
++G_DEFINE_TYPE_WITH_CODE (MetaScreenCastMonitorStreamSrc,
++                         meta_screen_cast_monitor_stream_src,
++                         META_TYPE_SCREEN_CAST_STREAM_SRC,
++                         G_IMPLEMENT_INTERFACE (META_TYPE_HW_CURSOR_INHIBITOR,
++                                                hw_cursor_inhibitor_iface_init))
+ 
+ static ClutterStage *
+ get_stage (MetaScreenCastMonitorStreamSrc *monitor_src)
+@@ -102,18 +115,163 @@ stage_painted (ClutterActor                   *actor,
+   meta_screen_cast_stream_src_maybe_record_frame (src);
+ }
+ 
++static MetaBackend *
++get_backend (MetaScreenCastMonitorStreamSrc *monitor_src)
++{
++  MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (monitor_src);
++  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);
++
++  return meta_screen_cast_get_backend (screen_cast);
++}
++
++static gboolean
++is_cursor_in_stream (MetaScreenCastMonitorStreamSrc *monitor_src)
++{
++  MetaBackend *backend = get_backend (monitor_src);
++  MetaCursorRenderer *cursor_renderer =
++    meta_backend_get_cursor_renderer (backend);
++  MetaMonitor *monitor;
++  MetaLogicalMonitor *logical_monitor;
++  MetaRectangle logical_monitor_layout;
++  ClutterRect logical_monitor_rect;
++  MetaCursorSprite *cursor_sprite;
++
++  monitor = get_monitor (monitor_src);
++  logical_monitor = meta_monitor_get_logical_monitor (monitor);
++  logical_monitor_layout = meta_logical_monitor_get_layout (logical_monitor);
++  logical_monitor_rect =
++    meta_rectangle_to_clutter_rect (&logical_monitor_layout);
++
++  cursor_sprite = meta_cursor_renderer_get_cursor (cursor_renderer);
++  if (cursor_sprite)
++    {
++      ClutterRect cursor_rect;
++
++      cursor_rect = meta_cursor_renderer_calculate_rect (cursor_renderer,
++                                                         cursor_sprite);
++      return clutter_rect_intersection (&cursor_rect,
++                                        &logical_monitor_rect,
++                                        NULL);
++    }
++  else
++    {
++      ClutterPoint cursor_position;
++
++      cursor_position = meta_cursor_renderer_get_position (cursor_renderer);
++      return clutter_rect_contains_point (&logical_monitor_rect,
++                                          &cursor_position);
++    }
++}
++
++static void
++sync_cursor_state (MetaScreenCastMonitorStreamSrc *monitor_src)
++{
++  MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (monitor_src);
++  ClutterStage *stage = get_stage (monitor_src);
++
++  if (!is_cursor_in_stream (monitor_src))
++    return;
++
++  if (clutter_stage_is_redraw_queued (stage))
++    return;
++
++  meta_screen_cast_stream_src_maybe_record_frame (src);
++}
++
++static void
++cursor_moved (MetaCursorTracker              *cursor_tracker,
++              float                           x,
++              float                           y,
++              MetaScreenCastMonitorStreamSrc *monitor_src)
++{
++  sync_cursor_state (monitor_src);
++}
++
++static void
++cursor_changed (MetaCursorTracker              *cursor_tracker,
++                MetaScreenCastMonitorStreamSrc *monitor_src)
++{
++  sync_cursor_state (monitor_src);
++}
++
++static MetaCursorRenderer *
++get_cursor_renderer (MetaScreenCastMonitorStreamSrc *monitor_src)
++{
++  MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (monitor_src);
++  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);
++  MetaBackend *backend = meta_screen_cast_get_backend (screen_cast);
++
++  return meta_backend_get_cursor_renderer (backend);
++}
++
++static void
++inhibit_hw_cursor (MetaScreenCastMonitorStreamSrc *monitor_src)
++{
++  MetaCursorRenderer *cursor_renderer;
++  MetaHwCursorInhibitor *inhibitor;
++
++  cursor_renderer = get_cursor_renderer (monitor_src);
++  inhibitor = META_HW_CURSOR_INHIBITOR (monitor_src);
++  meta_cursor_renderer_add_hw_cursor_inhibitor (cursor_renderer, inhibitor);
++}
++
++static void
++uninhibit_hw_cursor (MetaScreenCastMonitorStreamSrc *monitor_src)
++{
++  MetaCursorRenderer *cursor_renderer;
++  MetaHwCursorInhibitor *inhibitor;
++
++  cursor_renderer = get_cursor_renderer (monitor_src);
++  inhibitor = META_HW_CURSOR_INHIBITOR (monitor_src);
++  meta_cursor_renderer_remove_hw_cursor_inhibitor (cursor_renderer, inhibitor);
++}
++
+ static void
+ 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);
++  MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend);
+   ClutterStage *stage;
++  MetaScreenCastStream *stream;
+ 
++  stream = meta_screen_cast_stream_src_get_stream (src);
+   stage = get_stage (monitor_src);
+-  monitor_src->actors_painted_handler_id =
+-    g_signal_connect_after (stage, "actors-painted",
+-                            G_CALLBACK (stage_painted),
+-                            monitor_src);
++
++  switch (meta_screen_cast_stream_get_cursor_mode (stream))
++    {
++    case META_SCREEN_CAST_CURSOR_MODE_METADATA:
++      monitor_src->cursor_moved_handler_id =
++        g_signal_connect_after (cursor_tracker, "cursor-moved",
++                                G_CALLBACK (cursor_moved),
++                                monitor_src);
++      monitor_src->cursor_changed_handler_id =
++        g_signal_connect_after (cursor_tracker, "cursor-changed",
++                                G_CALLBACK (cursor_changed),
++                                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);
++      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);
++      break;
++    }
++
+   clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+ }
+ 
+@@ -122,14 +280,43 @@ meta_screen_cast_monitor_stream_src_disable (MetaScreenCastStreamSrc *src)
+ {
+   MetaScreenCastMonitorStreamSrc *monitor_src =
+     META_SCREEN_CAST_MONITOR_STREAM_SRC (src);
++  MetaBackend *backend = get_backend (monitor_src);
++  MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend);
+   ClutterStage *stage;
+ 
+   stage = get_stage (monitor_src);
+-  g_signal_handler_disconnect (stage, monitor_src->actors_painted_handler_id);
+-  monitor_src->actors_painted_handler_id = 0;
++
++  if (monitor_src->actors_painted_handler_id)
++    {
++      g_signal_handler_disconnect (stage,
++                                   monitor_src->actors_painted_handler_id);
++      monitor_src->actors_painted_handler_id = 0;
++    }
++
++  if (monitor_src->paint_handler_id)
++    {
++      g_signal_handler_disconnect (stage,
++                                   monitor_src->paint_handler_id);
++      monitor_src->paint_handler_id = 0;
++      uninhibit_hw_cursor (monitor_src);
++    }
++
++  if (monitor_src->cursor_moved_handler_id)
++    {
++      g_signal_handler_disconnect (cursor_tracker,
++                                   monitor_src->cursor_moved_handler_id);
++      monitor_src->cursor_moved_handler_id = 0;
++    }
++
++  if (monitor_src->cursor_changed_handler_id)
++    {
++      g_signal_handler_disconnect (cursor_tracker,
++                                   monitor_src->cursor_changed_handler_id);
++      monitor_src->cursor_changed_handler_id = 0;
++    }
+ }
+ 
+-static void
++static gboolean
+ meta_screen_cast_monitor_stream_src_record_frame (MetaScreenCastStreamSrc *src,
+                                                   uint8_t                 *data)
+ {
+@@ -140,9 +327,216 @@ meta_screen_cast_monitor_stream_src_record_frame (MetaScreenCastStreamSrc *src,
+   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);
++
++  return TRUE;
++}
++
++static gboolean
++draw_cursor_sprite_via_offscreen (MetaScreenCastMonitorStreamSrc  *monitor_src,
++                                  CoglTexture                     *cursor_texture,
++                                  int                              bitmap_width,
++                                  int                              bitmap_height,
++                                  uint32_t                        *bitmap_data,
++                                  GError                         **error)
++{
++  MetaBackend *backend = get_backend (monitor_src);
++  ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
++  CoglContext *cogl_context =
++    clutter_backend_get_cogl_context (clutter_backend);
++  CoglTexture2D *bitmap_texture;
++  CoglOffscreen *offscreen;
++  CoglFramebuffer *fb;
++  CoglPipeline *pipeline;
++  CoglColor clear_color;
++
++  bitmap_texture = cogl_texture_2d_new_with_size (cogl_context,
++                                                  bitmap_width, bitmap_height);
++  cogl_primitive_texture_set_auto_mipmap (COGL_PRIMITIVE_TEXTURE (bitmap_texture),
++                                          FALSE);
++  if (!cogl_texture_allocate (COGL_TEXTURE (bitmap_texture), error))
++    {
++      cogl_object_unref (bitmap_texture);
++      return FALSE;
++    }
++
++  offscreen = cogl_offscreen_new_with_texture (COGL_TEXTURE (bitmap_texture));
++  fb = COGL_FRAMEBUFFER (offscreen);
++  cogl_object_unref (bitmap_texture);
++  if (!cogl_framebuffer_allocate (fb, error))
++    {
++      cogl_object_unref (fb);
++      return FALSE;
++    }
++
++  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_color_init_from_4ub (&clear_color, 0, 0, 0, 0);
++  cogl_framebuffer_clear (fb, COGL_BUFFER_BIT_COLOR, &clear_color);
++  cogl_framebuffer_draw_rectangle (fb, pipeline,
++                                   -1, 1, 1, -1);
++  cogl_object_unref (pipeline);
++
++  cogl_framebuffer_read_pixels (fb,
++                                0, 0,
++                                bitmap_width, bitmap_height,
++                                COGL_PIXEL_FORMAT_RGBA_8888_PRE,
++                                (uint8_t *) bitmap_data);
++  cogl_object_unref (fb);
++
++  return TRUE;
++}
++
++static void
++meta_screen_cast_monitor_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src,
++                                                         struct spa_meta_cursor  *spa_meta_cursor)
++{
++  MetaScreenCastMonitorStreamSrc *monitor_src =
++    META_SCREEN_CAST_MONITOR_STREAM_SRC (src);
++  MetaBackend *backend = get_backend (monitor_src);
++  MetaCursorRenderer *cursor_renderer =
++    meta_backend_get_cursor_renderer (backend);
++  MetaRenderer *renderer = meta_backend_get_renderer (backend);
++  MetaSpaType *spa_type = meta_screen_cast_stream_src_get_spa_type (src);
++  GError *error = NULL;
++  MetaCursorSprite *cursor_sprite;
++  CoglTexture *cursor_texture;
++  MetaMonitor *monitor;
++  MetaLogicalMonitor *logical_monitor;
++  MetaRectangle logical_monitor_layout;
++  ClutterRect logical_monitor_rect;
++  MetaRendererView *view;
++  float view_scale;
++  ClutterPoint cursor_position;
++  struct spa_meta_bitmap *spa_meta_bitmap;
++
++  cursor_sprite = meta_cursor_renderer_get_cursor (cursor_renderer);
++  if (cursor_sprite)
++    cursor_texture = meta_cursor_sprite_get_cogl_texture (cursor_sprite);
++  else
++    cursor_texture = NULL;
++
++  if (!is_cursor_in_stream (monitor_src))
++    {
++      spa_meta_cursor->id = 0;
++      return;
++    }
++
++  monitor = get_monitor (monitor_src);
++  logical_monitor = meta_monitor_get_logical_monitor (monitor);
++  logical_monitor_layout = meta_logical_monitor_get_layout (logical_monitor);
++  logical_monitor_rect =
++    meta_rectangle_to_clutter_rect (&logical_monitor_layout);
++
++  view = meta_renderer_get_view_from_logical_monitor (renderer,
++                                                      logical_monitor);
++  if (view)
++    view_scale = clutter_stage_view_get_scale (CLUTTER_STAGE_VIEW (view));
++  else
++    view_scale = 1.0;
++
++  cursor_position = meta_cursor_renderer_get_position (cursor_renderer);
++  cursor_position.x -= logical_monitor_rect.origin.x;
++  cursor_position.y -= logical_monitor_rect.origin.y;
++  cursor_position.x *= view_scale;
++  cursor_position.y *= view_scale;
++
++  spa_meta_cursor->id = 1;
++  spa_meta_cursor->position.x = (int32_t) roundf (cursor_position.x);
++  spa_meta_cursor->position.y = (int32_t) roundf (cursor_position.y);
++  spa_meta_cursor->bitmap_offset = sizeof (struct spa_meta_cursor);
++
++  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->offset = sizeof (struct spa_meta_bitmap);
++
++  if (cursor_texture)
++    {
++      float cursor_scale;
++      float bitmap_scale;
++      int hotspot_x, hotspot_y;
++      int texture_width, texture_height;
++      int bitmap_width, bitmap_height;
++      uint32_t *bitmap_data;
++
++      cursor_scale = meta_cursor_sprite_get_texture_scale (cursor_sprite);
++      bitmap_scale = view_scale * cursor_scale;
++
++      meta_cursor_sprite_get_hotspot (cursor_sprite, &hotspot_x, &hotspot_y);
++      spa_meta_cursor->hotspot.x = (int32_t) roundf (hotspot_x * bitmap_scale);
++      spa_meta_cursor->hotspot.y = (int32_t) roundf (hotspot_y * bitmap_scale);
++
++      texture_width = cogl_texture_get_width (cursor_texture);
++      texture_height = cogl_texture_get_height (cursor_texture);
++      bitmap_width = texture_width * bitmap_scale;
++      bitmap_height = texture_height * bitmap_scale;
++
++      spa_meta_bitmap->size.width = bitmap_width;
++      spa_meta_bitmap->size.height = bitmap_height;
++      spa_meta_bitmap->stride = bitmap_width * 4;
++
++      bitmap_data = SPA_MEMBER (spa_meta_bitmap,
++                                spa_meta_bitmap->offset,
++                                uint32_t);
++
++      if (texture_width == bitmap_width &&
++          texture_height == bitmap_height)
++        {
++          cogl_texture_get_data (cursor_texture,
++                                 COGL_PIXEL_FORMAT_RGBA_8888_PRE,
++                                 texture_width * 4,
++                                 (uint8_t *) bitmap_data);
++        }
++      else
++        {
++          if (!draw_cursor_sprite_via_offscreen (monitor_src,
++                                                 cursor_texture,
++                                                 bitmap_width,
++                                                 bitmap_height,
++                                                 bitmap_data,
++                                                 &error))
++            {
++              g_warning ("Failed to draw cursor via offscreen: %s",
++                         error->message);
++              g_error_free (error);
++              spa_meta_cursor->id = 0;
++            }
++        }
++    }
++  else
++    {
++      spa_meta_cursor->hotspot.x = 0;
++      spa_meta_cursor->hotspot.y = 0;
++
++      *spa_meta_bitmap = (struct spa_meta_bitmap) { 0 };
++    }
++}
++
++static gboolean
++meta_screen_cast_monitor_stream_src_is_cursor_sprite_inhibited (MetaHwCursorInhibitor *inhibitor,
++                                                                MetaCursorSprite      *cursor_sprite)
++{
++  MetaScreenCastMonitorStreamSrc *monitor_src =
++    META_SCREEN_CAST_MONITOR_STREAM_SRC (inhibitor);
++
++  return is_cursor_in_stream (monitor_src);
++}
++
++static void
++hw_cursor_inhibitor_iface_init (MetaHwCursorInhibitorInterface *iface)
++{
++  iface->is_cursor_sprite_inhibited =
++    meta_screen_cast_monitor_stream_src_is_cursor_sprite_inhibited;
+ }
+ 
+ MetaScreenCastMonitorStreamSrc *
+@@ -169,4 +563,6 @@ 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->set_cursor_metadata =
++    meta_screen_cast_monitor_stream_src_set_cursor_metadata;
+ }
+diff --git a/src/backends/meta-screen-cast-monitor-stream.c b/src/backends/meta-screen-cast-monitor-stream.c
+index a6bed1b52..33b9f026a 100644
+--- a/src/backends/meta-screen-cast-monitor-stream.c
++++ b/src/backends/meta-screen-cast-monitor-stream.c
+@@ -105,11 +105,12 @@ meta_screen_cast_monitor_stream_get_monitor (MetaScreenCastMonitorStream *monito
+ }
+ 
+ MetaScreenCastMonitorStream *
+-meta_screen_cast_monitor_stream_new (MetaScreenCastSession  *session,
+-                                     GDBusConnection        *connection,
+-                                     MetaMonitor            *monitor,
+-                                     ClutterStage           *stage,
+-                                     GError                **error)
++meta_screen_cast_monitor_stream_new (MetaScreenCastSession     *session,
++                                     GDBusConnection           *connection,
++                                     MetaMonitor               *monitor,
++                                     ClutterStage              *stage,
++                                     MetaScreenCastCursorMode   cursor_mode,
++                                     GError                   **error)
+ {
+   MetaGpu *gpu = meta_monitor_get_gpu (monitor);
+   MetaMonitorManager *monitor_manager = meta_gpu_get_monitor_manager (gpu);
+@@ -126,6 +127,7 @@ meta_screen_cast_monitor_stream_new (MetaScreenCastSession  *session,
+                                    error,
+                                    "session", session,
+                                    "connection", connection,
++                                   "cursor-mode", cursor_mode,
+                                    "monitor", monitor,
+                                    NULL);
+   if (!monitor_stream)
+diff --git a/src/backends/meta-screen-cast-monitor-stream.h b/src/backends/meta-screen-cast-monitor-stream.h
+index 98f160c88..f8dc04181 100644
+--- a/src/backends/meta-screen-cast-monitor-stream.h
++++ b/src/backends/meta-screen-cast-monitor-stream.h
+@@ -35,11 +35,12 @@ G_DECLARE_FINAL_TYPE (MetaScreenCastMonitorStream,
+                       META, SCREEN_CAST_MONITOR_STREAM,
+                       MetaScreenCastStream)
+ 
+-MetaScreenCastMonitorStream * meta_screen_cast_monitor_stream_new (MetaScreenCastSession  *session,
+-                                                                   GDBusConnection        *connection,
+-                                                                   MetaMonitor            *monitor,
+-                                                                   ClutterStage           *stage,
+-                                                                   GError                **error);
++MetaScreenCastMonitorStream * meta_screen_cast_monitor_stream_new (MetaScreenCastSession     *session,
++                                                                   GDBusConnection           *connection,
++                                                                   MetaMonitor               *monitor,
++                                                                   ClutterStage              *stage,
++                                                                   MetaScreenCastCursorMode   cursor_mode,
++                                                                   GError                   **error);
+ 
+ ClutterStage * meta_screen_cast_monitor_stream_get_stage (MetaScreenCastMonitorStream *monitor_stream);
+ 
+diff --git a/src/backends/meta-screen-cast-session.c b/src/backends/meta-screen-cast-session.c
+index 6a8f2d328..45d403dca 100644
+--- a/src/backends/meta-screen-cast-session.c
++++ b/src/backends/meta-screen-cast-session.c
+@@ -262,6 +262,20 @@ on_stream_closed (MetaScreenCastStream  *stream,
+   meta_screen_cast_session_close (session);
+ }
+ 
++static gboolean
++is_valid_cursor_mode (MetaScreenCastCursorMode cursor_mode)
++{
++  switch (cursor_mode)
++    {
++    case META_SCREEN_CAST_CURSOR_MODE_HIDDEN:
++    case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED:
++    case META_SCREEN_CAST_CURSOR_MODE_METADATA:
++      return TRUE;
++    }
++
++  return FALSE;
++}
++
+ static gboolean
+ handle_record_monitor (MetaDBusScreenCastSession *skeleton,
+                        GDBusMethodInvocation     *invocation,
+@@ -275,6 +289,7 @@ handle_record_monitor (MetaDBusScreenCastSession *skeleton,
+   MetaMonitorManager *monitor_manager =
+     meta_backend_get_monitor_manager (backend);
+   MetaMonitor *monitor;
++  MetaScreenCastCursorMode cursor_mode;
+   ClutterStage *stage;
+   GError *error = NULL;
+   MetaScreenCastMonitorStream *monitor_stream;
+@@ -306,12 +321,28 @@ handle_record_monitor (MetaDBusScreenCastSession *skeleton,
+       return TRUE;
+     }
+ 
++  if (!g_variant_lookup (properties_variant, "cursor-mode", "u", &cursor_mode))
++    {
++      cursor_mode = META_SCREEN_CAST_CURSOR_MODE_HIDDEN;
++    }
++  else
++    {
++      if (!is_valid_cursor_mode (cursor_mode))
++        {
++          g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
++                                                 G_DBUS_ERROR_FAILED,
++                                                 "Unknown cursor mode");
++          return TRUE;
++        }
++    }
++
+   stage = CLUTTER_STAGE (meta_backend_get_stage (backend));
+ 
+   monitor_stream = meta_screen_cast_monitor_stream_new (session,
+                                                         connection,
+                                                         monitor,
+                                                         stage,
++                                                        cursor_mode,
+                                                         &error);
+   if (!monitor_stream)
+     {
+diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c
+index 673a4640b..9f97bf36d 100644
+--- a/src/backends/meta-screen-cast-stream-src.c
++++ b/src/backends/meta-screen-cast-stream-src.c
+@@ -40,6 +40,10 @@
+ #define PRIVATE_OWNER_FROM_FIELD(TypeName, field_ptr, field_name) \
+   (TypeName *)((guint8 *)(field_ptr) - G_PRIVATE_OFFSET (TypeName, field_name))
+ 
++#define CURSOR_META_SIZE(width, height) \
++  (sizeof (struct spa_meta_cursor) + \
++   sizeof (struct spa_meta_bitmap) + width * height * 4)
++
+ enum
+ {
+   PROP_0,
+@@ -57,14 +61,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;
+-} MetaSpaType;
+-
+ typedef struct _MetaPipeWireSource
+ {
+   GSource base;
+@@ -133,14 +129,68 @@ meta_screen_cast_stream_src_get_videocrop (MetaScreenCastStreamSrc *src,
+   return FALSE;
+ }
+ 
+-static void
++static gboolean
+ meta_screen_cast_stream_src_record_frame (MetaScreenCastStreamSrc *src,
+                                           uint8_t                 *data)
+ {
+   MetaScreenCastStreamSrcClass *klass =
+     META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src);
+ 
+-  klass->record_frame (src, data);
++  return klass->record_frame (src, data);
++}
++
++static void
++meta_screen_cast_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src,
++                                                 struct spa_meta_cursor  *spa_meta_cursor)
++{
++  MetaScreenCastStreamSrcClass *klass =
++    META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src);
++
++  if (klass->set_cursor_metadata)
++    klass->set_cursor_metadata (src, spa_meta_cursor);
++}
++
++MetaSpaType *
++meta_screen_cast_stream_src_get_spa_type (MetaScreenCastStreamSrc *src)
++{
++  MetaScreenCastStreamSrcPrivate *priv =
++    meta_screen_cast_stream_src_get_instance_private (src);
++
++  return &priv->spa_type;
++}
++
++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);
++  if (spa_meta_cursor)
++    meta_screen_cast_stream_src_set_cursor_metadata (src, spa_meta_cursor);
++}
++
++static void
++maybe_record_cursor (MetaScreenCastStreamSrc *src,
++                     struct spa_buffer       *spa_buffer,
++                     uint8_t                 *data)
++{
++  MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src);
++
++  switch (meta_screen_cast_stream_get_cursor_mode (stream))
++    {
++    case META_SCREEN_CAST_CURSOR_MODE_HIDDEN:
++    case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED:
++      return;
++    case META_SCREEN_CAST_CURSOR_MODE_METADATA:
++      add_cursor_metadata (src, spa_buffer);
++      return;
++    }
++
++  g_assert_not_reached ();
+ }
+ 
+ void
+@@ -151,7 +201,6 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src)
+   MetaRectangle crop_rect;
+   struct pw_buffer *buffer;
+   struct spa_buffer *spa_buffer;
+-  struct spa_meta_video_crop *spa_meta_video_crop;
+   uint8_t *map = NULL;
+   uint8_t *data;
+   uint64_t now_us;
+@@ -199,35 +248,45 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src)
+       return;
+     }
+ 
+-  meta_screen_cast_stream_src_record_frame (src, data);
+-
+-  /* Update VideoCrop if needed */
+-  spa_meta_video_crop = spa_buffer_find_meta (spa_buffer, priv->pipewire_type->meta.VideoCrop);
+-  if (spa_meta_video_crop)
++  if (meta_screen_cast_stream_src_record_frame (src, data))
+     {
+-      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;
+-        }
+-      else
++      struct spa_meta_video_crop *spa_meta_video_crop;
++
++      spa_buffer->datas[0].chunk->size = spa_buffer->datas[0].maxsize;
++
++      /* Update VideoCrop if needed */
++      spa_meta_video_crop =
++        spa_buffer_find_meta (spa_buffer, priv->pipewire_type->meta.VideoCrop);
++      if (spa_meta_video_crop)
+         {
+-          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;
++          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;
++            }
++          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;
++            }
+         }
+     }
++  else
++    {
++      spa_buffer->datas[0].chunk->size = 0;
++    }
++
++  maybe_record_cursor (src, spa_buffer, data);
+ 
+   priv->last_frame_timestamp_us = now_us;
+ 
+   if (map)
+     munmap (map, spa_buffer->datas[0].maxsize + spa_buffer->datas[0].mapoffset);
+ 
+-  spa_buffer->datas[0].chunk->size = spa_buffer->datas[0].maxsize;
+-
+   pw_stream_queue_buffer (priv->pipewire_stream, buffer);
+ }
+ 
+@@ -314,7 +373,7 @@ on_stream_format_changed (void                 *data,
+   uint8_t params_buffer[1024];
+   int32_t width, height, stride, size;
+   struct spa_pod_builder pod_builder;
+-  const struct spa_pod *params[2];
++  const struct spa_pod *params[3];
+   const int bpp = 4;
+ 
+   if (!format)
+@@ -348,6 +407,12 @@ on_stream_format_changed (void                 *data,
+     ":", pipewire_type->param_meta.type, "I", pipewire_type->meta.VideoCrop,
+     ":", pipewire_type->param_meta.size, "i", sizeof (struct spa_meta_video_crop));
+ 
++  params[2] = spa_pod_builder_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));
++
+   pw_stream_finish_format (priv->pipewire_stream, 0,
+                            params, G_N_ELEMENTS (params));
+ }
+@@ -517,6 +582,7 @@ init_spa_type (MetaSpaType         *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 *
+diff --git a/src/backends/meta-screen-cast-stream-src.h b/src/backends/meta-screen-cast-stream-src.h
+index f3b3fd779..f2f96f213 100644
+--- a/src/backends/meta-screen-cast-stream-src.h
++++ b/src/backends/meta-screen-cast-stream-src.h
+@@ -24,10 +24,26 @@
+ #define META_SCREEN_CAST_STREAM_SRC_H
+ 
+ #include <glib-object.h>
++#include <spa/param/video/format-utils.h>
++#include <spa/buffer/meta.h>
+ 
++#include "backends/meta-backend-private.h"
++#include "backends/meta-cursor-renderer.h"
++#include "backends/meta-cursor.h"
++#include "backends/meta-renderer.h"
+ #include "clutter/clutter.h"
++#include "cogl/cogl.h"
+ #include "meta/boxes.h"
+ 
++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 _MetaScreenCastStream MetaScreenCastStream;
+ 
+ #define META_TYPE_SCREEN_CAST_STREAM_SRC (meta_screen_cast_stream_src_get_type ())
+@@ -46,14 +62,18 @@ struct _MetaScreenCastStreamSrcClass
+                       float                   *frame_rate);
+   void (* enable) (MetaScreenCastStreamSrc *src);
+   void (* disable) (MetaScreenCastStreamSrc *src);
+-  void (* record_frame) (MetaScreenCastStreamSrc *src,
+-                         uint8_t                 *data);
++  gboolean (* record_frame) (MetaScreenCastStreamSrc *src,
++                             uint8_t                 *data);
+   gboolean (* get_videocrop) (MetaScreenCastStreamSrc *src,
+                               MetaRectangle           *crop_rect);
++  void (* set_cursor_metadata) (MetaScreenCastStreamSrc *src,
++                                struct spa_meta_cursor  *spa_meta_cursor);
+ };
+ 
+ void meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src);
+ 
+ MetaScreenCastStream * meta_screen_cast_stream_src_get_stream (MetaScreenCastStreamSrc *src);
+ 
++MetaSpaType * meta_screen_cast_stream_src_get_spa_type (MetaScreenCastStreamSrc *src);
++
+ #endif /* META_SCREEN_CAST_STREAM_SRC_H */
+diff --git a/src/backends/meta-screen-cast-stream.c b/src/backends/meta-screen-cast-stream.c
+index 875ada01a..c14f8fd85 100644
+--- a/src/backends/meta-screen-cast-stream.c
++++ b/src/backends/meta-screen-cast-stream.c
+@@ -34,6 +34,7 @@ enum
+ 
+   PROP_SESSION,
+   PROP_CONNECTION,
++  PROP_CURSOR_MODE,
+ };
+ 
+ enum
+@@ -52,6 +53,8 @@ typedef struct _MetaScreenCastStreamPrivate
+   GDBusConnection *connection;
+   char *object_path;
+ 
++  MetaScreenCastCursorMode cursor_mode;
++
+   MetaScreenCastStreamSrc *src;
+ } MetaScreenCastStreamPrivate;
+ 
+@@ -164,6 +167,15 @@ meta_screen_cast_stream_transform_position (MetaScreenCastStream *stream,
+                                                                   y);
+ }
+ 
++MetaScreenCastCursorMode
++meta_screen_cast_stream_get_cursor_mode (MetaScreenCastStream *stream)
++{
++  MetaScreenCastStreamPrivate *priv =
++    meta_screen_cast_stream_get_instance_private (stream);
++
++  return priv->cursor_mode;
++}
++
+ static void
+ meta_screen_cast_stream_set_property (GObject      *object,
+                                       guint         prop_id,
+@@ -182,6 +194,9 @@ meta_screen_cast_stream_set_property (GObject      *object,
+     case PROP_CONNECTION:
+       priv->connection = g_value_get_object (value);
+       break;
++    case PROP_CURSOR_MODE:
++      priv->cursor_mode = g_value_get_uint (value);
++      break;
+     default:
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+     }
+@@ -205,6 +220,9 @@ meta_screen_cast_stream_get_property (GObject    *object,
+     case PROP_CONNECTION:
+       g_value_set_object (value, priv->connection);
+       break;
++    case PROP_CURSOR_MODE:
++      g_value_set_uint (value, priv->cursor_mode);
++      break;
+     default:
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+     }
+@@ -296,6 +314,18 @@ meta_screen_cast_stream_class_init (MetaScreenCastStreamClass *klass)
+                                                         G_PARAM_CONSTRUCT_ONLY |
+                                                         G_PARAM_STATIC_STRINGS));
+ 
++  g_object_class_install_property (object_class,
++                                   PROP_CURSOR_MODE,
++                                   g_param_spec_uint ("cursor-mode",
++                                                      "cursor-mode",
++                                                      "Cursor mode",
++                                                      META_SCREEN_CAST_CURSOR_MODE_HIDDEN,
++                                                      META_SCREEN_CAST_CURSOR_MODE_METADATA,
++                                                      META_SCREEN_CAST_CURSOR_MODE_HIDDEN,
++                                                      G_PARAM_READWRITE |
++                                                      G_PARAM_CONSTRUCT_ONLY |
++                                                      G_PARAM_STATIC_STRINGS));
++
+   signals[CLOSED] = g_signal_new ("closed",
+                                   G_TYPE_FROM_CLASS (klass),
+                                   G_SIGNAL_RUN_LAST,
+diff --git a/src/backends/meta-screen-cast-stream.h b/src/backends/meta-screen-cast-stream.h
+index dcc280da6..28ca6f683 100644
+--- a/src/backends/meta-screen-cast-stream.h
++++ b/src/backends/meta-screen-cast-stream.h
+@@ -67,4 +67,6 @@ void meta_screen_cast_stream_transform_position (MetaScreenCastStream *stream,
+                                                  double               *x,
+                                                  double               *y);
+ 
++MetaScreenCastCursorMode meta_screen_cast_stream_get_cursor_mode (MetaScreenCastStream *stream);
++
+ #endif /* META_SCREEN_CAST_STREAM_H */
+diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c
+index c3f9cf5ca..32df9f127 100644
+--- a/src/backends/meta-screen-cast-window-stream-src.c
++++ b/src/backends/meta-screen-cast-window-stream-src.c
+@@ -207,7 +207,7 @@ meta_screen_cast_window_stream_src_disable (MetaScreenCastStreamSrc *src)
+   meta_screen_cast_window_stream_src_stop (window_src);
+ }
+ 
+-static void
++static gboolean
+ meta_screen_cast_window_stream_src_record_frame (MetaScreenCastStreamSrc *src,
+                                                  uint8_t                 *data)
+ {
+@@ -215,6 +215,8 @@ meta_screen_cast_window_stream_src_record_frame (MetaScreenCastStreamSrc *src,
+     META_SCREEN_CAST_WINDOW_STREAM_SRC (src);
+ 
+   capture_into (window_src, data);
++
++  return TRUE;
+ }
+ 
+ MetaScreenCastWindowStreamSrc *
+diff --git a/src/backends/meta-screen-cast.h b/src/backends/meta-screen-cast.h
+index 7e32b67b7..cadb6a2fe 100644
+--- a/src/backends/meta-screen-cast.h
++++ b/src/backends/meta-screen-cast.h
+@@ -29,6 +29,13 @@
+ #include "backends/meta-dbus-session-watcher.h"
+ #include "meta-dbus-screen-cast.h"
+ 
++typedef enum _MetaScreenCastCursorMode
++{
++  META_SCREEN_CAST_CURSOR_MODE_HIDDEN = 0,
++  META_SCREEN_CAST_CURSOR_MODE_EMBEDDED = 1,
++  META_SCREEN_CAST_CURSOR_MODE_METADATA = 2,
++} MetaScreenCastCursorMode;
++
+ #define META_TYPE_SCREEN_CAST (meta_screen_cast_get_type ())
+ G_DECLARE_FINAL_TYPE (MetaScreenCast, meta_screen_cast,
+                       META, SCREEN_CAST,
+diff --git a/src/org.gnome.Mutter.ScreenCast.xml b/src/org.gnome.Mutter.ScreenCast.xml
+index 3cd02b6cb..953809727 100644
+--- a/src/org.gnome.Mutter.ScreenCast.xml
++++ b/src/org.gnome.Mutter.ScreenCast.xml
+@@ -71,7 +71,15 @@
+ 
+ 	Record a single monitor.
+ 
+-	Available @properties include: (none)
++	Available @properties include:
++
++	* "cursor-mode" (u): Cursor mode. Default: 'hidden' (see below)
++
++	Available cursor mode values:
++
++	0: hidden - cursor is not included in the stream
++	1: embedded - cursor is included in the framebuffer
++	2: metadata - cursor is included as metadata in the PipeWire stream
+     -->
+     <method name="RecordMonitor">
+       <arg name="connector" type="s" direction="in" />
+@@ -84,7 +92,7 @@
+ 	@properties: Properties used determining what window to select
+ 	@stream_path: Path to the new stream object
+ 
+-	Record a single window.
++	Record a single window. The cursor will not be included.
+ 
+ 	Available @properties include:
+ 
+-- 
+2.20.1
+
+
+From 93c2b18337a8babe340775cd979ad540f0838ab3 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Thu, 3 Jan 2019 16:40:42 +0100
+Subject: [PATCH 11/12] cursor-tracker: Emit `cursor-changed` after renderer
+ was updated
+
+Otherwise the cursor retrieved via meta_cursor_renderer_get_cursor() is
+out of date.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/357
+---
+ src/backends/meta-cursor-tracker.c | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/src/backends/meta-cursor-tracker.c b/src/backends/meta-cursor-tracker.c
+index 060b6af3b..97e7f8cb4 100644
+--- a/src/backends/meta-cursor-tracker.c
++++ b/src/backends/meta-cursor-tracker.c
+@@ -118,11 +118,15 @@ change_cursor_renderer (MetaCursorTracker *tracker)
+ static void
+ sync_cursor (MetaCursorTracker *tracker)
+ {
+-  if (update_displayed_cursor (tracker))
+-    g_signal_emit (tracker, signals[CURSOR_CHANGED], 0);
++  gboolean cursor_changed = FALSE;
++
++  cursor_changed = update_displayed_cursor (tracker);
+ 
+   if (update_effective_cursor (tracker))
+     change_cursor_renderer (tracker);
++
++  if (cursor_changed)
++    g_signal_emit (tracker, signals[CURSOR_CHANGED], 0);
+ }
+ 
+ static void
+-- 
+2.20.1
+
+
+From 392205b1d0728ac1636df90144c0fe091bd45624 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Thu, 3 Jan 2019 16:51:08 +0100
+Subject: [PATCH 12/12] screen-cast-monitor-stream-src: Only send cursor bitmap
+ when it changes
+
+To avoid unnecessary pixel copying, only send the cursor bitmap when it
+changes. This also allows the receiver to know when the cursor bitmap
+actually changed.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/357
+---
+ src/backends/meta-screen-cast-monitor-stream-src.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c b/src/backends/meta-screen-cast-monitor-stream-src.c
+index 6be477989..1d6aea242 100644
+--- a/src/backends/meta-screen-cast-monitor-stream-src.c
++++ b/src/backends/meta-screen-cast-monitor-stream-src.c
+@@ -40,6 +40,8 @@ struct _MetaScreenCastMonitorStreamSrc
+ {
+   MetaScreenCastStreamSrc parent;
+ 
++  gboolean cursor_bitmap_invalid;
++
+   gulong actors_painted_handler_id;
+   gulong paint_handler_id;
+   gulong cursor_moved_handler_id;
+@@ -194,6 +196,7 @@ static void
+ cursor_changed (MetaCursorTracker              *cursor_tracker,
+                 MetaScreenCastMonitorStreamSrc *monitor_src)
+ {
++  monitor_src->cursor_bitmap_invalid = TRUE;
+   sync_cursor_state (monitor_src);
+ }
+ 
+@@ -452,6 +455,16 @@ meta_screen_cast_monitor_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc
+   spa_meta_cursor->id = 1;
+   spa_meta_cursor->position.x = (int32_t) roundf (cursor_position.x);
+   spa_meta_cursor->position.y = (int32_t) roundf (cursor_position.y);
++
++  if (!monitor_src->cursor_bitmap_invalid)
++    {
++      spa_meta_cursor->hotspot.x = 0;
++      spa_meta_cursor->hotspot.y = 0;
++      spa_meta_cursor->bitmap_offset = 0;
++      return;
++    }
++  monitor_src->cursor_bitmap_invalid = FALSE;
++
+   spa_meta_cursor->bitmap_offset = sizeof (struct spa_meta_cursor);
+ 
+   spa_meta_bitmap = SPA_MEMBER (spa_meta_cursor,
+@@ -551,6 +564,7 @@ meta_screen_cast_monitor_stream_src_new (MetaScreenCastMonitorStream  *monitor_s
+ static void
+ meta_screen_cast_monitor_stream_src_init (MetaScreenCastMonitorStreamSrc *monitor_src)
+ {
++  monitor_src->cursor_bitmap_invalid = TRUE;
+ }
+ 
+ static void
+-- 
+2.20.1
+
diff --git a/SOURCES/shaped-texture-get-image-via-offscreen.patch b/SOURCES/shaped-texture-get-image-via-offscreen.patch
new file mode 100644
index 0000000..fb94954
--- /dev/null
+++ b/SOURCES/shaped-texture-get-image-via-offscreen.patch
@@ -0,0 +1,1094 @@
+From 7bbe68b149d387ce65aaf35542a67bcc93a80d70 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Wed, 19 Dec 2018 10:08:05 +0100
+Subject: [PATCH 01/10] cogl/texture-2d-gl: Try to determine format for
+ external textures
+
+Don't just set the internal format to the dummy format "any", as that causes
+code intended to be unreachable code to be reached. It's not possible to
+actually know the internal format of an external texture, however, so it might
+not actually correspond to the real format.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/362
+---
+ cogl/cogl/driver/gl/cogl-texture-2d-gl.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/cogl/cogl/driver/gl/cogl-texture-2d-gl.c b/cogl/cogl/driver/gl/cogl-texture-2d-gl.c
+index d1eff4507..53be13216 100644
+--- a/cogl/cogl/driver/gl/cogl-texture-2d-gl.c
++++ b/cogl/cogl/driver/gl/cogl-texture-2d-gl.c
+@@ -470,7 +470,12 @@ allocate_custom_egl_image_external (CoglTexture2D *tex_2d,
+ {
+   CoglTexture *tex = COGL_TEXTURE (tex_2d);
+   CoglContext *ctx = tex->context;
+-  CoglPixelFormat internal_format = loader->src.egl_image_external.format;
++  CoglPixelFormat external_format;
++  CoglPixelFormat internal_format;
++
++  external_format = loader->src.egl_image_external.format;
++  internal_format = _cogl_texture_determine_internal_format (tex,
++                                                             external_format);
+ 
+   _cogl_gl_util_clear_gl_errors (ctx);
+ 
+-- 
+2.19.2
+
+
+From 320add39ea8e1d035f139167561ddb5ae7757d36 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Wed, 19 Dec 2018 10:12:49 +0100
+Subject: [PATCH 02/10] cogl/texture-2d-gl: Bind correct target when getting
+ data
+
+While for normal textures, GL_TEXTURE_2D should be used, when it's an external
+texture, binding it using GL_TEXTURE_2D results in an error.
+
+Reading the specification for GL_TEXTURE_EXTERNAL_OES it is unclear whether
+getting pixel data from a texture is possible, and tests show it doesn't result
+in any data, but in case it would eventually start working, at least bind the
+correct target for now.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/362
+---
+ cogl/cogl/driver/gl/cogl-texture-2d-gl.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/cogl/cogl/driver/gl/cogl-texture-2d-gl.c b/cogl/cogl/driver/gl/cogl-texture-2d-gl.c
+index 53be13216..2cf6fed51 100644
+--- a/cogl/cogl/driver/gl/cogl-texture-2d-gl.c
++++ b/cogl/cogl/driver/gl/cogl-texture-2d-gl.c
+@@ -859,12 +859,12 @@ _cogl_texture_2d_gl_get_data (CoglTexture2D *tex_2d,
+                                                     width,
+                                                     bpp);
+ 
+-  _cogl_bind_gl_texture_transient (GL_TEXTURE_2D,
++  _cogl_bind_gl_texture_transient (tex_2d->gl_target,
+                                    tex_2d->gl_texture,
+                                    tex_2d->is_foreign);
+ 
+   ctx->texture_driver->gl_get_tex_image (ctx,
+-                                         GL_TEXTURE_2D,
++                                         tex_2d->gl_target,
+                                          gl_format,
+                                          gl_type,
+                                          data);
+-- 
+2.19.2
+
+
+From f890ffe0f8970b640085d4cf767dc64bed1479a4 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Wed, 19 Dec 2018 11:46:45 +0100
+Subject: [PATCH 03/10] cogl/texture: Add API to check whether _get_data() will
+ work
+
+Currently, GL_TEXTURE_EXTERNAL_OES textures doesn't support getting pixel data.
+Make it possible for texture users to know this.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/362
+---
+ cogl/cogl/cogl-atlas-texture.c                   |  3 ++-
+ cogl/cogl/cogl-driver.h                          |  3 +++
+ cogl/cogl/cogl-sub-texture.c                     | 11 ++++++++++-
+ cogl/cogl/cogl-texture-2d-sliced.c               |  3 ++-
+ cogl/cogl/cogl-texture-2d.c                      | 12 +++++++++++-
+ cogl/cogl/cogl-texture-3d.c                      |  3 ++-
+ cogl/cogl/cogl-texture-private.h                 |  2 ++
+ cogl/cogl/cogl-texture-rectangle.c               |  3 ++-
+ cogl/cogl/cogl-texture.c                         |  9 +++++++++
+ cogl/cogl/cogl-texture.h                         |  6 ++++++
+ cogl/cogl/driver/gl/cogl-texture-2d-gl-private.h |  3 +++
+ cogl/cogl/driver/gl/cogl-texture-2d-gl.c         |  9 +++++++++
+ 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/winsys/cogl-texture-pixmap-x11.c       |  3 ++-
+ 16 files changed, 66 insertions(+), 7 deletions(-)
+
+diff --git a/cogl/cogl/cogl-atlas-texture.c b/cogl/cogl/cogl-atlas-texture.c
+index 97bb84a6a..1a1f99b11 100644
+--- a/cogl/cogl/cogl-atlas-texture.c
++++ b/cogl/cogl/cogl-atlas-texture.c
+@@ -1043,5 +1043,6 @@ cogl_atlas_texture_vtable =
+     _cogl_atlas_texture_get_gl_format,
+     _cogl_atlas_texture_get_type,
+     NULL, /* is_foreign */
+-    NULL /* set_auto_mipmap */
++    NULL, /* set_auto_mipmap */
++    NULL  /* is_get_data_supported */
+   };
+diff --git a/cogl/cogl/cogl-driver.h b/cogl/cogl/cogl-driver.h
+index 85aa0d870..33a7598a9 100644
+--- a/cogl/cogl/cogl-driver.h
++++ b/cogl/cogl/cogl-driver.h
+@@ -210,6 +210,9 @@ struct _CoglDriverVtable
+                            int rowstride,
+                            uint8_t *data);
+ 
++  CoglBool
++  (* texture_2d_is_get_data_supported) (CoglTexture2D *tex_2d);
++
+   /* Prepares for drawing by flushing the journal, framebuffer state,
+    * pipeline state and attribute state.
+    */
+diff --git a/cogl/cogl/cogl-sub-texture.c b/cogl/cogl/cogl-sub-texture.c
+index 9d7abea90..c3b436140 100644
+--- a/cogl/cogl/cogl-sub-texture.c
++++ b/cogl/cogl/cogl-sub-texture.c
+@@ -454,6 +454,14 @@ _cogl_sub_texture_get_type (CoglTexture *tex)
+   return _cogl_texture_get_type (sub_tex->full_texture);
+ }
+ 
++static CoglBool
++_cogl_sub_texture_is_get_data_supported (CoglTexture *tex)
++{
++  CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex);
++
++  return cogl_texture_is_get_data_supported (sub_tex->full_texture);
++}
++
+ static const CoglTextureVtable
+ cogl_sub_texture_vtable =
+   {
+@@ -476,5 +484,6 @@ cogl_sub_texture_vtable =
+     _cogl_sub_texture_get_gl_format,
+     _cogl_sub_texture_get_type,
+     NULL, /* is_foreign */
+-    NULL /* set_auto_mipmap */
++    NULL, /* set_auto_mipmap */
++    _cogl_sub_texture_is_get_data_supported
+   };
+diff --git a/cogl/cogl/cogl-texture-2d-sliced.c b/cogl/cogl/cogl-texture-2d-sliced.c
+index 4f586cde7..458b29ce5 100644
+--- a/cogl/cogl/cogl-texture-2d-sliced.c
++++ b/cogl/cogl/cogl-texture-2d-sliced.c
+@@ -1542,5 +1542,6 @@ cogl_texture_2d_sliced_vtable =
+     _cogl_texture_2d_sliced_get_gl_format,
+     _cogl_texture_2d_sliced_get_type,
+     _cogl_texture_2d_sliced_is_foreign,
+-    NULL /* set_auto_mipmap */
++    NULL, /* set_auto_mipmap */
++    NULL  /* is_get_data_supported */
+   };
+diff --git a/cogl/cogl/cogl-texture-2d.c b/cogl/cogl/cogl-texture-2d.c
+index 663125890..0e4a73de0 100644
+--- a/cogl/cogl/cogl-texture-2d.c
++++ b/cogl/cogl/cogl-texture-2d.c
+@@ -94,6 +94,15 @@ _cogl_texture_2d_set_auto_mipmap (CoglTexture *tex,
+   tex_2d->auto_mipmap = value;
+ }
+ 
++static CoglBool
++_cogl_texture_2d_is_get_data_supported (CoglTexture *tex)
++{
++  CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex);
++  CoglContext *ctx = tex->context;
++
++  return ctx->driver_vtable->texture_2d_is_get_data_supported (tex_2d);
++}
++
+ CoglTexture2D *
+ _cogl_texture_2d_create_base (CoglContext *ctx,
+                               int width,
+@@ -693,5 +702,6 @@ cogl_texture_2d_vtable =
+     _cogl_texture_2d_get_gl_format,
+     _cogl_texture_2d_get_type,
+     _cogl_texture_2d_is_foreign,
+-    _cogl_texture_2d_set_auto_mipmap
++    _cogl_texture_2d_set_auto_mipmap,
++    _cogl_texture_2d_is_get_data_supported
+   };
+diff --git a/cogl/cogl/cogl-texture-3d.c b/cogl/cogl/cogl-texture-3d.c
+index 5644119d7..00b3447ec 100644
+--- a/cogl/cogl/cogl-texture-3d.c
++++ b/cogl/cogl/cogl-texture-3d.c
+@@ -755,5 +755,6 @@ cogl_texture_3d_vtable =
+     _cogl_texture_3d_get_gl_format,
+     _cogl_texture_3d_get_type,
+     NULL, /* is_foreign */
+-    _cogl_texture_3d_set_auto_mipmap
++    _cogl_texture_3d_set_auto_mipmap,
++    NULL  /* is_get_data_supported */
+   };
+diff --git a/cogl/cogl/cogl-texture-private.h b/cogl/cogl/cogl-texture-private.h
+index 742983e2d..44100f0b7 100644
+--- a/cogl/cogl/cogl-texture-private.h
++++ b/cogl/cogl/cogl-texture-private.h
+@@ -149,6 +149,8 @@ struct _CoglTextureVtable
+   /* Only needs to be implemented if is_primitive == TRUE */
+   void (* set_auto_mipmap) (CoglTexture *texture,
+                             CoglBool value);
++
++  CoglBool (* is_get_data_supported) (CoglTexture *texture);
+ };
+ 
+ typedef enum _CoglTextureSoureType {
+diff --git a/cogl/cogl/cogl-texture-rectangle.c b/cogl/cogl/cogl-texture-rectangle.c
+index cc2e642d3..0179324a4 100644
+--- a/cogl/cogl/cogl-texture-rectangle.c
++++ b/cogl/cogl/cogl-texture-rectangle.c
+@@ -773,5 +773,6 @@ cogl_texture_rectangle_vtable =
+     _cogl_texture_rectangle_get_gl_format,
+     _cogl_texture_rectangle_get_type,
+     _cogl_texture_rectangle_is_foreign,
+-    _cogl_texture_rectangle_set_auto_mipmap
++    _cogl_texture_rectangle_set_auto_mipmap,
++    NULL  /* is_get_data_supported */
+   };
+diff --git a/cogl/cogl/cogl-texture.c b/cogl/cogl/cogl-texture.c
+index e2d37e225..eef2abdbe 100644
+--- a/cogl/cogl/cogl-texture.c
++++ b/cogl/cogl/cogl-texture.c
+@@ -205,6 +205,15 @@ _cogl_texture_is_foreign (CoglTexture *texture)
+     return FALSE;
+ }
+ 
++CoglBool
++cogl_texture_is_get_data_supported (CoglTexture *texture)
++{
++  if (texture->vtable->is_get_data_supported)
++    return texture->vtable->is_get_data_supported (texture);
++  else
++    return TRUE;
++}
++
+ unsigned int
+ cogl_texture_get_width (CoglTexture *texture)
+ {
+diff --git a/cogl/cogl/cogl-texture.h b/cogl/cogl/cogl-texture.h
+index ef7d14281..67647aa9c 100644
+--- a/cogl/cogl/cogl-texture.h
++++ b/cogl/cogl/cogl-texture.h
+@@ -511,6 +511,12 @@ CoglBool
+ cogl_texture_allocate (CoglTexture *texture,
+                        CoglError **error);
+ 
++/**
++ * cogl_texture_is_get_data_supported: (skip)
++ */
++CoglBool
++cogl_texture_is_get_data_supported (CoglTexture *texture);
++
+ COGL_END_DECLS
+ 
+ #endif /* __COGL_TEXTURE_H__ */
+diff --git a/cogl/cogl/driver/gl/cogl-texture-2d-gl-private.h b/cogl/cogl/driver/gl/cogl-texture-2d-gl-private.h
+index e5c658534..1379e9a93 100644
+--- a/cogl/cogl/driver/gl/cogl-texture-2d-gl-private.h
++++ b/cogl/cogl/driver/gl/cogl-texture-2d-gl-private.h
+@@ -116,4 +116,7 @@ _cogl_texture_2d_gl_get_data (CoglTexture2D *tex_2d,
+                               int rowstride,
+                               uint8_t *data);
+ 
++CoglBool
++_cogl_texture_2d_gl_is_get_data_supported (CoglTexture2D *tex_2d);
++
+ #endif /* _COGL_TEXTURE_2D_GL_PRIVATE_H_ */
+diff --git a/cogl/cogl/driver/gl/cogl-texture-2d-gl.c b/cogl/cogl/driver/gl/cogl-texture-2d-gl.c
+index 2cf6fed51..f04e3ebca 100644
+--- a/cogl/cogl/driver/gl/cogl-texture-2d-gl.c
++++ b/cogl/cogl/driver/gl/cogl-texture-2d-gl.c
+@@ -869,3 +869,12 @@ _cogl_texture_2d_gl_get_data (CoglTexture2D *tex_2d,
+                                          gl_type,
+                                          data);
+ }
++
++CoglBool
++_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;
++}
+diff --git a/cogl/cogl/driver/gl/gl/cogl-driver-gl.c b/cogl/cogl/driver/gl/gl/cogl-driver-gl.c
+index 178262ac0..9247e4e78 100644
+--- a/cogl/cogl/driver/gl/gl/cogl-driver-gl.c
++++ b/cogl/cogl/driver/gl/gl/cogl-driver-gl.c
+@@ -714,6 +714,7 @@ _cogl_driver_gl =
+     _cogl_texture_2d_gl_generate_mipmap,
+     _cogl_texture_2d_gl_copy_from_bitmap,
+     _cogl_texture_2d_gl_get_data,
++    _cogl_texture_2d_gl_is_get_data_supported,
+     _cogl_gl_flush_attributes_state,
+     _cogl_clip_stack_gl_flush,
+     _cogl_buffer_gl_create,
+diff --git a/cogl/cogl/driver/gl/gles/cogl-driver-gles.c b/cogl/cogl/driver/gl/gles/cogl-driver-gles.c
+index 521f6ef3d..14f9b282c 100644
+--- a/cogl/cogl/driver/gl/gles/cogl-driver-gles.c
++++ b/cogl/cogl/driver/gl/gles/cogl-driver-gles.c
+@@ -493,6 +493,7 @@ _cogl_driver_gles =
+     _cogl_texture_2d_gl_generate_mipmap,
+     _cogl_texture_2d_gl_copy_from_bitmap,
+     NULL, /* texture_2d_get_data */
++    NULL, /* texture_2d_is_get_data_supported */
+     _cogl_gl_flush_attributes_state,
+     _cogl_clip_stack_gl_flush,
+     _cogl_buffer_gl_create,
+diff --git a/cogl/cogl/driver/nop/cogl-driver-nop.c b/cogl/cogl/driver/nop/cogl-driver-nop.c
+index 6e04e7164..8424c64ef 100644
+--- a/cogl/cogl/driver/nop/cogl-driver-nop.c
++++ b/cogl/cogl/driver/nop/cogl-driver-nop.c
+@@ -82,6 +82,7 @@ _cogl_driver_nop =
+     _cogl_texture_2d_nop_generate_mipmap,
+     _cogl_texture_2d_nop_copy_from_bitmap,
+     NULL, /* texture_2d_get_data */
++    NULL, /* texture_2d_is_get_data_supported */
+     _cogl_nop_flush_attributes_state,
+     _cogl_clip_stack_nop_flush,
+   };
+diff --git a/cogl/cogl/winsys/cogl-texture-pixmap-x11.c b/cogl/cogl/winsys/cogl-texture-pixmap-x11.c
+index d03040c24..3bb057f4a 100644
+--- a/cogl/cogl/winsys/cogl-texture-pixmap-x11.c
++++ b/cogl/cogl/winsys/cogl-texture-pixmap-x11.c
+@@ -1180,5 +1180,6 @@ cogl_texture_pixmap_x11_vtable =
+     _cogl_texture_pixmap_x11_get_gl_format,
+     _cogl_texture_pixmap_x11_get_type,
+     NULL, /* is_foreign */
+-    NULL /* set_auto_mipmap */
++    NULL, /* set_auto_mipmap */
++    NULL  /* is_get_data_supported */
+   };
+-- 
+2.19.2
+
+
+From 363816fab233b965ff8b9e3a55bdb80b9cee4515 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Wed, 19 Dec 2018 11:55:43 +0100
+Subject: [PATCH 04/10] compositor: Make meta_actor_painting_untransformed take
+ a framebuffer
+
+Stop using the cogl draw framebuffer implicitly.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/362
+---
+ src/compositor/clutter-utils.c         | 9 +++++----
+ src/compositor/clutter-utils.h         | 9 +++++----
+ src/compositor/meta-background-actor.c | 7 ++++++-
+ src/compositor/meta-shaped-texture.c   | 7 +++++--
+ src/compositor/meta-window-group.c     | 6 +++++-
+ 5 files changed, 26 insertions(+), 12 deletions(-)
+
+diff --git a/src/compositor/clutter-utils.c b/src/compositor/clutter-utils.c
+index fb74732ce..6591ee2d3 100644
+--- a/src/compositor/clutter-utils.c
++++ b/src/compositor/clutter-utils.c
+@@ -143,7 +143,8 @@ meta_actor_is_untransformed (ClutterActor *actor,
+  * transform.
+  */
+ gboolean
+-meta_actor_painting_untransformed (int              paint_width,
++meta_actor_painting_untransformed (CoglFramebuffer *fb,
++                                   int              paint_width,
+                                    int              paint_height,
+                                    int             *x_origin,
+                                    int             *y_origin)
+@@ -153,8 +154,8 @@ meta_actor_painting_untransformed (int              paint_width,
+   float viewport[4];
+   int i;
+ 
+-  cogl_get_modelview_matrix (&modelview);
+-  cogl_get_projection_matrix (&projection);
++  cogl_framebuffer_get_modelview_matrix (fb, &modelview);
++  cogl_framebuffer_get_projection_matrix (fb, &projection);
+ 
+   cogl_matrix_multiply (&modelview_projection,
+                         &projection,
+@@ -173,7 +174,7 @@ meta_actor_painting_untransformed (int              paint_width,
+   vertices[3].y = paint_height;
+   vertices[3].z = 0;
+ 
+-  cogl_get_viewport (viewport);
++  cogl_framebuffer_get_viewport4fv (fb, viewport);
+ 
+   for (i = 0; i < 4; i++)
+     {
+diff --git a/src/compositor/clutter-utils.h b/src/compositor/clutter-utils.h
+index 36a5925cf..b96733e4a 100644
+--- a/src/compositor/clutter-utils.h
++++ b/src/compositor/clutter-utils.h
+@@ -31,9 +31,10 @@ gboolean meta_actor_is_untransformed (ClutterActor *actor,
+                                       int          *x_origin,
+                                       int          *y_origin);
+ 
+-gboolean meta_actor_painting_untransformed (int         paint_width,
+-                                            int         paint_height,
+-                                            int        *x_origin,
+-                                            int        *y_origin);
++gboolean meta_actor_painting_untransformed (CoglFramebuffer *fb,
++                                            int              paint_width,
++                                            int              paint_height,
++                                            int             *x_origin,
++                                            int             *y_origin);
+ 
+ #endif /* __META_CLUTTER_UTILS_H__ */
+diff --git a/src/compositor/meta-background-actor.c b/src/compositor/meta-background-actor.c
+index 197a62c0f..c4c0f9561 100644
+--- a/src/compositor/meta-background-actor.c
++++ b/src/compositor/meta-background-actor.c
+@@ -325,6 +325,7 @@ setup_pipeline (MetaBackgroundActor   *self,
+   PipelineFlags pipeline_flags = 0;
+   guint8 opacity;
+   float color_component;
++  CoglFramebuffer *fb;
+   CoglPipelineFilter filter;
+ 
+   opacity = clutter_actor_get_paint_opacity (CLUTTER_ACTOR (self));
+@@ -417,8 +418,12 @@ setup_pipeline (MetaBackgroundActor   *self,
+                              color_component,
+                              opacity / 255.);
+ 
++  fb = cogl_get_draw_framebuffer ();
+   if (!priv->force_bilinear &&
+-      meta_actor_painting_untransformed (actor_pixel_rect->width, actor_pixel_rect->height, NULL, NULL))
++      meta_actor_painting_untransformed (fb,
++                                         actor_pixel_rect->width,
++                                         actor_pixel_rect->height,
++                                         NULL, NULL))
+     filter = COGL_PIPELINE_FILTER_NEAREST;
+   else
+     filter = COGL_PIPELINE_FILTER_LINEAR;
+diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c
+index 98346c6ae..133041ba9 100644
+--- a/src/compositor/meta-shaped-texture.c
++++ b/src/compositor/meta-shaped-texture.c
+@@ -422,17 +422,20 @@ meta_shaped_texture_paint (ClutterActor *actor)
+ 
+   cairo_rectangle_int_t tex_rect = { 0, 0, tex_width, tex_height };
+ 
++  fb = cogl_get_draw_framebuffer ();
++
+   /* Use nearest-pixel interpolation if the texture is unscaled. This
+    * improves performance, especially with software rendering.
+    */
+ 
+   filter = COGL_PIPELINE_FILTER_LINEAR;
+ 
+-  if (meta_actor_painting_untransformed (tex_width, tex_height, NULL, NULL))
++  if (meta_actor_painting_untransformed (fb,
++                                         tex_width, tex_height,
++                                         NULL, NULL))
+     filter = COGL_PIPELINE_FILTER_NEAREST;
+ 
+   ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
+-  fb = cogl_get_draw_framebuffer ();
+ 
+   opacity = clutter_actor_get_paint_opacity (actor);
+   clutter_actor_get_allocation_box (actor, &alloc);
+diff --git a/src/compositor/meta-window-group.c b/src/compositor/meta-window-group.c
+index 665adee77..d41c00783 100644
+--- a/src/compositor/meta-window-group.c
++++ b/src/compositor/meta-window-group.c
+@@ -81,7 +81,11 @@ meta_window_group_paint (ClutterActor *actor)
+    */
+   if (clutter_actor_is_in_clone_paint (actor))
+     {
+-      if (!meta_actor_painting_untransformed (screen_width,
++      CoglFramebuffer *fb;
++
++      fb = cogl_get_draw_framebuffer ();
++      if (!meta_actor_painting_untransformed (fb,
++                                              screen_width,
+                                               screen_height,
+                                               &paint_x_origin,
+                                               &paint_y_origin) ||
+-- 
+2.19.2
+
+
+From ff3110a1a547e0a5523cc5ef33f52b1d706c072e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Wed, 19 Dec 2018 12:52:58 +0100
+Subject: [PATCH 05/10] shaped-texture: Put actual texture painting in helper
+
+This is so that it can be reused later by meta_shaped_texture_get_image() for
+drawing via an offscreen framebuffer.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/362
+---
+ src/compositor/meta-shaped-texture.c | 89 ++++++++++++++++------------
+ 1 file changed, 52 insertions(+), 37 deletions(-)
+
+diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c
+index 133041ba9..68098a536 100644
+--- a/src/compositor/meta-shaped-texture.c
++++ b/src/compositor/meta-shaped-texture.c
+@@ -373,47 +373,18 @@ set_cogl_texture (MetaShapedTexture *stex,
+ }
+ 
+ static void
+-meta_shaped_texture_paint (ClutterActor *actor)
++do_paint (MetaShapedTexture *stex,
++          CoglFramebuffer   *fb,
++          CoglTexture       *paint_tex,
++          cairo_region_t    *clip_region)
+ {
+-  MetaShapedTexture *stex = (MetaShapedTexture *) actor;
+   MetaShapedTexturePrivate *priv = stex->priv;
+   guint tex_width, tex_height;
+   guchar opacity;
+   CoglContext *ctx;
+-  CoglFramebuffer *fb;
+-  CoglTexture *paint_tex;
+   ClutterActorBox alloc;
+   CoglPipelineFilter filter;
+ 
+-  if (priv->clip_region && cairo_region_is_empty (priv->clip_region))
+-    return;
+-
+-  if (!CLUTTER_ACTOR_IS_REALIZED (CLUTTER_ACTOR (stex)))
+-    clutter_actor_realize (CLUTTER_ACTOR (stex));
+-
+-  /* The GL EXT_texture_from_pixmap extension does allow for it to be
+-   * used together with SGIS_generate_mipmap, however this is very
+-   * rarely supported. Also, even when it is supported there
+-   * are distinct performance implications from:
+-   *
+-   *  - Updating mipmaps that we don't need
+-   *  - Having to reallocate pixmaps on the server into larger buffers
+-   *
+-   * So, we just unconditionally use our mipmap emulation code. If we
+-   * wanted to use SGIS_generate_mipmap, we'd have to  query COGL to
+-   * see if it was supported (no API currently), and then if and only
+-   * if that was the case, set the clutter texture quality to HIGH.
+-   * Setting the texture quality to high without SGIS_generate_mipmap
+-   * support for TFP textures will result in fallbacks to XGetImage.
+-   */
+-  if (priv->create_mipmaps)
+-    paint_tex = meta_texture_tower_get_paint_texture (priv->paint_tower);
+-  else
+-    paint_tex = COGL_TEXTURE (priv->texture);
+-
+-  if (paint_tex == NULL)
+-    return;
+-
+   tex_width = priv->tex_width;
+   tex_height = priv->tex_height;
+ 
+@@ -422,8 +393,6 @@ meta_shaped_texture_paint (ClutterActor *actor)
+ 
+   cairo_rectangle_int_t tex_rect = { 0, 0, tex_width, tex_height };
+ 
+-  fb = cogl_get_draw_framebuffer ();
+-
+   /* Use nearest-pixel interpolation if the texture is unscaled. This
+    * improves performance, especially with software rendering.
+    */
+@@ -437,8 +406,8 @@ meta_shaped_texture_paint (ClutterActor *actor)
+ 
+   ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
+ 
+-  opacity = clutter_actor_get_paint_opacity (actor);
+-  clutter_actor_get_allocation_box (actor, &alloc);
++  opacity = clutter_actor_get_paint_opacity (CLUTTER_ACTOR (stex));
++  clutter_actor_get_allocation_box (CLUTTER_ACTOR (stex), &alloc);
+ 
+   cairo_region_t *blended_region;
+   gboolean use_opaque_region = (priv->opaque_region != NULL && opacity == 255);
+@@ -576,6 +545,52 @@ meta_shaped_texture_paint (ClutterActor *actor)
+     cairo_region_destroy (blended_region);
+ }
+ 
++static void
++meta_shaped_texture_paint (ClutterActor *actor)
++{
++  MetaShapedTexture *stex = META_SHAPED_TEXTURE (actor);
++  MetaShapedTexturePrivate *priv = stex->priv;
++  CoglTexture *paint_tex = NULL;
++  CoglFramebuffer *fb;
++
++  if (!priv->texture)
++    return;
++
++  if (priv->clip_region && cairo_region_is_empty (priv->clip_region))
++    return;
++
++  if (!CLUTTER_ACTOR_IS_REALIZED (CLUTTER_ACTOR (stex)))
++    clutter_actor_realize (CLUTTER_ACTOR (stex));
++
++  /* The GL EXT_texture_from_pixmap extension does allow for it to be
++   * used together with SGIS_generate_mipmap, however this is very
++   * rarely supported. Also, even when it is supported there
++   * are distinct performance implications from:
++   *
++   *  - Updating mipmaps that we don't need
++   *  - Having to reallocate pixmaps on the server into larger buffers
++   *
++   * So, we just unconditionally use our mipmap emulation code. If we
++   * wanted to use SGIS_generate_mipmap, we'd have to  query COGL to
++   * see if it was supported (no API currently), and then if and only
++   * if that was the case, set the clutter texture quality to HIGH.
++   * Setting the texture quality to high without SGIS_generate_mipmap
++   * support for TFP textures will result in fallbacks to XGetImage.
++   */
++  if (priv->create_mipmaps)
++    paint_tex = meta_texture_tower_get_paint_texture (priv->paint_tower);
++
++  if (!paint_tex)
++    paint_tex = COGL_TEXTURE (priv->texture);
++
++  if (cogl_texture_get_width (paint_tex) == 0 ||
++      cogl_texture_get_height (paint_tex) == 0)
++    return;
++
++  fb = cogl_get_draw_framebuffer ();
++  do_paint (META_SHAPED_TEXTURE (actor), fb, paint_tex, priv->clip_region);
++}
++
+ static void
+ meta_shaped_texture_get_preferred_width (ClutterActor *self,
+                                          gfloat        for_height,
+-- 
+2.19.2
+
+
+From f594c31d3f9fad5cafced8dc3e8cacb459f87bb0 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Thu, 20 Dec 2018 16:58:03 +0100
+Subject: [PATCH 06/10] boxes: Add helper to scale rectangles by a double
+
+And change the similar region scaling helper to use this one.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/362
+---
+ src/core/boxes-private.h | 10 ++++++++++
+ src/core/boxes.c         | 22 ++++++++++++++++++++++
+ 2 files changed, 32 insertions(+)
+
+diff --git a/src/core/boxes-private.h b/src/core/boxes-private.h
+index bf019b4d6..793f39527 100644
+--- a/src/core/boxes-private.h
++++ b/src/core/boxes-private.h
+@@ -38,6 +38,12 @@ typedef enum
+   FIXED_DIRECTION_Y    = 1 << 1,
+ } FixedDirections;
+ 
++typedef enum _MetaRoundingStrategy
++{
++  META_ROUNDING_STRATEGY_SHRINK,
++  META_ROUNDING_STRATEGY_GROW,
++} MetaRoundingStrategy;
++
+ /* Output functions -- note that the output buffer had better be big enough:
+  *   rect_to_string:   RECT_LENGTH
+  *   region_to_string: (RECT_LENGTH+strlen(separator_string)) *
+@@ -218,6 +224,10 @@ GList* meta_rectangle_find_nonintersected_monitor_edges (
+ gboolean meta_rectangle_is_adjecent_to (MetaRectangle *rect,
+                                         MetaRectangle *other);
+ 
++void meta_rectangle_scale_double (MetaRectangle        *rect,
++                                  double                scale,
++                                  MetaRoundingStrategy  rounding_strategy);
++
+ static inline ClutterRect
+ meta_rectangle_to_clutter_rect (MetaRectangle *rect)
+ {
+diff --git a/src/core/boxes.c b/src/core/boxes.c
+index 35e9ac3cd..0854ecf94 100644
+--- a/src/core/boxes.c
++++ b/src/core/boxes.c
+@@ -2036,3 +2036,25 @@ meta_rectangle_is_adjecent_to (MetaRectangle *rect,
+   else
+     return FALSE;
+ }
++
++void
++meta_rectangle_scale_double (MetaRectangle        *rect,
++                             double                scale,
++                             MetaRoundingStrategy  rounding_strategy)
++{
++  switch (rounding_strategy)
++    {
++    case META_ROUNDING_STRATEGY_SHRINK:
++      rect->x = (int) ceil (rect->x * scale);
++      rect->y = (int) ceil (rect->y * scale);
++      rect->width = (int) floor (rect->width * scale);
++      rect->height = (int) floor (rect->height * scale);
++      break;
++    case META_ROUNDING_STRATEGY_GROW:
++      rect->x = (int) floor (rect->x * scale);
++      rect->y = (int) floor (rect->y * scale);
++      rect->width = (int) ceil (rect->width * scale);
++      rect->height = (int) ceil (rect->height * scale);
++      break;
++    }
++}
+-- 
+2.19.2
+
+
+From 7826c0fdc45a62c6ca563d2f1526b81975243dbb Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Thu, 20 Dec 2018 17:21:26 +0100
+Subject: [PATCH 07/10] shaped-texture: Stop using gdk rect helper
+
+We have our own version, just use that.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/362
+---
+ src/compositor/meta-shaped-texture.c | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c
+index 68098a536..6cde86390 100644
+--- a/src/compositor/meta-shaped-texture.c
++++ b/src/compositor/meta-shaped-texture.c
+@@ -940,9 +940,7 @@ meta_shaped_texture_get_image (MetaShapedTexture     *stex,
+ 
+   if (clip != NULL)
+     {
+-      /* GdkRectangle is just a typedef of cairo_rectangle_int_t,
+-       * so we can use the gdk_rectangle_* APIs on these. */
+-      if (!gdk_rectangle_intersect (&texture_rect, clip, clip))
++      if (!meta_rectangle_intersect (&texture_rect, clip, clip))
+         return NULL;
+     }
+ 
+-- 
+2.19.2
+
+
+From a8cecf4997d28e6672bc51afea9682504225c997 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Thu, 20 Dec 2018 17:22:37 +0100
+Subject: [PATCH 08/10] shaped-texture: Add priv pointer to _get_image()
+
+The MetaShapedTexturePrivate is accessed more than once, so keep a
+pointer to it.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/362
+---
+ src/compositor/meta-shaped-texture.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c
+index 6cde86390..0fa5fa92f 100644
+--- a/src/compositor/meta-shaped-texture.c
++++ b/src/compositor/meta-shaped-texture.c
+@@ -924,13 +924,14 @@ cairo_surface_t *
+ meta_shaped_texture_get_image (MetaShapedTexture     *stex,
+                                cairo_rectangle_int_t *clip)
+ {
++  MetaShapedTexturePrivate *priv = stex->priv;
+   CoglTexture *texture, *mask_texture;
+   cairo_rectangle_int_t texture_rect = { 0, 0, 0, 0 };
+   cairo_surface_t *surface;
+ 
+   g_return_val_if_fail (META_IS_SHAPED_TEXTURE (stex), NULL);
+ 
+-  texture = COGL_TEXTURE (stex->priv->texture);
++  texture = COGL_TEXTURE (priv->texture);
+ 
+   if (texture == NULL)
+     return NULL;
+@@ -964,7 +965,7 @@ meta_shaped_texture_get_image (MetaShapedTexture     *stex,
+   if (clip != NULL)
+     cogl_object_unref (texture);
+ 
+-  mask_texture = stex->priv->mask_texture;
++  mask_texture = priv->mask_texture;
+   if (mask_texture != NULL)
+     {
+       cairo_t *cr;
+-- 
+2.19.2
+
+
+From a2fb231955a41a9e87ffcdf845fa4dbca0d31dc4 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Thu, 20 Dec 2018 17:32:27 +0100
+Subject: [PATCH 09/10] shaped-texture: Don't change the callers clip rect
+
+We intersected the callers clip rect. That is probably not a good idea,
+and easily avoided, so lets avoid it.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/362
+---
+ src/compositor/meta-shaped-texture.c | 36 +++++++++++++++-------------
+ 1 file changed, 20 insertions(+), 16 deletions(-)
+
+diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c
+index 0fa5fa92f..823bd47f2 100644
+--- a/src/compositor/meta-shaped-texture.c
++++ b/src/compositor/meta-shaped-texture.c
+@@ -925,6 +925,7 @@ meta_shaped_texture_get_image (MetaShapedTexture     *stex,
+                                cairo_rectangle_int_t *clip)
+ {
+   MetaShapedTexturePrivate *priv = stex->priv;
++  cairo_rectangle_int_t *transformed_clip = NULL;
+   CoglTexture *texture, *mask_texture;
+   cairo_rectangle_int_t texture_rect = { 0, 0, 0, 0 };
+   cairo_surface_t *surface;
+@@ -936,21 +937,23 @@ meta_shaped_texture_get_image (MetaShapedTexture     *stex,
+   if (texture == NULL)
+     return NULL;
+ 
+-  texture_rect.width = cogl_texture_get_width (texture);
+-  texture_rect.height = cogl_texture_get_height (texture);
+ 
+   if (clip != NULL)
+     {
+-      if (!meta_rectangle_intersect (&texture_rect, clip, clip))
++      transformed_clip = alloca (sizeof (cairo_rectangle_int_t));
++      *transformed_clip = *clip;
++
++      if (!meta_rectangle_intersect (&texture_rect, transformed_clip,
++                                     transformed_clip))
+         return NULL;
+     }
+ 
+-  if (clip != NULL)
++  if (transformed_clip)
+     texture = cogl_texture_new_from_sub_texture (texture,
+-                                                 clip->x,
+-                                                 clip->y,
+-                                                 clip->width,
+-                                                 clip->height);
++                                                 transformed_clip->x,
++                                                 transformed_clip->y,
++                                                 transformed_clip->width,
++                                                 transformed_clip->height);
+ 
+   surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                         cogl_texture_get_width (texture),
+@@ -962,7 +965,7 @@ meta_shaped_texture_get_image (MetaShapedTexture     *stex,
+ 
+   cairo_surface_mark_dirty (surface);
+ 
+-  if (clip != NULL)
++  if (transformed_clip)
+     cogl_object_unref (texture);
+ 
+   mask_texture = priv->mask_texture;
+@@ -971,12 +974,13 @@ meta_shaped_texture_get_image (MetaShapedTexture     *stex,
+       cairo_t *cr;
+       cairo_surface_t *mask_surface;
+ 
+-      if (clip != NULL)
+-        mask_texture = cogl_texture_new_from_sub_texture (mask_texture,
+-                                                          clip->x,
+-                                                          clip->y,
+-                                                          clip->width,
+-                                                          clip->height);
++      if (transformed_clip)
++        mask_texture =
++          cogl_texture_new_from_sub_texture (mask_texture,
++                                             transformed_clip->x,
++                                             transformed_clip->y,
++                                             transformed_clip->width,
++                                             transformed_clip->height);
+ 
+       mask_surface = cairo_image_surface_create (CAIRO_FORMAT_A8,
+                                                  cogl_texture_get_width (mask_texture),
+@@ -996,7 +1000,7 @@ meta_shaped_texture_get_image (MetaShapedTexture     *stex,
+ 
+       cairo_surface_destroy (mask_surface);
+ 
+-      if (clip != NULL)
++      if (transformed_clip)
+         cogl_object_unref (mask_texture);
+     }
+ 
+-- 
+2.19.2
+
+
+From 0adfd8966c2de7bfb3223911d194119f1ab46828 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Thu, 20 Dec 2018 17:34:18 +0100
+Subject: [PATCH 10/10] shaped-texture: Draw external textures via offscreen
+
+EGLStream textures are imported as GL_TEXTURE_EXTERNAL_OES and reading
+pixels directly from them is not supported. To make it possible to get
+pixels, create an offscreen framebuffer and paint the actor to it, then
+read pixels from the framebuffer instead of the texture directly.
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/362
+---
+ src/compositor/meta-shaped-texture.c | 136 ++++++++++++++++++++++++++-
+ 1 file changed, 134 insertions(+), 2 deletions(-)
+
+diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c
+index 823bd47f2..d8c250fc9 100644
+--- a/src/compositor/meta-shaped-texture.c
++++ b/src/compositor/meta-shaped-texture.c
+@@ -35,6 +35,7 @@
+ 
+ #include "clutter-utils.h"
+ #include "meta-texture-tower.h"
++#include "core/boxes-private.h"
+ 
+ #include "meta-cullable.h"
+ 
+@@ -906,6 +907,121 @@ meta_shaped_texture_get_opaque_region (MetaShapedTexture *stex)
+   return priv->opaque_region;
+ }
+ 
++static gboolean
++should_get_via_offscreen (MetaShapedTexture *stex)
++{
++  MetaShapedTexturePrivate *priv = stex->priv;
++
++  if (!cogl_texture_is_get_data_supported (priv->texture))
++    return TRUE;
++
++  return FALSE;
++}
++
++static cairo_surface_t *
++get_image_via_offscreen (MetaShapedTexture     *stex,
++                         cairo_rectangle_int_t *clip)
++{
++  MetaShapedTexturePrivate *priv = stex->priv;
++  ClutterBackend *clutter_backend = clutter_get_default_backend ();
++  CoglContext *cogl_context =
++    clutter_backend_get_cogl_context (clutter_backend);
++  CoglTexture *image_texture;
++  GError *error = NULL;
++  CoglOffscreen *offscreen;
++  CoglFramebuffer *fb;
++  CoglMatrix projection_matrix;
++  unsigned int fb_width, fb_height;
++  cairo_rectangle_int_t fallback_clip;
++  CoglColor clear_color;
++  cairo_surface_t *surface;
++
++  if (cogl_has_feature (cogl_context, COGL_FEATURE_ID_TEXTURE_NPOT))
++    {
++      fb_width = priv->tex_width;
++      fb_height = priv->tex_height;
++    }
++  else
++    {
++      fb_width = clutter_util_next_p2 (priv->tex_width);
++      fb_height = clutter_util_next_p2 (priv->tex_height);
++    }
++
++  if (!clip)
++    {
++      fallback_clip = (cairo_rectangle_int_t) {
++        .width = priv->tex_width,
++        .height = priv->tex_height,
++      };
++      clip = &fallback_clip;
++    }
++
++  image_texture =
++    COGL_TEXTURE (cogl_texture_2d_new_with_size (cogl_context,
++                                                 fb_width, fb_height));
++  cogl_primitive_texture_set_auto_mipmap (COGL_PRIMITIVE_TEXTURE (image_texture),
++                                          FALSE);
++  if (!cogl_texture_allocate (COGL_TEXTURE (image_texture), &error))
++    {
++      g_error_free (error);
++      cogl_object_unref (image_texture);
++      return FALSE;
++    }
++
++  if (fb_width != priv->tex_width || fb_height != priv->tex_height)
++    {
++      CoglSubTexture *sub_texture;
++
++      sub_texture = cogl_sub_texture_new (cogl_context,
++                                          image_texture,
++                                          0, 0,
++                                          priv->tex_width, priv->tex_height);
++      cogl_object_unref (image_texture);
++      image_texture = COGL_TEXTURE (sub_texture);
++    }
++
++  offscreen = cogl_offscreen_new_with_texture (COGL_TEXTURE (image_texture));
++  fb = COGL_FRAMEBUFFER (offscreen);
++  cogl_object_unref (image_texture);
++  if (!cogl_framebuffer_allocate (fb, &error))
++    {
++      g_error_free (error);
++      cogl_object_unref (fb);
++      return FALSE;
++    }
++
++  cogl_framebuffer_push_matrix (fb);
++  cogl_matrix_init_identity (&projection_matrix);
++  cogl_matrix_scale (&projection_matrix,
++                     1.0 / (priv->tex_width / 2.0),
++                     -1.0 / (priv->tex_height / 2.0), 0);
++  cogl_matrix_translate (&projection_matrix,
++                         -(priv->tex_width / 2.0),
++                         -(priv->tex_height / 2.0), 0);
++
++  cogl_framebuffer_set_projection_matrix (fb, &projection_matrix);
++
++  cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 0);
++  cogl_framebuffer_clear (fb, COGL_BUFFER_BIT_COLOR, &clear_color);
++
++  do_paint (stex, fb, priv->texture, NULL);
++
++  cogl_framebuffer_pop_matrix (fb);
++
++  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
++                                        clip->width, clip->height);
++  cogl_framebuffer_read_pixels (fb,
++                                clip->x, clip->y,
++                                clip->width, clip->height,
++                                CLUTTER_CAIRO_FORMAT_ARGB32,
++                                cairo_image_surface_get_data (surface));
++  cogl_object_unref (fb);
++
++  cairo_surface_mark_dirty (surface);
++
++  return surface;
++}
++
+ /**
+  * meta_shaped_texture_get_image:
+  * @stex: A #MetaShapedTexture
+@@ -927,7 +1043,6 @@ meta_shaped_texture_get_image (MetaShapedTexture     *stex,
+   MetaShapedTexturePrivate *priv = stex->priv;
+   cairo_rectangle_int_t *transformed_clip = NULL;
+   CoglTexture *texture, *mask_texture;
+-  cairo_rectangle_int_t texture_rect = { 0, 0, 0, 0 };
+   cairo_surface_t *surface;
+ 
+   g_return_val_if_fail (META_IS_SHAPED_TEXTURE (stex), NULL);
+@@ -937,17 +1052,34 @@ meta_shaped_texture_get_image (MetaShapedTexture     *stex,
+   if (texture == NULL)
+     return NULL;
+ 
++  if (priv->tex_width == 0 || priv->tex_height == 0)
++    return NULL;
+ 
+   if (clip != NULL)
+     {
++      double tex_scale;
++      cairo_rectangle_int_t tex_rect;
++
+       transformed_clip = alloca (sizeof (cairo_rectangle_int_t));
+       *transformed_clip = *clip;
+ 
+-      if (!meta_rectangle_intersect (&texture_rect, transformed_clip,
++      clutter_actor_get_scale (CLUTTER_ACTOR (stex), &tex_scale, NULL);
++      meta_rectangle_scale_double (transformed_clip, 1.0 / tex_scale,
++                                   META_ROUNDING_STRATEGY_GROW);
++
++      tex_rect = (cairo_rectangle_int_t) {
++        .width = priv->tex_width,
++        .height = priv->tex_height,
++      };
++
++      if (!meta_rectangle_intersect (&tex_rect, transformed_clip,
+                                      transformed_clip))
+         return NULL;
+     }
+ 
++  if (should_get_via_offscreen (stex))
++    return get_image_via_offscreen (stex, transformed_clip);
++
+   if (transformed_clip)
+     texture = cogl_texture_new_from_sub_texture (texture,
+                                                  transformed_clip->x,
+-- 
+2.19.2
+
diff --git a/SOURCES/startup-notification.patch b/SOURCES/startup-notification.patch
new file mode 100644
index 0000000..1a4dd0b
--- /dev/null
+++ b/SOURCES/startup-notification.patch
@@ -0,0 +1,45 @@
+From 4ed430b4ef3013c96fa56cdc57b925b42d20ead9 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
+Date: Thu, 20 Oct 2016 18:00:04 +0200
+Subject: [PATCH] gtk-shell: Work around non-working startup notifications
+
+GNOME Shell relies on the MetaScreen::startup-sequence-changed signal,
+which is tied to (lib)startup-notification and therefore X11. As a result,
+when we remove the startup sequence of a wayland client, GNOME Shell will
+not be notified about this until startup-notification's timeout is hit.
+As a temporary stop-gap, go through XWayland even for wayland clients,
+so that the signal is emitted when expected.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=768531
+---
+ src/wayland/meta-wayland-gtk-shell.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/src/wayland/meta-wayland-gtk-shell.c b/src/wayland/meta-wayland-gtk-shell.c
+index d6e249f..9d1a19e 100644
+--- a/src/wayland/meta-wayland-gtk-shell.c
++++ b/src/wayland/meta-wayland-gtk-shell.c
+@@ -219,11 +219,21 @@ gtk_shell_set_startup_id (struct wl_client   *client,
+                           struct wl_resource *resource,
+                           const char         *startup_id)
+ {
++#if 0
+   MetaDisplay *display;
+ 
+   display = meta_get_display ();
+   meta_startup_notification_remove_sequence (display->startup_notification,
+                                              startup_id);
++#else
++  /* HACK: MetaScreen::startup-sequence-changed is currently tied to
++           (lib)startup-notification, which means it only works on X11;
++           so for now, always go through XWayland, even for wayland clients */
++  gdk_x11_display_broadcast_startup_message (gdk_display_get_default (),
++                                             "remove",
++                                             "ID", startup_id,
++                                             NULL);
++#endif
+ }
+ 
+ static void
+-- 
+2.9.3
diff --git a/SPECS/mutter.spec b/SPECS/mutter.spec
new file mode 100644
index 0000000..d0a28fd
--- /dev/null
+++ b/SPECS/mutter.spec
@@ -0,0 +1,1260 @@
+%global gtk3_version 3.19.8
+%global glib_version 2.53.2
+%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
+
+Name:          mutter
+Version:       3.28.3
+Release:       18%{?dist}
+Summary:       Window and compositing manager based on Clutter
+
+License:       GPLv2+
+#VCS:          git:git://git.gnome.org/mutter
+URL:           http://www.gnome.org
+Source0:       http://download.gnome.org/sources/%{name}/3.28/%{name}-%{version}.tar.xz
+
+Patch0:        startup-notification.patch
+
+# Upload HW cursor on demand, avoiding stuttering on hybrid GPU laptops
+Patch1:        hw-cursor-on-demand-gnome-3-28.patch
+
+# Check hw support for calculated view transform
+Patch2:        0001-renderer-native-Check-calculated-transform-when-crea.patch
+
+# PipeWire 0.2.2+ support
+Patch3:        mutter-search-for-libpipewire-0_2.patch
+Patch4:        mutter-pipewire-0_2-API.patch
+
+# Backport remote desktop related fixes and features
+# From master/gnome-3-29
+Patch5:        0001-virtual-input-evdev-Translate-from-button-codes-inte.patch
+Patch6:        0001-Make-screen-cast-and-remote-desktop-non-experimental.patch
+Patch7:        0001-Add-remote-access-controller-API.patch
+
+# Allow Xwayland grabs by default, on a selected set of X11 apps
+# https://bugzilla.redhat.com/1500399
+Patch8: 0001-wayland-Allow-Xwayland-grabs-on-selected-apps.patch
+
+# From gnome-3-28
+#
+# Upstream issue: https://gitlab.gnome.org/GNOME/mutter/issues/189
+# Upstream issue: https://gitlab.gnome.org/GNOME/mutter/issues/192
+# Downstream bug: https://bugzilla.redhat.com/1635123
+Patch10: 0001-window-wayland-Always-update-monitor-for-non-user-op.patch
+Patch11: 0001-window-Pass-flag-to-meta_window_update_monitor-inste.patch
+Patch12: 0002-window-Force-update-monitor-on-hot-plugs.patch
+Patch13: 0001-window-wayland-Don-t-recursive-indefinitely-when-upd.patch
+
+# Upstream issue: https://gitlab.gnome.org/GNOME/mutter/issues/194
+# Downstream bug: https://bugzilla.redhat.com/1635106
+Patch14: 0001-wayland-Nullify-monitor-resources-when-updating-outp.patch
+Patch15: 0001-wayland-No-xdg-output-events-without-a-logical-monit.patch
+
+# Upstream issue: https://gitlab.gnome.org/GNOME/mutter/issues/255
+# Downstream bug: https://bugzilla.redhat.com/1635106
+Patch16: 0001-wayland-Clean-up-xwayland-grabs-even-if-surface-is-g.patch
+
+# Upstream issue: https://gitlab.gnome.org/GNOME/mutter/issues/70
+# Downstream bug: https://bugzilla.redhat.com/1635155
+Patch17: 0001-native-gpu-Handle-drmModeSetCrtc-failing-gracefully.patch
+
+# Upstream issue: https://gitlab.gnome.org/GNOME/mutter/issues/15
+# Downstream bug: https://bugzilla.redhat.com/1635159
+Patch18: 0001-window-Don-t-refuse-to-move-focus-to-the-grab-window.patch
+Patch19: 0002-window-Explicitly-exclude-unmanaging-window-from-foc.patch
+Patch20: 0003-tests-Add-closed-transient-test.patch
+
+# Upstream issue: https://gitlab.gnome.org/GNOME/mutter/issues/130
+# Downstream bug: https://bugzilla.redhat.com/1635164
+Patch21: 0001-monitor-Use-current-monitor-mode-to-check-whether-ac.patch
+
+# Upstream issue: https://gitlab.gnome.org/GNOME/mutter/issues/223
+# Downstream bug: https://bugzilla.redhat.com/1635167
+Patch22: 0001-gpu-kms-Don-t-crash-if-drmModeGetResources-returns-N.patch
+Patch23: 0001-gpu-kms-Handle-drmModeGetResources-failing.patch
+Patch24: 0002-monitor-manager-kms-Check-if-GPUs-can-have-outputs.patch
+
+# Upstream issue: https://gitlab.gnome.org/GNOME/mutter/issues/127
+# Downstream bug: https://bugzilla.redhat.com/1635170
+Patch25: 0001-renderer-native-Fallback-to-non-planar-API-if-gbm_bo.patch
+
+# Upstream issue: https://bugzilla.gnome.org/show_bug.cgi?id=784206
+# Downstream bug: https://bugzilla.redhat.com/1635235
+Patch26: 0001-wayland-keyboard-Create-a-separate-keymap-shm-file-p.patch
+
+# Upstream issue: https://bugzilla.gnome.org/show_bug.cgi?id=788834
+# Downstream bug: https://bugzilla.redhat.com/1635237
+Patch27: 0001-window-Return-1-if-meta_window_get_monitor-is-called.patch
+
+# Upstream issue: https://gitlab.gnome.org/GNOME/mutter/issues/254
+# Downstream bug: https://bugzilla.redhat.com/1635241
+Patch28: 0001-backends-x11-Only-free-cursor-if-it-was-created-succ.patch
+
+# Upstream issue: https://gitlab.gnome.org/GNOME/mutter/issues/134
+# Downstream bug: https://bugzilla.redhat.com/1635248
+Patch29: 0001-idle-monitor-Don-t-try-to-auto-start-SessionManager.patch
+
+# Upstream issue: https://gitlab.gnome.org/GNOME/mutter/issues/240
+# Upstream issue: https://gitlab.gnome.org/GNOME/mutter/issues/174
+# Upstream issue: https://gitlab.gnome.org/GNOME/mutter/issues/221
+# Downstream bug: https://bugzilla.redhat.com/1635718
+Patch30: 0001-window-unmanage-dialog-when-clearing-transient_for.patch
+Patch31: 0001-wayland-xdg-shell-Queue-frame-callbacks-on-new-actor.patch
+Patch32: 0002-wayland-surface-Add-API-to-cache-frame-callbacks.patch
+Patch33: 0003-wayland-xdg-shell-Cache-pending-frame-callbacks-on-p.patch
+Patch34: 0004-wayland-xdg-shell-Cache-frame-callbacks-if-toplevel-.patch
+Patch35: 0005-wayland-xdg-shell-Handle-requests-after-toplevel-was.patch
+Patch36: 0006-wayland-legacy-xdg-shell-Cache-frame-callbacks-if-to.patch
+Patch37: 0007-wayland-legacy-xdg-shell-Handle-requests-after-tople.patch
+Patch38: 0008-wayland-gtk-shell-Handle-requests-after-toplevel-was.patch
+
+# Fix garbled window titles (rhbz#1639194)
+Patch39: 0001-x11-window-props-Do-not-convert-WM_NAME.patch
+Patch40: 0002-xprops-Make-sure-text_property_to_utf8-returns-UTF8.patch
+
+# Upstream issue: https://gitlab.gnome.org/GNOME/mutter/issues/2
+# Upstream issue: https://bugzilla.gnome.org/show_bug.cgi?id=782575
+Patch41: 0001-renderer-native-Choose-first-EGL-config-for-non-GBM-.patch
+Patch42: 0001-wayland-buffer-Create-EGLStream-texture-at-buffer_re.patch
+Patch43: 0001-wayland-Always-realize-buffers-at-surface-commit-tim.patch
+Patch44: 0001-wayland-Realize-dmabuf-buffers-before-trying-to-atta.patch
+Patch45: 0001-wayland-Create-EGLStream-backed-buffers-through-wl_e.patch
+
+# Upstream issue: https://bugzilla.gnome.org/show_bug.cgi?id=790316
+Patch46: 0001-renderer-native-Also-wrap-flip-closures-for-EGLStrea.patch
+
+# Upstream issue: https://gitlab.gnome.org/GNOME/mutter/issues/106
+# Downstream bug: https://bugzilla.redhat.com/show_bug.cgi?id=1591250
+Patch51: 0001-renderer-native-Use-shadow-fb-on-software-GL-if-pref.patch
+
+# RecordWindow
+Patch61: 0001-clutter-evdev-Use-clutter_event_set_device.patch
+Patch62: 0002-clutter-Keep-a-device-reference-with-events.patch
+Patch63: 0003-remote-desktop-Do-not-leak-the-virtual-touchscreen.patch
+Patch64: 0004-screen-cast-src-Add-VideoCrop-support.patch
+Patch65: 0005-screen-cast-Add-screen-cast-window-interface.patch
+Patch66: 0006-window-actor-Implement-MetaScreenCastWindow-interfac.patch
+Patch67: 0007-screen-cast-session-Add-screen-cast-window-mode.patch
+Patch68: 0008-screen-cast-session-Add-support-for-RecordWindow.patch
+Patch69: 0009-window-Add-window-id.patch
+Patch70: 0010-screen-cast-session-Add-window-id-support.patch
+Patch71: 0011-window-Expose-the-client-type-in-the-API.patch
+Patch72: 0012-window-Fix-introspection-warnings.patch
+
+# Get texture pixels via offscreen for EGLStreams (rhbz#1656926)
+Patch80: shaped-texture-get-image-via-offscreen.patch
+
+# Avoid EGLStream backend deadlock (rhbz#1656905)
+Patch81: eglstream-mailbox-mode.patch
+
+# Screen cast cursor side channel (rhbz#1658971)
+Patch82: screen-cast-cursor-side-channel.patch
+
+# Regression due to a change in meta_shaped_texture_get_image() not
+# updating the callers' clipping rectangle anymore.
+Patch83: 0001-window-actor-Use-actual-image-size-for-capture.patch
+
+# Fix for HiDPI screen recording (rhbz#1670287)
+Patch84: 0001-screen-cast-Fix-monitor-recording-on-HiDPI.patch
+
+# Fix corruption on suspend and resume with nvidia (rhbz#1663440)
+Patch10001: 0001-cogl-add-new-UNSTABLE_TEXTURES-feature.patch
+Patch10002: 0002-backend-switch-to-using-generated-logind-proxy.patch
+Patch10003: 0003-backend-add-signals-for-reporting-suspend-and-resume.patch
+Patch10004: 0004-wayland-force-X-clients-to-redraw-on-resume.patch
+Patch10005: 0005-backends-native-emit-gl-video-memory-purged-when-bec.patch
+Patch10006: 0006-backends-native-update-glyph-cache-on-resume.patch
+Patch10007: 0007-backends-native-update-cursor-on-resume.patch
+Patch10008: 0008-background-purge-all-background-textures-on-suspend.patch
+Patch10009: 0009-MetaShapedTexture-save-and-restore-textures-on-suspe.patch
+
+# Restore "Always update monitor for non user op" previously dropped
+# Upstream issue: https://gitlab.gnome.org/GNOME/mutter/issues/189
+Patch10010: 0001-window-Really-force-update-monitor-on-hot-plugs.patch
+
+# RHEL 7 downstream patches
+Patch100: deal-more-gracefully-with-oversized-windows.patch
+# Work-around for Xvnc resizing (rhbz#1265511)
+Patch101: 0001-monitor-manager-xrandr-Work-around-spurious-hotplugs.patch
+Patch102: 0001-monitor-manager-xrandr-Force-an-update-when-resuming.patch
+Patch103: 0001-monitor-manager-Consider-external-layout-before-defa.patch
+Patch104: 0001-events-Don-t-move-sloppy-focus-while-buttons-are-pre.patch
+Patch105: 0001-backends-x11-Support-synaptics-configuration.patch
+Patch106: 0001-window-actor-Special-case-shaped-Java-windows.patch
+Patch107: 0001-clutter-Extend-touchpad-device-property-check-for-Sy.patch
+Patch108: 0001-clutter-x11-Implement-keycode-lookup-from-keysyms-on.patch
+# http://bugzilla.gnome.org/show_bug.cgi?id=733277
+Patch109: 0001-Add-support-for-quad-buffer-stereo.patch
+# https://bugzilla.redhat.com/show_bug.cgi?id=1618632
+# https://bugzilla.redhat.com/show_bug.cgi?id=1497303
+Patch110: 0001-monitor-manager-only-reuse-initial-config-if-monitor.patch
+Patch111: 0001-clutter-Do-not-latch-modifiers-on-modifier-keys.patch
+Patch112: add-support-for-plain-old-x-device-configuration.patch
+Patch113: 0001-main-be-more-aggressive-in-assuming-X11-backend.patch
+Patch114: 0001-clutter-Only-reset-scroll-axes-on-slave-devices.patch
+# https://bugzilla.redhat.com/show_bug.cgi?id=1668979
+Patch115: 0001-wayland-Relax-requirements-for-evdev-events-to-have-.patch
+Patch116: 0001-clutter-Add-clutter_input_method_forward_key-method.patch
+
+BuildRequires: chrpath
+BuildRequires: pango-devel
+BuildRequires: startup-notification-devel
+BuildRequires: gnome-desktop3-devel
+BuildRequires: glib2-devel >= %{glib_version}
+BuildRequires: gtk3-devel >= %{gtk3_version}
+BuildRequires: pkgconfig
+BuildRequires: gobject-introspection-devel >= 1.41.0
+BuildRequires: libSM-devel
+BuildRequires: libwacom-devel
+BuildRequires: libX11-devel
+BuildRequires: libXdamage-devel
+BuildRequires: libXext-devel
+BuildRequires: libXfixes-devel
+BuildRequires: libXi-devel
+BuildRequires: libXrandr-devel
+BuildRequires: libXrender-devel
+BuildRequires: libXcursor-devel
+BuildRequires: libXcomposite-devel
+BuildRequires: libxcb-devel
+BuildRequires: libxkbcommon-devel
+BuildRequires: libxkbcommon-x11-devel
+BuildRequires: libxkbfile-devel
+BuildRequires: libXtst-devel
+BuildRequires: mesa-libEGL-devel
+BuildRequires: mesa-libGLES-devel
+BuildRequires: mesa-libGL-devel
+BuildRequires: mesa-libgbm-devel
+BuildRequires: pam-devel
+BuildRequires: pipewire-devel >= %{pipewire_version}
+BuildRequires: systemd-devel
+BuildRequires: upower-devel
+BuildRequires: xkeyboard-config-devel
+BuildRequires: zenity
+BuildRequires: desktop-file-utils
+# Bootstrap requirements
+BuildRequires: gtk-doc gnome-common gettext-devel git
+BuildRequires: libcanberra-devel
+BuildRequires: gsettings-desktop-schemas-devel >= %{gsettings_desktop_schemas_version}
+BuildRequires: automake, autoconf, libtool
+BuildRequires: pkgconfig(gudev-1.0)
+BuildRequires: pkgconfig(libdrm)
+BuildRequires: pkgconfig(gbm)
+BuildRequires: pkgconfig(wayland-server)
+BuildRequires: pkgconfig(wayland-eglstream)
+
+BuildRequires: json-glib-devel >= %{json_glib_version}
+BuildRequires: libgudev1-devel
+BuildRequires: libwayland-server-devel
+BuildRequires: libinput-devel >= %{libinput_version}
+
+Obsoletes: mutter-wayland < 3.13.0
+Obsoletes: mutter-wayland-devel < 3.13.0
+
+# Make sure yum updates gnome-shell as well; otherwise we might end up with
+# broken gnome-shell installations due to mutter ABI changes.
+Conflicts: gnome-shell < 3.21.1
+
+Requires: control-center-filesystem
+Requires: gsettings-desktop-schemas%{?_isa} >= %{gsettings_desktop_schemas_version}
+Requires: gtk3%{?_isa} >= %{gtk3_version}
+Requires: pipewire%{_isa} >= %{pipewire_version}
+Requires: startup-notification
+Requires: dbus-x11
+Requires: zenity
+
+Requires:      json-glib%{?_isa} >= %{json_glib_version}
+Requires:      libinput%{?_isa} >= %{libinput_version}
+
+%description
+Mutter is a window and compositing manager that displays and manages
+your desktop via OpenGL. Mutter combines a sophisticated display engine
+using the Clutter toolkit with solid window-management logic inherited
+from the Metacity window manager.
+
+While Mutter can be used stand-alone, it is primarily intended to be
+used as the display core of a larger system such as GNOME Shell. For
+this reason, Mutter is very extensible via plugins, which are used both
+to add fancy visual effects and to rework the window management
+behaviors to meet the needs of the environment.
+
+%package devel
+Summary: Development package for %{name}
+Requires: %{name}%{?_isa} = %{version}-%{release}
+
+%description devel
+Header files and libraries for developing Mutter plugins. Also includes
+utilities for testing Metacity/Mutter themes.
+
+%package  tests
+Summary:  Tests for the %{name} package
+Requires: %{name}%{?_isa} = %{version}-%{release}
+
+%description tests
+The %{name}-tests package contains tests that can be used to verify
+the functionality of the installed %{name} package.
+
+%prep
+%autosetup -S git
+
+%build
+autoreconf -f -i
+(if ! test -x configure; then NOCONFIGURE=1 ./autogen.sh; fi;
+ %configure --disable-static --enable-compile-warnings=maximum --enable-remote-desktop --enable-installed-tests --with-libwacom --enable-egl-device)
+
+SHOULD_HAVE_DEFINED="HAVE_SM HAVE_RANDR HAVE_STARTUP_NOTIFICATION"
+
+for I in $SHOULD_HAVE_DEFINED; do
+  if ! grep -q "define $I" config.h; then
+    echo "$I was not defined in config.h"
+    grep "$I" config.h
+    exit 1
+  else
+    echo "$I was defined as it should have been"
+    grep "$I" config.h
+  fi
+done
+
+make %{?_smp_mflags} V=1
+
+%install
+%make_install
+
+#Remove libtool archives.
+rm -rf %{buildroot}/%{_libdir}/*.la
+
+%find_lang %{name}
+
+# Mutter contains a .desktop file so we just need to validate it
+desktop-file-validate %{buildroot}/%{_datadir}/applications/%{name}.desktop
+
+%post -p /sbin/ldconfig
+
+%postun -p /sbin/ldconfig
+
+%files -f %{name}.lang
+%license COPYING
+%doc NEWS
+%{_bindir}/mutter
+%{_datadir}/applications/*.desktop
+%{_libdir}/lib*.so.*
+%{_libdir}/mutter/
+%{_libexecdir}/mutter-restart-helper
+%{_datadir}/GConf/gsettings/mutter-schemas.convert
+%{_datadir}/glib-2.0/schemas/org.gnome.mutter.gschema.xml
+%{_datadir}/glib-2.0/schemas/org.gnome.mutter.wayland.gschema.xml
+%{_datadir}/gnome-control-center/keybindings/50-mutter-*.xml
+%{_mandir}/man1/mutter.1*
+
+%files devel
+%{_includedir}/*
+%{_libdir}/lib*.so
+%{_libdir}/pkgconfig/*
+
+%files tests
+%{_libexecdir}/installed-tests/mutter
+%{_libexecdir}/installed-tests/mutter-clutter
+%{_libexecdir}/installed-tests/mutter-cogl
+%{_datadir}/installed-tests/mutter
+%{_datadir}/installed-tests/mutter-clutter
+%{_datadir}/installed-tests/mutter-cogl
+%{_datadir}/mutter/tests
+
+%changelog
+* Thu Feb 21 2019 Jonas Ådahl <jadahl@redhat.com> - 3.28.3-18
+- Remove patch enabling monitor framebuffer scaling
+  Related: #1668883
+
+* Mon Feb 11 2019 Ray Strode <rstrode@redhat.com> - 3.28.3-17
+- Fix bug in suspend/resume corruption patch leading to inhibit fd
+  not getting fetched
+  Related: #1663440
+
+* Mon Feb 11 2019 Florian Müllner <fmuellner@redhat.com> - 3.28.3-16
+- Backport forward_key() method
+  Related: #1668979
+
+* Mon Feb 11 2019 Florian Müllner <fmuellner@redhat.com> - 3.28.3-15
+- Re-add dropped downstream patches (rhbz#1668883)
+
+* Tue Feb 05 2019 Olivier Fourdan <jadahl@redhat.com> - 3.28.3-14
+- Restore update monitor fix (rhbz#1635123)
+
+* Fri Feb 01 2019 Jonas Ådahl <jadahl@redhat.com> - 3.28.3-13
+- Fix screen recording on HiDPI monitor (rhbz#1670287)
+
+* Thu Jan 31 2019 Ray Strode <rstrode@redhat.com> - 3.28.3-12
+- Drop "Always update monitor for non user op" patch. It's already
+  in tree and getting misapplied
+  Related: #1663440
+- Fix suspend and resume corruption on NVidia
+  Resolves: #1663440
+
+* Tue Jan 22 2019 Olivier Fourdan <ofourdan@redhat.com> - 3.28.3-11
+- Fix a new crash in recordwindow related to behavior changes in
+  recent backport additions (rhbz#1657661)
+
+* Fri Jan 11 2019 Jonas Ådahl <jadahl@redhat.com> - 3.28.3-10
+- Backport screen cast cursor side channel patches (rhbz#1658971)
+
+* Fri Jan 11 2019 Jonas Ådahl <jadahl@redhat.com> - 3.28.3-9
+- Avoid EGLStream backend deadlock (rhbz#1656905)
+
+* Fri Jan 11 2019 Jonas Ådahl <jadahl@redhat.com> - 3.28.3-8
+- Get texture pixels via offscreen for EGLStreams (rhbz#1656926)
+
+* Mon Jan 07 2019 Olivier Fourdan <ofourdan@redhat.com> - 3.28.3-7
+- Backport the RecordWindow screencast mode (rhbz#1657661)
+
+* Fri Jan 04 2019 Ray Strode <rstrode@redhat.com> - 3.28.3-6
+- Add shadow framebuffer for server cards to fix blending
+  performance
+  Resolves: #1591250
+
+* Fri Oct 26 2018 Olivier Fourdan <ofourdan@redhat.com> - 3.28.3-5
+- Allow Xwayland grabs on a selected set of X11 applications.
+  (rhbz#1500399)
+
+* Tue Oct 23 2018 Olivier Fourdan <ofourdan@redhat.com> - 3.28.3-4
+- More backport fixes from upstream "gnome-3-28" branch
+- enable eglstream support (rhbz#1639782)
+
+* Mon Oct 15 2018 Jonas Ådahl <jadahl@redhat.com> - 3.28.3-3
+- Fix garbled window titles (rhbz#1639194)
+
+* Thu Oct 04 2018 Olivier Fourdan <ofourdan@redhat.com> - 3.28.3-2
+- Backport fixes from upstream "gnome-3-28" branch:
+- [wayland] laptop with lid closed and external monitor can't log in to
+  wayland session (rhbz#1635106)
+- [Wayland] Crash with Xwayland grabs enabled in mutter (rhbz#1635110)
+- [Wayland] Crash on monitor hotplug (rhbz#1635123)
+- [wayland] mutter crashes if drmModeSetCrtc() failed (rhbz#1635155)
+- mutter crashes if a modal window closes whilst being dragged (rhbz#1635159)
+- gnome-shell crashed with SIGSEGV in meta_monitor_mode_get_resolution()
+  (rhbz#1635164)
+- [wayland] crash when drmModeGetResources() fails (rhbz#1635167)
+- [wayland] Can't create new back buffer on Intel i915 (rhbz#1635170)
+- [wayland] keyboard: Create a separate keymap shm file per resource
+  (rhbz#1635235)
+- Crash in gnome-shell/mutter after a window is destroyed (rhbz#1635237)
+- [x11] Using a cursor theme missing cursors can crash mutter (rhbz#1635241)
+- [wayland] Warning messages when starting mutter (rhbz#1635248)
+- [wayland] mutter/gnome-shell crash after failed DnD in nautilus
+  (rhbz#1635718)
+
+* Thu Aug 09 2018 Kalev Lember <klember@redhat.com> - 3.28.3-1
+- Update to 3.28.3
+- Apply HW cursor on-demand patches
+- Apply monitor transform regression patch
+
+* Wed Aug 08 2018 Jonas Ådahl <jadahl@redhat.com> - 3.28.1-4
+- Backport remote-access controller API patch
+
+* Tue Aug 07 2018 Jonas Ådahl <jadahl@redhat.com> - 3.28.1-3
+- Backport remote desktop related patches
+
+* Wed Aug 01 2018 Jan Grulich <jgrulich@redhat.com> - 3.28.1-2
+- Support PipeWire 0.2.2+
+
+* Fri Apr 13 2018 Florian Müllner <fmuellner@redhat.com> - 3.28.1-1
+- Update to 3.28.1
+
+* Mon Mar 12 2018 Florian Müllner <fmuellner@redhat.com> - 3.28.0-1
+- Update to 3.28.0
+
+* Mon Mar 05 2018 Florian Müllner <fmuellner@redhat.com> - 3.27.92-1
+- Update to 3.27.92
+
+* Wed Feb 28 2018 Adam Williamson <awilliam@redhat.com> - 3.27.91-2
+- Backport MR#36 to fix RHBZ #1547691 (GGO #2), mouse issues
+
+* Wed Feb 21 2018 Florian Müllner <fmuellner@redhat.com> - 3.27.91-1
+- Update to 3.27.91
+
+* Tue Feb 13 2018 Björn Esser <besser82@fedoraproject.org> - 3.27.1-4
+- Rebuild against newer gnome-desktop3 package
+- Add patch for adjustments to pipewire 0.1.8 API
+
+* Thu Feb 08 2018 Fedora Release Engineering <releng@fedoraproject.org> - 3.27.1-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
+
+* Sat Jan 06 2018 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 3.27.1-2
+- Remove obsolete scriptlets
+
+* Mon Oct 30 2017 Florian Müllner <fmuellner@redhat.com> - 3.27.1-1
+- Include 32-bit build fixes
+
+* Tue Oct 17 2017 Florian Müllner <fmuellner@redhat.com> - 3.27.1-1
+- Update to 3.27.1
+
+* Fri Oct 06 2017 Florian Müllner <fmuellner@redhat.com> - 3.26.1-2
+- Fix screencasts
+
+* Wed Oct 04 2017 Florian Müllner <fmuellner@redhat.com> - 3.26.1-1
+- Update to 3.26.1
+
+* Thu Sep 21 2017 Florian Müllner <fmuellner@redhat.com> - 3.26.0-5
+- Adjust to pipewire API break
+
+* Wed Sep 20 2017 Florian Müllner <fmuellner@redhat.com> - 3.26.0-5
+- Enable tablet support
+
+* Tue Sep 12 2017 Adam Williamson <awilliam@redhat.com> - 3.26.0-4
+- Also backport BGO #787570 fix from upstream
+
+* Tue Sep 12 2017 Adam Williamson <awilliam@redhat.com> - 3.26.0-3
+- Backport upstream fixes for crasher bug BGO #787568
+
+* Tue Sep 12 2017 Florian Müllner <fmuellner@redhat.com> - 3.26.0-2
+- Enable remote desktop support
+
+* Tue Sep 12 2017 Florian Müllner <fmuellner@redhat.com> - 3.26.0-1
+- Update to 3.26.0
+
+* Thu Sep 07 2017 Florian Müllner <fmuellner@redhat.com> - 3.25.92-1
+- Update to 3.25.92
+
+* Thu Aug 24 2017 Bastien Nocera <bnocera@redhat.com> - 3.25.91-2
++ mutter-3.25.91-2
+- Fix inverted red and blue channels with newer Mesa
+
+* Tue Aug 22 2017 Florian Müllner <fmuellner@redhat.com> - 3.25.91-1
+- Update to 3.25.91
+
+* Thu Aug 10 2017 Florian Müllner <fmuellner@redhat.com> - 3.25.90-1
+- Update to 3.25.90
+
+* Thu Aug 03 2017 Fedora Release Engineering <releng@fedoraproject.org> - 3.25.4-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild
+
+* Wed Jul 26 2017 Fedora Release Engineering <releng@fedoraproject.org> - 3.25.4-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild
+
+* Wed Jul 19 2017 Florian Müllner <fmuellner@redhat.con> - 3.25.4-1
+- Update to 3.25.4
+
+* Wed Jun 21 2017 Florian Müllner <fmuellner@redhat.com> - 3.25.3-1
+- Update to 3.25.3
+
+* Wed May 24 2017 Florian Müllner <fmuellner@redhat.com> - 3.25.2-1
+- Update to 3.25.2
+
+* Thu May 18 2017 Florian Müllner <fmuellner@redhat.com> - 3.25.1-2
+- Fix copy+paste of UTF8 strings between X11 and wayland
+
+* Thu Apr 27 2017 Florian Müllner <fmuellner@redhat.com> - 3.25.1-1
+- Update to 3.25.1
+
+* Tue Apr 11 2017 Florian Müllner <fmuellner@redhat.com> - 3.24.1-1
+- Update to 3.24.1
+
+* Mon Mar 20 2017 Florian Müllner <fmuellner@redhat.com> - 3.24.0-1
+- Update to 3.24.0
+
+* Tue Mar 14 2017 Florian Müllner <fmuellner@redhat.com> - 3.23.92-1
+- Update to 3.23.92
+
+* Fri Mar 10 2017 Florian Müllner <fmuellner@redhat.com> - 3.23.91-4
+- Apply startup-notification hack again
+
+* Tue Mar 07 2017 Adam Williamson <awilliam@redhat.com> - 3.23.91-3
+- Backport more color fixes, should really fix BGO #779234, RHBZ #1428559
+
+* Thu Mar 02 2017 Adam Williamson <awilliam@redhat.com> - 3.23.91-2
+- Backport fix for a color issue in 3.23.91 (BGO #779234, RHBZ #1428559)
+
+* Wed Mar 01 2017 Florian Müllner <fmuellner@redhat.com> - 3.23.91-1
+- Update to 3.23.91
+
+* Thu Feb 16 2017 Florian Müllner <fmuellner@redhat.com> - 3.23.90-1
+- Update to 3.23.90
+
+* Fri Feb 10 2017 Fedora Release Engineering <releng@fedoraproject.org> - 3.23.3-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
+
+* Thu Dec 15 2016 Florian Müllner <fmuellner@redhat.com> - 3.23.3-1
+- Update to 3.23.3
+
+* Fri Dec 02 2016 Florian Müllner <fmuellner@redhat.com> - 3.23.2-2
+- Fix build error on 32-bit platforms
+
+* Thu Nov 24 2016 Kevin Fenzi <kevin@scrye.com> - 3.23.2-2
+- Some fixes to get building. Still needs patch1 rebased.
+
+* Wed Nov 23 2016 Florian Müllner <fmuellner@redhat.com> - 3.23.2-1
+- Update to 3.23.2
+
+* Tue Nov  8 2016 Matthias Clasen <mclasen@redhat.com> - 3.23.1-2
+- Fix 1376471
+
+* Sun Oct 30 2016 Florian Müllner <fmuellner@redhat.com> - 3.23.1-1
+- Update to 3.23.1
+
+* Tue Oct 18 2016 Kalev Lember <klember@redhat.com> - 3.22.1-3
+- Backport a fix to make gnome-screenshot --area work
+
+* Tue Oct 11 2016 Adam Jackson <ajax@redhat.com> - 3.22.1-2
+- Prefer eglGetPlatformDisplay() to eglGetDisplay()
+
+* Tue Oct 11 2016 Florian Müllner <fmuellner@redhat.com> - 3.22.1-1
+- Update to 3.22.1
+
+* Wed Sep 28 2016 Florian Müllner <fmuellner@redhat.com> - 3.22.0-2
+- Include fix for crash on VT switch
+
+* Mon Sep 19 2016 Florian Müllner <fmuellner@redhat.com> - 3.22.0-1
+- Update to 3.22.0
+
+* Tue Sep 13 2016 Florian Müllner <fmuellner@redhat.com> - 3.21.92-1
+- Update to 3.21.92
+
+* Thu Sep 08 2016 Kalev Lember <klember@redhat.com> - 3.21.91-2
+- wayland/cursor-role: Increase buffer use count on construction (#1373372)
+
+* Tue Aug 30 2016 Florian Müllner <fmuellner@redhat.com> - 3.21.91-1
+- Update to 3.21.91
+
+* Mon Aug 29 2016 Kalev Lember <klember@redhat.com> - 3.21.90-3
+- clutter/evdev: Fix absolute pointer motion events (#1369492)
+
+* Sat Aug 20 2016 Kalev Lember <klember@redhat.com> - 3.21.90-2
+- Update minimum dep versions
+
+* Fri Aug 19 2016 Florian Müllner <fmuellner@redhat.com> - 3.21.90-1
+- Update to 3.21.90
+
+* Wed Jul 20 2016 Florian Müllner <fmuellner@redhat.com> - 3.21.4-1
+- Update to 3.21.4
+- Drop downstream patch
+- Fix build error on 32-bit
+
+* Tue Jun 21 2016 Florian Müllner <fmuellner@redhat.com> - 3.21.3-1
+- Update to 3.21.3
+
+* Fri May 27 2016 Florian Müllner <fmuellner@redhat.com> - 3.21.2-1
+- Update to 3.21.2
+
+* Fri Apr 29 2016 Florian Müllner <fmuellner@redhat.com> - 3.21.1-1
+- Update to 3.21.1
+
+* Wed Apr 13 2016 Florian Müllner <fmuellner@redhat.com> - 3.20.1-1
+- Update to 3.20.1
+
+* Tue Mar 22 2016 Florian Müllner <fmuellner@redhat.com> - 3.20.0-1
+- Update to 3.20.0
+
+* Wed Mar 16 2016 Florian Müllner <fmuellner@redhat.com> - 3.19.92-1
+- Update to 3.19.92
+
+* Thu Mar 03 2016 Florian Müllner <fmuellner@redhat.com> - 3.19.91-2
+- Include fix for invalid cursor wl_buffer access
+
+* Thu Mar 03 2016 Florian Müllner <fmuellner@redhat.com> - 3.19.91-1
+- Update to 3.19.91
+
+* Fri Feb 19 2016 Florian Müllner <fmuellner@redhat.com> - 3.19.90-1
+- Update to 3.19.90
+
+* Thu Feb 04 2016 Fedora Release Engineering <releng@fedoraproject.org> - 3.19.4-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild
+
+* Thu Jan 21 2016 Florian Müllner <fmuellner@redhat.com> - 3.19.4-1
+- Update to 3.19.4
+
+* Thu Dec 17 2015 Florian Müllner <fmuellner@redhat.com> - 3.19.3-1
+- Update to 3.19.3
+
+* Wed Nov 25 2015 Florian Müllner <fmuellner@redhat.com> - 3.19.2-1
+- Update to 3.19.2
+
+* Tue Nov 10 2015 Ray Strode <rstrode@redhat.com> 3.19.1-5.20151110git049f1556d
+- Update to git snapshot
+
+* Thu Oct 29 2015 Florian Müllner <fmuellner@redhat.com> - 3.19.1-1
+- Update to 3.19.1
+
+* Wed Oct 21 2015 Ray Strode <rstrode@redhat.com> 3.18.1-4
+- Force the cursor visible on vt switches after setting
+  the crtc to workaround that qxl bug from before in a
+  different situation
+  Related: #1273247
+
+* Wed Oct 21 2015 Kalev Lember <klember@redhat.com> - 3.18.1-3
+- Backport a fix for a common Wayland crash (#1266486)
+
+* Thu Oct 15 2015 Kalev Lember <klember@redhat.com> - 3.18.1-2
+- Bump gnome-shell conflicts version
+
+* Thu Oct 15 2015 Florian Müllner <fmuellner@redhat.com> - 3.18.1-1
+- Update to 3.18.1
+
+* Mon Sep 21 2015 Florian Müllner <fmuellner@redhat.com> - 3.18.0-1
+- Update to 3.18.0
+
+* Wed Sep 16 2015 Florian Müllner <fmuellner@redhat.com> - 3.17.92-1
+- Update to 3.17.92
+
+* Thu Sep 03 2015 Florian Müllner <fmuellner@redhat.com> - 3.17.91-1
+- Update to 3.17.91
+
+* Thu Sep 03 2015 Ray Strode <rstrode@redhat.com> 3.17.90-2
+- Add workaround for qxl cursor visibility wonkiness that we
+  did for f22
+  Related: #1200901
+
+* Thu Aug 20 2015 Florian Müllner <fmuellner@redhat.com> - 3.17.90-1
+- Update to 3.17.90
+
+* Thu Jul 23 2015 Florian Müllner <fmuellner@redhat.com> - 3.17.4-1
+- Update to 3.17.4
+
+* Wed Jul 22 2015 David King <amigadave@amigadave.com> - 3.17.3-2
+- Bump for new gnome-desktop3
+
+* Thu Jul 02 2015 Florian Müllner <fmuellner@redhat.com> - 3.17.3-1
+- Update to 3.17.3
+
+* Wed Jun 17 2015 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 3.17.2-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild
+
+* Wed May 27 2015 Florian Müllner <fmuellner@redhat.com> - 3.17.2-1
+- Update to 3.17.2
+
+* Thu Apr 30 2015 Florian Müllner <fmuellner@redhat.com> - 3.17.1-1
+- Update to 3.17.1
+
+* Thu Apr 16 2015 Kalev Lember <kalevlember@gmail.com> - 3.16.1.1-2
+- Bump gnome-shell conflicts version
+
+* Wed Apr 15 2015 Rui Matos <rmatos@redhat.com> - 3.16.1.1-1
+- Update to 3.16.1.1
+
+* Tue Apr 14 2015 Florian Müllner <fmuellner@redhat.com> - 3.16.1-1
+- Update to 3.16.1
+
+* Mon Mar 23 2015 Florian Müllner <fmuellner@redhat.com> - 3.16.0-1
+- Update to 3.16.0
+
+* Tue Mar 17 2015 Kalev Lember <kalevlember@gmail.com> - 3.15.92-2
+- Update minimum dep versions
+- Use license macro for the COPYING file
+
+* Tue Mar 17 2015 Florian Müllner <fmuellner@redhat.com> - 3.15.92-1
+- Update to 3.15.92
+
+* Tue Mar 10 2015 Peter Hutterer <peter.hutterer@redhat.com> - 3.15.91-2
+- Rebuild for libinput soname bump
+
+* Wed Mar 04 2015 Florian Müllner <fmuellner@redhat.com> - 3.15.91-1
+- Update to 3.15.91
+
+* Fri Feb 20 2015 Florian Müllner <fmuellner@redhat.com> - 3.15.90-1
+- Update to 3.15.90
+
+* Mon Feb 02 2015 Adam Williamson <awilliam@redhat.com> - 3.15.4-2
+- backport ad90b7dd to fix BGO #743412 / RHBZ #1185811
+
+* Wed Jan 21 2015 Florian Müllner <fmuellner@redhat.com> - 3.15.4-1
+- Update to 3.15.4
+
+* Mon Jan 19 2015 Peter Hutterer <peter.hutterer@redhat.com> 3.15.3-3
+- Rebuild for libinput soname bump
+
+* Mon Jan 12 2015 Ray Strode <rstrode@redhat.com> 3.15.3-2
+- Add specific BuildRequires for wayland bits, so we don't
+  get wayland support by happenstance.
+- Add BuildRequires for autogoo since ./autogen.sh is run as part of
+  the build process
+
+* Fri Dec 19 2014 Florian Müllner <fmuellner@redhat.com> - 3.15.3-1
+- Revert unsatisfiable wayland requirement
+
+* Fri Dec 19 2014 Florian Müllner <fmuellner@redhat.com> - 3.15.3-1
+- Update to 3.15.3
+
+* Thu Nov 27 2014 Florian Müllner <fmuellner@redhat.com> - 3.15.2-1
+- Update to 3.15.2
+
+* Wed Nov 12 2014 Vadim Rutkovsky <vrutkovs@redhat.com> - 3.15.1-2
+- Build installed tests
+
+* Thu Oct 30 2014 Florian Müllner <fmuellner@redhat.com> - 3.15.1-1
+- Update to 3.15.1
+
+* Tue Oct 21 2014 Florian Müllner <fmuellner@redhat.com> - 3.14.1-2
+- Fix regression in handling raise-on-click option (rhbz#1151918)
+
+* Tue Oct 14 2014 Florian Müllner <fmuellner@redhat.com> - 3.14.1-1
+- Update to 3.14.1
+
+* Fri Oct 03 2014 Adam Williamson <awilliam@redhat.com> - 3.14.0-3
+- backport fix for BGO #737233 / RHBZ #1145952 (desktop right click broken)
+
+* Mon Sep 22 2014 Kalev Lember <kalevlember@gmail.com> - 3.14.0-2
+- Bump gnome-shell conflicts version
+
+* Mon Sep 22 2014 Florian Müllner <fmuellner@redhat.com> - 3.14.0-1
+- Update to 3.14.0
+
+* Wed Sep 17 2014 Florian Müllner <fmuellner@redhat.com> - 3.13.92-1
+- Update to 3.13.92
+
+* Fri Sep 12 2014 Peter Hutterer <peter.hutterer@redhat.com> - 3.13.91-2
+- Rebuild for libinput soname bump
+
+* Wed Sep 03 2014 Florian Müllner <fmuellner@redhat.com> - 3.31.91-1
+- Update to 3.13.91, drop downstream patches
+
+* Tue Aug 26 2014 Adel Gadllah <adel.gadllah@gmail.com> - 3.13.90-4
+- Apply fix for RH #1133166
+
+* Mon Aug 25 2014 Hans de Goede <hdegoede@redhat.com> - 3.13.90-3
+- Add a patch from upstream fixing gnome-shell crashing non stop on
+  multi monitor setups (rhbz#1103221)
+
+* Fri Aug 22 2014 Kevin Fenzi <kevin@scrye.com> 3.13.90-2
+- Rebuild for new wayland
+
+* Wed Aug 20 2014 Florian Müllner <fmuellner@redhat.com> - 3.13.90-1
+- Update to 3.13.90
+
+* Mon Aug 18 2014 Kalev Lember <kalevlember@gmail.com> - 3.13.4-3
+- Rebuilt for upower 0.99.1 soname bump
+
+* Sun Aug 17 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 3.13.4-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild
+
+* Wed Jul 23 2014 Florian Müllner <fmuellner@redhat.com> - 3.13.4-1
+- Update to 3.13.4
+
+* Tue Jul 22 2014 Kalev Lember <kalevlember@gmail.com> - 3.13.3-2
+- Rebuilt for gobject-introspection 1.41.4
+
+* Fri Jun 27 2014 Florian Müllner <fmuellner@redhat.com> - 3.13.3-1
+- New gobject-introspection has been built, drop the last patch again
+
+* Wed Jun 25 2014 Florian Müllner <fmuellner@redhat.com> - 3.13.3-1
+- Revert annotation updates until we get a new gobject-introspection build
+
+* Wed Jun 25 2014 Florian Müllner <fmuellner@redhat.com> - 3.13.3-1
+- Update to 3.13.1
+
+* Wed Jun 11 2014 Florian Müllner <fmuellner@redhat.com> - 3.13.2-2
+- Backport fix for legacy fullscreen check
+
+* Sat Jun 07 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 3.13.2-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild
+
+* Tue May 27 2014 Florian Müllner <fmuellner@redhat.com> - 3.13.2-1
+- Update to 3.13.2, drop upstreamed patches
+
+* Thu May  8 2014 Matthias Clasen <mclasen@redhat.com> - 3.13.1-5
+- Fix shrinking terminals
+
+* Wed May 07 2014 Kalev Lember <kalevlember@gmail.com> - 3.13.1-4
+- Backport an upstream fix for a Wayland session crash
+
+* Wed May 07 2014 Kalev Lember <kalevlember@gmail.com> - 3.13.1-3
+- Install mutter-launch as setuid root
+
+* Thu May 01 2014 Kalev Lember <kalevlember@gmail.com> - 3.13.1-2
+- Obsolete mutter-wayland
+
+* Wed Apr 30 2014 Florian Müllner <fmuellner@redhat.com> - 3.13.1-1
+- Update to 3.13.1
+
+* Tue Apr 15 2014 Florian Müllner <fmuellner@redhat.com> - 3.12.1-1
+- Update to 3.12.1
+
+* Sat Apr 05 2014 Kalev Lember <kalevlember@gmail.com> - 3.12.0-2
+- Update dep versions
+
+* Tue Mar 25 2014 Florian Müllner <fmuellner@redhat.com> - 3.12.0-1
+- Update to 3.12.0
+
+* Wed Mar 19 2014 Florian Müllner <fmuellner@redhat.com> - 3.11.92-1
+- Update to 3.11.92
+
+* Thu Mar 06 2014 Florian Müllner <fmuellner@redhat.com> - 3.11.91-1
+- Update to 3.11.91
+
+* Thu Feb 20 2014 Kalev Lember <kalevlember@gmail.com> - 3.11.90-2
+- Rebuilt for cogl soname bump
+
+* Wed Feb 19 2014 Florian Müllner <fmuellner@redhat.com> - 3.11.90-1
+- Update to 3.11.90
+
+* Wed Feb 19 2014 Richard Hughes <rhughes@redhat.com> - 3.11.5-4
+- Rebuilt for gnome-desktop soname bump
+
+* Mon Feb 10 2014 Peter Hutterer <peter.hutterer@redhat.com> - 3.11.5-3
+- Rebuild for libevdev soname bump
+
+* Wed Feb 05 2014 Richard Hughes <rhughes@redhat.com> - 3.11.5-2
+- Rebuilt for cogl soname bump
+
+* Wed Feb 05 2014 Florian Müllner <fmuellner@redhat.com> - 3.11.5-1
+- Update to 3.11.5
+
+* Wed Jan 15 2014 Florian Müllner <fmuellner@redhat.com> - 3.11.4-1
+- Update to 3.11.4
+
+* Fri Dec 20 2013 Florian Müllner <fmuellner@redhat.com> - 3.11.3-1
+- Update to 3.11.3
+
+* Wed Nov 13 2013 Florian Müllner <fmuellner@redhat.com> - 3.11.2-1
+- Update to 3.11.2
+
+* Wed Oct 30 2013 Florian Müllner <fmuellner@redhat.com> - 3.11.1-1
+- Update to 3.11.1
+
+* Tue Oct 15 2013 Florian Müllner <fmuellner@redhat.com> - 3.10.1.1-1
+- Update to 3.10.1.1
+
+* Mon Oct 14 2013 Florian Müllner <fmuellner@redhat.com> - 3.10.1-1
+- Update to 3.10.1
+
+* Wed Sep 25 2013 Florian Müllner <fmuellner@redhat.com> - 3.10.0.1-1
+- Update to 3.10.0.1
+
+* Mon Sep 23 2013 Florian Müllner <fmuellner@redhat.com> - 3.10.0-1
+- Update to 3.10.0
+
+* Tue Sep 17 2013 Kalev Lember <kalevlember@gmail.com> - 3.9.92-2
+- Update the description and URL
+- Tighten -devel subpackage deps with _isa
+- Use the make_install macro
+
+* Mon Sep 16 2013 Florian Müllner <fmuellner@redhat.com> - 3.9.92-1
+- Update to 3.9.92
+
+* Tue Sep 03 2013 Kalev Lember <kalevlember@gmail.com> - 3.9.91-2
+- Rebuilt for libgnome-desktop soname bump
+
+* Tue Sep 03 2013 Florian Müllner <fmuellner@redhat.com> - 3.9.91-1
+- Update to 3.9.91
+
+* Thu Aug 22 2013 Florian Müllner <fmuellner@redhat.com> - 3.9.90-1
+- Update to 3.9.90
+
+* Fri Aug 09 2013 Kalev Lember <kalevlember@gmail.com> - 3.9.5-2
+- Rebuilt for cogl 1.15.4 soname bump
+
+* Tue Jul 30 2013 Florian Müllner <fmuellner@redhat.com> - 3.9.5-1
+- Update to 3.9.5
+
+* Wed Jul 10 2013 Florian Müllner <fmuellner@redhat.com> - 3.9.4-1
+- Update to 3.9.4
+
+* Tue Jun 18 2013 Florian Müllner <fmuellner@redhat.com> - 3.9.3-1
+- Update to 3.9.3
+
+* Tue May 28 2013 Florian Müllner <fmuellner@redhat.com> - 3.9.2-1
+- Update to 3.9.2
+
+* Wed May 01 2013 Florian Müllner <fmuellner@redhat.com> - 3.9.1-1
+- Update to 3.9.1
+
+* Tue Apr 23 2013 Florian Müllner <fmuellner@redhat.com> - 3.8.1-1
+- Update to 3.8.1
+
+* Tue Mar 26 2013 Florian Müllner <fmuellner@redhat.com> - 3.8.0-1
+- Update to 3.8.0
+
+* Tue Mar 19 2013 Florian Müllner <fmuellner@redhat.com> - 3.7.92-1
+- Update to 3.7.92
+
+* Mon Mar 04 2013 Florian Müllner <fmuellner@redhat.com> - 3.7.91-1
+- Update to 3.7.91
+
+* Wed Feb 20 2013 Florian Müllner <fmuellner@redhat.com> - 3.7.90-1
+- Update to 3.7.90
+
+* Tue Feb 05 2013 Florian Müllner <fmuellner@redhat.com> - 3.7.5-1
+- Update to 3.7.5
+
+* Fri Jan 25 2013 Peter Robinson <pbrobinson@fedoraproject.org> 3.7.4-2
+- Rebuild for new cogl
+
+* Tue Jan 15 2013 Florian Müllner <fmuellner@redhat.com> - 3.7.4-1
+- Update to 3.7.4
+
+* Tue Dec 18 2012 Florian Müllner <fmuellner@redhat.com> - 3.7.3-1
+- Update to 3.7.3
+
+* Mon Nov 19 2012 Florian Müllner <fmuellner@redhat.com> - 3.7.2-1
+- Update to 3.7.2
+
+* Fri Nov 09 2012 Kalev Lember <kalevlember@gmail.com> - 3.7.1-1
+- Update to 3.7.1
+
+* Mon Oct 15 2012 Florian Müllner <fmuellner@redhat.com> - 3.6.1-1
+- Update to 3.6.1
+
+* Tue Sep 25 2012 Florian Müllner <fmuellner@redhat.com> - 3.6.0-1
+- Update to 3.6.0
+
+* Wed Sep 19 2012 Florian Müllner <fmuellner@redhat.com> - 3.5.92-1
+- Update to 3.5.92
+
+* Tue Sep 04 2012 Debarshi Ray <rishi@fedoraproject.org> - 3.5.91-2
+- Rebuild against new cogl
+
+* Tue Sep 04 2012 Debarshi Ray <rishi@fedoraproject.org> - 3.5.91-1
+- Update to 3.5.91
+
+* Tue Aug 28 2012 Matthias Clasen <mclasen@redhat.com> - 3.5.90-2
+- Rebuild against new cogl/clutter
+
+* Tue Aug 21 2012 Richard Hughes <hughsient@gmail.com> - 3.5.90-1
+- Update to 3.5.90
+
+* Tue Aug 07 2012 Richard Hughes <hughsient@gmail.com> - 3.5.5-1
+- Update to 3.5.5
+
+* Fri Jul 27 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 3.5.4-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild
+
+* Tue Jul 17 2012 Richard Hughes <hughsient@gmail.com> - 3.5.4-1
+- Update to 3.5.4
+
+* Tue Jun 26 2012 Matthias Clasen <mclasen@redhat.com> - 3.5.3-1
+- Update to 3.5.3
+
+* Fri Jun  8 2012 Matthias Clasen <mclasen@redhat.com> - 3.5.2-3
+- Make resize grip area larger
+
+* Thu Jun 07 2012 Matthias Clasen <mclasen@redhat.com> - 3.5.2-2
+- Don't check for Xinerama anymore - it is now mandatory
+
+* Thu Jun 07 2012 Richard Hughes <hughsient@gmail.com> - 3.5.2-1
+- Update to 3.5.2
+- Remove upstreamed patches
+
+* Wed May 09 2012 Adam Jackson <ajax@redhat.com> 3.4.1-3
+- mutter-never-slice-shape-mask.patch, mutter-use-cogl-texrect-api.patch:
+  Fix window texturing on hardware without ARB_texture_non_power_of_two
+  (#813648)
+
+* Wed Apr 18 2012 Kalev Lember <kalevlember@gmail.com> - 3.4.1-2
+- Silence glib-compile-schemas scriplets
+
+* Wed Apr 18 2012 Kalev Lember <kalevlember@gmail.com> - 3.4.1-1
+- Update to 3.4.1
+- Conflict with gnome-shell versions older than 3.4.1
+
+* Tue Mar 27 2012 Richard Hughes <hughsient@gmail.com> - 3.4.0-1
+- Update to 3.4.0
+
+* Wed Mar 21 2012 Kalev Lember <kalevlember@gmail.com> - 3.3.92-1
+- Update to 3.3.92
+
+* Sat Mar 10 2012 Matthias Clasen <mclasen@redhat.com> - 3.3.90-2
+- Rebuild against new cogl
+
+* Sat Feb 25 2012 Matthias Clasen <mclasen@redhat.com> - 3.3.90-1
+- Update to 3.3.90
+
+* Tue Feb  7 2012 Matthias Clasen <mclasen@redhat.com> - 3.3.5-1
+- Update to 3.3.5
+
+* Fri Jan 20 2012 Matthias Clasen <mclasen@redhat.com> - 3.3.4-1
+- Update to 3.3.4
+
+* Thu Jan 19 2012 Matthias Clasen <mclasen@redhat.com> - 3.3.3-2
+- Rebuild against new cogl
+
+* Thu Jan  5 2012 Matthias Clasen <mclasen@redhat.com> - 3.3.3-1
+- Update to 3.3.3
+
+* Wed Nov 23 2011 Matthias Clasen <mclasen@redhat.com> - 3.3.2-2
+- Rebuild against new clutter
+
+* Tue Nov 22 2011 Matthias Clasen <mclasen@redhat.com> - 3.3.2-1
+- Update to 3.3.2
+
+* Wed Oct 26 2011 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 3.2.1-2
+- Rebuilt for glibc bug#747377
+
+* Wed Oct 19 2011 Matthias Clasen <mclasen@redhat.com> - 3.2.1-1
+- Update to 3.2.1
+
+* Mon Sep 26 2011 Owen Taylor <otaylor@redhat.com> - 3.2.0-1
+- Update to 3.2.0
+
+* Tue Sep 20 2011 Matthias Clasen <mclasen@redhat.com> - 3.1.92-1
+- Update to 3.1.92
+
+* Wed Sep 14 2011 Owen Taylor <otaylor@redhat.com> - 3.1.91.1-1
+- Update to 3.1.91.1
+
+* Wed Aug 31 2011 Matthias Clasen <mclasen@redhat.com> - 3.1.90.1-1
+- Update to 3.1.90.1
+
+* Wed Jul 27 2011 Matthias Clasen <mclasen@redhat.com> - 3.1.4-1
+- Update to 3.1.4
+
+* Wed Jul 27 2011 Matthias Clasen <mclasen@redhat.com> - 3.1.3.1-3
+- Rebuild
+
+* Mon Jul  4 2011 Peter Robinson <pbrobinson@gmail.com> - 3.1.3.1-2
+- rebuild against new clutter/cogl
+
+* Mon Jul 04 2011 Adam Williamson <awilliam@redhat.com> - 3.1.3.1-1
+- Update to 3.1.3.1
+
+* Thu Jun 30 2011 Owen Taylor <otaylor@redhat.com> - 3.1.3-1
+- Update to 3.1.3
+
+* Wed May 25 2011 Owen Taylor <otaylor@redhat.com> - 3.0.2.1-1
+- Update to 3.0.2.1
+
+* Fri Apr 29 2011 Matthias Clasen <mclasen@redhat.com> - 3.0.1-3
+- Actually apply the patch for #700276
+
+* Thu Apr 28 2011 Matthias Clasen <mclasen@redhat.com> - 3.0.1-2
+- Make session saving of gnome-shell work
+
+* Mon Apr 25 2011 Owen Taylor <otaylor@redhat.com> - 3.0.1-1
+- Update to 3.0.1
+
+* Mon Apr  4 2011 Owen Taylor <otaylor@redhat.com> - 3.0.0-1
+- Update to 3.0.0
+
+* Mon Mar 28 2011 Matthias Clasen <mclasen@redhat.com> - 2.91.93-1
+- Update to 2.91.93
+
+* Wed Mar 23 2011 Matthias Clasen <mclasen@redhat.com> - 2.91.92-1
+- Update to 2.91.92
+
+* Mon Mar  7 2011 Owen Taylor <otaylor@redhat.com> - 2.91.91-1
+- Update to 2.91.91
+
+* Tue Mar  1 2011 Matthias Clasen <mclasen@redhat.com> - 2.91.90-2
+- Build against libcanberra, to enable AccessX feedback features
+
+* Tue Feb 22 2011 Matthias Clasen <mclasen@redhat.com> - 2.91.90-1
+- Update to 2.91.90
+
+* Thu Feb 10 2011 Matthias Clasen <mclasen@redhat.com> - 2.91.6-4
+- Rebuild against newer gtk
+
+* Tue Feb 08 2011 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 2.91.6-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild
+
+* Wed Feb  2 2011 Matthias Clasen <mclasen@redhat.com> - 2.91.6-2
+- Rebuild against newer gtk
+
+* Tue Feb  1 2011 Owen Taylor <otaylor@redhat.com> - 2.91.6-1
+- Update to 2.91.6
+
+* Tue Jan 11 2011 Matthias Clasen <mclasen@redhat.com> - 2.91.5-1
+- Update to 2.91.5
+
+* Fri Jan  7 2011 Matthias Clasen <mclasen@redhat.com> - 2.91.4-1
+- Update to 2.91.4
+
+* Fri Dec  3 2010 Matthias Clasen <mclasen@redhat.com> - 2.91.3-2
+- Rebuild against new gtk
+- Drop no longer needed %%clean etc
+
+* Mon Nov 29 2010 Owen Taylor <otaylor@redhat.com> - 2.91.3-1
+- Update to 2.91.3
+
+* Tue Nov  9 2010 Owen Taylor <otaylor@redhat.com> - 2.91.2-1
+- Update to 2.91.2
+
+* Tue Nov  2 2010 Matthias Clasen <mclasen@redhat.com> - 2.91.1-2
+- Rebuild against newer gtk3
+
+* Fri Oct 29 2010 Owen Taylor <otaylor@redhat.com> - 2.91.1-1
+- Update to 2.91.1
+
+* Mon Oct  4 2010 Owen Taylor <otaylor@redhat.com> - 2.91.0-1
+- Update to 2.91.0
+
+* Wed Sep 22 2010 Matthias Clasen <mclasen@redhat.com> - 2.31.5-4
+- Rebuild against newer gobject-introspection
+
+* Wed Jul 14 2010 Colin Walters <walters@verbum.org> - 2.31.5-3
+- Rebuild for new gobject-introspection
+
+* Tue Jul 13 2010 Adel Gadllah <adel.gadllah@gmail.com> - 2.31.5-2
+- Build against gtk3
+
+* Mon Jul 12 2010 Colin Walters <walters@pocket> - 2.31.5-1
+- New upstream version
+
+* Mon Jul 12 2010 Colin Walters <walters@verbum.org> - 2.31.2-5
+- Rebuild against new gobject-introspection
+
+* Tue Jul  6 2010 Colin Walters <walters@verbum.org> - 2.31.2-4
+- Changes to support snapshot builds
+
+* Fri Jun 25 2010 Colin Walters <walters@megatron> - 2.31.2-3
+- drop gir-repository-devel dep
+
+* Wed May 26 2010 Adam Miller <maxamillion@fedoraproject.org> - 2.31.2-2
+- removed "--with-clutter" as configure is claiming it to be an unknown option
+
+* Wed May 26 2010 Adam Miller <maxamillion@fedoraproject.org> - 2.31.2-1
+- New upstream 2.31.2 release
+
+* Thu Mar 25 2010 Peter Robinson <pbrobinson@gmail.com> 2.29.1-1
+- New upstream 2.29.1 release
+
+* Wed Mar 17 2010 Peter Robinson <pbrobinson@gmail.com> 2.29.0-1
+- New upstream 2.29.0 release
+
+* Tue Feb 16 2010 Adam Jackson <ajax@redhat.com> 2.28.1-0.2
+- mutter-2.28.1-add-needed.patch: Fix FTBFS from --no-add-needed
+
+* Thu Feb  4 2010 Peter Robinson <pbrobinson@gmail.com> 2.28.1-0.1
+- Move to git snapshot
+
+* Wed Oct  7 2009 Owen Taylor <otaylor@redhat.com> - 2.28.0-1
+- Update to 2.28.0
+
+* Tue Sep 15 2009 Owen Taylor <otaylor@redhat.com> - 2.27.5-1
+- Update to 2.27.5
+
+* Fri Sep  4 2009 Owen Taylor <otaylor@redhat.com> - 2.27.4-1
+- Remove workaround for #520209
+- Update to 2.27.4
+
+* Sat Aug 29 2009 Owen Taylor <otaylor@redhat.com> - 2.27.3-3
+- Fix %%preun GConf script to properly be for package removal
+
+* Fri Aug 28 2009 Owen Taylor <otaylor@redhat.com> - 2.27.3-2
+- Add a workaround for Red Hat bug #520209
+
+* Fri Aug 28 2009 Owen Taylor <otaylor@redhat.com> - 2.27.3-1
+- Update to 2.27.3, remove mutter-metawindow.patch
+
+* Fri Aug 21 2009 Peter Robinson <pbrobinson@gmail.com> 2.27.2-2
+- Add upstream patch needed by latest mutter-moblin
+
+* Tue Aug 11 2009 Peter Robinson <pbrobinson@gmail.com> 2.27.2-1
+- New upstream 2.27.2 release. Drop upstreamed patches.
+
+* Wed Jul 29 2009 Peter Robinson <pbrobinson@gmail.com> 2.27.1-5
+- Add upstream patches for clutter 1.0
+
+* Wed Jul 29 2009 Peter Robinson <pbrobinson@gmail.com> 2.27.1-4
+- Add patch to fix mutter --replace
+
+* Sat Jul 25 2009 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 2.27.1-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild
+
+* Sat Jul 18 2009 Peter Robinson <pbrobinson@gmail.com> 2.27.1-2
+- Updates from review request
+
+* Fri Jul 17 2009 Peter Robinson <pbrobinson@gmail.com> 2.27.1-1
+- Update to official 2.27.1 and review updates
+
+* Thu Jun 18 2009 Peter Robinson <pbrobinson@gmail.com> 2.27.0-0.2
+- Updates from initial reviews
+
+* Thu Jun 18 2009 Peter Robinson <pbrobinson@gmail.com> 2.27.0-0.1
+- Initial packaging