File 3746.patch of Package mutter

diff -ruN a/src/backends/meta-crtc.c b/src/backends/meta-crtc.c
--- a/src/backends/meta-crtc.c	2024-07-04 00:10:40.000000000 +0200
+++ b/src/backends/meta-crtc.c	2024-07-30 18:45:12.321605889 +0200
@@ -130,6 +130,10 @@
 meta_crtc_unset_config (MetaCrtc *crtc)
 {
   MetaCrtcPrivate *priv = meta_crtc_get_instance_private (crtc);
+  MetaCrtcClass *klass = META_CRTC_GET_CLASS (crtc);
+
+  if (klass->unset_config)
+    klass->unset_config (crtc);
 
   g_clear_pointer (&priv->config, g_free);
 }
@@ -470,3 +474,14 @@
 
   return config;
 }
+
+gboolean
+meta_crtc_is_leased (MetaCrtc *crtc)
+{
+  MetaCrtcClass *klass = META_CRTC_GET_CLASS (crtc);
+
+  if (klass->is_leased)
+    return klass->is_leased (crtc);
+  else
+    return FALSE;
+}
diff -ruN a/src/backends/meta-crtc.h b/src/backends/meta-crtc.h
--- a/src/backends/meta-crtc.h	2024-07-04 00:10:40.000000000 +0200
+++ b/src/backends/meta-crtc.h	2024-07-30 18:45:12.321605889 +0200
@@ -55,6 +55,10 @@
   void (* set_config) (MetaCrtc             *crtc,
                        const MetaCrtcConfig *config,
                        gpointer              backend_private);
+
+  void (* unset_config) (MetaCrtc *crtc);
+
+  gboolean (* is_leased) (MetaCrtc *crtc);
 };
 
 META_EXPORT_TEST
@@ -132,4 +136,6 @@
                                        MetaCrtcMode         *mode,
                                        MetaMonitorTransform  transform);
 
+gboolean meta_crtc_is_leased (MetaCrtc *crtc);
+
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (MetaGammaLut, meta_gamma_lut_free)
diff -ruN a/src/backends/meta-monitor-config-manager.c b/src/backends/meta-monitor-config-manager.c
--- a/src/backends/meta-monitor-config-manager.c	2024-07-04 00:10:40.000000000 +0200
+++ b/src/backends/meta-monitor-config-manager.c	2024-07-30 18:45:12.314939177 +0200
@@ -99,6 +99,9 @@
 {
   unsigned int i;
 
+  if (meta_crtc_is_leased (crtc))
+    return TRUE;
+
   for (i = 0; i < crtc_assignments->len; i++)
     {
       MetaCrtcAssignment *assigned_crtc_assignment =
@@ -383,9 +386,9 @@
                                     GPtrArray         **out_output_assignments,
                                     GError            **error)
 {
-  GPtrArray *crtc_assignments;
-  GPtrArray *output_assignments;
-  GArray *reserved_crtcs;
+  g_autoptr (GPtrArray) crtc_assignments = NULL;
+  g_autoptr (GPtrArray) output_assignments = NULL;
+  g_autoptr (GArray) reserved_crtcs = NULL;
   GList *l;
 
   crtc_assignments =
@@ -432,18 +435,11 @@
                                          config, logical_monitor_config,
                                          crtc_assignments, output_assignments,
                                          reserved_crtcs, error))
-        {
-          g_ptr_array_free (crtc_assignments, TRUE);
-          g_ptr_array_free (output_assignments, TRUE);
-          g_array_free (reserved_crtcs, TRUE);
-          return FALSE;
-        }
+        return FALSE;
     }
 
-  g_array_free (reserved_crtcs, TRUE);
-
-  *out_crtc_assignments = crtc_assignments;
-  *out_output_assignments = output_assignments;
+  *out_crtc_assignments = g_steal_pointer (&crtc_assignments);
+  *out_output_assignments = g_steal_pointer (&output_assignments);
 
   return TRUE;
 }
diff -ruN a/src/backends/native/meta-crtc-kms.c b/src/backends/native/meta-crtc-kms.c
--- a/src/backends/native/meta-crtc-kms.c	2024-07-04 00:10:40.000000000 +0200
+++ b/src/backends/native/meta-crtc-kms.c	2024-07-30 18:45:12.321605889 +0200
@@ -206,6 +206,15 @@
   clutter_stage_schedule_update (CLUTTER_STAGE (stage));
 }
 
+static gboolean
+meta_crtc_kms_is_leased (MetaCrtc *crtc)
+{
+  MetaCrtcKms *crtc_kms = META_CRTC_KMS (crtc);
+  MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms);
+
+  return meta_kms_crtc_is_leased (kms_crtc);
+}
+
 typedef struct _CrtcKmsAssignment
 {
   MetaKmsPlane *primary_plane;
@@ -247,6 +256,25 @@
   return FALSE;
 }
 
+static gboolean
+is_plane_leased (MetaKmsDevice *kms_device,
+                 MetaKmsPlane  *kms_plane)
+{
+  GList *l;
+
+  for (l = meta_kms_device_get_crtcs (kms_device); l; l = l->next)
+    {
+      MetaKmsCrtc *kms_crtc = l->data;
+      MetaCrtcKms *crtc_kms = meta_crtc_kms_from_kms_crtc (kms_crtc);
+
+      if (meta_kms_crtc_is_leased (kms_crtc) &&
+          crtc_kms->assigned_primary_plane == kms_plane)
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
 static MetaKmsPlane *
 find_unassigned_plane (MetaCrtcKms      *crtc_kms,
                        MetaKmsPlaneType  kms_plane_type,
@@ -270,6 +298,9 @@
                              crtc_assignments))
         continue;
 
+      if (is_plane_leased (kms_device, kms_plane))
+        continue;
+
       return kms_plane;
     }
 
@@ -326,6 +357,27 @@
   crtc_kms->assigned_cursor_plane = kms_assignment->cursor_plane;
 }
 
+void
+meta_crtc_kms_assign_planes (MetaCrtcKms  *crtc_kms,
+                             MetaKmsPlane *primary_plane,
+                             MetaKmsPlane *cursor_plane)
+{
+  crtc_kms->assigned_primary_plane = primary_plane;
+  crtc_kms->assigned_cursor_plane = cursor_plane;
+}
+
+static void
+meta_crtc_kms_unset_config (MetaCrtc *crtc)
+{
+  MetaCrtcKms *crtc_kms = META_CRTC_KMS (crtc);
+
+  if (meta_crtc_kms_is_leased (crtc))
+    return;
+
+  crtc_kms->assigned_primary_plane = NULL;
+  crtc_kms->assigned_cursor_plane = NULL;
+}
+
 static gboolean
 meta_crtc_kms_is_transform_handled (MetaCrtcNative       *crtc_native,
                                     MetaMonitorTransform  transform)
@@ -476,6 +528,8 @@
   crtc_class->set_gamma_lut = meta_crtc_kms_set_gamma_lut;
   crtc_class->assign_extra = meta_crtc_kms_assign_extra;
   crtc_class->set_config = meta_crtc_kms_set_config;
+  crtc_class->unset_config = meta_crtc_kms_unset_config;
+  crtc_class->is_leased = meta_crtc_kms_is_leased;
 
   crtc_native_class->is_transform_handled = meta_crtc_kms_is_transform_handled;
   crtc_native_class->is_hw_cursor_supported = meta_crtc_kms_is_hw_cursor_supported;
diff -ruN a/src/backends/native/meta-crtc-kms.h b/src/backends/native/meta-crtc-kms.h
--- a/src/backends/native/meta-crtc-kms.h	2024-07-04 00:10:40.000000000 +0200
+++ b/src/backends/native/meta-crtc-kms.h	2024-07-30 18:45:12.321605889 +0200
@@ -41,6 +41,10 @@
 
 MetaKmsPlane * meta_crtc_kms_get_assigned_cursor_plane (MetaCrtcKms *crtc_kms);
 
+void meta_crtc_kms_assign_planes (MetaCrtcKms  *crtc_kms,
+                                  MetaKmsPlane *primary_plane,
+                                  MetaKmsPlane *cursor_plane);
+
 void meta_crtc_kms_set_mode (MetaCrtcKms   *crtc_kms,
                              MetaKmsUpdate *kms_update);
 
diff -ruN a/src/backends/native/meta-drm-lease.c b/src/backends/native/meta-drm-lease.c
--- a/src/backends/native/meta-drm-lease.c	1970-01-01 01:00:00.000000000 +0100
+++ b/src/backends/native/meta-drm-lease.c	2024-07-30 18:45:12.321605889 +0200
@@ -0,0 +1,962 @@
+/*
+ * Copyright (C) 2023 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "backends/native/meta-drm-lease.h"
+
+#include <glib.h>
+
+#include "backends/native/meta-crtc-kms.h"
+#include "backends/native/meta-kms.h"
+#include "backends/native/meta-kms-connector.h"
+#include "backends/native/meta-kms-crtc-private.h"
+#include "backends/native/meta-kms-device.h"
+#include "backends/native/meta-kms-plane.h"
+
+enum
+{
+  PROP_0,
+
+  PROP_MANAGER_META_KMS,
+
+  N_PROPS_MANAGER,
+};
+
+static GParamSpec *props_manager[N_PROPS_MANAGER] = { NULL };
+
+enum
+{
+  MANAGER_DEVICE_ADDED,
+  MANAGER_DEVICE_REMOVED,
+  MANAGER_CONNECTOR_ADDED,
+  MANAGER_CONNECTOR_REMOVED,
+
+  N_SIGNALS_MANAGER,
+};
+
+static guint signals_manager[N_SIGNALS_MANAGER] = { 0 };
+
+enum
+{
+  LEASE_REVOKED,
+
+  N_SIGNALS_LEASE,
+};
+
+static guint signals_lease[N_SIGNALS_LEASE] = { 0 };
+
+struct _MetaDrmLeaseManager
+{
+  GObject parent;
+
+  MetaKms *kms;
+
+  gulong resources_changed_handler_id;
+  gulong lease_changed_handler_id;
+
+  GList *devices;
+  GHashTable *leases;
+  GHashTable *connectors;
+};
+
+G_DEFINE_TYPE (MetaDrmLeaseManager, meta_drm_lease_manager, G_TYPE_OBJECT)
+
+typedef struct _LeasingKmsAssignment
+{
+  MetaKmsConnector *connector;
+  MetaKmsCrtc *crtc;
+  MetaKmsPlane *primary_plane;
+  MetaKmsPlane *cursor_plane;
+} LeasingKmsAssignment;
+
+struct _MetaDrmLease
+{
+  GObject parent;
+
+  uint32_t lessee_id;
+  int fd;
+  MetaKmsDevice *kms_device;
+  GList *assignments;
+};
+
+G_DEFINE_TYPE (MetaDrmLease, meta_drm_lease, G_TYPE_OBJECT)
+
+static MetaKmsCrtc *
+find_crtc_to_lease (MetaKmsConnector *kms_connector)
+{
+  MetaKmsDevice *device = meta_kms_connector_get_device (kms_connector);
+  const MetaKmsConnectorState *connector_state =
+    meta_kms_connector_get_current_state (kms_connector);
+  GList *l;
+
+  for (l = meta_kms_device_get_crtcs (device); l; l = l->next)
+    {
+      MetaKmsCrtc *kms_crtc = l->data;
+      MetaCrtcKms *crtc_kms = meta_crtc_kms_from_kms_crtc (kms_crtc);
+      uint32_t crtc_idx;
+
+      if (meta_crtc_is_leased (META_CRTC (crtc_kms)))
+        continue;
+
+      if (meta_crtc_get_outputs (META_CRTC (crtc_kms)) != NULL)
+        continue;
+
+      crtc_idx = meta_kms_crtc_get_idx (kms_crtc);
+      if (!(connector_state->common_possible_crtcs & (1 << crtc_idx)))
+        continue;
+
+      return kms_crtc;
+    }
+
+  return NULL;
+}
+
+static gboolean
+is_plane_assigned (MetaKmsDevice *kms_device,
+                   MetaKmsPlane  *kms_plane)
+{
+  GList *l;
+
+  for (l = meta_kms_device_get_crtcs (kms_device); l; l = l->next)
+    {
+      MetaKmsCrtc *kms_crtc = l->data;
+      MetaCrtcKms *crtc_kms = meta_crtc_kms_from_kms_crtc (kms_crtc);
+
+      if (meta_crtc_kms_get_assigned_primary_plane (crtc_kms) == kms_plane)
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+static MetaKmsPlane *
+find_plane_to_lease (MetaKmsCrtc      *kms_crtc,
+                     MetaKmsPlaneType  plane_type)
+{
+  MetaKmsDevice *kms_device = meta_kms_crtc_get_device (kms_crtc);
+  GList *l;
+
+  for (l = meta_kms_device_get_planes (kms_device); l; l = l->next)
+    {
+      MetaKmsPlane *kms_plane = l->data;
+
+      if (meta_kms_plane_get_plane_type (kms_plane) != plane_type)
+        continue;
+
+      if (!meta_kms_plane_is_usable_with (kms_plane, kms_crtc))
+        continue;
+
+      if (is_plane_assigned (kms_device, kms_plane))
+        continue;
+
+      return kms_plane;
+    }
+
+  return NULL;
+}
+
+static gboolean
+find_resources_to_lease (MetaDrmLeaseManager  *lease_manager,
+                         MetaKmsDevice        *kms_device,
+                         GList                *connectors,
+                         GList               **out_assignments,
+                         GList               **out_crtcs,
+                         GList               **out_planes,
+                         GError              **error)
+{
+  MetaKms *kms = lease_manager->kms;
+  g_autoptr (GList) assignments = NULL;
+  g_autoptr (GList) crtcs = NULL;
+  g_autoptr (GList) planes = NULL;
+  GList *available_devices;
+  GList *available_connectors;
+  GList *l;
+
+  if (!kms_device)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                   "Cannot create lease without device");
+      return FALSE;
+    }
+
+  if (!connectors)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                   "Cannot create lease without connectors");
+      return FALSE;
+    }
+
+  available_devices = meta_kms_get_devices (kms);
+  if (!g_list_find (available_devices, kms_device))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                   "Failed to find KMS device %s",
+                   meta_kms_device_get_path (kms_device));
+      return FALSE;
+    }
+
+  available_connectors = meta_kms_device_get_connectors (kms_device);
+
+  for (l = connectors; l; l = l->next)
+    {
+      MetaKmsConnector *connector = l->data;
+      MetaKmsDevice *connector_device;
+
+      if (!g_list_find (available_connectors, connector) ||
+          !meta_kms_connector_is_for_lease (connector))
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                       "Failed to find connector %u (%s)",
+                       meta_kms_connector_get_id (connector),
+                       meta_kms_device_get_path (kms_device));
+          return FALSE;
+        }
+
+      connector_device = meta_kms_connector_get_device (connector);
+      if (connector_device != kms_device)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                       "Cannot create lease on multiple devices");
+          return FALSE;
+        }
+    }
+
+  for (l = connectors; l; l = l->next)
+    {
+      MetaKmsConnector *connector = l->data;
+      LeasingKmsAssignment *assignment;
+      MetaKmsCrtc *crtc;
+      MetaKmsPlane *primary_plane;
+      MetaKmsPlane *cursor_plane;
+
+      crtc = find_crtc_to_lease (connector);
+      if (!crtc)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                       "Failed to find CRTC to lease with connector %u (%s)",
+                       meta_kms_connector_get_id (connector),
+                       meta_kms_device_get_path (kms_device));
+          return FALSE;
+        }
+
+      crtcs = g_list_append (crtcs, crtc);
+
+      primary_plane = find_plane_to_lease (crtc, META_KMS_PLANE_TYPE_PRIMARY);
+      if (!primary_plane)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                       "Failed to find primary plane "
+                       "to lease with connector %u (%s)",
+                       meta_kms_connector_get_id (connector),
+                       meta_kms_device_get_path (kms_device));
+          return FALSE;
+        }
+
+      planes = g_list_append (planes, primary_plane);
+
+      cursor_plane = find_plane_to_lease (crtc, META_KMS_PLANE_TYPE_CURSOR);
+      if (!cursor_plane)
+        {
+          g_warning ("Failed to find cursor plane "
+                     "to lease with connector %u (%s)",
+                     meta_kms_connector_get_id (connector),
+                     meta_kms_device_get_path (kms_device));
+        }
+      else
+        {
+          planes = g_list_append (planes, cursor_plane);
+        }
+
+      assignment = g_new0 (LeasingKmsAssignment, 1);
+      assignment->connector = connector;
+      assignment->crtc = crtc;
+      assignment->primary_plane = primary_plane;
+      assignment->cursor_plane = cursor_plane;
+
+      assignments = g_list_append (assignments, assignment);
+    }
+
+  *out_assignments = g_steal_pointer (&assignments);
+  *out_crtcs = g_steal_pointer (&crtcs);
+  *out_planes = g_steal_pointer (&planes);
+  return TRUE;
+}
+
+uint32_t
+meta_drm_lease_get_id (MetaDrmLease *lease)
+{
+  return lease->lessee_id;
+}
+
+int
+meta_drm_lease_steal_fd (MetaDrmLease *lease)
+{
+  int fd = lease->fd;
+  lease->fd = -1;
+  return fd;
+}
+
+gboolean
+meta_drm_lease_is_active (MetaDrmLease *lease)
+{
+  return lease->lessee_id != 0;
+}
+
+static void
+meta_drm_lease_assign (MetaDrmLease *lease)
+{
+  GList *l;
+
+  for (l = lease->assignments; l; l = l->next)
+    {
+      LeasingKmsAssignment *assignment = l->data;
+      MetaCrtcKms *crtc_kms = meta_crtc_kms_from_kms_crtc (assignment->crtc);
+
+      meta_kms_crtc_set_is_leased (assignment->crtc, TRUE);
+      meta_crtc_kms_assign_planes (crtc_kms,
+                                   assignment->primary_plane,
+                                   assignment->cursor_plane);
+    }
+}
+
+static void
+meta_drm_lease_unassign (MetaDrmLease *lease)
+{
+  GList *l;
+
+  for (l = lease->assignments; l; l = l->next)
+    {
+      LeasingKmsAssignment *assignment = l->data;
+      MetaCrtcKms *crtc_kms = meta_crtc_kms_from_kms_crtc (assignment->crtc);
+
+      meta_kms_crtc_set_is_leased (assignment->crtc, FALSE);
+      meta_crtc_kms_assign_planes (crtc_kms, NULL, NULL);
+    }
+}
+
+static void
+mark_revoked (MetaDrmLease *lease)
+{
+  meta_drm_lease_unassign (lease);
+
+  g_signal_emit (lease, signals_lease[LEASE_REVOKED], 0);
+  lease->lessee_id = 0;
+}
+
+void
+meta_drm_lease_revoke (MetaDrmLease *lease)
+{
+  g_autoptr (GError) error;
+
+  if (!lease->lessee_id)
+    return;
+
+  if (!meta_kms_device_revoke_lease (lease->kms_device, lease->lessee_id, &error))
+    {
+      g_warning ("Failed to revoke DRM lease on %s: %s",
+                 meta_kms_device_get_path (lease->kms_device),
+                 error->message);
+      return;
+    }
+
+  mark_revoked (lease);
+}
+
+static void
+meta_drm_lease_disappeared (MetaDrmLease *lease)
+{
+  mark_revoked (lease);
+}
+
+static void
+meta_drm_lease_dispose (GObject *object)
+{
+  MetaDrmLease *lease = META_DRM_LEASE (object);
+
+  g_clear_object (&lease->kms_device);
+
+  if (lease->assignments)
+    {
+      g_list_free_full (lease->assignments, g_free);
+      lease->assignments = NULL;
+    }
+
+  G_OBJECT_CLASS (meta_drm_lease_parent_class)->dispose (object);
+}
+
+static void
+meta_drm_lease_finalize (GObject *object)
+{
+  MetaDrmLease *lease = META_DRM_LEASE (object);
+
+  close (lease->fd);
+
+  G_OBJECT_CLASS (meta_drm_lease_parent_class)->finalize (object);
+}
+
+static void
+meta_drm_lease_class_init (MetaDrmLeaseClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->dispose = meta_drm_lease_dispose;
+  object_class->finalize = meta_drm_lease_finalize;
+
+  signals_lease[LEASE_REVOKED] =
+    g_signal_new ("revoked",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE, 0);
+}
+
+static void
+meta_drm_lease_init (MetaDrmLease *lease)
+{
+}
+
+static void
+on_lease_revoked (MetaDrmLease        *lease,
+                  MetaDrmLeaseManager *lease_manager)
+{
+  GHashTableIter iter;
+  MetaKmsConnector *connector;
+  MetaDrmLease *other_lease;
+
+  g_signal_handlers_disconnect_by_func (lease,
+                                        on_lease_revoked,
+                                        lease_manager);
+
+  g_hash_table_iter_init (&iter, lease_manager->connectors);
+  while (g_hash_table_iter_next (&iter,
+                                 (gpointer *)&connector,
+                                 (gpointer *)&other_lease))
+    {
+      if (lease == other_lease)
+        g_hash_table_insert (lease_manager->connectors, connector, NULL);
+    }
+
+  g_hash_table_remove (lease_manager->leases,
+                       GUINT_TO_POINTER (lease->lessee_id));
+}
+
+MetaDrmLease *
+meta_drm_lease_manager_lease_connectors (MetaDrmLeaseManager  *lease_manager,
+                                         MetaKmsDevice        *kms_device,
+                                         GList                *connectors,
+                                         GError              **error)
+{
+  MetaDrmLease *lease;
+  g_autoptr (GList) assignments = NULL;
+  g_autoptr (GList) crtcs = NULL;
+  g_autoptr (GList) planes = NULL;
+  int fd;
+  uint32_t lessee_id;
+  GList *l;
+
+  if (!find_resources_to_lease (lease_manager,
+                                kms_device, connectors,
+                                &assignments, &crtcs, &planes,
+                                error))
+    return NULL;
+
+  if (!meta_kms_device_lease_objects (kms_device,
+                                      connectors, crtcs, planes,
+                                      &fd, &lessee_id,
+                                      error))
+    return NULL;
+
+  lease = g_object_new (META_TYPE_DRM_LEASE, NULL);
+  lease->lessee_id = lessee_id;
+  lease->fd = fd;
+  lease->kms_device = g_object_ref (kms_device);
+  lease->assignments = g_steal_pointer (&assignments);
+
+  meta_drm_lease_assign (lease);
+
+  g_signal_connect_after (lease, "revoked", G_CALLBACK (on_lease_revoked),
+                          lease_manager);
+
+  for (l = connectors; l; l = l->next)
+    {
+      MetaKmsConnector *connector = l->data;
+
+      g_hash_table_insert (lease_manager->connectors,
+                           connector, lease);
+    }
+
+  g_hash_table_insert (lease_manager->leases,
+                       GUINT_TO_POINTER (lessee_id), g_object_ref (lease));
+
+  return lease;
+}
+
+GList *
+meta_drm_lease_manager_get_devices (MetaDrmLeaseManager *lease_manager)
+{
+  return lease_manager->devices;
+}
+
+GList *
+meta_drm_lease_manager_get_connectors (MetaDrmLeaseManager *lease_manager,
+                                       MetaKmsDevice       *kms_device)
+{
+  GHashTableIter iter;
+  MetaKmsConnector *connector;
+  GList *connectors = NULL;
+
+  g_hash_table_iter_init (&iter, lease_manager->connectors);
+  while (g_hash_table_iter_next (&iter, (gpointer *)&connector, NULL))
+    {
+      if (meta_kms_connector_get_device (connector) == kms_device)
+        connectors = g_list_append (connectors, connector);
+    }
+
+  return connectors;
+}
+
+MetaKmsConnector *
+meta_drm_lease_manager_get_connector_from_id (MetaDrmLeaseManager *lease_manager,
+                                              uint32_t             connector_id)
+{
+  GHashTableIter iter;
+  MetaKmsConnector *connector;
+
+  g_hash_table_iter_init (&iter, lease_manager->connectors);
+  while (g_hash_table_iter_next (&iter, (gpointer *)&connector, NULL))
+    {
+      if (meta_kms_connector_get_id (connector) == connector_id)
+        return connector;
+    }
+
+  return NULL;
+}
+
+MetaDrmLease *
+meta_drm_lease_manager_get_lease_from_connector (MetaDrmLeaseManager *lease_manager,
+                                                 MetaKmsConnector    *kms_connector)
+{
+  return g_hash_table_lookup (lease_manager->connectors, kms_connector);
+}
+
+
+
+MetaDrmLease *
+meta_drm_lease_manager_get_lease_from_id (MetaDrmLeaseManager *lease_manager,
+                                          uint32_t             lessee_id)
+{
+  return g_hash_table_lookup (lease_manager->leases,
+                              GUINT_TO_POINTER (lessee_id));
+}
+
+static void
+update_devices (MetaDrmLeaseManager  *lease_manager,
+                GList               **added_devices_out,
+                GList               **removed_devices_out)
+{
+  g_autoptr (GList) added_devices = NULL;
+  GList *new_devices;
+  GList *l;
+
+  new_devices = g_list_copy (meta_kms_get_devices (lease_manager->kms));
+
+  for (l = new_devices; l; l = l->next)
+    {
+      MetaKmsDevice *kms_device = l->data;
+
+      if (g_list_find (lease_manager->devices, kms_device))
+        {
+          lease_manager->devices = g_list_remove (lease_manager->devices,
+                                                  kms_device);
+        }
+      else
+        {
+          added_devices = g_list_append (added_devices, kms_device);
+        }
+    }
+
+  *removed_devices_out = g_steal_pointer (&lease_manager->devices);
+  *added_devices_out = g_steal_pointer (&added_devices);
+  lease_manager->devices = new_devices;
+}
+
+static void
+update_connectors (MetaDrmLeaseManager  *lease_manager,
+                   GList               **added_connectors_out,
+                   GList               **removed_connectors_out,
+                   GList               **leases_to_revoke_out)
+{
+  MetaKms *kms = lease_manager->kms;
+  GHashTable *new_connectors;
+  MetaDrmLease *lease = NULL;
+  GList *l;
+  GList *o;
+  g_autoptr (GList) added_connectors = NULL;
+  g_autoptr (GList) removed_connectors = NULL;
+  g_autoptr (GList) leases_to_revoke = NULL;
+  MetaKmsConnector *kms_connector;
+  GHashTableIter iter;
+
+  new_connectors = g_hash_table_new_similar (lease_manager->connectors);
+
+  for (l = meta_kms_get_devices (kms); l; l = l->next)
+    {
+      MetaKmsDevice *kms_device = l->data;
+
+      for (o = meta_kms_device_get_connectors (kms_device); o; o = o->next)
+        {
+          kms_connector = o->data;
+
+          if (!meta_kms_connector_is_for_lease (kms_connector))
+            continue;
+
+          if (!g_hash_table_steal_extended (lease_manager->connectors,
+                                            kms_connector,
+                                            NULL, (gpointer *) &lease))
+            added_connectors = g_list_append (added_connectors, kms_connector);
+          g_hash_table_insert (new_connectors, kms_connector, lease);
+        }
+    }
+
+  g_hash_table_iter_init (&iter, lease_manager->connectors);
+  while (g_hash_table_iter_next (&iter, (gpointer *)&kms_connector, NULL))
+    {
+      removed_connectors = g_list_append (removed_connectors, kms_connector);
+
+      lease = meta_drm_lease_manager_get_lease_from_connector (lease_manager,
+                                                               kms_connector);
+      if (lease && meta_drm_lease_is_active (lease))
+        leases_to_revoke = g_list_append (leases_to_revoke, lease);
+    }
+
+  g_clear_pointer (&lease_manager->connectors, g_hash_table_unref);
+  lease_manager->connectors = new_connectors;
+
+  *added_connectors_out = g_steal_pointer (&added_connectors);
+  *removed_connectors_out = g_steal_pointer (&removed_connectors);
+  *leases_to_revoke_out = g_steal_pointer (&leases_to_revoke);
+}
+
+static void
+update_resources (MetaDrmLeaseManager *lease_manager)
+{
+  g_autoptr (GList) added_devices = NULL;
+  g_autoptr (GList) removed_devices = NULL;
+  g_autoptr (GList) added_connectors = NULL;
+  g_autoptr (GList) removed_connectors = NULL;
+  g_autoptr (GList) leases_to_revoke = NULL;
+  GList *l;
+
+  update_devices (lease_manager, &added_devices, &removed_devices);
+  update_connectors (lease_manager, &added_connectors, &removed_connectors,
+                     &leases_to_revoke);
+
+  for (l = added_devices; l; l = l->next)
+    {
+      MetaKmsDevice *kms_device = l->data;
+
+      g_object_ref (kms_device);
+      g_signal_emit (lease_manager, signals_manager[MANAGER_DEVICE_ADDED],
+                     0, kms_device);
+    }
+
+  for (l = added_connectors; l; l = l->next)
+    {
+      MetaKmsConnector *kms_connector = l->data;
+      gboolean is_last_connector_update = FALSE;
+
+      if (g_list_length (removed_connectors) == 0 &&
+          kms_connector == g_list_last (added_connectors)->data)
+        is_last_connector_update = TRUE;
+
+      g_object_ref (kms_connector);
+      g_signal_emit (lease_manager, signals_manager[MANAGER_CONNECTOR_ADDED],
+                     0, kms_connector, is_last_connector_update);
+    }
+
+  for (l = removed_connectors; l; l = l->next)
+    {
+      MetaKmsConnector *kms_connector = l->data;
+      gboolean is_last_connector_update = FALSE;
+
+      if (kms_connector == g_list_last (removed_connectors)->data)
+        is_last_connector_update = TRUE;
+
+      g_signal_emit (lease_manager, signals_manager[MANAGER_CONNECTOR_REMOVED],
+                     0, kms_connector, is_last_connector_update);
+      g_object_unref (kms_connector);
+    }
+
+  for (l = leases_to_revoke; l; l = l->next)
+    {
+      MetaDrmLease *lease = l->data;
+
+      meta_drm_lease_revoke (lease);
+    }
+
+  for (l = removed_devices; l; l = l->next)
+    {
+      MetaKmsDevice *kms_device = l->data;
+
+      g_signal_emit (lease_manager, signals_manager[MANAGER_DEVICE_REMOVED],
+                     0, kms_device);
+      g_object_unref (kms_device);
+    }
+}
+
+static void
+lease_disappeared (MetaDrmLeaseManager *lease_manager,
+                   MetaDrmLease        *lease)
+{
+  GList *l;
+
+  for (l = lease->assignments; l; l = l->next)
+    {
+      LeasingKmsAssignment *assignment = l->data;
+      MetaKmsConnector *kms_connector = assignment->connector;
+
+      if (g_hash_table_lookup_extended (lease_manager->connectors,
+                                        kms_connector,
+                                        NULL, NULL))
+        g_hash_table_insert (lease_manager->connectors, kms_connector, NULL);
+    }
+
+  meta_drm_lease_disappeared (lease);
+}
+
+static gboolean
+did_lease_disappear (MetaDrmLease  *lease,
+                     uint32_t      *lessees,
+                     int            num_lessees,
+                     MetaKmsDevice *kms_device)
+{
+  int i;
+
+  if (lease->kms_device != kms_device)
+    return FALSE;
+
+  for (i = 0; i < num_lessees; i++)
+    {
+      if (lease->lessee_id == lessees[i])
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+static void
+update_leases (MetaDrmLeaseManager *lease_manager)
+{
+  MetaKms *kms = lease_manager->kms;
+  MetaDrmLease *lease;
+  GList *l;
+  g_autoptr (GList) disappeared_leases = NULL;
+
+  for (l = meta_kms_get_devices (kms); l; l = l->next)
+    {
+      MetaKmsDevice *kms_device = l->data;
+      g_autofree uint32_t *lessees = NULL;
+      int num_lessees;
+      g_autoptr (GError) error = NULL;
+      GHashTableIter iter;
+
+      if (!meta_kms_device_list_lessees (kms_device,
+                                         &lessees, &num_lessees,
+                                         &error))
+        {
+          g_warning ("Failed to list leases: %s", error->message);
+          continue;
+        }
+
+      g_hash_table_iter_init (&iter, lease_manager->leases);
+      while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&lease))
+        {
+          if (did_lease_disappear (lease, lessees, num_lessees, kms_device))
+            disappeared_leases = g_list_append (disappeared_leases, lease);
+        }
+    }
+
+  for (l = disappeared_leases; l; l = l->next)
+    {
+      lease = l->data;
+
+      lease_disappeared (lease_manager, lease);
+    }
+}
+
+static void
+on_resources_changed (MetaKms                *kms,
+                      MetaKmsResourceChanges  changes,
+                      MetaDrmLeaseManager    *lease_manager)
+{
+  if (changes != META_KMS_RESOURCE_CHANGE_FULL)
+    return;
+
+  update_resources (lease_manager);
+}
+
+static void
+on_lease_changed (MetaKms             *kms,
+                  MetaDrmLeaseManager *lease_manager)
+{
+  update_leases (lease_manager);
+}
+
+static void
+meta_drm_lease_manager_constructed (GObject *object)
+{
+  MetaDrmLeaseManager *lease_manager = META_DRM_LEASE_MANAGER (object);
+  MetaKms *kms = lease_manager->kms;
+
+  lease_manager->resources_changed_handler_id =
+    g_signal_connect (kms, "resources-changed",
+                      G_CALLBACK (on_resources_changed),
+                      lease_manager);
+  lease_manager->lease_changed_handler_id =
+    g_signal_connect (kms, "lease-changed",
+                      G_CALLBACK (on_lease_changed),
+                      lease_manager);
+
+  lease_manager->leases =
+    g_hash_table_new_full (NULL, NULL,
+                           NULL,
+                           (GDestroyNotify) g_object_unref);
+
+  lease_manager->connectors =
+    g_hash_table_new_full (NULL, NULL,
+                           NULL, NULL);
+
+  update_resources (lease_manager);
+
+  G_OBJECT_CLASS (meta_drm_lease_manager_parent_class)->constructed (object);
+}
+
+static void
+meta_drm_lease_manager_set_property (GObject      *object,
+                                     guint         prop_id,
+                                     const GValue *value,
+                                     GParamSpec   *pspec)
+{
+  MetaDrmLeaseManager *lease_manager = META_DRM_LEASE_MANAGER (object);
+  switch (prop_id)
+    {
+    case PROP_MANAGER_META_KMS:
+      lease_manager->kms = g_value_get_object (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+meta_drm_lease_manager_get_property (GObject    *object,
+                                     guint       prop_id,
+                                     GValue     *value,
+                                     GParamSpec *pspec)
+{
+  MetaDrmLeaseManager *lease_manager = META_DRM_LEASE_MANAGER (object);
+  switch (prop_id)
+    {
+    case PROP_MANAGER_META_KMS:
+      g_value_set_object (value, lease_manager->kms);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+meta_drm_lease_manager_dispose (GObject *object)
+{
+  MetaDrmLeaseManager *lease_manager = META_DRM_LEASE_MANAGER (object);
+  MetaKms *kms = lease_manager->kms;
+
+  g_clear_signal_handler (&lease_manager->resources_changed_handler_id, kms);
+  g_clear_signal_handler (&lease_manager->lease_changed_handler_id, kms);
+
+  g_list_free_full (g_steal_pointer (&lease_manager->devices), g_object_unref);
+  g_clear_pointer (&lease_manager->leases, g_hash_table_unref);
+  g_clear_pointer (&lease_manager->connectors, g_hash_table_unref);
+
+  G_OBJECT_CLASS (meta_drm_lease_manager_parent_class)->dispose (object);
+}
+
+static void
+meta_drm_lease_manager_class_init (MetaDrmLeaseManagerClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->constructed = meta_drm_lease_manager_constructed;
+  object_class->set_property = meta_drm_lease_manager_set_property;
+  object_class->get_property = meta_drm_lease_manager_get_property;
+  object_class->dispose = meta_drm_lease_manager_dispose;
+
+  props_manager[PROP_MANAGER_META_KMS] =
+    g_param_spec_object ("meta-kms", NULL, NULL,
+                         META_TYPE_KMS,
+                         G_PARAM_READWRITE |
+                         G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (object_class,
+                                     N_PROPS_MANAGER, props_manager);
+
+  signals_manager[MANAGER_DEVICE_ADDED] =
+    g_signal_new ("device-added",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE, 1,
+                  META_TYPE_KMS_DEVICE);
+
+  signals_manager[MANAGER_DEVICE_REMOVED] =
+    g_signal_new ("device-removed",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE, 1,
+                  META_TYPE_KMS_DEVICE);
+
+  signals_manager[MANAGER_CONNECTOR_ADDED] =
+    g_signal_new ("connector-added",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE, 2,
+                  META_TYPE_KMS_CONNECTOR,
+                  G_TYPE_BOOLEAN);
+
+  signals_manager[MANAGER_CONNECTOR_REMOVED] =
+    g_signal_new ("connector-removed",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE, 2,
+                  META_TYPE_KMS_CONNECTOR,
+                  G_TYPE_BOOLEAN);
+}
+
+static void
+meta_drm_lease_manager_init (MetaDrmLeaseManager *lease_manager)
+{
+}
diff -ruN a/src/backends/native/meta-drm-lease.h b/src/backends/native/meta-drm-lease.h
--- a/src/backends/native/meta-drm-lease.h	1970-01-01 01:00:00.000000000 +0100
+++ b/src/backends/native/meta-drm-lease.h	2024-07-30 18:45:12.321605889 +0200
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2023 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <glib-object.h>
+
+#include "backends/native/meta-backend-native.h"
+
+#define META_TYPE_DRM_LEASE (meta_drm_lease_get_type ())
+G_DECLARE_FINAL_TYPE (MetaDrmLease, meta_drm_lease,
+                      META, DRM_LEASE, GObject)
+
+#define META_TYPE_DRM_LEASE_MANAGER (meta_drm_lease_manager_get_type ())
+G_DECLARE_FINAL_TYPE (MetaDrmLeaseManager, meta_drm_lease_manager,
+                      META, DRM_LEASE_MANAGER, GObject)
+
+uint32_t meta_drm_lease_get_id (MetaDrmLease *lease);
+
+int meta_drm_lease_steal_fd (MetaDrmLease *lease);
+
+gboolean meta_drm_lease_is_active (MetaDrmLease *lease);
+
+void meta_drm_lease_revoke (MetaDrmLease *lease);
+
+MetaDrmLease * meta_drm_lease_manager_lease_connectors (MetaDrmLeaseManager  *lease_manager,
+                                                        MetaKmsDevice        *kms_device,
+                                                        GList                *connectors,
+                                                        GError              **error);
+
+GList * meta_drm_lease_manager_get_devices (MetaDrmLeaseManager *lease_manager);
+
+GList * meta_drm_lease_manager_get_connectors (MetaDrmLeaseManager *lease_manager,
+                                               MetaKmsDevice       *kms_device);
+
+MetaKmsConnector * meta_drm_lease_manager_get_connector_from_id (MetaDrmLeaseManager *lease_manager,
+                                                                 uint32_t             connector_id);
+
+MetaDrmLease * meta_drm_lease_manager_get_lease_from_connector (MetaDrmLeaseManager *lease_manager,
+                                                                MetaKmsConnector    *kms_connector);
+
+MetaDrmLease * meta_drm_lease_manager_get_lease_from_id (MetaDrmLeaseManager *lease_manager,
+                                                         uint32_t             lessee_id);
diff -ruN a/src/backends/native/meta-gpu-kms.c b/src/backends/native/meta-gpu-kms.c
--- a/src/backends/native/meta-gpu-kms.c	2024-07-04 00:10:40.000000000 +0200
+++ b/src/backends/native/meta-gpu-kms.c	2024-07-30 18:45:12.311605822 +0200
@@ -348,13 +348,14 @@
   for (l = meta_kms_device_get_connectors (gpu_kms->kms_device); l; l = l->next)
     {
       MetaKmsConnector *kms_connector = l->data;
-      const MetaKmsConnectorState *connector_state;
       MetaOutputKms *output_kms;
       MetaOutput *old_output;
       GError *error = NULL;
 
-      connector_state = meta_kms_connector_get_current_state (kms_connector);
-      if (!connector_state || connector_state->non_desktop)
+      if (!meta_kms_connector_get_current_state (kms_connector))
+        continue;
+
+      if (meta_kms_connector_is_for_lease (kms_connector))
         continue;
 
       old_output =
diff -ruN a/src/backends/native/meta-kms.c b/src/backends/native/meta-kms.c
--- a/src/backends/native/meta-kms.c	2024-07-04 00:10:40.000000000 +0200
+++ b/src/backends/native/meta-kms.c	2024-07-30 18:45:12.318272533 +0200
@@ -34,6 +34,8 @@
 enum
 {
   RESOURCES_CHANGED,
+  DEVICE_ADDED,
+  LEASE_CHANGED,
 
   N_SIGNALS
 };
@@ -47,6 +49,7 @@
   MetaKmsFlags flags;
 
   gulong hotplug_handler_id;
+  gulong lease_handler_id;
   gulong removed_handler_id;
 
   MetaKmsImpl *impl;
@@ -307,6 +310,14 @@
   handle_hotplug_event (kms, NULL, META_KMS_RESOURCE_CHANGE_NONE);
 }
 
+static void
+on_udev_lease (MetaUdev    *udev,
+               GUdevDevice *udev_device,
+               MetaKms     *kms)
+{
+  g_signal_emit (kms, signals[LEASE_CHANGED], 0);
+}
+
 MetaBackend *
 meta_kms_get_backend (MetaKms *kms)
 {
@@ -336,6 +347,8 @@
 
   kms->devices = g_list_append (kms->devices, device);
 
+  g_signal_emit (kms, signals[DEVICE_ADDED], 0, device);
+
   return device;
 }
 
@@ -400,6 +413,8 @@
     {
       kms->hotplug_handler_id =
         g_signal_connect (udev, "hotplug", G_CALLBACK (on_udev_hotplug), kms);
+      kms->lease_handler_id =
+        g_signal_connect (udev, "lease", G_CALLBACK (on_udev_lease), kms);
     }
 
   kms->removed_handler_id =
@@ -424,6 +439,7 @@
   g_list_free_full (kms->devices, g_object_unref);
 
   g_clear_signal_handler (&kms->hotplug_handler_id, udev);
+  g_clear_signal_handler (&kms->lease_handler_id, udev);
   g_clear_signal_handler (&kms->removed_handler_id, udev);
 
   G_OBJECT_CLASS (meta_kms_parent_class)->finalize (object);
@@ -452,6 +468,23 @@
                   G_TYPE_NONE, 1,
                   META_TYPE_KMS_RESOURCE_CHANGES);
 
+  signals[DEVICE_ADDED] =
+    g_signal_new ("device-added",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE, 1,
+                  META_TYPE_KMS_DEVICE);
+
+  signals[LEASE_CHANGED] =
+    g_signal_new ("lease-changed",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE, 0);
+
   meta_thread_class_register_impl_type (thread_class, META_TYPE_KMS_IMPL);
 }
 
diff -ruN a/src/backends/native/meta-kms-connector.c b/src/backends/native/meta-kms-connector.c
--- a/src/backends/native/meta-kms-connector.c	2024-07-04 00:10:40.000000000 +0200
+++ b/src/backends/native/meta-kms-connector.c	2024-07-30 18:45:12.314939177 +0200
@@ -164,6 +164,31 @@
   return connector->current_state;
 }
 
+gboolean
+meta_kms_connector_is_for_lease (MetaKmsConnector *connector)
+{
+  const char *lease_connectors_str;
+
+  if (!connector->current_state)
+    return FALSE;
+
+  lease_connectors_str = getenv ("MUTTER_DEBUG_LEASE_CONNECTORS");
+  if (lease_connectors_str && *lease_connectors_str != '\0')
+    {
+      int n;
+      g_auto (GStrv) names;
+
+      names = g_strsplit (lease_connectors_str, ":", -1);
+      for (n = 0; n < g_strv_length (names); n++)
+        {
+          if (g_str_equal (meta_kms_connector_get_name (connector), names[n]))
+            return TRUE;
+        }
+    }
+
+  return connector->current_state->non_desktop;
+}
+
 static gboolean
 has_privacy_screen_software_toggle (MetaKmsConnector *connector)
 {
diff -ruN a/src/backends/native/meta-kms-connector.h b/src/backends/native/meta-kms-connector.h
--- a/src/backends/native/meta-kms-connector.h	2024-07-04 00:10:40.000000000 +0200
+++ b/src/backends/native/meta-kms-connector.h	2024-07-30 18:45:12.314939177 +0200
@@ -101,3 +101,5 @@
 
 META_EXPORT_TEST
 const MetaKmsConnectorState * meta_kms_connector_get_current_state (MetaKmsConnector *connector);
+
+gboolean meta_kms_connector_is_for_lease (MetaKmsConnector *connector);
diff -ruN a/src/backends/native/meta-kms-crtc.c b/src/backends/native/meta-kms-crtc.c
--- a/src/backends/native/meta-kms-crtc.c	2024-07-04 00:10:40.000000000 +0200
+++ b/src/backends/native/meta-kms-crtc.c	2024-07-30 18:45:12.321605889 +0200
@@ -50,6 +50,8 @@
   MetaKmsCrtcState current_state;
 
   MetaKmsCrtcPropTable prop_table;
+
+  gboolean is_leased;
 };
 
 G_DEFINE_TYPE (MetaKmsCrtc, meta_kms_crtc, G_TYPE_OBJECT)
@@ -107,6 +109,19 @@
   return crtc->current_state.is_active;
 }
 
+gboolean
+meta_kms_crtc_is_leased (MetaKmsCrtc *crtc)
+{
+  return crtc->is_leased;
+}
+
+void
+meta_kms_crtc_set_is_leased (MetaKmsCrtc *crtc,
+                             gboolean     leased)
+{
+  crtc->is_leased = leased;
+}
+
 static void
 read_crtc_gamma (MetaKmsCrtc       *crtc,
                  MetaKmsCrtcState  *crtc_state,
diff -ruN a/src/backends/native/meta-kms-crtc.h b/src/backends/native/meta-kms-crtc.h
--- a/src/backends/native/meta-kms-crtc.h	2024-07-04 00:10:40.000000000 +0200
+++ b/src/backends/native/meta-kms-crtc.h	2024-07-30 18:45:12.318272533 +0200
@@ -65,3 +65,5 @@
 
 META_EXPORT_TEST
 gboolean meta_kms_crtc_is_active (MetaKmsCrtc *crtc);
+
+gboolean meta_kms_crtc_is_leased (MetaKmsCrtc *crtc);
diff -ruN a/src/backends/native/meta-kms-crtc-private.h b/src/backends/native/meta-kms-crtc-private.h
--- a/src/backends/native/meta-kms-crtc-private.h	2024-07-04 00:10:40.000000000 +0200
+++ b/src/backends/native/meta-kms-crtc-private.h	2024-07-30 18:45:12.321605889 +0200
@@ -58,3 +58,6 @@
                                            int64_t      *out_next_deadline_us,
                                            int64_t      *out_next_presentation_us,
                                            GError      **error);
+
+void meta_kms_crtc_set_is_leased (MetaKmsCrtc *crtc,
+                                  gboolean     leased);
diff -ruN a/src/backends/native/meta-kms-device.c b/src/backends/native/meta-kms-device.c
--- a/src/backends/native/meta-kms-device.c	2024-07-04 00:10:40.000000000 +0200
+++ b/src/backends/native/meta-kms-device.c	2024-07-30 18:45:12.318272533 +0200
@@ -423,6 +423,145 @@
   return needs_flush;
 }
 
+typedef struct
+{
+  MetaKmsDevice *device;
+  GList *connectors;
+  GList *crtcs;
+  GList *planes;
+
+  int fd;
+  uint32_t lessee_id;
+} LeaseRequestData;
+
+static gpointer
+lease_objects_in_impl (MetaThreadImpl  *thread_impl,
+                       gpointer         user_data,
+                       GError         **error)
+{
+  LeaseRequestData *data = user_data;
+  MetaKmsImplDevice *impl_device =
+    meta_kms_device_get_impl_device (data->device);
+  uint32_t lessee_id;
+  int fd;
+
+  if (!meta_kms_impl_device_lease_objects (impl_device,
+                                           data->connectors,
+                                           data->crtcs,
+                                           data->planes,
+                                           &fd,
+                                           &lessee_id,
+                                           error))
+    return GINT_TO_POINTER (FALSE);
+
+  data->fd = fd;
+  data->lessee_id = lessee_id;
+
+  return GINT_TO_POINTER (TRUE);
+}
+
+gboolean
+meta_kms_device_lease_objects (MetaKmsDevice  *device,
+                               GList          *connectors,
+                               GList          *crtcs,
+                               GList          *planes,
+                               int            *out_fd,
+                               uint32_t       *out_lessee_id,
+                               GError        **error)
+{
+  LeaseRequestData data = {};
+
+  data.device = device;
+  data.connectors = connectors;
+  data.crtcs = crtcs;
+  data.planes = planes;
+
+  if (!meta_kms_run_impl_task_sync (device->kms, lease_objects_in_impl, &data,
+                                    error))
+    return FALSE;
+
+  *out_fd = data.fd;
+  *out_lessee_id = data.lessee_id;
+  return TRUE;
+}
+
+typedef struct
+{
+  MetaKmsDevice *device;
+  uint32_t lessee_id;
+} RevokeLeaseData;
+
+static gpointer
+revoke_lease_in_impl (MetaThreadImpl  *thread_impl,
+                      gpointer         user_data,
+                      GError         **error)
+{
+  LeaseRequestData *data = user_data;
+  MetaKmsImplDevice *impl_device =
+    meta_kms_device_get_impl_device (data->device);
+
+  if (!meta_kms_impl_device_revoke_lease (impl_device, data->lessee_id, error))
+    return GINT_TO_POINTER (FALSE);
+  else
+    return GINT_TO_POINTER (TRUE);
+}
+
+gboolean
+meta_kms_device_revoke_lease (MetaKmsDevice  *device,
+                              uint32_t        lessee_id,
+                              GError        **error)
+{
+  LeaseRequestData data = {};
+
+  data.device = device;
+  data.lessee_id = lessee_id;
+
+  return !!meta_kms_run_impl_task_sync (device->kms, revoke_lease_in_impl, &data,
+                                        error);
+}
+
+typedef struct
+{
+  MetaKmsDevice *device;
+  uint32_t **out_lessee_ids;
+  int *out_num_lessee_ids;
+} ListLesseesData;
+
+static gpointer
+list_lessees_in_impl (MetaThreadImpl  *thread_impl,
+                      gpointer         user_data,
+                      GError         **error)
+{
+  ListLesseesData *data = user_data;
+  MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (data->device);
+
+  if (!meta_kms_impl_device_list_lessees (impl_device,
+                                          data->out_lessee_ids,
+                                          data->out_num_lessee_ids,
+                                          error))
+    return GINT_TO_POINTER (FALSE);
+  else
+    return GINT_TO_POINTER (TRUE);
+}
+
+gboolean
+meta_kms_device_list_lessees (MetaKmsDevice  *device,
+                              uint32_t      **out_lessee_ids,
+                              int            *out_num_lessee_ids,
+                              GError        **error)
+{
+  ListLesseesData data = {};
+
+  data.device = device;
+  data.out_lessee_ids = out_lessee_ids;
+  data.out_num_lessee_ids = out_num_lessee_ids;
+
+  return !!meta_kms_run_impl_task_sync (device->kms,
+                                        list_lessees_in_impl,
+                                        &data,
+                                        error);
+}
+
 typedef struct _CreateImplDeviceData
 {
   MetaKmsDevice *device;
diff -ruN a/src/backends/native/meta-kms-device.h b/src/backends/native/meta-kms-device.h
--- a/src/backends/native/meta-kms-device.h	2024-07-04 00:10:40.000000000 +0200
+++ b/src/backends/native/meta-kms-device.h	2024-07-30 18:45:12.318272533 +0200
@@ -86,6 +86,23 @@
 META_EXPORT_TEST
 void meta_kms_device_disable (MetaKmsDevice *device);
 
+gboolean meta_kms_device_lease_objects (MetaKmsDevice  *device,
+                                        GList          *connectors,
+                                        GList          *crtcs,
+                                        GList          *planes,
+                                        int            *out_fd,
+                                        uint32_t       *out_lessee_id,
+                                        GError        **error);
+
+gboolean meta_kms_device_revoke_lease (MetaKmsDevice  *device,
+                                       uint32_t        lessee_id,
+                                       GError        **error);
+
+gboolean meta_kms_device_list_lessees (MetaKmsDevice  *device,
+                                       uint32_t      **out_lessee_ids,
+                                       int            *out_num_lessee_ids,
+                                       GError        **error);
+
 MetaKmsDevice * meta_kms_device_new (MetaKms            *kms,
                                      const char         *path,
                                      MetaKmsDeviceFlag   flags,
diff -ruN a/src/backends/native/meta-kms-impl-device.c b/src/backends/native/meta-kms-impl-device.c
--- a/src/backends/native/meta-kms-impl-device.c	2024-07-04 00:10:40.000000000 +0200
+++ b/src/backends/native/meta-kms-impl-device.c	2024-07-30 18:45:12.321605889 +0200
@@ -20,6 +20,7 @@
 #include "backends/native/meta-kms-impl-device.h"
 
 #include <errno.h>
+#include <fcntl.h>
 #include <glib/gstdio.h>
 #include <linux/dma-buf.h>
 #include <sys/ioctl.h>
@@ -263,6 +264,188 @@
   return priv->path;
 }
 
+static MetaDeviceFile *
+meta_kms_impl_device_open_device_file (MetaKmsImplDevice  *impl_device,
+                                       const char         *path,
+                                       GError            **error)
+{
+  MetaKmsImplDevicePrivate *priv =
+    meta_kms_impl_device_get_instance_private (impl_device);
+  MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device);
+
+  return klass->open_device_file (impl_device, priv->path, error);
+}
+
+static gpointer
+kms_event_dispatch_in_impl (MetaThreadImpl  *impl,
+                            gpointer         user_data,
+                            GError         **error)
+{
+  MetaKmsImplDevice *impl_device = user_data;
+  gboolean ret;
+
+  ret = meta_kms_impl_device_dispatch (impl_device, error);
+  return GINT_TO_POINTER (ret);
+}
+
+static gboolean
+ensure_device_file (MetaKmsImplDevice  *impl_device,
+                    GError            **error)
+{
+  MetaKmsImplDevicePrivate *priv =
+    meta_kms_impl_device_get_instance_private (impl_device);
+  MetaDeviceFile *device_file;
+
+  if (priv->device_file)
+    return TRUE;
+
+  device_file = meta_kms_impl_device_open_device_file (impl_device,
+                                                       priv->path,
+                                                       error);
+  if (!device_file)
+    return FALSE;
+
+  priv->device_file = device_file;
+
+  if (!(priv->flags & META_KMS_DEVICE_FLAG_NO_MODE_SETTING))
+    {
+      priv->fd_source =
+        meta_thread_impl_register_fd (META_THREAD_IMPL (priv->impl),
+                                      meta_device_file_get_fd (device_file),
+                                      kms_event_dispatch_in_impl,
+                                      impl_device);
+      g_source_set_priority (priv->fd_source, G_PRIORITY_HIGH);
+    }
+
+  return TRUE;
+}
+
+gboolean
+meta_kms_impl_device_lease_objects (MetaKmsImplDevice  *impl_device,
+                                    GList              *connectors,
+                                    GList              *crtcs,
+                                    GList              *planes,
+                                    int                *out_fd,
+                                    uint32_t           *out_lessee_id,
+                                    GError            **error)
+{
+  MetaKmsImplDevicePrivate *priv =
+    meta_kms_impl_device_get_instance_private (impl_device);
+  uint32_t *object_ids;
+  int n_object_ids;
+  GList *l;
+  int retval;
+  uint32_t lessee_id;
+  int i = 0;
+
+  meta_assert_in_kms_impl (meta_kms_impl_get_kms (priv->impl));
+
+  if (!ensure_device_file (impl_device, error))
+    return FALSE;
+
+  meta_kms_impl_device_hold_fd (impl_device);
+
+  n_object_ids = (g_list_length (connectors) +
+                  g_list_length (crtcs) +
+                  g_list_length (planes));
+  object_ids = g_alloca (sizeof (uint32_t) * n_object_ids);
+
+  for (l = connectors; l; l = l->next)
+    {
+      MetaKmsConnector *connector = l->data;
+
+      object_ids[i++] = meta_kms_connector_get_id (connector);
+    }
+
+  for (l = crtcs; l; l = l->next)
+    {
+      MetaKmsCrtc *crtc = l->data;
+
+      object_ids[i++] = meta_kms_crtc_get_id (crtc);
+    }
+
+  for (l = planes; l; l = l->next)
+    {
+      MetaKmsPlane *plane = l->data;
+
+      object_ids[i++] = meta_kms_plane_get_id (plane);
+    }
+
+  retval = drmModeCreateLease (meta_kms_impl_device_get_fd (impl_device),
+                               object_ids, n_object_ids, 0,
+                               &lessee_id);
+
+  if (retval < 0)
+    {
+      meta_kms_impl_device_unhold_fd (impl_device);
+      g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-retval),
+                   "Failed to create lease: %s", g_strerror (-retval));
+      return FALSE;
+    }
+
+  *out_fd = retval;
+  *out_lessee_id = lessee_id;
+
+  return TRUE;
+}
+
+gboolean
+meta_kms_impl_device_revoke_lease (MetaKmsImplDevice  *impl_device,
+                                   uint32_t            lessee_id,
+                                   GError            **error)
+{
+  MetaKmsImplDevicePrivate *priv =
+    meta_kms_impl_device_get_instance_private (impl_device);
+  int retval;
+
+  meta_assert_in_kms_impl (meta_kms_impl_get_kms (priv->impl));
+
+  retval = drmModeRevokeLease (meta_kms_impl_device_get_fd (impl_device),
+                               lessee_id);
+  meta_kms_impl_device_unhold_fd (impl_device);
+
+  if (retval != 0)
+    {
+      g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-retval),
+                   "Failed to revoke lease: %s", g_strerror (-retval));
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+gboolean
+meta_kms_impl_device_list_lessees (MetaKmsImplDevice  *impl_device,
+                                   uint32_t          **out_lessee_ids,
+                                   int                *out_num_lessee_ids,
+                                   GError            **error)
+{
+  MetaKmsImplDevicePrivate *priv =
+    meta_kms_impl_device_get_instance_private (impl_device);
+  drmModeLesseeListRes *list;
+  int i;
+  uint32_t *lessee_ids;
+
+  meta_assert_in_kms_impl (meta_kms_impl_get_kms (priv->impl));
+
+  list = drmModeListLessees (meta_kms_impl_device_get_fd (impl_device));
+
+  if (!list)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Failed to list lessees");
+      return FALSE;
+    }
+
+  lessee_ids = g_new0 (uint32_t, list->count);
+  for (i = 0; i < list->count; i++)
+    lessee_ids[i] = list->lessees[i];
+
+  *out_lessee_ids = lessee_ids;
+  *out_num_lessee_ids = list->count;
+  return TRUE;
+}
+
 gboolean
 meta_kms_impl_device_dispatch (MetaKmsImplDevice  *impl_device,
                                GError            **error)
@@ -313,18 +496,6 @@
   return TRUE;
 }
 
-static gpointer
-kms_event_dispatch_in_impl (MetaThreadImpl  *impl,
-                            gpointer         user_data,
-                            GError         **error)
-{
-  MetaKmsImplDevice *impl_device = user_data;
-  gboolean ret;
-
-  ret = meta_kms_impl_device_dispatch (impl_device, error);
-  return GINT_TO_POINTER (ret);
-}
-
 drmModePropertyPtr
 meta_kms_impl_device_find_property (MetaKmsImplDevice       *impl_device,
                                     drmModeObjectProperties *props,
@@ -889,50 +1060,6 @@
   priv->fallback_modes = g_list_reverse (modes);
 }
 
-static MetaDeviceFile *
-meta_kms_impl_device_open_device_file (MetaKmsImplDevice  *impl_device,
-                                       const char         *path,
-                                       GError            **error)
-{
-  MetaKmsImplDevicePrivate *priv =
-    meta_kms_impl_device_get_instance_private (impl_device);
-  MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device);
-
-  return klass->open_device_file (impl_device, priv->path, error);
-}
-
-static gboolean
-ensure_device_file (MetaKmsImplDevice  *impl_device,
-                    GError            **error)
-{
-  MetaKmsImplDevicePrivate *priv =
-    meta_kms_impl_device_get_instance_private (impl_device);
-  MetaDeviceFile *device_file;
-
-  if (priv->device_file)
-    return TRUE;
-
-  device_file = meta_kms_impl_device_open_device_file (impl_device,
-                                                       priv->path,
-                                                       error);
-  if (!device_file)
-    return FALSE;
-
-  priv->device_file = device_file;
-
-  if (!(priv->flags & META_KMS_DEVICE_FLAG_NO_MODE_SETTING))
-    {
-      priv->fd_source =
-        meta_thread_impl_register_fd (META_THREAD_IMPL (priv->impl),
-                                      meta_device_file_get_fd (device_file),
-                                      kms_event_dispatch_in_impl,
-                                      impl_device);
-      g_source_set_priority (priv->fd_source, G_PRIORITY_HIGH);
-    }
-
-  return TRUE;
-}
-
 static void
 ensure_latched_fd_hold (MetaKmsImplDevice *impl_device)
 {
@@ -1067,6 +1194,52 @@
 }
 
 /**
+ * meta_kms_impl_device_open_non_privileged_fd:
+ * @impl_device: a #MetaKmsImplDevice object
+ *
+ * Returns a non-master file descriptor for the given impl_device. The caller is
+ * responsable of closing the file descriptor.
+ *
+ * On error, returns a negative value.
+ */
+int
+meta_kms_impl_device_open_non_privileged_fd (MetaKmsImplDevice *impl_device)
+{
+  int fd;
+  const char *path;
+  MetaKmsImplDevicePrivate *priv =
+    meta_kms_impl_device_get_instance_private (impl_device);
+
+  if (!priv->device_file)
+    return -1;
+
+  path = meta_device_file_get_path (priv->device_file);
+
+  fd = open (path, O_RDWR | O_CLOEXEC);
+  if (fd < 0)
+    {
+      meta_topic (META_DEBUG_KMS,
+                  "Error getting non-master fd for device at '%s': %s",
+                  path,
+                  g_strerror (errno));
+      return -1;
+    }
+
+  if (drmIsMaster (fd))
+    {
+      if (drmDropMaster (fd) < 0)
+        {
+          meta_topic (META_DEBUG_KMS,
+                      "Error dropping master for device at '%s'",
+                      path);
+          return -1;
+        }
+    }
+
+  return fd;
+}
+
+/**
  * meta_kms_impl_device_get_signaled_sync_file:
  * @impl_device: a #MetaKmsImplDevice object
  *
diff -ruN a/src/backends/native/meta-kms-impl-device.h b/src/backends/native/meta-kms-impl-device.h
--- a/src/backends/native/meta-kms-impl-device.h	2024-07-04 00:10:40.000000000 +0200
+++ b/src/backends/native/meta-kms-impl-device.h	2024-07-30 18:45:12.321605889 +0200
@@ -132,6 +132,23 @@
 
 const char * meta_kms_impl_device_get_path (MetaKmsImplDevice *impl_device);
 
+gboolean meta_kms_impl_device_lease_objects (MetaKmsImplDevice  *impl_device,
+                                             GList              *connectors,
+                                             GList              *crtcs,
+                                             GList              *planes,
+                                             int                *out_fd,
+                                             uint32_t           *out_lessee_id,
+                                             GError            **error);
+
+gboolean meta_kms_impl_device_revoke_lease (MetaKmsImplDevice  *impl_device,
+                                            uint32_t            lessee_id,
+                                            GError            **error);
+
+gboolean meta_kms_impl_device_list_lessees (MetaKmsImplDevice  *impl_device,
+                                            uint32_t          **out_lessee_ids,
+                                            int                *out_num_lessee_ids,
+                                            GError            **error);
+
 gboolean meta_kms_impl_device_dispatch (MetaKmsImplDevice  *impl_device,
                                         GError            **error);
 
@@ -148,6 +165,8 @@
 
 void meta_kms_impl_device_unhold_fd (MetaKmsImplDevice *impl_device);
 
+int meta_kms_impl_device_open_non_privileged_fd (MetaKmsImplDevice *impl_device);
+
 int meta_kms_impl_device_get_signaled_sync_file (MetaKmsImplDevice *impl_device);
 
 MetaKmsResourceChanges meta_kms_impl_device_update_states (MetaKmsImplDevice *impl_device,
diff -ruN a/src/backends/native/meta-udev.c b/src/backends/native/meta-udev.c
--- a/src/backends/native/meta-udev.c	2024-07-04 00:10:40.000000000 +0200
+++ b/src/backends/native/meta-udev.c	2024-07-30 18:45:12.321605889 +0200
@@ -28,6 +28,7 @@
 enum
 {
   HOTPLUG,
+  LEASE,
   DEVICE_ADDED,
   DEVICE_REMOVED,
 
@@ -230,6 +231,9 @@
 
   if (g_udev_device_get_property_as_boolean (device, "HOTPLUG"))
     g_signal_emit (udev, signals[HOTPLUG], 0, device);
+
+  if (g_udev_device_get_property_as_boolean (device, "LEASE"))
+    g_signal_emit (udev, signals[LEASE], 0, device);
 }
 
 MetaUdev *
@@ -289,6 +293,13 @@
                   G_TYPE_FROM_CLASS (object_class),
                   G_SIGNAL_RUN_LAST,
                   0, NULL, NULL, NULL,
+                  G_TYPE_NONE, 1,
+                  G_UDEV_TYPE_DEVICE);
+  signals[LEASE] =
+    g_signal_new ("lease",
+                  G_TYPE_FROM_CLASS (object_class),
+                  G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL, NULL,
                   G_TYPE_NONE, 1,
                   G_UDEV_TYPE_DEVICE);
   signals[DEVICE_ADDED] =
diff -ruN a/src/meson.build b/src/meson.build
--- a/src/meson.build	2024-07-30 18:44:47.141439350 +0200
+++ b/src/meson.build	2024-07-30 18:45:12.321605889 +0200
@@ -726,6 +726,13 @@
       'wayland/meta-xwayland-surface.h',
     ]
   endif
+
+  if have_native_backend
+    mutter_sources += [
+      'wayland/meta-wayland-drm-lease.c',
+      'wayland/meta-wayland-drm-lease.h',
+    ]
+  endif
 endif
 
 if have_native_backend
@@ -809,6 +816,8 @@
     'backends/native/meta-kms-impl-device.h',
     'backends/native/meta-kms-impl.c',
     'backends/native/meta-kms-impl.h',
+    'backends/native/meta-drm-lease.c',
+    'backends/native/meta-drm-lease.h',
     'backends/native/meta-kms-mode.c',
     'backends/native/meta-kms-mode.h',
     'backends/native/meta-kms-page-flip.c',
@@ -1073,6 +1082,7 @@
   #  - protocol stability ('private', 'stable' or 'unstable')
   #  - protocol version (if stability is 'unstable')
   wayland_protocols = [
+    ['drm-lease', 'staging', 'v1', ],
     ['fractional-scale', 'staging', 'v1', ],
     ['gtk-shell', 'private', ],
     ['idle-inhibit', 'unstable', 'v1', ],
diff -ruN a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c
--- a/src/wayland/meta-wayland.c	2024-07-30 18:44:47.144772705 +0200
+++ b/src/wayland/meta-wayland.c	2024-07-30 18:45:12.324939244 +0200
@@ -69,6 +69,7 @@
 #ifdef HAVE_NATIVE_BACKEND
 #include "backends/native/meta-frame-native.h"
 #include "backends/native/meta-renderer-native.h"
+#include "wayland/meta-wayland-drm-lease.h"
 #endif
 
 enum
@@ -872,6 +873,10 @@
   meta_wayland_idle_inhibit_init (compositor);
   meta_wayland_drm_syncobj_init (compositor);
 
+#ifdef HAVE_NATIVE_BACKEND
+  meta_wayland_drm_lease_manager_init (compositor);
+#endif
+
 #ifdef HAVE_WAYLAND_EGLSTREAM
   {
     gboolean should_enable_eglstream_controller = TRUE;
diff -ruN a/src/wayland/meta-wayland-drm-lease.c b/src/wayland/meta-wayland-drm-lease.c
--- a/src/wayland/meta-wayland-drm-lease.c	1970-01-01 01:00:00.000000000 +0100
+++ b/src/wayland/meta-wayland-drm-lease.c	2024-07-30 18:45:12.324939244 +0200
@@ -0,0 +1,757 @@
+/*
+ * Copyright (C) 2016 Red Hat Inc.
+ * Copyright (C) 2017 Intel Corporation
+ * Copyright (C) 2018,2019 DisplayLink (UK) Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * 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 "wayland/meta-wayland-drm-lease.h"
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "backends/native/meta-backend-native.h"
+#include "backends/native/meta-drm-lease.h"
+#include "backends/native/meta-kms-connector.h"
+#include "backends/native/meta-kms-device.h"
+#include "backends/native/meta-kms-device-private.h"
+#include "backends/native/meta-kms-impl-device.h"
+#include "backends/native/meta-kms.h"
+#include "backends/edid.h"
+#include "wayland/meta-wayland-private.h"
+
+#include "drm-lease-v1-server-protocol.h"
+
+struct _MetaWaylandDrmLeaseManager
+{
+  MetaWaylandCompositor *compositor;
+  MetaDrmLeaseManager *drm_lease_manager;
+
+  /* Key:   MetaKmsDevice *kms_device
+   * Value: MetaWaylandDrmLeaseDevice *lease_device
+   */
+  GHashTable *devices;
+
+  GList *leases;
+};
+
+typedef struct _MetaWaylandDrmLeaseDevice
+{
+  MetaWaylandDrmLeaseManager *lease_manager;
+
+  struct wl_global *global;
+  MetaKmsDevice *kms_device;
+
+  /* Key:   MetaKmsConnector *kms_connector
+   * Value: MetaWaylandDrmLeaseConnector *lease_connector
+   */
+  GHashTable *connectors;
+
+  GList *resources;
+} MetaWaylandDrmLeaseDevice;
+
+typedef struct _MetaWaylandDrmLeaseConnector
+{
+  MetaWaylandDrmLeaseDevice *lease_device;
+
+  MetaKmsConnector *kms_connector;
+  char *description;
+
+  GList *resources;
+} MetaWaylandDrmLeaseConnector;
+
+typedef struct _MetaWaylandDrmLeaseRequest
+{
+  MetaWaylandDrmLeaseDevice *lease_device;
+  GList *lease_connectors;
+  struct wl_resource *resource;
+} MetaWaylandDrmLeaseRequest;
+
+typedef struct _MetaWaylandDrmLease
+{
+  MetaWaylandDrmLeaseManager *lease_manager;
+  MetaWaylandDrmLeaseDevice *lease_device;
+  uint32_t lessee_id;
+  struct wl_resource *resource;
+} MetaWaylandDrmLease;
+
+static void
+meta_wayland_drm_lease_device_free (MetaWaylandDrmLeaseDevice *lease_device)
+{
+  g_object_unref (lease_device->kms_device);
+  g_clear_pointer (&lease_device->connectors, g_hash_table_unref);
+}
+
+static void
+meta_wayland_drm_lease_device_release (MetaWaylandDrmLeaseDevice *lease_device)
+{
+  g_rc_box_release_full (lease_device,
+                         (GDestroyNotify) meta_wayland_drm_lease_device_free);
+}
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MetaWaylandDrmLeaseDevice,
+                               meta_wayland_drm_lease_device_release);
+
+static void
+meta_wayland_drm_lease_connector_free (MetaWaylandDrmLeaseConnector *lease_connector)
+{
+  g_object_unref (lease_connector->kms_connector);
+  g_free (lease_connector->description);
+  meta_wayland_drm_lease_device_release (lease_connector->lease_device);
+}
+
+static void
+meta_wayland_drm_lease_connector_release (MetaWaylandDrmLeaseConnector *lease_connector)
+{
+  g_rc_box_release_full (lease_connector,
+                         (GDestroyNotify) meta_wayland_drm_lease_connector_free);
+}
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MetaWaylandDrmLeaseConnector,
+                               meta_wayland_drm_lease_connector_release);
+
+static void
+meta_wayland_drm_lease_free (MetaWaylandDrmLease *lease)
+{
+  meta_wayland_drm_lease_device_release (lease->lease_device);
+}
+
+static void
+meta_wayland_drm_lease_release (MetaWaylandDrmLease *lease)
+{
+  g_rc_box_release_full (lease, (GDestroyNotify) meta_wayland_drm_lease_free);
+}
+
+static void
+meta_wayland_drm_lease_revoke (MetaWaylandDrmLease *lease)
+{
+  MetaDrmLease *drm_lease =
+    meta_drm_lease_manager_get_lease_from_id (lease->lease_manager->drm_lease_manager,
+                                              lease->lessee_id);
+
+  if (drm_lease)
+    meta_drm_lease_revoke (drm_lease);
+}
+
+static void
+on_lease_revoked (MetaDrmLease        *drm_lease,
+                  struct wl_resource  *resource)
+{
+  wp_drm_lease_v1_send_finished (resource);
+}
+
+static void
+wp_drm_lease_destroy (struct wl_client   *client,
+                      struct wl_resource *resource)
+{
+  MetaWaylandDrmLease *lease = wl_resource_get_user_data (resource);
+
+  meta_wayland_drm_lease_revoke (lease);
+
+  wl_resource_destroy (resource);
+}
+
+static const struct wp_drm_lease_v1_interface drm_lease_implementation = {
+  wp_drm_lease_destroy,
+};
+
+static void
+wp_drm_lease_destructor (struct wl_resource *resource)
+{
+  MetaWaylandDrmLease *lease = wl_resource_get_user_data (resource);
+  MetaDrmLease *drm_lease;
+
+  meta_wayland_drm_lease_revoke (lease);
+
+  drm_lease =
+    meta_drm_lease_manager_get_lease_from_id (lease->lease_manager->drm_lease_manager,
+                                              lease->lessee_id);
+  if (drm_lease)
+    {
+      g_signal_handlers_disconnect_by_func (drm_lease,
+                                            (gpointer) on_lease_revoked,
+                                            lease->resource);
+    }
+
+  lease->lease_manager->leases = g_list_remove (lease->lease_manager->leases,
+                                                lease);
+  meta_wayland_drm_lease_release (lease);
+}
+
+static void
+wp_drm_lease_request_request_connector (struct wl_client   *client,
+                                        struct wl_resource *resource,
+                                        struct wl_resource *connector)
+{
+  MetaWaylandDrmLeaseRequest *lease_request =
+    wl_resource_get_user_data (resource);
+  MetaWaylandDrmLeaseConnector *lease_connector =
+    wl_resource_get_user_data (connector);
+
+  if (lease_request->lease_device != lease_connector->lease_device)
+    {
+      wl_resource_post_error (resource,
+                              WP_DRM_LEASE_REQUEST_V1_ERROR_WRONG_DEVICE,
+                              "Wrong lease device");
+      return;
+    }
+
+  if (g_list_find (lease_request->lease_connectors, lease_connector))
+    {
+      wl_resource_post_error (resource,
+                              WP_DRM_LEASE_REQUEST_V1_ERROR_DUPLICATE_CONNECTOR,
+                              "Connector requested twice");
+      return;
+    }
+
+  lease_request->lease_connectors =
+    g_list_append (lease_request->lease_connectors,
+                   g_rc_box_acquire (lease_connector));
+}
+
+static void
+wp_drm_lease_request_submit (struct wl_client   *client,
+                             struct wl_resource *resource,
+                             uint32_t            id)
+{
+  MetaWaylandDrmLeaseRequest *lease_request =
+    wl_resource_get_user_data (resource);
+  MetaWaylandDrmLeaseDevice *lease_device = lease_request->lease_device;
+  MetaWaylandDrmLeaseManager *lease_manager = lease_device->lease_manager;
+  MetaKmsDevice *kms_device = lease_device->kms_device;
+  MetaDrmLeaseManager *drm_lease_manager = lease_manager->drm_lease_manager;
+  MetaWaylandDrmLease *lease;
+  g_autoptr (GList) connectors = NULL;
+  g_autoptr (MetaDrmLease) drm_lease = NULL;
+  g_autoptr (GError) error = NULL;
+  g_autofd int fd = -1;
+  GList *l;
+
+  if (!lease_request->lease_connectors)
+    {
+      wl_resource_post_error (resource,
+                              WP_DRM_LEASE_REQUEST_V1_ERROR_EMPTY_LEASE,
+                              "Empty DRM lease request");
+      wl_resource_destroy (resource);
+      return;
+    }
+
+  lease = g_rc_box_new0 (MetaWaylandDrmLease);
+  lease->lease_manager = lease_manager;
+  lease->lease_device = g_rc_box_acquire (lease_device);
+  lease->resource =
+    wl_resource_create (client, &wp_drm_lease_v1_interface,
+                        wl_resource_get_version (resource), id);
+
+  wl_resource_set_implementation (lease->resource,
+                                  &drm_lease_implementation,
+                                  lease,
+                                  wp_drm_lease_destructor);
+
+  lease_manager->leases = g_list_append (lease_manager->leases, lease);
+
+  for (l = lease_request->lease_connectors; l; l = l->next)
+    {
+      MetaWaylandDrmLeaseConnector *lease_connector = l->data;
+      MetaKmsConnector *kms_connector = lease_connector->kms_connector;
+
+      connectors = g_list_append (connectors, kms_connector);
+    }
+
+  drm_lease = meta_drm_lease_manager_lease_connectors (drm_lease_manager,
+                                                       kms_device,
+                                                       connectors,
+                                                       &error);
+  if (!drm_lease)
+    {
+      g_warning ("Failed to create lease from connector list: %s",
+                 error->message);
+      wp_drm_lease_v1_send_finished (lease->resource);
+      wl_resource_destroy (resource);
+      return;
+    }
+
+  g_signal_connect (drm_lease, "revoked",
+                    G_CALLBACK (on_lease_revoked),
+                    lease->resource);
+
+  fd = meta_drm_lease_steal_fd (drm_lease);
+  wp_drm_lease_v1_send_lease_fd (lease->resource, fd);
+
+  lease->lessee_id = meta_drm_lease_get_id (drm_lease);
+
+  wl_resource_destroy (resource);
+}
+
+static const struct wp_drm_lease_request_v1_interface drm_lease_request_implementation = {
+  wp_drm_lease_request_request_connector,
+  wp_drm_lease_request_submit,
+};
+
+static void
+wp_drm_lease_request_destructor (struct wl_resource *resource)
+{
+  MetaWaylandDrmLeaseRequest *lease_request =
+    wl_resource_get_user_data (resource);
+
+  meta_wayland_drm_lease_device_release (lease_request->lease_device);
+  g_list_foreach (lease_request->lease_connectors,
+                  (GFunc) meta_wayland_drm_lease_connector_release,
+                  NULL);
+  g_free (lease_request);
+}
+
+static void
+wp_drm_lease_device_create_lease_request (struct wl_client   *client,
+                                          struct wl_resource *resource,
+                                          uint32_t            id)
+{
+  MetaWaylandDrmLeaseDevice *lease_device =
+    wl_resource_get_user_data (resource);
+  MetaWaylandDrmLeaseRequest *lease_request;
+
+  lease_request = g_new0 (MetaWaylandDrmLeaseRequest, 1);
+  lease_request->lease_device = g_rc_box_acquire (lease_device);
+  lease_request->resource =
+    wl_resource_create (client, &wp_drm_lease_request_v1_interface,
+                        wl_resource_get_version (resource), id);
+
+  wl_resource_set_implementation (lease_request->resource,
+                                  &drm_lease_request_implementation,
+                                  lease_request,
+                                  wp_drm_lease_request_destructor);
+}
+
+static void
+wp_drm_lease_device_release (struct wl_client   *client,
+                             struct wl_resource *resource)
+{
+  wp_drm_lease_device_v1_send_released (resource);
+  wl_resource_destroy (resource);
+}
+
+static const struct wp_drm_lease_device_v1_interface drm_lease_device_implementation = {
+  wp_drm_lease_device_create_lease_request,
+  wp_drm_lease_device_release,
+};
+
+static char *
+get_connector_description (MetaKmsConnector *kms_connector)
+{
+  const MetaKmsConnectorState *connector_state;
+  gconstpointer edid_data;
+  g_autofree MetaEdidInfo *edid_info = NULL;
+  size_t edid_size;
+  g_autofree char *vendor = NULL;
+  g_autofree char *product = NULL;
+  GString *description;
+
+  connector_state = meta_kms_connector_get_current_state (kms_connector);
+  if (!connector_state || !connector_state->edid_data)
+    return g_strdup ("");
+
+  edid_data = g_bytes_get_data (connector_state->edid_data, &edid_size);
+  edid_info = meta_edid_info_new_parse (edid_data, edid_size);
+
+  description = g_string_new (NULL);
+
+  vendor = g_strndup (edid_info->manufacturer_code, 4);
+  if (vendor && g_utf8_validate (vendor, -1, NULL))
+    g_string_append_printf (description, "%s", vendor);
+
+  product = g_strndup (edid_info->dsc_product_name, 14);
+  if (product && g_utf8_validate (product, -1, NULL))
+    {
+      if (description->len > 0)
+        g_string_append_c (description, ' ');
+      g_string_append_printf (description, "%s", product);
+    }
+
+  if (description->len == 0)
+    {
+      g_string_append_printf (description, "%s",
+                              meta_kms_connector_get_name (kms_connector));
+    }
+
+  return g_string_free_and_steal (description);
+}
+
+static MetaWaylandDrmLeaseConnector *
+meta_wayland_drm_lease_connector_new (MetaWaylandDrmLeaseDevice *lease_device,
+                                      MetaKmsConnector          *kms_connector)
+{
+  MetaWaylandDrmLeaseConnector *lease_connector;
+
+  lease_connector = g_rc_box_new0 (MetaWaylandDrmLeaseConnector);
+  lease_connector->lease_device = g_rc_box_acquire (lease_device);
+  lease_connector->kms_connector = g_object_ref (kms_connector);
+  lease_connector->description = get_connector_description (kms_connector);
+
+  return lease_connector;
+}
+
+static void
+meta_wayland_drm_lease_connector_send_withdrawn (MetaWaylandDrmLeaseConnector *lease_connector)
+{
+  GList *l;
+
+  for (l = lease_connector->resources; l; l = l->next)
+    {
+      struct wl_resource *resource = l->data;
+
+      if (wl_resource_get_user_data (resource) == lease_connector)
+        wp_drm_lease_connector_v1_send_withdrawn (resource);
+    }
+}
+
+static void
+drm_lease_connector_destroy (struct wl_client   *client,
+                             struct wl_resource *resource)
+{
+  wl_resource_destroy (resource);
+}
+
+static const struct wp_drm_lease_connector_v1_interface drm_lease_connector_implementation = {
+  drm_lease_connector_destroy,
+};
+
+static void
+wp_drm_lease_connector_destructor (struct wl_resource *resource)
+{
+  MetaWaylandDrmLeaseConnector *lease_connector =
+    wl_resource_get_user_data (resource);
+
+  lease_connector->resources = g_list_remove (lease_connector->resources,
+                                              resource);
+  meta_wayland_drm_lease_connector_release (lease_connector);
+}
+
+static void
+send_new_connector_resource (MetaWaylandDrmLeaseDevice    *lease_device,
+                             struct wl_resource           *device_resource,
+                             MetaWaylandDrmLeaseConnector *lease_connector)
+{
+  struct wl_resource *connector_resource;
+  const char *connector_name;
+  uint32_t connector_id;
+
+  connector_resource =
+    wl_resource_create (wl_resource_get_client (device_resource),
+                        &wp_drm_lease_connector_v1_interface,
+                        wl_resource_get_version (device_resource),
+                        0);
+  wl_resource_set_implementation (connector_resource,
+                                  &drm_lease_connector_implementation,
+                                  g_rc_box_acquire (lease_connector),
+                                  wp_drm_lease_connector_destructor);
+
+  lease_connector->resources = g_list_append (lease_connector->resources,
+                                              connector_resource);
+
+  connector_name = meta_kms_connector_get_name (lease_connector->kms_connector);
+  connector_id = meta_kms_connector_get_id (lease_connector->kms_connector);
+
+  wp_drm_lease_device_v1_send_connector (device_resource, connector_resource);
+  wp_drm_lease_connector_v1_send_name (connector_resource, connector_name);
+  wp_drm_lease_connector_v1_send_description (connector_resource,
+                                              lease_connector->description);
+  wp_drm_lease_connector_v1_send_connector_id (connector_resource,
+                                               connector_id);
+  wp_drm_lease_connector_v1_send_done (connector_resource);
+}
+
+static void
+send_connectors (MetaWaylandDrmLeaseDevice *lease_device,
+                 struct wl_resource        *device_resource)
+{
+  GHashTableIter iter;
+  MetaWaylandDrmLeaseConnector *lease_connector;
+
+  g_hash_table_iter_init (&iter, lease_device->connectors);
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &lease_connector))
+    send_new_connector_resource (lease_device, device_resource, lease_connector);
+}
+
+static void
+send_drm_fd (struct wl_client          *client,
+             MetaWaylandDrmLeaseDevice *lease_device,
+             struct wl_resource        *device_resource)
+{
+  g_autofd int fd = -1;
+  MetaKmsImplDevice *impl_device;
+
+  impl_device = meta_kms_device_get_impl_device (lease_device->kms_device);
+  fd = meta_kms_impl_device_open_non_privileged_fd (impl_device);
+  if (fd < 0)
+    {
+      wl_client_post_implementation_error (client,
+                                           "Error getting DRM lease device fd");
+      return;
+    }
+
+  wp_drm_lease_device_v1_send_drm_fd (device_resource, fd);
+}
+
+static void
+wp_drm_lease_device_destructor (struct wl_resource *resource)
+{
+  MetaWaylandDrmLeaseDevice *lease_device =
+    wl_resource_get_user_data (resource);
+
+  lease_device->resources = g_list_remove (lease_device->resources, resource);
+  meta_wayland_drm_lease_device_release (lease_device);
+}
+
+static void
+lease_device_bind (struct wl_client *client,
+                   void             *user_data,
+                   uint32_t          version,
+                   uint32_t          id)
+{
+  MetaWaylandDrmLeaseDevice *lease_device = user_data;
+  struct wl_resource *resource;
+
+  resource = wl_resource_create (client, &wp_drm_lease_device_v1_interface,
+                                 version, id);
+  wl_resource_set_implementation (resource,
+                                  &drm_lease_device_implementation,
+                                  g_rc_box_acquire (lease_device),
+                                  wp_drm_lease_device_destructor);
+
+  send_drm_fd (client, lease_device, resource);
+  send_connectors (lease_device, resource);
+  wp_drm_lease_device_v1_send_done (resource);
+
+  lease_device->resources = g_list_prepend (lease_device->resources, resource);
+}
+
+static void
+meta_wayland_drm_lease_device_add_connector (MetaKmsConnector          *kms_connector,
+                                             MetaWaylandDrmLeaseDevice *lease_device)
+{
+  g_autoptr (MetaWaylandDrmLeaseConnector) lease_connector = NULL;
+
+  lease_connector = meta_wayland_drm_lease_connector_new (lease_device,
+                                                          kms_connector);
+  g_hash_table_insert (lease_device->connectors,
+                       kms_connector,
+                       g_steal_pointer (&lease_connector));
+}
+
+static MetaWaylandDrmLeaseDevice *
+meta_wayland_drm_lease_device_new (MetaWaylandDrmLeaseManager *lease_manager,
+                                   MetaKmsDevice              *kms_device)
+{
+  struct wl_display *wayland_display =
+    meta_wayland_compositor_get_wayland_display (lease_manager->compositor);
+  MetaDrmLeaseManager *drm_lease_manager = lease_manager->drm_lease_manager;
+  MetaWaylandDrmLeaseDevice *lease_device;
+  g_autoptr (GList) kms_connectors = NULL;
+
+  lease_device = g_rc_box_new0 (MetaWaylandDrmLeaseDevice);
+  lease_device->lease_manager = lease_manager;
+  lease_device->kms_device = g_object_ref (kms_device);
+
+  lease_device->connectors =
+    g_hash_table_new_full (NULL, NULL,
+                           NULL,
+                           (GDestroyNotify) meta_wayland_drm_lease_connector_release);
+
+  kms_connectors = meta_drm_lease_manager_get_connectors (drm_lease_manager,
+                                                          kms_device);
+  g_list_foreach (kms_connectors,
+                  (GFunc) meta_wayland_drm_lease_device_add_connector,
+                  lease_device);
+
+  lease_device->global = wl_global_create (wayland_display,
+                                           &wp_drm_lease_device_v1_interface,
+                                           META_WP_DRM_LEASE_DEVICE_V1_VERSION,
+                                           lease_device,
+                                           lease_device_bind);
+
+  return lease_device;
+}
+
+static void
+meta_wayland_drm_lease_manager_add_device (MetaKmsDevice              *kms_device,
+                                           MetaWaylandDrmLeaseManager *lease_manager)
+{
+  g_autoptr (MetaWaylandDrmLeaseDevice) lease_device = NULL;
+
+  lease_device = meta_wayland_drm_lease_device_new (lease_manager, kms_device);
+  g_hash_table_insert (lease_manager->devices,
+                       kms_device,
+                       g_steal_pointer (&lease_device));
+}
+
+static void
+on_device_added (MetaDrmLeaseManager        *drm_lease_manager,
+                 MetaKmsDevice              *kms_device,
+                 MetaWaylandDrmLeaseManager *lease_manager)
+{
+  meta_wayland_drm_lease_manager_add_device (kms_device, lease_manager);
+}
+
+static void
+on_device_removed (MetaDrmLeaseManager        *drm_lease_manager,
+                   MetaKmsDevice              *kms_device,
+                   MetaWaylandDrmLeaseManager *lease_manager)
+{
+  MetaWaylandDrmLeaseDevice *lease_device;
+
+  lease_device = g_hash_table_lookup (lease_manager->devices, kms_device);
+  g_return_if_fail (lease_device != NULL);
+
+  wl_global_remove (lease_device->global);
+  g_hash_table_remove (lease_manager->devices, kms_device);
+}
+
+static void
+on_connector_added (MetaDrmLeaseManager        *drm_lease_manager,
+                    MetaKmsConnector           *kms_connector,
+                    gboolean                    is_last_connector_update,
+                    MetaWaylandDrmLeaseManager *lease_manager)
+{
+  MetaWaylandDrmLeaseConnector *lease_connector;
+  MetaWaylandDrmLeaseDevice *lease_device;
+  MetaKmsDevice *kms_device;
+  GList *l;
+
+  kms_device = meta_kms_connector_get_device (kms_connector);
+  lease_device = g_hash_table_lookup (lease_manager->devices, kms_device);
+  g_return_if_fail (lease_device != NULL);
+
+  meta_wayland_drm_lease_device_add_connector (kms_connector, lease_device);
+  lease_connector = g_hash_table_lookup (lease_device->connectors,
+                                         kms_connector);
+  g_return_if_fail (lease_connector != NULL);
+
+  for (l = lease_device->resources; l; l = l->next)
+    {
+      struct wl_resource *resource = l->data;
+
+      if (wl_resource_get_user_data (resource) == lease_device)
+        send_new_connector_resource (lease_device, resource, lease_connector);
+    }
+
+  if (is_last_connector_update)
+    {
+      g_list_foreach (lease_device->resources,
+                      (GFunc) wp_drm_lease_device_v1_send_done,
+                      NULL);
+    }
+}
+
+static void
+on_connector_removed (MetaDrmLeaseManager        *drm_lease_manager,
+                      MetaKmsConnector           *kms_connector,
+                      gboolean                    is_last_connector_update,
+                      MetaWaylandDrmLeaseManager *lease_manager)
+{
+  MetaWaylandDrmLeaseConnector *lease_connector;
+  MetaWaylandDrmLeaseDevice *lease_device;
+  MetaKmsDevice *kms_device;
+
+  kms_device = meta_kms_connector_get_device (kms_connector);
+  lease_device = g_hash_table_lookup (lease_manager->devices, kms_device);
+  g_return_if_fail (lease_device != NULL);
+
+  lease_connector = g_hash_table_lookup (lease_device->connectors,
+                                         kms_connector);
+  g_return_if_fail (lease_connector != NULL);
+
+  meta_wayland_drm_lease_connector_send_withdrawn (lease_connector);
+  g_hash_table_remove (lease_device->connectors, kms_connector);
+
+  if (is_last_connector_update)
+    {
+      g_list_foreach (lease_device->resources,
+                      (GFunc) wp_drm_lease_device_v1_send_done,
+                      NULL);
+    }
+}
+
+static MetaWaylandDrmLeaseManager *
+meta_wayland_drm_lease_manager_new (MetaWaylandCompositor *compositor)
+{
+  MetaContext *context = meta_wayland_compositor_get_context (compositor);
+  MetaBackend *backend = meta_context_get_backend (context);
+  MetaBackendNative *backend_native;
+  MetaKms *kms;
+  MetaWaylandDrmLeaseManager *lease_manager;
+  MetaDrmLeaseManager *drm_lease_manager;
+
+  if (!META_IS_BACKEND_NATIVE (backend))
+    return NULL;
+
+  backend_native = META_BACKEND_NATIVE (backend);
+  kms = meta_backend_native_get_kms (backend_native);
+  drm_lease_manager = g_object_new (META_TYPE_DRM_LEASE_MANAGER,
+                                    "meta-kms", kms,
+                                    NULL);
+
+  lease_manager = g_new0 (MetaWaylandDrmLeaseManager, 1);
+  lease_manager->compositor = compositor;
+  lease_manager->drm_lease_manager = drm_lease_manager;
+  lease_manager->devices =
+    g_hash_table_new_full (NULL, NULL,
+                           NULL,
+                           (GDestroyNotify) meta_wayland_drm_lease_device_release);
+
+  g_list_foreach (meta_drm_lease_manager_get_devices (drm_lease_manager),
+                  (GFunc) meta_wayland_drm_lease_manager_add_device,
+                  lease_manager);
+
+  g_signal_connect (lease_manager->drm_lease_manager, "device-added",
+                    G_CALLBACK (on_device_added),
+                    lease_manager);
+  g_signal_connect (lease_manager->drm_lease_manager, "device-removed",
+                    G_CALLBACK (on_device_removed),
+                    lease_manager);
+  g_signal_connect (lease_manager->drm_lease_manager, "connector-added",
+                    G_CALLBACK (on_connector_added),
+                    lease_manager);
+  g_signal_connect (lease_manager->drm_lease_manager, "connector-removed",
+                    G_CALLBACK (on_connector_removed),
+                    lease_manager);
+
+  return lease_manager;
+}
+
+static void
+meta_wayland_drm_lease_manager_free (gpointer data)
+{
+  MetaWaylandDrmLeaseManager *lease_manager = data;
+
+  g_clear_pointer (&lease_manager->devices, g_hash_table_unref);
+  g_clear_pointer (&lease_manager->drm_lease_manager, g_object_unref);
+  g_list_foreach (lease_manager->leases,
+                  (GFunc) meta_wayland_drm_lease_release,
+                  NULL);
+  g_free (lease_manager);
+}
+
+void
+meta_wayland_drm_lease_manager_init (MetaWaylandCompositor *compositor)
+{
+  g_object_set_data_full (G_OBJECT (compositor), "-meta-wayland-drm-lease",
+                          meta_wayland_drm_lease_manager_new (compositor),
+                          meta_wayland_drm_lease_manager_free);
+}
diff -ruN a/src/wayland/meta-wayland-drm-lease.h b/src/wayland/meta-wayland-drm-lease.h
--- a/src/wayland/meta-wayland-drm-lease.h	1970-01-01 01:00:00.000000000 +0100
+++ b/src/wayland/meta-wayland-drm-lease.h	2024-07-30 18:45:12.321605889 +0200
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2021 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.
+ *
+ */
+
+#pragma once
+
+#include "wayland/meta-wayland-types.h"
+
+void meta_wayland_drm_lease_manager_init (MetaWaylandCompositor *compositor);
diff -ruN a/src/wayland/meta-wayland-types.h b/src/wayland/meta-wayland-types.h
--- a/src/wayland/meta-wayland-types.h	2024-07-04 00:10:40.000000000 +0200
+++ b/src/wayland/meta-wayland-types.h	2024-07-30 18:45:12.321605889 +0200
@@ -71,3 +71,5 @@
 typedef struct _MetaWaylandFilterManager MetaWaylandFilterManager;
 
 typedef struct _MetaWaylandClient MetaWaylandClient;
+
+typedef struct _MetaWaylandDrmLeaseManager MetaWaylandDrmLeaseManager;
diff -ruN a/src/wayland/meta-wayland-versions.h b/src/wayland/meta-wayland-versions.h
--- a/src/wayland/meta-wayland-versions.h	2024-07-30 18:44:47.144772705 +0200
+++ b/src/wayland/meta-wayland-versions.h	2024-07-30 18:47:55.306017193 +0200
@@ -58,3 +58,4 @@
 #define META_WP_SINGLE_PIXEL_BUFFER_V1_VERSION 1
 #define META_MUTTER_X11_INTEROP_VERSION 1
 #define META_WP_FRACTIONAL_SCALE_VERSION 1
+#define META_WP_DRM_LEASE_DEVICE_V1_VERSION 1
openSUSE Build Service is sponsored by