diff --git a/data/dbus-interfaces/org.gnome.Mutter.ServiceChannel.xml b/data/dbus-interfaces/org.gnome.Mutter.ServiceChannel.xml
new file mode 100644
index 000000000..3717827f3
--- /dev/null
+++ b/data/dbus-interfaces/org.gnome.Mutter.ServiceChannel.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/core/meta-context-private.h b/src/core/meta-context-private.h
index c8b9066a7..b6954bc59 100644
--- a/src/core/meta-context-private.h
+++ b/src/core/meta-context-private.h
@@ -22,6 +22,7 @@
#define META_CONTEXT_PRIVATE_H
#include "core/meta-private-enums.h"
+#include "core/meta-service-channel.h"
#include "core/util-private.h"
#include "meta/meta-backend.h"
#include "meta/meta-context.h"
@@ -63,9 +64,14 @@ gboolean meta_context_get_unsafe_mode (MetaContext *context);
void meta_context_set_unsafe_mode (MetaContext *context,
gboolean enable);
+#ifdef HAVE_WAYLAND
META_EXPORT_TEST
MetaWaylandCompositor * meta_context_get_wayland_compositor (MetaContext *context);
+META_EXPORT_TEST
+MetaServiceChannel * meta_context_get_service_channel (MetaContext *context);
+#endif
+
MetaX11DisplayPolicy meta_context_get_x11_display_policy (MetaContext *context);
#ifdef HAVE_X11
diff --git a/src/core/meta-context.c b/src/core/meta-context.c
index 689ce07f6..42a9f4887 100644
--- a/src/core/meta-context.c
+++ b/src/core/meta-context.c
@@ -28,6 +28,7 @@
#include "backends/meta-backend-private.h"
#include "compositor/meta-plugin-manager.h"
#include "core/display-private.h"
+#include "core/meta-service-channel.h"
#include "core/prefs-private.h"
#include "core/util-private.h"
@@ -99,6 +100,10 @@ typedef struct _MetaContextPrivate
#ifdef HAVE_PROFILER
MetaProfiler *profiler;
#endif
+
+#ifdef HAVE_WAYLAND
+ MetaServiceChannel *service_channel;
+#endif
} MetaContextPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (MetaContext, meta_context, G_TYPE_OBJECT)
@@ -243,6 +248,14 @@ meta_context_get_wayland_compositor (MetaContext *context)
return priv->wayland_compositor;
}
+
+MetaServiceChannel *
+meta_context_get_service_channel (MetaContext *context)
+{
+ MetaContextPrivate *priv = meta_context_get_instance_private (context);
+
+ return priv->service_channel;
+}
#endif
MetaCompositorType
@@ -437,6 +450,10 @@ meta_context_start (MetaContext *context,
return FALSE;
}
+#ifdef HAVE_WAYLAND
+ priv->service_channel = meta_service_channel_new (context);
+#endif
+
priv->main_loop = g_main_loop_new (NULL, FALSE);
priv->state = META_CONTEXT_STATE_STARTED;
@@ -682,6 +699,8 @@ meta_context_dispose (GObject *object)
g_signal_emit (context, signals[PREPARE_SHUTDOWN], 0);
#ifdef HAVE_WAYLAND
+ g_clear_object (&priv->service_channel);
+
if (priv->wayland_compositor)
meta_wayland_compositor_prepare_shutdown (priv->wayland_compositor);
#endif
diff --git a/src/core/meta-service-channel.c b/src/core/meta-service-channel.c
new file mode 100644
index 000000000..31be68c40
--- /dev/null
+++ b/src/core/meta-service-channel.c
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2023 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include "core/meta-service-channel.h"
+
+#include "wayland/meta-wayland-client-private.h"
+
+#define META_SERVICE_CHANNEL_DBUS_SERVICE "org.gnome.Mutter.ServiceChannel"
+#define META_SERVICE_CHANNEL_DBUS_PATH "/org/gnome/Mutter/ServiceChannel"
+
+struct _MetaServiceChannel
+{
+ MetaDBusServiceChannelSkeleton parent;
+
+ guint dbus_name_id;
+
+ MetaContext *context;
+
+ GHashTable *service_clients;
+};
+
+typedef struct _MetaServiceClient
+{
+ MetaWaylandClient *wayland_client;
+ gulong destroyed_handler_id;
+ MetaServiceChannel *service_channel;
+} MetaServiceClient;
+
+static void meta_service_channel_init_iface (MetaDBusServiceChannelIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (MetaServiceChannel, meta_service_channel,
+ META_DBUS_TYPE_SERVICE_CHANNEL_SKELETON,
+ G_IMPLEMENT_INTERFACE (META_DBUS_TYPE_SERVICE_CHANNEL,
+ meta_service_channel_init_iface))
+
+static void
+meta_service_client_free (MetaServiceClient *service_client)
+{
+ g_signal_handler_disconnect (service_client->wayland_client,
+ service_client->destroyed_handler_id);
+ g_object_unref (service_client->wayland_client);
+ g_free (service_client);
+}
+
+static void
+on_service_client_destroyed (MetaWaylandClient *wayland_client,
+ MetaServiceClient *service_client)
+{
+ MetaServiceClientType service_client_type;
+
+ service_client_type =
+ meta_wayland_client_get_service_client_type (wayland_client);
+ g_return_if_fail (service_client_type != META_SERVICE_CLIENT_TYPE_NONE);
+
+ g_hash_table_remove (service_client->service_channel->service_clients,
+ GINT_TO_POINTER (service_client_type));
+}
+
+static MetaServiceClient *
+meta_service_client_new (MetaServiceChannel *service_channel,
+ MetaWaylandClient *wayland_client)
+{
+ MetaServiceClient *service_client;
+
+ service_client = g_new0 (MetaServiceClient, 1);
+ service_client->service_channel = service_channel;
+ service_client->wayland_client = g_object_ref (wayland_client);
+ service_client->destroyed_handler_id =
+ g_signal_connect (wayland_client, "client-destroyed",
+ G_CALLBACK (on_service_client_destroyed),
+ service_client);
+
+ return service_client;
+}
+
+static gboolean
+verify_service_client_type (uint32_t service_client_type)
+{
+ switch ((MetaServiceClientType) service_client_type)
+ {
+ case META_SERVICE_CLIENT_TYPE_NONE:
+ return FALSE;
+ case META_SERVICE_CLIENT_TYPE_PORTAL_BACKEND:
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+handle_open_wayland_service_connection (MetaDBusServiceChannel *object,
+ GDBusMethodInvocation *invocation,
+ GUnixFDList *in_fd_list,
+ uint32_t service_client_type)
+{
+#ifdef HAVE_WAYLAND
+ MetaServiceChannel *service_channel = META_SERVICE_CHANNEL (object);
+ g_autoptr (GError) error = NULL;
+ g_autoptr (MetaWaylandClient) wayland_client = NULL;
+ g_autoptr (GUnixFDList) out_fd_list = NULL;
+ int fd;
+ int fd_id;
+
+ if (meta_context_get_compositor_type (service_channel->context) !=
+ META_COMPOSITOR_TYPE_WAYLAND)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_NOT_SUPPORTED,
+ "Not a Wayland compositor");
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ if (!verify_service_client_type (service_client_type))
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Invalid service client type");
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ wayland_client = meta_wayland_client_new_indirect (service_channel->context,
+ &error);
+ if (!wayland_client)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_NOT_SUPPORTED,
+ "Failed to create Wayland client: %s",
+ error->message);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ meta_wayland_client_assign_service_client_type (wayland_client,
+ service_client_type);
+
+ fd = meta_wayland_client_setup_fd (wayland_client, &error);
+ if (fd < 0)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_NOT_SUPPORTED,
+ "Failed to setup Wayland client socket: %s",
+ error->message);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ out_fd_list = g_unix_fd_list_new ();
+ fd_id = g_unix_fd_list_append (out_fd_list, fd, &error);
+ close (fd);
+
+ if (fd_id == -1)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_ACCESS_DENIED,
+ "Failed to append fd: %s",
+ error->message);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ g_hash_table_replace (service_channel->service_clients,
+ GUINT_TO_POINTER (service_client_type),
+ meta_service_client_new (service_channel,
+ wayland_client));
+
+ meta_dbus_service_channel_complete_open_wayland_service_connection (
+ object, invocation, out_fd_list, g_variant_new_handle (fd_id));
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+#else /* HAVE_WAYLAND */
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_NOT_SUPPORTED,
+ "Wayland not supported",
+ error->message);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+#endif /* HAVE_WAYLAND */
+}
+
+static void
+meta_service_channel_init_iface (MetaDBusServiceChannelIface *iface)
+{
+ iface->handle_open_wayland_service_connection =
+ handle_open_wayland_service_connection;
+}
+
+static void
+on_bus_acquired (GDBusConnection *connection,
+ const char *name,
+ gpointer user_data)
+{
+ MetaServiceChannel *service_channel = user_data;
+ GDBusInterfaceSkeleton *interface_skeleton =
+ G_DBUS_INTERFACE_SKELETON (service_channel);
+ g_autoptr (GError) error = NULL;
+
+ if (!g_dbus_interface_skeleton_export (interface_skeleton,
+ connection,
+ META_SERVICE_CHANNEL_DBUS_PATH,
+ &error))
+ g_warning ("Failed to export service channel object: %s", error->message);
+}
+
+static void
+on_name_acquired (GDBusConnection *connection,
+ const char *name,
+ gpointer user_data)
+{
+ g_info ("Acquired name %s", name);
+}
+
+static void
+on_name_lost (GDBusConnection *connection,
+ const char *name,
+ gpointer user_data)
+{
+ g_warning ("Lost or failed to acquire name %s", name);
+}
+
+static void
+meta_service_channel_constructed (GObject *object)
+{
+ MetaServiceChannel *service_channel = META_SERVICE_CHANNEL (object);
+
+ service_channel->service_clients =
+ g_hash_table_new_full (NULL, NULL,
+ NULL, (GDestroyNotify) meta_service_client_free);
+
+ service_channel->dbus_name_id =
+ g_bus_own_name (G_BUS_TYPE_SESSION,
+ META_SERVICE_CHANNEL_DBUS_SERVICE,
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ on_bus_acquired,
+ on_name_acquired,
+ on_name_lost,
+ service_channel,
+ NULL);
+}
+
+static void
+meta_service_channel_finalize (GObject *object)
+{
+ MetaServiceChannel *service_channel = META_SERVICE_CHANNEL (object);
+
+ g_clear_pointer (&service_channel->service_clients, g_hash_table_unref);
+ g_clear_handle_id (&service_channel->dbus_name_id, g_bus_unown_name);
+
+ G_OBJECT_CLASS (meta_service_channel_parent_class)->finalize (object);
+}
+
+static void
+meta_service_channel_class_init (MetaServiceChannelClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = meta_service_channel_constructed;
+ object_class->finalize = meta_service_channel_finalize;
+}
+
+static void
+meta_service_channel_init (MetaServiceChannel *service_channel)
+{
+}
+
+MetaServiceChannel *
+meta_service_channel_new (MetaContext *context)
+{
+ MetaServiceChannel *service_channel;
+
+ service_channel = g_object_new (META_TYPE_SERVICE_CHANNEL, NULL);
+ service_channel->context = context;
+
+ return service_channel;
+}
+
+MetaWaylandClient *
+meta_service_channel_get_service_client (MetaServiceChannel *service_channel,
+ MetaServiceClientType service_client_type)
+{
+ MetaServiceClient *service_client;
+
+ service_client = g_hash_table_lookup (service_channel->service_clients,
+ GINT_TO_POINTER (service_client_type));
+ if (!service_client)
+ return NULL;
+
+ return service_client->wayland_client;
+}
diff --git a/src/core/meta-service-channel.h b/src/core/meta-service-channel.h
new file mode 100644
index 000000000..e88e4d775
--- /dev/null
+++ b/src/core/meta-service-channel.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef META_SERVICE_CHANNEL_H
+#define META_SERVICE_CHANNEL_H
+
+#ifdef HAVE_WAYLAND
+
+#include "meta/meta-context.h"
+
+#include "core/util-private.h"
+#include "wayland/meta-wayland-types.h"
+
+#include "meta-dbus-service-channel.h"
+
+typedef enum _MetaServiceClientType
+{
+ META_SERVICE_CLIENT_TYPE_NONE,
+ META_SERVICE_CLIENT_TYPE_PORTAL_BACKEND,
+} MetaServiceClientType;
+
+#define META_TYPE_SERVICE_CHANNEL (meta_service_channel_get_type ())
+G_DECLARE_FINAL_TYPE (MetaServiceChannel, meta_service_channel,
+ META, SERVICE_CHANNEL,
+ MetaDBusServiceChannelSkeleton)
+
+MetaServiceChannel * meta_service_channel_new (MetaContext *context);
+
+META_EXPORT_TEST
+MetaWaylandClient * meta_service_channel_get_service_client (MetaServiceChannel *service_channel,
+ MetaServiceClientType service_client_type);
+
+#endif /* HAVE_WAYLAND */
+
+#endif /* META_SERVICE_CHANNEL_H */
diff --git a/src/meson.build b/src/meson.build
index 947f7b45a..cf3abd0d9 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -560,6 +560,8 @@ if have_wayland
'compositor/meta-surface-actor-wayland.h',
'compositor/meta-window-actor-wayland.c',
'compositor/meta-window-actor-wayland.h',
+ 'core/meta-service-channel.c',
+ 'core/meta-service-channel.h',
'wayland/meta-cursor-sprite-wayland.c',
'wayland/meta-cursor-sprite-wayland.h',
'wayland/meta-pointer-confinement-wayland.c',
@@ -879,6 +881,11 @@ dbus_interfaces = [
'interface': 'org.gnome.Mutter.InputMapping.xml',
'prefix': 'org.gnome.Mutter.',
},
+ {
+ 'name': 'meta-dbus-service-channel',
+ 'interface': 'org.gnome.Mutter.ServiceChannel.xml',
+ 'prefix': 'org.gnome.Mutter.',
+ },
]
if have_profiler
diff --git a/src/wayland/meta-wayland-client-private.h b/src/wayland/meta-wayland-client-private.h
index c1524dead..63623dfdd 100644
--- a/src/wayland/meta-wayland-client-private.h
+++ b/src/wayland/meta-wayland-client-private.h
@@ -22,6 +22,7 @@
#include
+#include "core/meta-service-channel.h"
#include "core/util-private.h"
#include "meta/meta-wayland-client.h"
@@ -37,4 +38,9 @@ META_EXPORT_TEST
gboolean meta_wayland_client_matches (MetaWaylandClient *client,
const struct wl_client *wayland_client);
+void meta_wayland_client_assign_service_client_type (MetaWaylandClient *client,
+ MetaServiceClientType service_client_type);
+
+MetaServiceClientType meta_wayland_client_get_service_client_type (MetaWaylandClient *client);
+
#endif /* META_WAYLAND_CLIENT_PRIVATE_H */
diff --git a/src/wayland/meta-wayland-client.c b/src/wayland/meta-wayland-client.c
index f7e1ff20c..2f26b553b 100644
--- a/src/wayland/meta-wayland-client.c
+++ b/src/wayland/meta-wayland-client.c
@@ -73,6 +73,7 @@ struct _MetaWaylandClient
struct wl_client *wayland_client;
struct wl_listener client_destroy_listener;
+ MetaServiceClientType service_client_type;
};
G_DEFINE_TYPE (MetaWaylandClient, meta_wayland_client, G_TYPE_OBJECT)
@@ -109,6 +110,7 @@ meta_wayland_client_class_init (MetaWaylandClientClass *klass)
static void
meta_wayland_client_init (MetaWaylandClient *client)
{
+ client->service_client_type = META_SERVICE_CLIENT_TYPE_NONE;
}
static void
@@ -483,3 +485,18 @@ meta_wayland_client_matches (MetaWaylandClient *client,
return client->wayland_client == wayland_client;
}
+
+void
+meta_wayland_client_assign_service_client_type (MetaWaylandClient *client,
+ MetaServiceClientType service_client_type)
+{
+ g_return_if_fail (client->service_client_type ==
+ META_SERVICE_CLIENT_TYPE_NONE);
+ client->service_client_type = service_client_type;
+}
+
+MetaServiceClientType
+meta_wayland_client_get_service_client_type (MetaWaylandClient *client)
+{
+ return client->service_client_type;
+}
diff --git a/src/wayland/meta-wayland-types.h b/src/wayland/meta-wayland-types.h
index 81c38d9e2..0b96abd90 100644
--- a/src/wayland/meta-wayland-types.h
+++ b/src/wayland/meta-wayland-types.h
@@ -75,4 +75,6 @@ typedef struct _MetaWaylandXdgForeign MetaWaylandXdgForeign;
typedef struct _MetaWaylandFilterManager MetaWaylandFilterManager;
+typedef struct _MetaWaylandClient MetaWaylandClient;
+
#endif