From 776610deeb337b95f348991685acd952efa760eb Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: May 07 2019 06:51:47 +0000 Subject: import mutter-3.28.3-18.el8 --- 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?= +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 + #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 ++ ++#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" +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 ++ ++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 + + 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 + + #include ++#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 + #include "util-private.h" + #include "display-private.h" ++#include "stereo.h" + #include + #include "ui.h" + #include +@@ -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 . ++ */ ++ ++/* ++ * 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 ++ ++#include ++#include ++#include ++ ++#include ++#include "ui.h" ++#include ++#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 . ++ */ ++ ++#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?= +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. + + + +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 +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 +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 + #include + #include + #include +@@ -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 +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 +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 +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 +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 +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 +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 +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?= +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 +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 +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?= +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 +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?= +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?= +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 +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 +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 +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?= +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?= +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?= +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" +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?= +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?= +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?= +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?= +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 +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@"> + + +- false ++ true + Allow grabs with Xwayland + + Allow keyboard grabs issued by X11 applications running in Xwayland +@@ -73,7 +73,7 @@ + + + +- [] ++ ['@XWAYLAND_GRAB_DEFAULT_ACCESS_RULES@'] + Xwayland applications allowed to issue keyboard grabs + + 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" +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 +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" +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 ++ ++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 +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 +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" +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 +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" +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?= +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//. + +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?= +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?= +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?= +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 +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 +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?= +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 +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 +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 +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?= +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?= +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 +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 + */ + + #include "config.h" + + #include + + #include + #include + #include + #include + #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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + +-- +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 +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 +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?= +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?= +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?= +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?= +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 +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 + */ + + #include "config.h" + + #include + ++#include ++ + #include + #include + #include + #include + #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 +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?= +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?= +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 +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 + + #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 +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 + */ + + #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 + #include + #include + + #include + #include + #include + #include + #include + + #include + #include + + #include + + #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 + */ + + #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?= +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 +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 +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 ++#include ++ ++#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?= +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 +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?= +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 +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 + + #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 +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 +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 ++ ++#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?= +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 +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 +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?= +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 +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 + * and Jasper St. Pierre + * + * 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 . + */ + + /** + * SECTION:meta-shaped-texture + * @title: MetaShapedTexture + * @short_description: An actor to draw a masked texture. + */ + + #include + + #include + #include "meta-shaped-texture-private.h" ++#include "meta-texture-rectangle.h" + + #include + #include /* for gdk_rectangle_intersect() */ + + #include "clutter-utils.h" + #include "meta-texture-tower.h" + #include "core/boxes-private.h" + + #include "meta-cullable.h" ++#include + + 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 +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 +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. ++ + --> + + +-- +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 +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?= +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 +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 +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 + #endif ++#ifdef __linux ++#include ++#endif + + #include + #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 +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?= +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?= +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?= +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 +-#include +-#ifdef HAVE_WAYLAND +-#include +-#endif + + #include + #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 . ++ * ++ */ ++ ++#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 . ++ * ++ */ ++ ++#ifndef META_CURSOR_SPRITE_XCURSOR_H ++#define META_CURSOR_SPRITE_XCURSOR_H ++ ++#include ++#include ++ ++#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 + #include +-#include + + #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 ++#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 +- +-#include +-#include +-#include +- +-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 + #include + +-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 + + #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 . ++ * ++ */ ++ ++#include "config.h" ++ ++#include "backends/x11/cm/meta-cursor-sprite-xfixes.h" ++ ++#include ++ ++#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 . ++ * ++ */ ++ ++#ifndef META_CURSOR_SPRITE_XFIXES_H ++#define META_CURSOR_SPRITE_XFIXES_H ++ ++#include ++ ++#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 ++ + #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 . ++ * ++ */ ++ ++#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 . ++ * ++ */ ++ ++#ifndef META_CURSOR_SPRITE_WAYLAND_H ++#define META_CURSOR_SPRITE_WAYLAND_H ++ ++#include ++ ++#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 + #include +-#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 + #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?= +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?= +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?= +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?= +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 + + #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 + ++#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?= +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?= +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?= +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?= +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?= +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 + #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?= +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 ++ + #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 ++#include ++#include + ++#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 + --> + + +@@ -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?= +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?= +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?= +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?= +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?= +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?= +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?= +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?= +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?= +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?= +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?= +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?= +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?= +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 - 3.28.3-18 +- Remove patch enabling monitor framebuffer scaling + Related: #1668883 + +* Mon Feb 11 2019 Ray Strode - 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 - 3.28.3-16 +- Backport forward_key() method + Related: #1668979 + +* Mon Feb 11 2019 Florian Müllner - 3.28.3-15 +- Re-add dropped downstream patches (rhbz#1668883) + +* Tue Feb 05 2019 Olivier Fourdan - 3.28.3-14 +- Restore update monitor fix (rhbz#1635123) + +* Fri Feb 01 2019 Jonas Ådahl - 3.28.3-13 +- Fix screen recording on HiDPI monitor (rhbz#1670287) + +* Thu Jan 31 2019 Ray Strode - 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 - 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 - 3.28.3-10 +- Backport screen cast cursor side channel patches (rhbz#1658971) + +* Fri Jan 11 2019 Jonas Ådahl - 3.28.3-9 +- Avoid EGLStream backend deadlock (rhbz#1656905) + +* Fri Jan 11 2019 Jonas Ådahl - 3.28.3-8 +- Get texture pixels via offscreen for EGLStreams (rhbz#1656926) + +* Mon Jan 07 2019 Olivier Fourdan - 3.28.3-7 +- Backport the RecordWindow screencast mode (rhbz#1657661) + +* Fri Jan 04 2019 Ray Strode - 3.28.3-6 +- Add shadow framebuffer for server cards to fix blending + performance + Resolves: #1591250 + +* Fri Oct 26 2018 Olivier Fourdan - 3.28.3-5 +- Allow Xwayland grabs on a selected set of X11 applications. + (rhbz#1500399) + +* Tue Oct 23 2018 Olivier Fourdan - 3.28.3-4 +- More backport fixes from upstream "gnome-3-28" branch +- enable eglstream support (rhbz#1639782) + +* Mon Oct 15 2018 Jonas Ådahl - 3.28.3-3 +- Fix garbled window titles (rhbz#1639194) + +* Thu Oct 04 2018 Olivier Fourdan - 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 - 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 - 3.28.1-4 +- Backport remote-access controller API patch + +* Tue Aug 07 2018 Jonas Ådahl - 3.28.1-3 +- Backport remote desktop related patches + +* Wed Aug 01 2018 Jan Grulich - 3.28.1-2 +- Support PipeWire 0.2.2+ + +* Fri Apr 13 2018 Florian Müllner - 3.28.1-1 +- Update to 3.28.1 + +* Mon Mar 12 2018 Florian Müllner - 3.28.0-1 +- Update to 3.28.0 + +* Mon Mar 05 2018 Florian Müllner - 3.27.92-1 +- Update to 3.27.92 + +* Wed Feb 28 2018 Adam Williamson - 3.27.91-2 +- Backport MR#36 to fix RHBZ #1547691 (GGO #2), mouse issues + +* Wed Feb 21 2018 Florian Müllner - 3.27.91-1 +- Update to 3.27.91 + +* Tue Feb 13 2018 Björn Esser - 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 - 3.27.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Sat Jan 06 2018 Igor Gnatenko - 3.27.1-2 +- Remove obsolete scriptlets + +* Mon Oct 30 2017 Florian Müllner - 3.27.1-1 +- Include 32-bit build fixes + +* Tue Oct 17 2017 Florian Müllner - 3.27.1-1 +- Update to 3.27.1 + +* Fri Oct 06 2017 Florian Müllner - 3.26.1-2 +- Fix screencasts + +* Wed Oct 04 2017 Florian Müllner - 3.26.1-1 +- Update to 3.26.1 + +* Thu Sep 21 2017 Florian Müllner - 3.26.0-5 +- Adjust to pipewire API break + +* Wed Sep 20 2017 Florian Müllner - 3.26.0-5 +- Enable tablet support + +* Tue Sep 12 2017 Adam Williamson - 3.26.0-4 +- Also backport BGO #787570 fix from upstream + +* Tue Sep 12 2017 Adam Williamson - 3.26.0-3 +- Backport upstream fixes for crasher bug BGO #787568 + +* Tue Sep 12 2017 Florian Müllner - 3.26.0-2 +- Enable remote desktop support + +* Tue Sep 12 2017 Florian Müllner - 3.26.0-1 +- Update to 3.26.0 + +* Thu Sep 07 2017 Florian Müllner - 3.25.92-1 +- Update to 3.25.92 + +* Thu Aug 24 2017 Bastien Nocera - 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 - 3.25.91-1 +- Update to 3.25.91 + +* Thu Aug 10 2017 Florian Müllner - 3.25.90-1 +- Update to 3.25.90 + +* Thu Aug 03 2017 Fedora Release Engineering - 3.25.4-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Wed Jul 26 2017 Fedora Release Engineering - 3.25.4-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Wed Jul 19 2017 Florian Müllner - 3.25.4-1 +- Update to 3.25.4 + +* Wed Jun 21 2017 Florian Müllner - 3.25.3-1 +- Update to 3.25.3 + +* Wed May 24 2017 Florian Müllner - 3.25.2-1 +- Update to 3.25.2 + +* Thu May 18 2017 Florian Müllner - 3.25.1-2 +- Fix copy+paste of UTF8 strings between X11 and wayland + +* Thu Apr 27 2017 Florian Müllner - 3.25.1-1 +- Update to 3.25.1 + +* Tue Apr 11 2017 Florian Müllner - 3.24.1-1 +- Update to 3.24.1 + +* Mon Mar 20 2017 Florian Müllner - 3.24.0-1 +- Update to 3.24.0 + +* Tue Mar 14 2017 Florian Müllner - 3.23.92-1 +- Update to 3.23.92 + +* Fri Mar 10 2017 Florian Müllner - 3.23.91-4 +- Apply startup-notification hack again + +* Tue Mar 07 2017 Adam Williamson - 3.23.91-3 +- Backport more color fixes, should really fix BGO #779234, RHBZ #1428559 + +* Thu Mar 02 2017 Adam Williamson - 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 - 3.23.91-1 +- Update to 3.23.91 + +* Thu Feb 16 2017 Florian Müllner - 3.23.90-1 +- Update to 3.23.90 + +* Fri Feb 10 2017 Fedora Release Engineering - 3.23.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Thu Dec 15 2016 Florian Müllner - 3.23.3-1 +- Update to 3.23.3 + +* Fri Dec 02 2016 Florian Müllner - 3.23.2-2 +- Fix build error on 32-bit platforms + +* Thu Nov 24 2016 Kevin Fenzi - 3.23.2-2 +- Some fixes to get building. Still needs patch1 rebased. + +* Wed Nov 23 2016 Florian Müllner - 3.23.2-1 +- Update to 3.23.2 + +* Tue Nov 8 2016 Matthias Clasen - 3.23.1-2 +- Fix 1376471 + +* Sun Oct 30 2016 Florian Müllner - 3.23.1-1 +- Update to 3.23.1 + +* Tue Oct 18 2016 Kalev Lember - 3.22.1-3 +- Backport a fix to make gnome-screenshot --area work + +* Tue Oct 11 2016 Adam Jackson - 3.22.1-2 +- Prefer eglGetPlatformDisplay() to eglGetDisplay() + +* Tue Oct 11 2016 Florian Müllner - 3.22.1-1 +- Update to 3.22.1 + +* Wed Sep 28 2016 Florian Müllner - 3.22.0-2 +- Include fix for crash on VT switch + +* Mon Sep 19 2016 Florian Müllner - 3.22.0-1 +- Update to 3.22.0 + +* Tue Sep 13 2016 Florian Müllner - 3.21.92-1 +- Update to 3.21.92 + +* Thu Sep 08 2016 Kalev Lember - 3.21.91-2 +- wayland/cursor-role: Increase buffer use count on construction (#1373372) + +* Tue Aug 30 2016 Florian Müllner - 3.21.91-1 +- Update to 3.21.91 + +* Mon Aug 29 2016 Kalev Lember - 3.21.90-3 +- clutter/evdev: Fix absolute pointer motion events (#1369492) + +* Sat Aug 20 2016 Kalev Lember - 3.21.90-2 +- Update minimum dep versions + +* Fri Aug 19 2016 Florian Müllner - 3.21.90-1 +- Update to 3.21.90 + +* Wed Jul 20 2016 Florian Müllner - 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 - 3.21.3-1 +- Update to 3.21.3 + +* Fri May 27 2016 Florian Müllner - 3.21.2-1 +- Update to 3.21.2 + +* Fri Apr 29 2016 Florian Müllner - 3.21.1-1 +- Update to 3.21.1 + +* Wed Apr 13 2016 Florian Müllner - 3.20.1-1 +- Update to 3.20.1 + +* Tue Mar 22 2016 Florian Müllner - 3.20.0-1 +- Update to 3.20.0 + +* Wed Mar 16 2016 Florian Müllner - 3.19.92-1 +- Update to 3.19.92 + +* Thu Mar 03 2016 Florian Müllner - 3.19.91-2 +- Include fix for invalid cursor wl_buffer access + +* Thu Mar 03 2016 Florian Müllner - 3.19.91-1 +- Update to 3.19.91 + +* Fri Feb 19 2016 Florian Müllner - 3.19.90-1 +- Update to 3.19.90 + +* Thu Feb 04 2016 Fedora Release Engineering - 3.19.4-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Thu Jan 21 2016 Florian Müllner - 3.19.4-1 +- Update to 3.19.4 + +* Thu Dec 17 2015 Florian Müllner - 3.19.3-1 +- Update to 3.19.3 + +* Wed Nov 25 2015 Florian Müllner - 3.19.2-1 +- Update to 3.19.2 + +* Tue Nov 10 2015 Ray Strode 3.19.1-5.20151110git049f1556d +- Update to git snapshot + +* Thu Oct 29 2015 Florian Müllner - 3.19.1-1 +- Update to 3.19.1 + +* Wed Oct 21 2015 Ray Strode 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 - 3.18.1-3 +- Backport a fix for a common Wayland crash (#1266486) + +* Thu Oct 15 2015 Kalev Lember - 3.18.1-2 +- Bump gnome-shell conflicts version + +* Thu Oct 15 2015 Florian Müllner - 3.18.1-1 +- Update to 3.18.1 + +* Mon Sep 21 2015 Florian Müllner - 3.18.0-1 +- Update to 3.18.0 + +* Wed Sep 16 2015 Florian Müllner - 3.17.92-1 +- Update to 3.17.92 + +* Thu Sep 03 2015 Florian Müllner - 3.17.91-1 +- Update to 3.17.91 + +* Thu Sep 03 2015 Ray Strode 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 - 3.17.90-1 +- Update to 3.17.90 + +* Thu Jul 23 2015 Florian Müllner - 3.17.4-1 +- Update to 3.17.4 + +* Wed Jul 22 2015 David King - 3.17.3-2 +- Bump for new gnome-desktop3 + +* Thu Jul 02 2015 Florian Müllner - 3.17.3-1 +- Update to 3.17.3 + +* Wed Jun 17 2015 Fedora Release Engineering - 3.17.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Wed May 27 2015 Florian Müllner - 3.17.2-1 +- Update to 3.17.2 + +* Thu Apr 30 2015 Florian Müllner - 3.17.1-1 +- Update to 3.17.1 + +* Thu Apr 16 2015 Kalev Lember - 3.16.1.1-2 +- Bump gnome-shell conflicts version + +* Wed Apr 15 2015 Rui Matos - 3.16.1.1-1 +- Update to 3.16.1.1 + +* Tue Apr 14 2015 Florian Müllner - 3.16.1-1 +- Update to 3.16.1 + +* Mon Mar 23 2015 Florian Müllner - 3.16.0-1 +- Update to 3.16.0 + +* Tue Mar 17 2015 Kalev Lember - 3.15.92-2 +- Update minimum dep versions +- Use license macro for the COPYING file + +* Tue Mar 17 2015 Florian Müllner - 3.15.92-1 +- Update to 3.15.92 + +* Tue Mar 10 2015 Peter Hutterer - 3.15.91-2 +- Rebuild for libinput soname bump + +* Wed Mar 04 2015 Florian Müllner - 3.15.91-1 +- Update to 3.15.91 + +* Fri Feb 20 2015 Florian Müllner - 3.15.90-1 +- Update to 3.15.90 + +* Mon Feb 02 2015 Adam Williamson - 3.15.4-2 +- backport ad90b7dd to fix BGO #743412 / RHBZ #1185811 + +* Wed Jan 21 2015 Florian Müllner - 3.15.4-1 +- Update to 3.15.4 + +* Mon Jan 19 2015 Peter Hutterer 3.15.3-3 +- Rebuild for libinput soname bump + +* Mon Jan 12 2015 Ray Strode 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 - 3.15.3-1 +- Revert unsatisfiable wayland requirement + +* Fri Dec 19 2014 Florian Müllner - 3.15.3-1 +- Update to 3.15.3 + +* Thu Nov 27 2014 Florian Müllner - 3.15.2-1 +- Update to 3.15.2 + +* Wed Nov 12 2014 Vadim Rutkovsky - 3.15.1-2 +- Build installed tests + +* Thu Oct 30 2014 Florian Müllner - 3.15.1-1 +- Update to 3.15.1 + +* Tue Oct 21 2014 Florian Müllner - 3.14.1-2 +- Fix regression in handling raise-on-click option (rhbz#1151918) + +* Tue Oct 14 2014 Florian Müllner - 3.14.1-1 +- Update to 3.14.1 + +* Fri Oct 03 2014 Adam Williamson - 3.14.0-3 +- backport fix for BGO #737233 / RHBZ #1145952 (desktop right click broken) + +* Mon Sep 22 2014 Kalev Lember - 3.14.0-2 +- Bump gnome-shell conflicts version + +* Mon Sep 22 2014 Florian Müllner - 3.14.0-1 +- Update to 3.14.0 + +* Wed Sep 17 2014 Florian Müllner - 3.13.92-1 +- Update to 3.13.92 + +* Fri Sep 12 2014 Peter Hutterer - 3.13.91-2 +- Rebuild for libinput soname bump + +* Wed Sep 03 2014 Florian Müllner - 3.31.91-1 +- Update to 3.13.91, drop downstream patches + +* Tue Aug 26 2014 Adel Gadllah - 3.13.90-4 +- Apply fix for RH #1133166 + +* Mon Aug 25 2014 Hans de Goede - 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 3.13.90-2 +- Rebuild for new wayland + +* Wed Aug 20 2014 Florian Müllner - 3.13.90-1 +- Update to 3.13.90 + +* Mon Aug 18 2014 Kalev Lember - 3.13.4-3 +- Rebuilt for upower 0.99.1 soname bump + +* Sun Aug 17 2014 Fedora Release Engineering - 3.13.4-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + +* Wed Jul 23 2014 Florian Müllner - 3.13.4-1 +- Update to 3.13.4 + +* Tue Jul 22 2014 Kalev Lember - 3.13.3-2 +- Rebuilt for gobject-introspection 1.41.4 + +* Fri Jun 27 2014 Florian Müllner - 3.13.3-1 +- New gobject-introspection has been built, drop the last patch again + +* Wed Jun 25 2014 Florian Müllner - 3.13.3-1 +- Revert annotation updates until we get a new gobject-introspection build + +* Wed Jun 25 2014 Florian Müllner - 3.13.3-1 +- Update to 3.13.1 + +* Wed Jun 11 2014 Florian Müllner - 3.13.2-2 +- Backport fix for legacy fullscreen check + +* Sat Jun 07 2014 Fedora Release Engineering - 3.13.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Tue May 27 2014 Florian Müllner - 3.13.2-1 +- Update to 3.13.2, drop upstreamed patches + +* Thu May 8 2014 Matthias Clasen - 3.13.1-5 +- Fix shrinking terminals + +* Wed May 07 2014 Kalev Lember - 3.13.1-4 +- Backport an upstream fix for a Wayland session crash + +* Wed May 07 2014 Kalev Lember - 3.13.1-3 +- Install mutter-launch as setuid root + +* Thu May 01 2014 Kalev Lember - 3.13.1-2 +- Obsolete mutter-wayland + +* Wed Apr 30 2014 Florian Müllner - 3.13.1-1 +- Update to 3.13.1 + +* Tue Apr 15 2014 Florian Müllner - 3.12.1-1 +- Update to 3.12.1 + +* Sat Apr 05 2014 Kalev Lember - 3.12.0-2 +- Update dep versions + +* Tue Mar 25 2014 Florian Müllner - 3.12.0-1 +- Update to 3.12.0 + +* Wed Mar 19 2014 Florian Müllner - 3.11.92-1 +- Update to 3.11.92 + +* Thu Mar 06 2014 Florian Müllner - 3.11.91-1 +- Update to 3.11.91 + +* Thu Feb 20 2014 Kalev Lember - 3.11.90-2 +- Rebuilt for cogl soname bump + +* Wed Feb 19 2014 Florian Müllner - 3.11.90-1 +- Update to 3.11.90 + +* Wed Feb 19 2014 Richard Hughes - 3.11.5-4 +- Rebuilt for gnome-desktop soname bump + +* Mon Feb 10 2014 Peter Hutterer - 3.11.5-3 +- Rebuild for libevdev soname bump + +* Wed Feb 05 2014 Richard Hughes - 3.11.5-2 +- Rebuilt for cogl soname bump + +* Wed Feb 05 2014 Florian Müllner - 3.11.5-1 +- Update to 3.11.5 + +* Wed Jan 15 2014 Florian Müllner - 3.11.4-1 +- Update to 3.11.4 + +* Fri Dec 20 2013 Florian Müllner - 3.11.3-1 +- Update to 3.11.3 + +* Wed Nov 13 2013 Florian Müllner - 3.11.2-1 +- Update to 3.11.2 + +* Wed Oct 30 2013 Florian Müllner - 3.11.1-1 +- Update to 3.11.1 + +* Tue Oct 15 2013 Florian Müllner - 3.10.1.1-1 +- Update to 3.10.1.1 + +* Mon Oct 14 2013 Florian Müllner - 3.10.1-1 +- Update to 3.10.1 + +* Wed Sep 25 2013 Florian Müllner - 3.10.0.1-1 +- Update to 3.10.0.1 + +* Mon Sep 23 2013 Florian Müllner - 3.10.0-1 +- Update to 3.10.0 + +* Tue Sep 17 2013 Kalev Lember - 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 - 3.9.92-1 +- Update to 3.9.92 + +* Tue Sep 03 2013 Kalev Lember - 3.9.91-2 +- Rebuilt for libgnome-desktop soname bump + +* Tue Sep 03 2013 Florian Müllner - 3.9.91-1 +- Update to 3.9.91 + +* Thu Aug 22 2013 Florian Müllner - 3.9.90-1 +- Update to 3.9.90 + +* Fri Aug 09 2013 Kalev Lember - 3.9.5-2 +- Rebuilt for cogl 1.15.4 soname bump + +* Tue Jul 30 2013 Florian Müllner - 3.9.5-1 +- Update to 3.9.5 + +* Wed Jul 10 2013 Florian Müllner - 3.9.4-1 +- Update to 3.9.4 + +* Tue Jun 18 2013 Florian Müllner - 3.9.3-1 +- Update to 3.9.3 + +* Tue May 28 2013 Florian Müllner - 3.9.2-1 +- Update to 3.9.2 + +* Wed May 01 2013 Florian Müllner - 3.9.1-1 +- Update to 3.9.1 + +* Tue Apr 23 2013 Florian Müllner - 3.8.1-1 +- Update to 3.8.1 + +* Tue Mar 26 2013 Florian Müllner - 3.8.0-1 +- Update to 3.8.0 + +* Tue Mar 19 2013 Florian Müllner - 3.7.92-1 +- Update to 3.7.92 + +* Mon Mar 04 2013 Florian Müllner - 3.7.91-1 +- Update to 3.7.91 + +* Wed Feb 20 2013 Florian Müllner - 3.7.90-1 +- Update to 3.7.90 + +* Tue Feb 05 2013 Florian Müllner - 3.7.5-1 +- Update to 3.7.5 + +* Fri Jan 25 2013 Peter Robinson 3.7.4-2 +- Rebuild for new cogl + +* Tue Jan 15 2013 Florian Müllner - 3.7.4-1 +- Update to 3.7.4 + +* Tue Dec 18 2012 Florian Müllner - 3.7.3-1 +- Update to 3.7.3 + +* Mon Nov 19 2012 Florian Müllner - 3.7.2-1 +- Update to 3.7.2 + +* Fri Nov 09 2012 Kalev Lember - 3.7.1-1 +- Update to 3.7.1 + +* Mon Oct 15 2012 Florian Müllner - 3.6.1-1 +- Update to 3.6.1 + +* Tue Sep 25 2012 Florian Müllner - 3.6.0-1 +- Update to 3.6.0 + +* Wed Sep 19 2012 Florian Müllner - 3.5.92-1 +- Update to 3.5.92 + +* Tue Sep 04 2012 Debarshi Ray - 3.5.91-2 +- Rebuild against new cogl + +* Tue Sep 04 2012 Debarshi Ray - 3.5.91-1 +- Update to 3.5.91 + +* Tue Aug 28 2012 Matthias Clasen - 3.5.90-2 +- Rebuild against new cogl/clutter + +* Tue Aug 21 2012 Richard Hughes - 3.5.90-1 +- Update to 3.5.90 + +* Tue Aug 07 2012 Richard Hughes - 3.5.5-1 +- Update to 3.5.5 + +* Fri Jul 27 2012 Fedora Release Engineering - 3.5.4-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Tue Jul 17 2012 Richard Hughes - 3.5.4-1 +- Update to 3.5.4 + +* Tue Jun 26 2012 Matthias Clasen - 3.5.3-1 +- Update to 3.5.3 + +* Fri Jun 8 2012 Matthias Clasen - 3.5.2-3 +- Make resize grip area larger + +* Thu Jun 07 2012 Matthias Clasen - 3.5.2-2 +- Don't check for Xinerama anymore - it is now mandatory + +* Thu Jun 07 2012 Richard Hughes - 3.5.2-1 +- Update to 3.5.2 +- Remove upstreamed patches + +* Wed May 09 2012 Adam Jackson 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 - 3.4.1-2 +- Silence glib-compile-schemas scriplets + +* Wed Apr 18 2012 Kalev Lember - 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 - 3.4.0-1 +- Update to 3.4.0 + +* Wed Mar 21 2012 Kalev Lember - 3.3.92-1 +- Update to 3.3.92 + +* Sat Mar 10 2012 Matthias Clasen - 3.3.90-2 +- Rebuild against new cogl + +* Sat Feb 25 2012 Matthias Clasen - 3.3.90-1 +- Update to 3.3.90 + +* Tue Feb 7 2012 Matthias Clasen - 3.3.5-1 +- Update to 3.3.5 + +* Fri Jan 20 2012 Matthias Clasen - 3.3.4-1 +- Update to 3.3.4 + +* Thu Jan 19 2012 Matthias Clasen - 3.3.3-2 +- Rebuild against new cogl + +* Thu Jan 5 2012 Matthias Clasen - 3.3.3-1 +- Update to 3.3.3 + +* Wed Nov 23 2011 Matthias Clasen - 3.3.2-2 +- Rebuild against new clutter + +* Tue Nov 22 2011 Matthias Clasen - 3.3.2-1 +- Update to 3.3.2 + +* Wed Oct 26 2011 Fedora Release Engineering - 3.2.1-2 +- Rebuilt for glibc bug#747377 + +* Wed Oct 19 2011 Matthias Clasen - 3.2.1-1 +- Update to 3.2.1 + +* Mon Sep 26 2011 Owen Taylor - 3.2.0-1 +- Update to 3.2.0 + +* Tue Sep 20 2011 Matthias Clasen - 3.1.92-1 +- Update to 3.1.92 + +* Wed Sep 14 2011 Owen Taylor - 3.1.91.1-1 +- Update to 3.1.91.1 + +* Wed Aug 31 2011 Matthias Clasen - 3.1.90.1-1 +- Update to 3.1.90.1 + +* Wed Jul 27 2011 Matthias Clasen - 3.1.4-1 +- Update to 3.1.4 + +* Wed Jul 27 2011 Matthias Clasen - 3.1.3.1-3 +- Rebuild + +* Mon Jul 4 2011 Peter Robinson - 3.1.3.1-2 +- rebuild against new clutter/cogl + +* Mon Jul 04 2011 Adam Williamson - 3.1.3.1-1 +- Update to 3.1.3.1 + +* Thu Jun 30 2011 Owen Taylor - 3.1.3-1 +- Update to 3.1.3 + +* Wed May 25 2011 Owen Taylor - 3.0.2.1-1 +- Update to 3.0.2.1 + +* Fri Apr 29 2011 Matthias Clasen - 3.0.1-3 +- Actually apply the patch for #700276 + +* Thu Apr 28 2011 Matthias Clasen - 3.0.1-2 +- Make session saving of gnome-shell work + +* Mon Apr 25 2011 Owen Taylor - 3.0.1-1 +- Update to 3.0.1 + +* Mon Apr 4 2011 Owen Taylor - 3.0.0-1 +- Update to 3.0.0 + +* Mon Mar 28 2011 Matthias Clasen - 2.91.93-1 +- Update to 2.91.93 + +* Wed Mar 23 2011 Matthias Clasen - 2.91.92-1 +- Update to 2.91.92 + +* Mon Mar 7 2011 Owen Taylor - 2.91.91-1 +- Update to 2.91.91 + +* Tue Mar 1 2011 Matthias Clasen - 2.91.90-2 +- Build against libcanberra, to enable AccessX feedback features + +* Tue Feb 22 2011 Matthias Clasen - 2.91.90-1 +- Update to 2.91.90 + +* Thu Feb 10 2011 Matthias Clasen - 2.91.6-4 +- Rebuild against newer gtk + +* Tue Feb 08 2011 Fedora Release Engineering - 2.91.6-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Wed Feb 2 2011 Matthias Clasen - 2.91.6-2 +- Rebuild against newer gtk + +* Tue Feb 1 2011 Owen Taylor - 2.91.6-1 +- Update to 2.91.6 + +* Tue Jan 11 2011 Matthias Clasen - 2.91.5-1 +- Update to 2.91.5 + +* Fri Jan 7 2011 Matthias Clasen - 2.91.4-1 +- Update to 2.91.4 + +* Fri Dec 3 2010 Matthias Clasen - 2.91.3-2 +- Rebuild against new gtk +- Drop no longer needed %%clean etc + +* Mon Nov 29 2010 Owen Taylor - 2.91.3-1 +- Update to 2.91.3 + +* Tue Nov 9 2010 Owen Taylor - 2.91.2-1 +- Update to 2.91.2 + +* Tue Nov 2 2010 Matthias Clasen - 2.91.1-2 +- Rebuild against newer gtk3 + +* Fri Oct 29 2010 Owen Taylor - 2.91.1-1 +- Update to 2.91.1 + +* Mon Oct 4 2010 Owen Taylor - 2.91.0-1 +- Update to 2.91.0 + +* Wed Sep 22 2010 Matthias Clasen - 2.31.5-4 +- Rebuild against newer gobject-introspection + +* Wed Jul 14 2010 Colin Walters - 2.31.5-3 +- Rebuild for new gobject-introspection + +* Tue Jul 13 2010 Adel Gadllah - 2.31.5-2 +- Build against gtk3 + +* Mon Jul 12 2010 Colin Walters - 2.31.5-1 +- New upstream version + +* Mon Jul 12 2010 Colin Walters - 2.31.2-5 +- Rebuild against new gobject-introspection + +* Tue Jul 6 2010 Colin Walters - 2.31.2-4 +- Changes to support snapshot builds + +* Fri Jun 25 2010 Colin Walters - 2.31.2-3 +- drop gir-repository-devel dep + +* Wed May 26 2010 Adam Miller - 2.31.2-2 +- removed "--with-clutter" as configure is claiming it to be an unknown option + +* Wed May 26 2010 Adam Miller - 2.31.2-1 +- New upstream 2.31.2 release + +* Thu Mar 25 2010 Peter Robinson 2.29.1-1 +- New upstream 2.29.1 release + +* Wed Mar 17 2010 Peter Robinson 2.29.0-1 +- New upstream 2.29.0 release + +* Tue Feb 16 2010 Adam Jackson 2.28.1-0.2 +- mutter-2.28.1-add-needed.patch: Fix FTBFS from --no-add-needed + +* Thu Feb 4 2010 Peter Robinson 2.28.1-0.1 +- Move to git snapshot + +* Wed Oct 7 2009 Owen Taylor - 2.28.0-1 +- Update to 2.28.0 + +* Tue Sep 15 2009 Owen Taylor - 2.27.5-1 +- Update to 2.27.5 + +* Fri Sep 4 2009 Owen Taylor - 2.27.4-1 +- Remove workaround for #520209 +- Update to 2.27.4 + +* Sat Aug 29 2009 Owen Taylor - 2.27.3-3 +- Fix %%preun GConf script to properly be for package removal + +* Fri Aug 28 2009 Owen Taylor - 2.27.3-2 +- Add a workaround for Red Hat bug #520209 + +* Fri Aug 28 2009 Owen Taylor - 2.27.3-1 +- Update to 2.27.3, remove mutter-metawindow.patch + +* Fri Aug 21 2009 Peter Robinson 2.27.2-2 +- Add upstream patch needed by latest mutter-moblin + +* Tue Aug 11 2009 Peter Robinson 2.27.2-1 +- New upstream 2.27.2 release. Drop upstreamed patches. + +* Wed Jul 29 2009 Peter Robinson 2.27.1-5 +- Add upstream patches for clutter 1.0 + +* Wed Jul 29 2009 Peter Robinson 2.27.1-4 +- Add patch to fix mutter --replace + +* Sat Jul 25 2009 Fedora Release Engineering - 2.27.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild + +* Sat Jul 18 2009 Peter Robinson 2.27.1-2 +- Updates from review request + +* Fri Jul 17 2009 Peter Robinson 2.27.1-1 +- Update to official 2.27.1 and review updates + +* Thu Jun 18 2009 Peter Robinson 2.27.0-0.2 +- Updates from initial reviews + +* Thu Jun 18 2009 Peter Robinson 2.27.0-0.1 +- Initial packaging