1
0
Fork 0
gdm3/daemon/gdm-remote-display-factory.c
Daniel Baumann 83b37a3d94
Adding upstream version 48.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 19:45:29 +02:00

357 lines
13 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2022 Joan Torres <joan.torres@suse.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "config.h"
#include "gdm-remote-display.h"
#include "gdm-remote-display-factory.h"
#include "gdm-remote-display-factory-glue.h"
#define GDM_REMOTE_DISPLAY_FACTORY_DBUS_PATH "/org/gnome/DisplayManager/RemoteDisplayFactory"
struct _GdmRemoteDisplayFactory
{
GdmDisplayFactory parent;
GdmDBusRemoteDisplayFactory *skeleton;
GDBusConnection *connection;
int remote_displays_count;
gboolean suspend_inhibited;
int inhibit_suspend_fd;
};
static void gdm_remote_display_factory_class_init (GdmRemoteDisplayFactoryClass *klass);
static void gdm_remote_display_factory_init (GdmRemoteDisplayFactory *factory);
static void gdm_remote_display_factory_finalize (GObject *object);
static gpointer remote_display_factory_object = NULL;
G_DEFINE_TYPE (GdmRemoteDisplayFactory, gdm_remote_display_factory, GDM_TYPE_DISPLAY_FACTORY)
static void
on_display_status_changed (GdmDisplay *display,
GParamSpec *arg1,
GdmRemoteDisplayFactory *factory)
{
g_debug ("GdmRemoteDisplayFactory: remote display status changed: %d",
gdm_display_get_status (display));
}
static gboolean
gdm_remote_display_factory_create_remote_display (GdmRemoteDisplayFactory *factory,
const char *remote_id)
{
GdmDisplay *display = NULL;
GdmDisplayStore *store;
g_debug ("GdmRemoteDisplayFactory: Creating remote display");
display = gdm_remote_display_new (remote_id);
store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
gdm_display_store_add (store, display);
if (!gdm_display_prepare (display)) {
gdm_display_unmanage (display);
g_object_unref (display);
return FALSE;
}
g_signal_connect_after (display,
"notify::status",
G_CALLBACK (on_display_status_changed),
factory);
g_object_unref (display);
return TRUE;
}
static gboolean
handle_create_remote_display (GdmDBusRemoteDisplayFactory *skeleton,
GDBusMethodInvocation *invocation,
const char *remote_id,
GdmRemoteDisplayFactory *factory)
{
if (!gdm_remote_display_factory_create_remote_display (factory, remote_id))
g_dbus_method_invocation_return_error_literal (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_FAILED,
"Error creating remote display");
else
gdm_dbus_remote_display_factory_complete_create_remote_display (factory->skeleton,
invocation);
return TRUE;
}
static gboolean
register_factory (GdmRemoteDisplayFactory *factory)
{
GError *error = NULL;
factory->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (factory->connection == NULL) {
g_critical ("Error getting system bus: %s", error->message);
g_error_free (error);
exit (EXIT_FAILURE);
}
factory->skeleton = GDM_DBUS_REMOTE_DISPLAY_FACTORY (gdm_dbus_remote_display_factory_skeleton_new ());
g_signal_connect (factory->skeleton,
"handle-create-remote-display",
G_CALLBACK (handle_create_remote_display),
factory);
if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (factory->skeleton),
factory->connection,
GDM_REMOTE_DISPLAY_FACTORY_DBUS_PATH,
&error)) {
g_critical ("Error exporting RemoteDisplayFactory object: %s", error->message);
g_error_free (error);
exit (EXIT_FAILURE);
}
return TRUE;
}
static void
gdm_remote_display_factory_finalize (GObject *object)
{
GdmRemoteDisplayFactory *factory;
g_return_if_fail (object != NULL);
g_return_if_fail (GDM_IS_REMOTE_DISPLAY_FACTORY (object));
factory = GDM_REMOTE_DISPLAY_FACTORY (object);
g_return_if_fail (factory != NULL);
g_clear_object (&factory->connection);
g_clear_object (&factory->skeleton);
if (factory->inhibit_suspend_fd != -1)
close (factory->inhibit_suspend_fd);
G_OBJECT_CLASS (gdm_remote_display_factory_parent_class)->finalize (object);
}
static GObject *
gdm_remote_display_factory_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties)
{
GdmRemoteDisplayFactory *factory;
gboolean res;
factory = GDM_REMOTE_DISPLAY_FACTORY (G_OBJECT_CLASS (gdm_remote_display_factory_parent_class)->constructor (type,
n_construct_properties,
construct_properties));
res = register_factory (factory);
if (!res) {
g_warning ("Unable to register remote display factory with system bus");
}
return G_OBJECT (factory);
}
static void
on_inhibit_suspend_done (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
GDBusProxy *proxy = G_DBUS_PROXY (source);
GdmRemoteDisplayFactory *factory = user_data;
g_autoptr (GUnixFDList) fd_list = NULL;
g_autoptr (GVariant) res = NULL;
g_autoptr (GError) error = NULL;
int idx;
res = g_dbus_proxy_call_with_unix_fd_list_finish (proxy,
&fd_list,
result,
&error);
if (!res) {
g_warning ("Unable to inhibit suspend: %s", error->message);
factory->suspend_inhibited = FALSE;
return;
}
g_variant_get (res, "(h)", &idx);
factory->inhibit_suspend_fd = g_unix_fd_list_get (fd_list, idx, &error);
if (factory->inhibit_suspend_fd == -1) {
g_warning ("Failed to receive system inhibitor fd: %s", error->message);
factory->suspend_inhibited = FALSE;
}
}
static void
inhibit_suspend (GdmRemoteDisplayFactory *factory)
{
g_autoptr (GDBusProxy) proxy = NULL;
g_autoptr (GError) error = NULL;
if (factory->suspend_inhibited)
return;
proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
G_DBUS_PROXY_FLAGS_NONE,
NULL,
"org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
NULL,
&error);
if (!proxy) {
g_warning ("Failed to acquire login1 manager proxy: %s", error->message);
return;
}
factory->suspend_inhibited = TRUE;
g_dbus_proxy_call_with_unix_fd_list (proxy,
"Inhibit",
g_variant_new ("(ssss)",
"sleep",
g_get_user_name (),
"Remote sessions would die on suspend",
"block"),
G_DBUS_CALL_FLAGS_NONE,
G_MAXINT,
NULL,
NULL,
on_inhibit_suspend_done,
factory);
}
static void
on_display_added (GdmDisplayStore *store,
const char *id,
GdmRemoteDisplayFactory *factory)
{
GdmDisplay *display = gdm_display_store_lookup (store, id);
if (!GDM_IS_REMOTE_DISPLAY (display))
return;
factory->remote_displays_count++;
if (factory->remote_displays_count > 0)
inhibit_suspend (factory);
}
static void
uninhibit_suspend (GdmRemoteDisplayFactory *factory)
{
if (factory->inhibit_suspend_fd == -1)
return;
close (factory->inhibit_suspend_fd);
factory->inhibit_suspend_fd = -1;
factory->suspend_inhibited = FALSE;
}
static void
on_display_removed (GdmDisplayStore *display_store,
GdmDisplay *display,
GdmRemoteDisplayFactory *factory)
{
g_assert (factory->remote_displays_count >= 0);
if (!GDM_IS_REMOTE_DISPLAY (display))
return;
factory->remote_displays_count--;
if (factory->remote_displays_count == 0)
uninhibit_suspend (factory);
}
static gboolean
gdm_remote_display_factory_start (GdmDisplayFactory *base_factory)
{
GdmRemoteDisplayFactory *factory = GDM_REMOTE_DISPLAY_FACTORY (base_factory);
GdmDisplayStore *store;
g_return_val_if_fail (GDM_IS_REMOTE_DISPLAY_FACTORY (factory), FALSE);
store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
g_signal_connect_object (G_OBJECT (store),
"display-added",
G_CALLBACK (on_display_added),
factory,
G_CONNECT_DEFAULT);
g_signal_connect_object (G_OBJECT (store),
"display-removed",
G_CALLBACK (on_display_removed),
factory,
G_CONNECT_DEFAULT);
return TRUE;
}
static gboolean
gdm_remote_display_factory_stop (GdmDisplayFactory *base_factory)
{
GdmRemoteDisplayFactory *factory = GDM_REMOTE_DISPLAY_FACTORY (base_factory);
g_return_val_if_fail (GDM_IS_REMOTE_DISPLAY_FACTORY (factory), FALSE);
return TRUE;
}
static void
gdm_remote_display_factory_class_init (GdmRemoteDisplayFactoryClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GdmDisplayFactoryClass *factory_class = GDM_DISPLAY_FACTORY_CLASS (klass);
object_class->finalize = gdm_remote_display_factory_finalize;
object_class->constructor = gdm_remote_display_factory_constructor;
factory_class->start = gdm_remote_display_factory_start;
factory_class->stop = gdm_remote_display_factory_stop;
}
static void
gdm_remote_display_factory_init (GdmRemoteDisplayFactory *factory)
{
factory->inhibit_suspend_fd = -1;
}
GdmRemoteDisplayFactory *
gdm_remote_display_factory_new (GdmDisplayStore *store)
{
if (remote_display_factory_object != NULL) {
g_object_ref (remote_display_factory_object);
} else {
remote_display_factory_object = g_object_new (GDM_TYPE_REMOTE_DISPLAY_FACTORY,
"display-store", store,
NULL);
g_object_add_weak_pointer (remote_display_factory_object,
(gpointer *) &remote_display_factory_object);
}
return GDM_REMOTE_DISPLAY_FACTORY (remote_display_factory_object);
}