diff --git a/.gitignore b/.gitignore index 19ad0ec..cba22b3 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -gnome-remote-desktop-47.3.tar.xz +gnome-remote-desktop-49.3.tar.xz diff --git a/0001-Patch-out-multi-touch-support.patch b/0001-Patch-out-multi-touch-support.patch new file mode 100644 index 0000000..8527514 --- /dev/null +++ b/0001-Patch-out-multi-touch-support.patch @@ -0,0 +1,210 @@ +From 26114b9ce739f748db1fd1379a977f55815346bf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 12 Nov 2025 16:53:36 +0100 +Subject: [PATCH] Patch out multi touch support + +This is needed to allow building on older FreeRDP. +--- + meson.build | 2 +- + src/grd-rdp-dvc.c | 2 ++ + src/grd-rdp-private.h | 2 ++ + src/grd-session-rdp.c | 14 ++++++++++++++ + src/grd-session-rdp.h | 2 ++ + src/grd-session.c | 2 ++ + src/grd-settings.c | 1 + + src/grd-types.h | 2 ++ + src/meson.build | 4 ++-- + 9 files changed, 28 insertions(+), 3 deletions(-) + +diff --git a/meson.build b/meson.build +index 5dcb77c3..041b771b 100644 +--- a/meson.build ++++ b/meson.build +@@ -6,7 +6,7 @@ project('gnome-remote-desktop', 'c', + + cuda_req = '>= 11.1.5.0' + epoxy_req = '>= 1.4' +-freerdp_req = '>= 3.15.0' ++freerdp_req = '>= 3.10.0' + fuse_req = '>= 3.9.1' + polkit_req = '>= 122' + vulkan_req = '>= 1.2.0' +diff --git a/src/grd-rdp-dvc.c b/src/grd-rdp-dvc.c +index 65ec1554..4c2210b8 100644 +--- a/src/grd-rdp-dvc.c ++++ b/src/grd-rdp-dvc.c +@@ -139,8 +139,10 @@ channel_to_string (GrdRdpChannel channel) + return "DISP"; + case GRD_RDP_CHANNEL_GRAPHICS_PIPELINE: + return "RDPGFX"; ++#if 0 + case GRD_RDP_CHANNEL_INPUT: + return "INPUT"; ++#endif + case GRD_RDP_CHANNEL_TELEMETRY: + return "TELEMETRY"; + } +diff --git a/src/grd-rdp-private.h b/src/grd-rdp-private.h +index 29139b30..47057f1a 100644 +--- a/src/grd-rdp-private.h ++++ b/src/grd-rdp-private.h +@@ -46,6 +46,8 @@ typedef struct _RdpPeerContext + GrdRdpDvcAudioPlayback *audio_playback; + GrdRdpDvcDisplayControl *display_control; + GrdRdpDvcGraphicsPipeline *graphics_pipeline; ++#if 0 + GrdRdpDvcInput *input; ++#endif + GrdRdpDvcTelemetry *telemetry; + } RdpPeerContext; +diff --git a/src/grd-session-rdp.c b/src/grd-session-rdp.c +index cc026064..6cf0c4f1 100644 +--- a/src/grd-session-rdp.c ++++ b/src/grd-session-rdp.c +@@ -37,7 +37,9 @@ + #include "grd-rdp-dvc-display-control.h" + #include "grd-rdp-dvc-graphics-pipeline.h" + #include "grd-rdp-dvc-handler.h" ++#if 0 + #include "grd-rdp-dvc-input.h" ++#endif + #include "grd-rdp-dvc-telemetry.h" + #include "grd-rdp-event-queue.h" + #include "grd-rdp-layout-manager.h" +@@ -453,9 +455,11 @@ grd_session_rdp_tear_down_channel (GrdSessionRdp *session_rdp, + case GRD_RDP_CHANNEL_GRAPHICS_PIPELINE: + g_assert_not_reached (); + break; ++#if 0 + case GRD_RDP_CHANNEL_INPUT: + g_clear_object (&rdp_peer_context->input); + break; ++#endif + case GRD_RDP_CHANNEL_TELEMETRY: + g_clear_object (&rdp_peer_context->telemetry); + break; +@@ -1417,7 +1421,9 @@ socket_thread_func (gpointer data) + { + GrdRdpDvcTelemetry *telemetry; + GrdRdpDvcGraphicsPipeline *graphics_pipeline; ++#if 0 + GrdRdpDvcInput *input; ++#endif + GrdRdpDvcAudioPlayback *audio_playback; + GrdRdpDvcDisplayControl *display_control; + GrdRdpDvcAudioInput *audio_input; +@@ -1435,7 +1441,9 @@ socket_thread_func (gpointer data) + g_mutex_lock (&rdp_peer_context->channel_mutex); + telemetry = rdp_peer_context->telemetry; + graphics_pipeline = rdp_peer_context->graphics_pipeline; ++#if 0 + input = rdp_peer_context->input; ++#endif + audio_playback = rdp_peer_context->audio_playback; + display_control = rdp_peer_context->display_control; + audio_input = rdp_peer_context->audio_input; +@@ -1444,8 +1452,10 @@ socket_thread_func (gpointer data) + grd_rdp_dvc_maybe_init (GRD_RDP_DVC (telemetry)); + if (graphics_pipeline && !session_rdp->session_should_stop) + grd_rdp_dvc_maybe_init (GRD_RDP_DVC (graphics_pipeline)); ++#if 0 + if (input && !session_rdp->session_should_stop) + grd_rdp_dvc_maybe_init (GRD_RDP_DVC (input)); ++#endif + if (audio_playback && !session_rdp->session_should_stop) + grd_rdp_dvc_maybe_init (GRD_RDP_DVC (audio_playback)); + if (display_control && !session_rdp->session_should_stop) +@@ -1620,7 +1630,9 @@ grd_session_rdp_stop (GrdSession *session) + g_clear_object (&rdp_peer_context->clipboard_rdp); + g_clear_object (&rdp_peer_context->audio_playback); + g_clear_object (&rdp_peer_context->display_control); ++#if 0 + g_clear_object (&rdp_peer_context->input); ++#endif + g_clear_object (&rdp_peer_context->graphics_pipeline); + g_clear_object (&rdp_peer_context->telemetry); + g_mutex_unlock (&rdp_peer_context->channel_mutex); +@@ -1705,9 +1717,11 @@ initialize_remaining_virtual_channels (GrdSessionRdp *session_rdp) + GrdRdpDvcHandler *dvc_handler = rdp_peer_context->dvc_handler; + HANDLE vcm = rdp_peer_context->vcm; + ++#if 0 + rdp_peer_context->input = + grd_rdp_dvc_input_new (session_rdp->layout_manager, + session_rdp, dvc_handler, vcm); ++#endif + + if (session_rdp->screen_share_mode == GRD_RDP_SCREEN_SHARE_MODE_EXTEND) + { +diff --git a/src/grd-session-rdp.h b/src/grd-session-rdp.h +index d49dd43d..a6358e65 100644 +--- a/src/grd-session-rdp.h ++++ b/src/grd-session-rdp.h +@@ -48,7 +48,9 @@ typedef enum _GrdRdpChannel + GRD_RDP_CHANNEL_AUDIO_PLAYBACK, + GRD_RDP_CHANNEL_DISPLAY_CONTROL, + GRD_RDP_CHANNEL_GRAPHICS_PIPELINE, ++#if 0 + GRD_RDP_CHANNEL_INPUT, ++#endif + GRD_RDP_CHANNEL_TELEMETRY, + } GrdRdpChannel; + +diff --git a/src/grd-session.c b/src/grd-session.c +index e247d342..7ced5d98 100644 +--- a/src/grd-session.c ++++ b/src/grd-session.c +@@ -1440,7 +1440,9 @@ grd_ei_source_dispatch (gpointer user_data) + EI_DEVICE_CAP_POINTER_ABSOLUTE, + EI_DEVICE_CAP_BUTTON, + EI_DEVICE_CAP_SCROLL, ++#if 0 + EI_DEVICE_CAP_TOUCH, ++#endif + NULL); + break; + case EI_EVENT_SEAT_REMOVED: +diff --git a/src/grd-settings.c b/src/grd-settings.c +index 8393ace5..e259a574 100644 +--- a/src/grd-settings.c ++++ b/src/grd-settings.c +@@ -25,6 +25,7 @@ + #include + + #ifdef HAVE_RDP ++#include + #include + #endif + +diff --git a/src/grd-types.h b/src/grd-types.h +index 2b3f64f2..9ae6e8cb 100644 +--- a/src/grd-types.h ++++ b/src/grd-types.h +@@ -50,7 +50,9 @@ typedef struct _GrdRdpDvcAudioPlayback GrdRdpDvcAudioPlayback; + typedef struct _GrdRdpDvcDisplayControl GrdRdpDvcDisplayControl; + typedef struct _GrdRdpDvcGraphicsPipeline GrdRdpDvcGraphicsPipeline; + typedef struct _GrdRdpDvcHandler GrdRdpDvcHandler; ++#if 0 + typedef struct _GrdRdpDvcInput GrdRdpDvcInput; ++#endif + typedef struct _GrdRdpDvcTelemetry GrdRdpDvcTelemetry; + typedef struct _GrdRdpEventQueue GrdRdpEventQueue; + typedef struct _GrdRdpFrame GrdRdpFrame; +diff --git a/src/meson.build b/src/meson.build +index 1b2cb93d..ed671125 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -148,8 +148,8 @@ if have_rdp + 'grd-rdp-dvc-graphics-pipeline.h', + 'grd-rdp-dvc-handler.c', + 'grd-rdp-dvc-handler.h', +- 'grd-rdp-dvc-input.c', +- 'grd-rdp-dvc-input.h', ++ #'grd-rdp-dvc-input.c', ++ #'grd-rdp-dvc-input.h', + 'grd-rdp-dvc-telemetry.c', + 'grd-rdp-dvc-telemetry.h', + 'grd-rdp-event-queue.c', +-- +2.51.0 + diff --git a/0001-daemon-handover-Move-callback-definitions-earlier-in.patch b/0001-daemon-handover-Move-callback-definitions-earlier-in.patch new file mode 100644 index 0000000..a14eb2b --- /dev/null +++ b/0001-daemon-handover-Move-callback-definitions-earlier-in.patch @@ -0,0 +1,591 @@ +From d102edca608d5ca210b1bcde72e7644d0709b806 Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Mon, 26 Jan 2026 17:39:20 +0100 +Subject: [PATCH 1/4] daemon-handover: Move callback definitions earlier in the + file + +Preparatory refactoring for upcoming setup/teardown handover functions. + +Part-of: +--- + src/grd-daemon-handover.c | 88 +++++++++++++++++++-------------------- + 1 file changed, 44 insertions(+), 44 deletions(-) + +diff --git a/src/grd-daemon-handover.c b/src/grd-daemon-handover.c +index 3c9e34f..40fed79 100644 +--- a/src/grd-daemon-handover.c ++++ b/src/grd-daemon-handover.c +@@ -415,6 +415,50 @@ inform_about_insecure_connection (GrdDaemonHandover *daemon_handover) + show_insecure_connection_prompt (daemon_handover); + } + ++static void ++on_redirect_client (GrdDBusRemoteDesktopRdpHandover *interface, ++ const char *routing_token, ++ const char *username, ++ const char *password, ++ GrdDaemonHandover *daemon_handover) ++{ ++ const char *object_path = ++ g_dbus_proxy_get_object_path (G_DBUS_PROXY (interface)); ++ GrdContext *context = grd_daemon_get_context (GRD_DAEMON (daemon_handover)); ++ GrdSettings *settings = grd_context_get_settings (context); ++ GrdSessionRdp *session_rdp = GRD_SESSION_RDP (daemon_handover->session); ++ g_autofree char *certificate = NULL; ++ ++ g_debug ("[DaemonHandover] At: %s, received RedirectClient signal", ++ object_path); ++ ++ g_object_get (G_OBJECT (settings), ++ "rdp-server-cert", &certificate, ++ NULL); ++ ++ if (!grd_session_rdp_send_server_redirection (session_rdp, routing_token, ++ username, password, ++ certificate)) ++ grd_session_stop (daemon_handover->session); ++} ++ ++static void ++on_handover_is_waiting_changed (GrdDBusRemoteDesktopRdpHandover *proxy, ++ GParamSpec *pspec, ++ GrdDaemonHandover *daemon_handover) ++{ ++ gboolean handover_is_waiting; ++ ++ handover_is_waiting = ++ grd_dbus_remote_desktop_rdp_handover_get_handover_is_waiting ( ++ daemon_handover->remote_desktop_handover); ++ ++ if (!handover_is_waiting) ++ return; ++ ++ start_handover (daemon_handover); ++} ++ + static void + on_incoming_new_connection (GrdRdpServer *rdp_server, + GrdSession *session, +@@ -485,50 +529,6 @@ on_rdp_server_stopped (GrdDaemonHandover *daemon_handover) + daemon_handover); + } + +-static void +-on_redirect_client (GrdDBusRemoteDesktopRdpHandover *interface, +- const char *routing_token, +- const char *username, +- const char *password, +- GrdDaemonHandover *daemon_handover) +-{ +- const char *object_path = +- g_dbus_proxy_get_object_path (G_DBUS_PROXY (interface)); +- GrdContext *context = grd_daemon_get_context (GRD_DAEMON (daemon_handover)); +- GrdSettings *settings = grd_context_get_settings (context); +- GrdSessionRdp *session_rdp = GRD_SESSION_RDP (daemon_handover->session); +- g_autofree char *certificate = NULL; +- +- g_debug ("[DaemonHandover] At: %s, received RedirectClient signal", +- object_path); +- +- g_object_get (G_OBJECT (settings), +- "rdp-server-cert", &certificate, +- NULL); +- +- if (!grd_session_rdp_send_server_redirection (session_rdp, routing_token, +- username, password, +- certificate)) +- grd_session_stop (daemon_handover->session); +-} +- +-static void +-on_handover_is_waiting_changed (GrdDBusRemoteDesktopRdpHandover *proxy, +- GParamSpec *pspec, +- GrdDaemonHandover *daemon_handover) +-{ +- gboolean handover_is_waiting; +- +- handover_is_waiting = +- grd_dbus_remote_desktop_rdp_handover_get_handover_is_waiting ( +- daemon_handover->remote_desktop_handover); +- +- if (!handover_is_waiting) +- return; +- +- start_handover (daemon_handover); +-} +- + static void + on_remote_desktop_rdp_handover_proxy_acquired (GObject *object, + GAsyncResult *result, +-- +2.51.0 + + +From 6358e82332ed62144bfe611dfb13093188863bd3 Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Mon, 26 Jan 2026 17:54:53 +0100 +Subject: [PATCH 2/4] daemon-handover: Use setup/teardown functions for signal + management + +Replace manual signal connection and disconnection with centralized +setup_handover and teardown_handover functions in RDP server start/stop +handlers and when the handover proxy is acquired or the system daemon +name vanishes. + +Part-of: +--- + src/grd-daemon-handover.c | 79 ++++++++++++++++++++++++--------------- + 1 file changed, 49 insertions(+), 30 deletions(-) + +diff --git a/src/grd-daemon-handover.c b/src/grd-daemon-handover.c +index 40fed79..95b5a54 100644 +--- a/src/grd-daemon-handover.c ++++ b/src/grd-daemon-handover.c +@@ -488,45 +488,72 @@ on_incoming_new_connection (GrdRdpServer *rdp_server, + } + + static void +-on_rdp_server_started (GrdDaemonHandover *daemon_handover) ++setup_handover (GrdDaemonHandover *daemon_handover) + { +- GrdDaemon *daemon = GRD_DAEMON (daemon_handover); +- GrdRdpServer *rdp_server = grd_daemon_get_rdp_server (daemon); ++ GrdRdpServer *rdp_server; + gboolean handover_is_waiting; + +- g_signal_connect (daemon_handover->remote_desktop_handover, +- "take-client-ready", G_CALLBACK (on_take_client_ready), +- daemon_handover); ++ if (!daemon_handover->remote_desktop_handover) ++ return; ++ ++ rdp_server = grd_daemon_get_rdp_server (GRD_DAEMON (daemon_handover)); ++ if (!rdp_server) ++ return; ++ ++ g_signal_connect (daemon_handover->remote_desktop_handover, "take-client-ready", ++ G_CALLBACK (on_take_client_ready), daemon_handover); ++ g_signal_connect (daemon_handover->remote_desktop_handover, "redirect-client", ++ G_CALLBACK (on_redirect_client), daemon_handover); ++ g_signal_connect (daemon_handover->remote_desktop_handover, "notify::handover-is-waiting", ++ G_CALLBACK (on_handover_is_waiting_changed), daemon_handover); + + g_signal_connect (rdp_server, "incoming-new-connection", +- G_CALLBACK (on_incoming_new_connection), +- daemon_handover); ++ G_CALLBACK (on_incoming_new_connection), daemon_handover); + +- handover_is_waiting = +- grd_dbus_remote_desktop_rdp_handover_get_handover_is_waiting ( +- daemon_handover->remote_desktop_handover); ++ handover_is_waiting = grd_dbus_remote_desktop_rdp_handover_get_handover_is_waiting ( ++ daemon_handover->remote_desktop_handover); + + if (handover_is_waiting) + start_handover (daemon_handover); + } + + static void +-on_rdp_server_stopped (GrdDaemonHandover *daemon_handover) ++teardown_handover (GrdDaemonHandover *daemon_handover) + { + GrdRdpServer *rdp_server = + grd_daemon_get_rdp_server (GRD_DAEMON (daemon_handover)); + + if (daemon_handover->remote_desktop_handover) + { +- g_signal_handlers_disconnect_by_func ( +- daemon_handover->remote_desktop_handover, +- G_CALLBACK (on_take_client_ready), +- daemon_handover); ++ g_signal_handlers_disconnect_by_func (daemon_handover->remote_desktop_handover, ++ G_CALLBACK (on_take_client_ready), ++ daemon_handover); ++ g_signal_handlers_disconnect_by_func (daemon_handover->remote_desktop_handover, ++ G_CALLBACK (on_redirect_client), ++ daemon_handover); ++ g_signal_handlers_disconnect_by_func (daemon_handover->remote_desktop_handover, ++ G_CALLBACK (on_handover_is_waiting_changed), ++ daemon_handover); + } + +- g_signal_handlers_disconnect_by_func (rdp_server, +- G_CALLBACK (on_incoming_new_connection), +- daemon_handover); ++ if (rdp_server) ++ { ++ g_signal_handlers_disconnect_by_func (rdp_server, ++ G_CALLBACK (on_incoming_new_connection), ++ daemon_handover); ++ } ++} ++ ++static void ++on_rdp_server_started (GrdDaemonHandover *daemon_handover) ++{ ++ setup_handover (daemon_handover); ++} ++ ++static void ++on_rdp_server_stopped (GrdDaemonHandover *daemon_handover) ++{ ++ teardown_handover (daemon_handover); + } + + static void +@@ -554,10 +581,7 @@ on_remote_desktop_rdp_handover_proxy_acquired (GObject *object, + daemon_handover = GRD_DAEMON_HANDOVER (user_data); + daemon_handover->remote_desktop_handover = g_steal_pointer (&proxy); + +- g_signal_connect (daemon_handover->remote_desktop_handover, "redirect-client", +- G_CALLBACK (on_redirect_client), daemon_handover); +- g_signal_connect (daemon_handover->remote_desktop_handover, "notify::handover-is-waiting", +- G_CALLBACK (on_handover_is_waiting_changed), daemon_handover); ++ setup_handover (daemon_handover); + + grd_daemon_maybe_enable_services (GRD_DAEMON (daemon_handover)); + } +@@ -673,13 +697,8 @@ on_gnome_remote_desktop_name_vanished (GDBusConnection *connection, + + g_warning ("[DaemonHandover] %s name vanished", name); + +- if (daemon_handover->remote_desktop_handover) +- { +- g_signal_handlers_disconnect_by_func ( +- daemon_handover->remote_desktop_handover, +- G_CALLBACK (on_redirect_client), +- daemon_handover); +- } ++ teardown_handover (daemon_handover); ++ + g_clear_object (&daemon_handover->remote_desktop_handover); + g_clear_object (&daemon_handover->remote_desktop_dispatcher); + +-- +2.51.0 + + +From 882ce2a8db58c407ac691e406b6dd1ad17b8d712 Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Mon, 26 Jan 2026 19:18:55 +0100 +Subject: [PATCH 3/4] types: Add GRD_DBUS_ERROR domain for D-Bus errors + +Add a custom D-Bus error domain with GRD_DBUS_ERROR_NO_HANDOVER code, +returned when the handover daemon requests a handover but no interface +exists yet. This allows distinguishing temporary unavailability from +other errors. + +Next commit will make handover daemon use it. + +Part-of: +--- + src/grd-daemon-system.c | 6 +++--- + src/grd-daemon.c | 20 ++++++++++++++++++++ + src/grd-types.h | 10 ++++++++++ + 3 files changed, 33 insertions(+), 3 deletions(-) + +diff --git a/src/grd-daemon-system.c b/src/grd-daemon-system.c +index 950d539..58b1f71 100644 +--- a/src/grd-daemon-system.c ++++ b/src/grd-daemon-system.c +@@ -306,9 +306,9 @@ get_handover_object_path_for_call (GrdDaemonSystem *daemon_system, + if (!object) + { + g_set_error (error, +- G_DBUS_ERROR, +- G_DBUS_ERROR_UNKNOWN_OBJECT, +- "No connection waiting for handover"); ++ GRD_DBUS_ERROR, ++ GRD_DBUS_ERROR_NO_HANDOVER, ++ "No handover interface for session"); + return NULL; + } + +diff --git a/src/grd-daemon.c b/src/grd-daemon.c +index d1cb1ac..50bcd3d 100644 +--- a/src/grd-daemon.c ++++ b/src/grd-daemon.c +@@ -40,6 +40,7 @@ + #include "grd-settings-headless.h" + #include "grd-settings-system.h" + #include "grd-settings-user.h" ++#include "grd-types.h" + #include "grd-vnc-server.h" + + #ifdef HAVE_LIBSYSTEMD +@@ -103,6 +104,23 @@ G_DEFINE_TYPE_WITH_PRIVATE (GrdDaemon, grd_daemon, G_TYPE_APPLICATION) + static void maybe_start_rdp_server (GrdDaemon *daemon); + #endif + ++static const GDBusErrorEntry grd_dbus_error_entries[] = ++{ ++ { GRD_DBUS_ERROR_NO_HANDOVER, "org.gnome.RemoteDesktop.Error.NoHandover" }, ++}; ++ ++GQuark ++grd_dbus_error_quark (void) ++{ ++ static gsize quark = 0; ++ ++ g_dbus_error_register_error_domain ("grd-dbus-error-quark", ++ &quark, ++ grd_dbus_error_entries, ++ G_N_ELEMENTS (grd_dbus_error_entries)); ++ return (GQuark) quark; ++} ++ + GCancellable * + grd_daemon_get_cancellable (GrdDaemon *daemon) + { +@@ -1027,6 +1045,8 @@ grd_daemon_class_init (GrdDaemonClass *klass) + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); ++ ++ grd_dbus_error_quark (); + } + + static void +diff --git a/src/grd-types.h b/src/grd-types.h +index 32d4cbd..16f5a25 100644 +--- a/src/grd-types.h ++++ b/src/grd-types.h +@@ -22,6 +22,8 @@ + + #pragma once + ++#include ++ + typedef struct _GrdAVCFrameInfo GrdAVCFrameInfo; + typedef struct _GrdBitstream GrdBitstream; + typedef struct _GrdClipboard GrdClipboard; +@@ -97,3 +99,11 @@ typedef enum _GrdPixelFormat + { + GRD_PIXEL_FORMAT_RGBA8888, + } GrdPixelFormat; ++ ++typedef enum _GrdDBusError ++{ ++ GRD_DBUS_ERROR_NO_HANDOVER, ++} GrdDBusError; ++ ++#define GRD_DBUS_ERROR grd_dbus_error_quark () ++GQuark grd_dbus_error_quark (void); +-- +2.51.0 + + +From 670e1f54f52bbaa1aaf33599bd72eef7fc7699d8 Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Mon, 26 Jan 2026 19:22:09 +0100 +Subject: [PATCH 4/4] daemon-handover: Watch for handover interface + availability + +When the handover request fails with GRD_DBUS_ERROR_NO_HANDOVER, use a +GDBusObjectManager to watch for new handover objects. When one becomes +available, retry the handover request. This handles the race condition +where the handover daemon requests a handover before the system daemon +has created the interface. + +Part-of: +--- + src/grd-daemon-handover.c | 128 ++++++++++++++++++++++++++++++++++++-- + src/meson.build | 1 + + 2 files changed, 124 insertions(+), 5 deletions(-) + +diff --git a/src/grd-daemon-handover.c b/src/grd-daemon-handover.c +index 95b5a54..709f14b 100644 +--- a/src/grd-daemon-handover.c ++++ b/src/grd-daemon-handover.c +@@ -46,6 +46,8 @@ struct _GrdDaemonHandover + GrdDBusRemoteDesktopRdpDispatcher *remote_desktop_dispatcher; + GrdDBusRemoteDesktopRdpHandover *remote_desktop_handover; + ++ GDBusObjectManager *handover_object_manager; ++ + GrdSession *session; + + gboolean use_system_credentials; +@@ -59,6 +61,11 @@ struct _GrdDaemonHandover + + G_DEFINE_TYPE (GrdDaemonHandover, grd_daemon_handover, GRD_TYPE_DAEMON) + ++static void ++on_remote_desktop_rdp_dispatcher_handover_requested (GObject *object, ++ GAsyncResult *result, ++ gpointer user_data); ++ + static gboolean + grd_daemon_handover_is_ready (GrdDaemon *daemon) + { +@@ -556,6 +563,105 @@ on_rdp_server_stopped (GrdDaemonHandover *daemon_handover) + teardown_handover (daemon_handover); + } + ++static void ++on_handover_object_added (GDBusObjectManager *manager, ++ GDBusObject *object, ++ GrdDaemonHandover *daemon_handover) ++{ ++ g_autofree char *session_id = NULL; ++ g_autofree char *expected_object_path = NULL; ++ const char *object_path; ++ GCancellable *cancellable; ++ ++ session_id = grd_get_session_id_from_pid (getpid ()); ++ if (!session_id) ++ { ++ g_warning ("[DaemonHandover] Could not get session id"); ++ return; ++ } ++ ++ expected_object_path = g_strdup_printf ("%s/session%s", ++ REMOTE_DESKTOP_HANDOVERS_OBJECT_PATH, ++ session_id); ++ object_path = g_dbus_object_get_object_path (object); ++ ++ if (g_strcmp0 (object_path, expected_object_path) != 0) ++ { ++ g_debug ("[DaemonHandover] Ignoring handover object at: %s, " ++ "expected: %s", object_path, expected_object_path); ++ return; ++ } ++ ++ cancellable = grd_daemon_get_cancellable (GRD_DAEMON (daemon_handover)); ++ grd_dbus_remote_desktop_rdp_dispatcher_call_request_handover ( ++ daemon_handover->remote_desktop_dispatcher, ++ cancellable, ++ on_remote_desktop_rdp_dispatcher_handover_requested, ++ daemon_handover); ++} ++ ++static void ++on_handover_object_manager_acquired (GObject *source_object, ++ GAsyncResult *result, ++ gpointer user_data) ++{ ++ g_autoptr (GDBusObjectManager) manager = NULL; ++ g_autoptr (GError) error = NULL; ++ GrdDaemonHandover *daemon_handover; ++ ++ manager = ++ grd_dbus_remote_desktop_object_manager_client_new_for_bus_finish (result, ++ &error); ++ if (!manager) ++ { ++ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) ++ g_warning ("[DaemonHandover] Failed to create handover object manager: %s", ++ error->message); ++ return; ++ } ++ ++ daemon_handover = GRD_DAEMON_HANDOVER (user_data); ++ daemon_handover->handover_object_manager = g_steal_pointer (&manager); ++ ++ g_debug ("[DaemonHandover] Watching handover objects"); ++ ++ g_signal_connect (daemon_handover->handover_object_manager, "object-added", ++ G_CALLBACK (on_handover_object_added), daemon_handover); ++} ++ ++static void ++start_watching_handover_objects (GrdDaemonHandover *daemon_handover) ++{ ++ GCancellable *cancellable; ++ ++ if (daemon_handover->handover_object_manager) ++ return; ++ ++ cancellable = grd_daemon_get_cancellable (GRD_DAEMON (daemon_handover)); ++ ++ grd_dbus_remote_desktop_object_manager_client_new_for_bus ( ++ G_BUS_TYPE_SYSTEM, ++ G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START, ++ REMOTE_DESKTOP_BUS_NAME, ++ REMOTE_DESKTOP_HANDOVERS_OBJECT_PATH, ++ cancellable, ++ on_handover_object_manager_acquired, ++ daemon_handover); ++} ++ ++static void ++stop_watching_handover_objects (GrdDaemonHandover *daemon_handover) ++{ ++ if (!daemon_handover->handover_object_manager) ++ return; ++ ++ g_signal_handlers_disconnect_by_func (daemon_handover->handover_object_manager, ++ G_CALLBACK (on_handover_object_added), ++ daemon_handover); ++ ++ g_clear_object (&daemon_handover->handover_object_manager); ++} ++ + static void + on_remote_desktop_rdp_handover_proxy_acquired (GObject *object, + GAsyncResult *result, +@@ -579,6 +685,9 @@ on_remote_desktop_rdp_handover_proxy_acquired (GObject *object, + } + + daemon_handover = GRD_DAEMON_HANDOVER (user_data); ++ ++ stop_watching_handover_objects (daemon_handover); ++ + daemon_handover->remote_desktop_handover = g_steal_pointer (&proxy); + + setup_handover (daemon_handover); +@@ -607,11 +716,16 @@ on_remote_desktop_rdp_dispatcher_handover_requested (GObject *object, + &error); + if (!success) + { +- if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) +- return; +- +- g_warning ("[DaemonHandover] Failed to request remote desktop " +- "handover: %s", error->message); ++ if (g_error_matches (error, GRD_DBUS_ERROR, GRD_DBUS_ERROR_NO_HANDOVER)) ++ { ++ daemon_handover = GRD_DAEMON_HANDOVER (user_data); ++ start_watching_handover_objects (daemon_handover); ++ } ++ else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) ++ { ++ g_warning ("[DaemonHandover] Failed to request remote desktop " ++ "handover: %s", error->message); ++ } + return; + } + +@@ -702,6 +816,8 @@ on_gnome_remote_desktop_name_vanished (GDBusConnection *connection, + g_clear_object (&daemon_handover->remote_desktop_handover); + g_clear_object (&daemon_handover->remote_desktop_dispatcher); + ++ stop_watching_handover_objects (daemon_handover); ++ + if (grd_is_remote_login ()) + grd_session_manager_call_logout_sync (); + } +@@ -765,6 +881,8 @@ grd_daemon_handover_shutdown (GApplication *app) + g_clear_object (&daemon_handover->remote_desktop_handover); + g_clear_object (&daemon_handover->remote_desktop_dispatcher); + ++ stop_watching_handover_objects (daemon_handover); ++ + g_clear_handle_id (&daemon_handover->gnome_remote_desktop_watch_name_id, + g_bus_unwatch_name); + +diff --git a/src/meson.build b/src/meson.build +index 525bb5d..185041e 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -314,6 +314,7 @@ gen_daemon_sources += gnome.gdbus_codegen('grd-dbus-mutter-remote-desktop', + namespace: 'GrdDBusMutter') + dbus_gen_remote_desktop = gnome.gdbus_codegen('grd-dbus-remote-desktop', + 'org.gnome.RemoteDesktop.xml', ++ object_manager: true, + interface_prefix: 'org.gnome.RemoteDesktop.', + namespace: 'GrdDBusRemoteDesktop') + gen_daemon_sources += dbus_gen_remote_desktop +-- +2.51.0 + diff --git a/0001-daemon-system-Respect-existing-remote_id-from-third-.patch b/0001-daemon-system-Respect-existing-remote_id-from-third-.patch new file mode 100644 index 0000000..ffc3926 --- /dev/null +++ b/0001-daemon-system-Respect-existing-remote_id-from-third-.patch @@ -0,0 +1,89 @@ +From 9d11bc6ef3fd5889c8fa7a21e0f6a8df8cf011c1 Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Tue, 3 Mar 2026 17:57:16 +0100 +Subject: [PATCH] daemon-system: Respect existing remote_id from third-party + remote displays + +When a remote display already has a valid remote_id set (not "/" or null), +reuse that id instead of generating a new one and overwriting it. This +allows third-party remote desktop solutions to work correctly by preserving +their original remote_id. Only generate and set a new remote_id when the +display doesn't have one. + +Part-of: +--- + src/grd-daemon-system.c | 46 +++++++++++++++++++++++++++++++++++------ + 1 file changed, 40 insertions(+), 6 deletions(-) + +diff --git a/src/grd-daemon-system.c b/src/grd-daemon-system.c +index 5c7f7ec..d5c382a 100644 +--- a/src/grd-daemon-system.c ++++ b/src/grd-daemon-system.c +@@ -1092,17 +1092,32 @@ on_gdm_remote_display_session_id_changed (GrdDBusGdmRemoteDisplay *remote_displa + register_handover_iface (remote_client, session_id); + } + +-static GrdRemoteClient * +-remote_client_new_from_display (GrdDaemonSystem *daemon_system, +- GrdDBusGdmRemoteDisplay *remote_display) ++static char * ++get_remote_id_from_remote_display (GrdDaemonSystem *daemon_system, ++ GrdDBusGdmRemoteDisplay *remote_display) + { +- g_autoptr (GrdRemoteClient) remote_client = NULL; ++ const char *display_remote_id; ++ g_autofree char *new_remote_id = NULL; + g_autoptr (GError) error = NULL; + +- remote_client = remote_client_new (daemon_system, NULL); ++ display_remote_id = grd_dbus_gdm_remote_display_get_remote_id (remote_display); ++ if (display_remote_id && !g_str_equal (display_remote_id, "/")) ++ { ++ if (g_hash_table_contains (daemon_system->remote_clients, display_remote_id)) ++ { ++ g_warning ("[DaemonSystem] A remote client with remote_id %s already " ++ "exists", display_remote_id); ++ return NULL; ++ } ++ ++ return g_strdup (display_remote_id); ++ } + ++ /* If the remote display doesn't have a valid remote_id, ++ * generate a new one and set it */ ++ new_remote_id = get_next_available_id (daemon_system); + if (!grd_dbus_gdm_remote_display_call_set_remote_id_sync (remote_display, +- remote_client->id, ++ new_remote_id, + NULL, + &error)) + { +@@ -1111,6 +1126,25 @@ remote_client_new_from_display (GrdDaemonSystem *daemon_system, + return NULL; + } + ++ return g_steal_pointer (&new_remote_id); ++} ++ ++static GrdRemoteClient * ++remote_client_new_from_display (GrdDaemonSystem *daemon_system, ++ GrdDBusGdmRemoteDisplay *remote_display) ++{ ++ g_autoptr (GrdRemoteClient) remote_client = NULL; ++ char *remote_id; ++ ++ remote_client = g_new0 (GrdRemoteClient, 1); ++ remote_client->daemon_system = daemon_system; ++ ++ remote_id = get_remote_id_from_remote_display (daemon_system, remote_display); ++ if (!remote_id) ++ return NULL; ++ ++ remote_client->id = remote_id; ++ + return g_steal_pointer (&remote_client); + } + +-- +2.51.0 + diff --git a/0001-daemon-system-Use-timeout_add_seconds_once-on-abort_.patch b/0001-daemon-system-Use-timeout_add_seconds_once-on-abort_.patch new file mode 100644 index 0000000..931dcda --- /dev/null +++ b/0001-daemon-system-Use-timeout_add_seconds_once-on-abort_.patch @@ -0,0 +1,1226 @@ +From 05c1ede310a22ff8d9044b1e87d8a562526635d3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Joan=20Torres=20L=C3=B3pez?= +Date: Wed, 4 Jun 2025 11:32:25 +0200 +Subject: [PATCH 01/12] daemon-system: Use timeout_add_seconds_once on + abort_handover + +--- + src/grd-daemon-system.c | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +diff --git a/src/grd-daemon-system.c b/src/grd-daemon-system.c +index 8229978..41715f0 100644 +--- a/src/grd-daemon-system.c ++++ b/src/grd-daemon-system.c +@@ -36,7 +36,7 @@ + #include "grd-session-rdp.h" + #include "grd-settings.h" + +-#define MAX_HANDOVER_WAIT_TIME_MS (30 * 1000) ++#define MAX_HANDOVER_WAIT_TIME_S 30 + + typedef struct + { +@@ -182,7 +182,7 @@ on_handle_take_client (GrdDBusRemoteDesktopRdpHandover *interface, + return G_DBUS_METHOD_INVOCATION_HANDLED; + } + +-static gboolean ++static void + abort_handover (gpointer user_data) + { + GrdRemoteClient *remote_client = user_data; +@@ -198,8 +198,6 @@ abort_handover (gpointer user_data) + } + + g_hash_table_remove (daemon_system->remote_clients, remote_client->id); +- +- return G_SOURCE_REMOVE; + } + + static char * +@@ -412,7 +410,9 @@ on_handle_start_handover (GrdDBusRemoteDesktopRdpHandover *interface, + if (remote_client->abort_handover_source_id == 0) + { + remote_client->abort_handover_source_id = +- g_timeout_add (MAX_HANDOVER_WAIT_TIME_MS, abort_handover, remote_client); ++ g_timeout_add_seconds_once (MAX_HANDOVER_WAIT_TIME_S, ++ abort_handover, ++ remote_client); + } + + return G_DBUS_METHOD_INVOCATION_HANDLED; +@@ -607,7 +607,9 @@ remote_client_new (GrdDaemonSystem *daemon_system, + remote_client); + + remote_client->abort_handover_source_id = +- g_timeout_add (MAX_HANDOVER_WAIT_TIME_MS, abort_handover, remote_client); ++ g_timeout_add_seconds_once (MAX_HANDOVER_WAIT_TIME_S, ++ abort_handover, ++ remote_client); + + return remote_client; + } +From 66f462124e295db2d977f721f77cdab28c2b49cc Mon Sep 17 00:00:00 2001 +From: Joan Torres +Date: Sat, 17 May 2025 02:13:35 +0200 +Subject: [PATCH 02/12] daemon-system: Move session_id_changed + +This function will depend on remote_display_remote_id_changed in the +next commit. +--- + src/grd-daemon-system.c | 44 ++++++++++++++++++++--------------------- + 1 file changed, 22 insertions(+), 22 deletions(-) + +diff --git a/src/grd-daemon-system.c b/src/grd-daemon-system.c +index 70142a0..3ad3612 100644 +--- a/src/grd-daemon-system.c ++++ b/src/grd-daemon-system.c +@@ -923,28 +923,6 @@ on_remote_display_factory_proxy_acquired (GObject *object, + grd_daemon_maybe_enable_services (GRD_DAEMON (daemon_system)); + } + +-static void +-on_gdm_remote_display_session_id_changed (GrdDBusGdmRemoteDisplay *remote_display, +- GParamSpec *pspec, +- GrdRemoteClient *remote_client) +-{ +- const char *session_id; +- +- session_id = grd_dbus_gdm_remote_display_get_session_id (remote_display); +- +- g_debug ("[DaemonSystem] GDM added a new remote display with remote id: %s " +- "and session: %s", +- remote_client->id, +- session_id); +- +- register_handover_iface (remote_client, session_id); +- +- g_signal_handlers_disconnect_by_func ( +- remote_display, +- G_CALLBACK (on_gdm_remote_display_session_id_changed), +- remote_client); +-} +- + static void + on_remote_display_remote_id_changed (GrdDBusGdmRemoteDisplay *remote_display, + GParamSpec *pspec, +@@ -983,6 +961,28 @@ on_remote_display_remote_id_changed (GrdDBusGdmRemoteDisplay *remote_display, + new_remote_client->handover_dst->interface); + } + ++static void ++on_gdm_remote_display_session_id_changed (GrdDBusGdmRemoteDisplay *remote_display, ++ GParamSpec *pspec, ++ GrdRemoteClient *remote_client) ++{ ++ const char *session_id; ++ ++ session_id = grd_dbus_gdm_remote_display_get_session_id (remote_display); ++ ++ g_debug ("[DaemonSystem] GDM added a new remote display with remote id: %s " ++ "and session: %s", ++ remote_client->id, ++ session_id); ++ ++ register_handover_iface (remote_client, session_id); ++ ++ g_signal_handlers_disconnect_by_func ( ++ remote_display, ++ G_CALLBACK (on_gdm_remote_display_session_id_changed), ++ remote_client); ++} ++ + static void + register_handover_for_display (GrdDaemonSystem *daemon_system, + GrdDBusGdmRemoteDisplay *remote_display) +-- +2.51.0 + + +From 3d480acc40627fa61c8461a08b798adea5f37443 Mon Sep 17 00:00:00 2001 +From: Joan Torres +Date: Sat, 17 May 2025 02:18:09 +0200 +Subject: [PATCH 03/12] daemon-system: Don't register handover if session_id is + unset + +There can be some remote displays that won't have a session_id, +those ones are created by GDM as intermediate ones for the autologin +process. + +Right now there isn't any remote_client associated with these remote +displays, but in the next commit that will change. +--- + src/grd-daemon-system.c | 27 ++++++++++++++++++--------- + 1 file changed, 18 insertions(+), 9 deletions(-) + +diff --git a/src/grd-daemon-system.c b/src/grd-daemon-system.c +index 3ad3612..92b4d76 100644 +--- a/src/grd-daemon-system.c ++++ b/src/grd-daemon-system.c +@@ -970,17 +970,23 @@ on_gdm_remote_display_session_id_changed (GrdDBusGdmRemoteDisplay *remote_displa + + session_id = grd_dbus_gdm_remote_display_get_session_id (remote_display); + ++ g_signal_handlers_disconnect_by_func (remote_display, ++ G_CALLBACK (on_gdm_remote_display_session_id_changed), ++ remote_client); ++ ++ if (!session_id || g_str_equal (session_id, "")) ++ return; ++ + g_debug ("[DaemonSystem] GDM added a new remote display with remote id: %s " + "and session: %s", + remote_client->id, + session_id); + +- register_handover_iface (remote_client, session_id); ++ g_signal_connect (remote_display, "notify::remote-id", ++ G_CALLBACK (on_remote_display_remote_id_changed), ++ remote_client); + +- g_signal_handlers_disconnect_by_func ( +- remote_display, +- G_CALLBACK (on_gdm_remote_display_session_id_changed), +- remote_client); ++ register_handover_iface (remote_client, session_id); + } + + static void +@@ -1004,10 +1010,6 @@ register_handover_for_display (GrdDaemonSystem *daemon_system, + disconnect_from_remote_display (remote_client); + remote_client->remote_display = g_object_ref (remote_display); + +- g_signal_connect (remote_display, "notify::remote-id", +- G_CALLBACK (on_remote_display_remote_id_changed), +- remote_client); +- + session_id = grd_dbus_gdm_remote_display_get_session_id (remote_display); + if (!session_id || strcmp (session_id, "") == 0) + { +@@ -1022,6 +1024,10 @@ register_handover_for_display (GrdDaemonSystem *daemon_system, + remote_client->id, + session_id); + ++ g_signal_connect (remote_display, "notify::remote-id", ++ G_CALLBACK (on_remote_display_remote_id_changed), ++ remote_client); ++ + register_handover_iface (remote_client, session_id); + } + +@@ -1045,6 +1051,9 @@ unregister_handover_for_display (GrdDaemonSystem *daemon_system, + } + + session_id = grd_dbus_gdm_remote_display_get_session_id (remote_display); ++ if (!session_id || g_str_equal (session_id, "")) ++ return; ++ + object_path = g_strdup_printf ("%s/session%s", + REMOTE_DESKTOP_HANDOVERS_OBJECT_PATH, + session_id); +-- +2.51.0 + + +From 06b4f4a9b717145c9bd926e755b9700fe927b191 Mon Sep 17 00:00:00 2001 +From: Joan Torres +Date: Sat, 17 May 2025 02:46:50 +0200 +Subject: [PATCH 04/12] daemon-system: Consider remote sessions from autologin + +GDM daemon can start a remote user session from the grd system daemon. +Now, it can start a remote user session independently too, from a GDM tool +which uses the autologin service. + +In the autologin process, two remote displays will be created +sequentially: + 1. A first remote display with no session_id and remote_id: "/". + 2. A second remote display with a user session_id and a remote_id + inherited from the first one. + +For the first display, create an empty remote_client, generate a +remote_id and set it to the display calling "SetRemoteId". + +Then the second display will be created, which has a session_id and a +remote_id, so a handover interface will be registered to allow connecting +with a potential handover daemon from a remote login. + +There could be the case where both displays have as remote_id: "/". +This means that two remote clients will be created and "SetRemoteId" +will be used on both of them: + - The first remote display won't have any handover interfaces, so when + removed, its remote_client will be removed. + - The second remote display will have one handover interface, so it will + be available for a potential handover. +--- + data/org.gnome.RemoteDesktop.conf.in | 2 + + src/grd-daemon-system.c | 81 ++++++++++++++++++++++++---- + src/org.gnome.DisplayManager.xml | 3 ++ + 3 files changed, 75 insertions(+), 11 deletions(-) + +diff --git a/data/org.gnome.RemoteDesktop.conf.in b/data/org.gnome.RemoteDesktop.conf.in +index 891d219..ca87c90 100644 +--- a/data/org.gnome.RemoteDesktop.conf.in ++++ b/data/org.gnome.RemoteDesktop.conf.in +@@ -12,6 +12,8 @@ + + ++ + + + +diff --git a/src/grd-daemon-system.c b/src/grd-daemon-system.c +index 92b4d76..05b57f2 100644 +--- a/src/grd-daemon-system.c ++++ b/src/grd-daemon-system.c +@@ -83,6 +83,11 @@ struct _GrdDaemonSystem + + G_DEFINE_TYPE (GrdDaemonSystem, grd_daemon_system, GRD_TYPE_DAEMON) + ++static void ++grd_remote_client_free (GrdRemoteClient *remote_client); ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC (GrdRemoteClient, grd_remote_client_free) ++ + static void + on_remote_display_remote_id_changed (GrdDBusGdmRemoteDisplay *remote_display, + GParamSpec *pspec, +@@ -604,6 +609,10 @@ remote_client_new (GrdDaemonSystem *daemon_system, + remote_client = g_new0 (GrdRemoteClient, 1); + remote_client->id = get_next_available_id (daemon_system); + remote_client->daemon_system = daemon_system; ++ ++ if (!session) ++ return remote_client; ++ + remote_client->is_client_mstsc = grd_session_rdp_is_client_mstsc (GRD_SESSION_RDP (session)); + remote_client->session = session; + g_object_weak_ref (G_OBJECT (session), +@@ -943,6 +952,9 @@ on_remote_display_remote_id_changed (GrdDBusGdmRemoteDisplay *remote_display, + return; + } + ++ if (remote_client == new_remote_client) ++ return; ++ + g_debug ("[DaemonSystem] GDM updated a remote display with a new remote id: " + "%s", remote_id); + +@@ -989,23 +1001,65 @@ on_gdm_remote_display_session_id_changed (GrdDBusGdmRemoteDisplay *remote_displa + register_handover_iface (remote_client, session_id); + } + ++static GrdRemoteClient * ++remote_client_new_from_display (GrdDaemonSystem *daemon_system, ++ GrdDBusGdmRemoteDisplay *remote_display) ++{ ++ g_autoptr (GrdRemoteClient) remote_client = NULL; ++ g_autoptr (GError) error = NULL; ++ ++ remote_client = remote_client_new (daemon_system, NULL); ++ ++ if (!grd_dbus_gdm_remote_display_call_set_remote_id_sync (remote_display, ++ remote_client->id, ++ NULL, ++ &error)) ++ { ++ g_warning ("[DaemonSystem] Failed to set remote_id on display: %s", ++ error->message); ++ return NULL; ++ } ++ ++ return g_steal_pointer (&remote_client); ++} ++ ++static GrdRemoteClient * ++get_remote_client_from_remote_display (GrdDaemonSystem *daemon_system, ++ GrdDBusGdmRemoteDisplay *remote_display) ++{ ++ GrdRemoteClient *remote_client = NULL; ++ g_autoptr (GError) error = NULL; ++ const char *remote_id; ++ ++ remote_id = grd_dbus_gdm_remote_display_get_remote_id (remote_display); ++ if (g_hash_table_lookup_extended (daemon_system->remote_clients, ++ remote_id, NULL, ++ (gpointer *) &remote_client)) ++ return remote_client; ++ ++ remote_client = remote_client_new_from_display (daemon_system, ++ remote_display); ++ if (!remote_client) ++ return NULL; ++ ++ g_hash_table_insert (daemon_system->remote_clients, ++ remote_client->id, ++ remote_client); ++ ++ return remote_client; ++} ++ + static void + register_handover_for_display (GrdDaemonSystem *daemon_system, + GrdDBusGdmRemoteDisplay *remote_display) + { + GrdRemoteClient *remote_client; + const char *session_id; +- const char *remote_id; + +- remote_id = grd_dbus_gdm_remote_display_get_remote_id (remote_display); +- if (!g_hash_table_lookup_extended (daemon_system->remote_clients, +- remote_id, NULL, +- (gpointer *) &remote_client)) +- { +- g_debug ("[DaemonSystem] GDM added a new remote display with a remote " +- "id %s we didn't know about", remote_id); +- return; +- } ++ remote_client = get_remote_client_from_remote_display (daemon_system, ++ remote_display); ++ if (!remote_client) ++ return; + + disconnect_from_remote_display (remote_client); + remote_client->remote_display = g_object_ref (remote_display); +@@ -1052,7 +1106,12 @@ unregister_handover_for_display (GrdDaemonSystem *daemon_system, + + session_id = grd_dbus_gdm_remote_display_get_session_id (remote_display); + if (!session_id || g_str_equal (session_id, "")) +- return; ++ { ++ if (!remote_client->handover_src && !remote_client->handover_dst) ++ g_hash_table_remove (daemon_system->remote_clients, remote_id); ++ ++ return; ++ } + + object_path = g_strdup_printf ("%s/session%s", + REMOTE_DESKTOP_HANDOVERS_OBJECT_PATH, +diff --git a/src/org.gnome.DisplayManager.xml b/src/org.gnome.DisplayManager.xml +index 73e073c..145fe21 100644 +--- a/src/org.gnome.DisplayManager.xml ++++ b/src/org.gnome.DisplayManager.xml +@@ -3,6 +3,9 @@ + + + ++ ++ ++ + + + +-- +2.51.0 + + +From 259d160eb1a23ecd0bfffc207b8282087c78b597 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Joan=20Torres=20L=C3=B3pez?= +Date: Wed, 4 Jun 2025 12:12:41 +0200 +Subject: [PATCH 05/12] daemon-system: Consider previous remote displays + +Now that GDM has the SetRemoteId, the system daemon can use it to +register handovers to allow clients to connect to previously unknown remote +sessions. +--- + src/grd-daemon-system.c | 37 +++++++++++++++++++++++++++++++------ + 1 file changed, 31 insertions(+), 6 deletions(-) + +diff --git a/src/grd-daemon-system.c b/src/grd-daemon-system.c +index 05b57f2..8a372cb 100644 +--- a/src/grd-daemon-system.c ++++ b/src/grd-daemon-system.c +@@ -83,6 +83,9 @@ struct _GrdDaemonSystem + + G_DEFINE_TYPE (GrdDaemonSystem, grd_daemon_system, GRD_TYPE_DAEMON) + ++typedef void (*ForeachRemoteDisplayCallback) (GrdDaemonSystem *daemon_system, ++ GrdDBusGdmRemoteDisplay *remote_display); ++ + static void + grd_remote_client_free (GrdRemoteClient *remote_client); + +@@ -989,7 +992,7 @@ on_gdm_remote_display_session_id_changed (GrdDBusGdmRemoteDisplay *remote_displa + if (!session_id || g_str_equal (session_id, "")) + return; + +- g_debug ("[DaemonSystem] GDM added a new remote display with remote id: %s " ++ g_debug ("[DaemonSystem] Found a new remote display with remote id: %s " + "and session: %s", + remote_client->id, + session_id); +@@ -1073,7 +1076,7 @@ register_handover_for_display (GrdDaemonSystem *daemon_system, + return; + } + +- g_debug ("[DaemonSystem] GDM added a new remote display with remote id: %s " ++ g_debug ("[DaemonSystem] Found a new remote display with remote id: %s " + "and session: %s", + remote_client->id, + session_id); +@@ -1099,8 +1102,8 @@ unregister_handover_for_display (GrdDaemonSystem *daemon_system, + remote_id, NULL, + (gpointer *) &remote_client)) + { +- g_debug ("[DaemonSystem] GDM removed a remote display with remote id " +- "%s we didn't know about", remote_id); ++ g_debug ("[DaemonSystem] Tried to unregister handover for a remote " ++ "display with remote id %s we didn't know about", remote_id); + return; + } + +@@ -1117,8 +1120,8 @@ unregister_handover_for_display (GrdDaemonSystem *daemon_system, + REMOTE_DESKTOP_HANDOVERS_OBJECT_PATH, + session_id); + +- g_debug ("[DaemonSystem] GDM removed a remote display with remote id: %s " +- "and session: %s", remote_client->id, session_id); ++ g_debug ("[DaemonSystem] Unregistering handover for remote display with " ++ "remote id: %s and session: %s", remote_client->id, session_id); + + if (remote_client->handover_src) + { +@@ -1148,6 +1151,26 @@ on_gdm_object_remote_display_interface_changed (GrdDaemonSystem *daemon_system, + register_handover_for_display (daemon_system, remote_display); + } + ++static void ++foreach_remote_display (GrdDaemonSystem *daemon_system, ++ ForeachRemoteDisplayCallback callback) ++{ ++ GList *objects, *l; ++ ++ objects = g_dbus_object_manager_get_objects (daemon_system->display_objects); ++ for (l = objects; l; l = l->next) ++ { ++ GrdDBusGdmObject *object = l->data; ++ GrdDBusGdmRemoteDisplay *remote_display; ++ ++ remote_display = grd_dbus_gdm_object_peek_remote_display (object); ++ if (remote_display) ++ callback (daemon_system, remote_display); ++ } ++ ++ g_list_free_full (objects, g_object_unref); ++} ++ + static void + on_gdm_object_added (GrdDaemonSystem *daemon_system, + GrdDBusGdmObject *object) +@@ -1219,6 +1242,8 @@ on_gdm_object_manager_client_acquired (GObject *source_object, + return; + } + ++ foreach_remote_display (daemon_system, register_handover_for_display); ++ + g_signal_connect_object (daemon_system->display_objects, + "object-added", + G_CALLBACK (on_gdm_object_added), +-- +2.51.0 + + +From 308890ed9dbdf401e9dd6f927273e32b43116164 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Joan=20Torres=20L=C3=B3pez?= +Date: Wed, 4 Jun 2025 12:16:23 +0200 +Subject: [PATCH 06/12] daemon-handover: Only logout when system daemon + vanishes for greeters + +Previously, if the system daemon was restarted, it didn't have any way +of reconnecting to previous handover daemons, so the remote sessions +were left orphaned without a way of connecting to them. Now it's changed and +will register handover interfaces again. + +Clear the remote_desktop_handover and remote_desktop_dispatcher proxies +when the system daemon disappears, as they become invalid. This allows +them to be re-acquired when the system daemon reappears. + +This can only work for user remote sessions, to perform a handover from +a login session. It can't work for login sessions. In that case, these +login sessions will be terminated. +--- + src/grd-daemon-handover.c | 18 ++++++++++++++---- + 1 file changed, 14 insertions(+), 4 deletions(-) + +diff --git a/src/grd-daemon-handover.c b/src/grd-daemon-handover.c +index f4eeafc..06f1814 100644 +--- a/src/grd-daemon-handover.c ++++ b/src/grd-daemon-handover.c +@@ -649,6 +649,8 @@ on_gnome_remote_desktop_name_appeared (GDBusConnection *connection, + GCancellable *cancellable = + grd_daemon_get_cancellable (GRD_DAEMON (daemon_handover)); + ++ g_debug ("[DaemonHandover] %s name appeared", name); ++ + grd_dbus_remote_desktop_rdp_dispatcher_proxy_new ( + connection, + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, +@@ -666,12 +668,20 @@ on_gnome_remote_desktop_name_vanished (GDBusConnection *connection, + { + GrdDaemonHandover *daemon_handover = user_data; + +- g_warning ("[DaemonHandover] %s name vanished, shutting down daemon", +- REMOTE_DESKTOP_BUS_NAME); ++ g_warning ("[DaemonHandover] %s name vanished", name); + +- g_application_release (G_APPLICATION (daemon_handover)); ++ if (daemon_handover->remote_desktop_handover) ++ { ++ g_signal_handlers_disconnect_by_func ( ++ daemon_handover->remote_desktop_handover, ++ G_CALLBACK (on_redirect_client), ++ daemon_handover); ++ } ++ g_clear_object (&daemon_handover->remote_desktop_handover); ++ g_clear_object (&daemon_handover->remote_desktop_dispatcher); + +- grd_session_manager_call_logout_sync (); ++ if (grd_is_remote_login ()) ++ grd_session_manager_call_logout_sync (); + } + + GrdDaemonHandover * +-- +2.51.0 + + +From f9ba1f19522fd84a98a6fbc3fce1f075cd25faee Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Joan=20Torres=20L=C3=B3pez?= +Date: Fri, 20 Jun 2025 17:33:29 +0200 +Subject: [PATCH 07/12] daemon-system: Steal handover instead of + registering/unregistering + +The interface unregistered and registered is the same. + +Let's keep the same handover interface to communicate with the handover +daemon instead of unregistering and registering. + +This way we ensure a consistent communication with the handover daemon. +--- + src/grd-daemon-system.c | 47 ++++++++++++++++++++++++++++++++++++----- + 1 file changed, 42 insertions(+), 5 deletions(-) + +diff --git a/src/grd-daemon-system.c b/src/grd-daemon-system.c +index 8a372cb..3b5e4ca 100644 +--- a/src/grd-daemon-system.c ++++ b/src/grd-daemon-system.c +@@ -935,6 +935,45 @@ on_remote_display_factory_proxy_acquired (GObject *object, + grd_daemon_maybe_enable_services (GRD_DAEMON (daemon_system)); + } + ++static void ++steal_handover_from_client (GrdRemoteClient *new_remote_client, ++ GrdRemoteClient *old_remote_client) ++{ ++ HandoverInterface *handover; ++ ++ handover = g_steal_pointer (&old_remote_client->handover_dst); ++ ++ g_signal_handlers_disconnect_by_func (handover->interface, ++ G_CALLBACK (on_authorize_handover_method), ++ old_remote_client); ++ g_signal_handlers_disconnect_by_func (handover->interface, ++ G_CALLBACK (on_handle_start_handover), ++ old_remote_client); ++ g_signal_handlers_disconnect_by_func (handover->interface, ++ G_CALLBACK (on_handle_take_client), ++ old_remote_client); ++ g_signal_handlers_disconnect_by_func (handover->interface, ++ G_CALLBACK (on_handle_get_system_credentials), ++ old_remote_client); ++ ++ g_signal_connect (handover->interface, "g-authorize-method", ++ G_CALLBACK (on_authorize_handover_method), ++ new_remote_client); ++ g_signal_connect (handover->interface, "handle-start-handover", ++ G_CALLBACK (on_handle_start_handover), ++ new_remote_client); ++ g_signal_connect (handover->interface, "handle-take-client", ++ G_CALLBACK (on_handle_take_client), ++ new_remote_client); ++ g_signal_connect (handover->interface, "handle-get-system-credentials", ++ G_CALLBACK (on_handle_get_system_credentials), ++ new_remote_client); ++ ++ g_clear_pointer (&new_remote_client->handover_src, handover_iface_free); ++ new_remote_client->handover_src = new_remote_client->handover_dst; ++ new_remote_client->handover_dst = handover; ++} ++ + static void + on_remote_display_remote_id_changed (GrdDBusGdmRemoteDisplay *remote_display, + GParamSpec *pspec, +@@ -943,7 +982,6 @@ on_remote_display_remote_id_changed (GrdDBusGdmRemoteDisplay *remote_display, + GrdDaemonSystem *daemon_system = remote_client->daemon_system; + GrdRemoteClient *new_remote_client; + const char *remote_id; +- const char *session_id; + + remote_id = grd_dbus_gdm_remote_display_get_remote_id (remote_display); + if (!g_hash_table_lookup_extended (daemon_system->remote_clients, +@@ -967,13 +1005,12 @@ on_remote_display_remote_id_changed (GrdDBusGdmRemoteDisplay *remote_display, + G_CALLBACK (on_remote_display_remote_id_changed), + new_remote_client); + +- g_hash_table_remove (daemon_system->remote_clients, remote_client->id); +- +- session_id = grd_dbus_gdm_remote_display_get_session_id (remote_display); +- register_handover_iface (new_remote_client, session_id); ++ steal_handover_from_client (new_remote_client, remote_client); + + grd_dbus_remote_desktop_rdp_handover_emit_restart_handover ( + new_remote_client->handover_dst->interface); ++ ++ g_hash_table_remove (daemon_system->remote_clients, remote_client->id); + } + + static void +-- +2.51.0 + + +From 0c9b6d1f5e7cc6cc76e54653d6fcf61d027403f3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Joan=20Torres=20L=C3=B3pez?= +Date: Fri, 20 Jun 2025 17:46:06 +0200 +Subject: [PATCH 08/12] daemon-handover: Move get_credentials in start_handover + +This avoids duplicating the code. +--- + src/grd-daemon-handover.c | 41 +++++++++++++-------------------------- + 1 file changed, 14 insertions(+), 27 deletions(-) + +diff --git a/src/grd-daemon-handover.c b/src/grd-daemon-handover.c +index 06f1814..a9ec8d8 100644 +--- a/src/grd-daemon-handover.c ++++ b/src/grd-daemon-handover.c +@@ -265,14 +265,21 @@ on_start_handover_finished (GObject *object, + } + + static void +-start_handover (GrdDaemonHandover *daemon_handover, +- const char *username, +- const char *password) ++start_handover (GrdDaemonHandover *daemon_handover) + { +- GCancellable *cancellable = +- grd_daemon_get_cancellable (GRD_DAEMON (daemon_handover)); ++ GrdDaemon *daemon = GRD_DAEMON (daemon_handover); ++ GrdContext *context = grd_daemon_get_context (daemon); ++ GrdSettings *settings = grd_context_get_settings (context); ++ GCancellable *cancellable = grd_daemon_get_cancellable (daemon); ++ g_autofree char *username = NULL; ++ g_autofree char *password = NULL; + const char *object_path; + ++ if (!grd_settings_get_rdp_credentials (settings, ++ &username, &password, ++ NULL)) ++ g_assert_not_reached (); ++ + object_path = g_dbus_proxy_get_object_path ( + G_DBUS_PROXY (daemon_handover->remote_desktop_handover)); + +@@ -440,22 +447,13 @@ static void + on_rdp_server_started (GrdDaemonHandover *daemon_handover) + { + GrdDaemon *daemon = GRD_DAEMON (daemon_handover); +- GrdContext *context = grd_daemon_get_context (daemon); +- GrdSettings *settings = grd_context_get_settings (context); + GrdRdpServer *rdp_server = grd_daemon_get_rdp_server (daemon); +- g_autofree char *username = NULL; +- g_autofree char *password = NULL; +- +- if (!grd_settings_get_rdp_credentials (settings, +- &username, &password, +- NULL)) +- g_assert_not_reached (); + + g_signal_connect (daemon_handover->remote_desktop_handover, + "take-client-ready", G_CALLBACK (on_take_client_ready), + daemon_handover); + +- start_handover (daemon_handover, username, password); ++ start_handover (daemon_handover); + + g_signal_connect (rdp_server, "incoming-new-connection", + G_CALLBACK (on_incoming_new_connection), +@@ -512,18 +510,7 @@ static void + on_restart_handover (GrdDBusRemoteDesktopRdpHandover *proxy, + GrdDaemonHandover *daemon_handover) + { +- GrdDaemon *daemon = GRD_DAEMON (daemon_handover); +- GrdContext *context = grd_daemon_get_context (daemon); +- GrdSettings *settings = grd_context_get_settings (context); +- g_autofree char *username = NULL; +- g_autofree char *password = NULL; +- +- if (!grd_settings_get_rdp_credentials (settings, +- &username, &password, +- NULL)) +- g_assert_not_reached (); +- +- start_handover (daemon_handover, username, password); ++ start_handover (daemon_handover); + } + + static void +-- +2.51.0 + + +From 9e51b8936dfe413f782c51179e45a868ffeff4d1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Joan=20Torres=20L=C3=B3pez?= +Date: Fri, 20 Jun 2025 17:50:27 +0200 +Subject: [PATCH 09/12] daemon-system: Change restart signal to + handover_waiting prop + +Instead of listening to restart signal, now it listens to the +handover_waiting property being updated. + +The handover daemon was calling start_handover on start because it +expected to always have a handover waiting. Now there are cases where +the handover daemon is started in a remote session but there's not a +client waiting to connect to it, checking this property ensures +start_handover will only be called when needed. +--- + src/grd-daemon-handover.c | 28 ++++++++++++++++++++++------ + src/grd-daemon-system.c | 12 ++++++++++-- + src/org.gnome.RemoteDesktop.xml | 9 ++++----- + 3 files changed, 36 insertions(+), 13 deletions(-) + +diff --git a/src/grd-daemon-handover.c b/src/grd-daemon-handover.c +index a9ec8d8..3c9e34f 100644 +--- a/src/grd-daemon-handover.c ++++ b/src/grd-daemon-handover.c +@@ -448,16 +448,22 @@ on_rdp_server_started (GrdDaemonHandover *daemon_handover) + { + GrdDaemon *daemon = GRD_DAEMON (daemon_handover); + GrdRdpServer *rdp_server = grd_daemon_get_rdp_server (daemon); ++ gboolean handover_is_waiting; + + g_signal_connect (daemon_handover->remote_desktop_handover, + "take-client-ready", G_CALLBACK (on_take_client_ready), + daemon_handover); + +- start_handover (daemon_handover); +- + g_signal_connect (rdp_server, "incoming-new-connection", + G_CALLBACK (on_incoming_new_connection), + daemon_handover); ++ ++ handover_is_waiting = ++ grd_dbus_remote_desktop_rdp_handover_get_handover_is_waiting ( ++ daemon_handover->remote_desktop_handover); ++ ++ if (handover_is_waiting) ++ start_handover (daemon_handover); + } + + static void +@@ -507,9 +513,19 @@ on_redirect_client (GrdDBusRemoteDesktopRdpHandover *interface, + } + + static void +-on_restart_handover (GrdDBusRemoteDesktopRdpHandover *proxy, +- GrdDaemonHandover *daemon_handover) ++on_handover_is_waiting_changed (GrdDBusRemoteDesktopRdpHandover *proxy, ++ GParamSpec *pspec, ++ GrdDaemonHandover *daemon_handover) + { ++ gboolean handover_is_waiting; ++ ++ handover_is_waiting = ++ grd_dbus_remote_desktop_rdp_handover_get_handover_is_waiting ( ++ daemon_handover->remote_desktop_handover); ++ ++ if (!handover_is_waiting) ++ return; ++ + start_handover (daemon_handover); + } + +@@ -540,8 +556,8 @@ on_remote_desktop_rdp_handover_proxy_acquired (GObject *object, + + g_signal_connect (daemon_handover->remote_desktop_handover, "redirect-client", + G_CALLBACK (on_redirect_client), daemon_handover); +- g_signal_connect (daemon_handover->remote_desktop_handover, "restart-handover", +- G_CALLBACK (on_restart_handover), daemon_handover); ++ g_signal_connect (daemon_handover->remote_desktop_handover, "notify::handover-is-waiting", ++ G_CALLBACK (on_handover_is_waiting_changed), daemon_handover); + + grd_daemon_maybe_enable_services (GRD_DAEMON (daemon_handover)); + } +diff --git a/src/grd-daemon-system.c b/src/grd-daemon-system.c +index 3b5e4ca..950d539 100644 +--- a/src/grd-daemon-system.c ++++ b/src/grd-daemon-system.c +@@ -63,6 +63,7 @@ typedef struct + + gboolean is_client_mstsc; + gboolean use_system_credentials; ++ gboolean needs_handover; + + GrdDBusGdmRemoteDisplay *remote_display; + } GrdRemoteClient; +@@ -189,6 +190,9 @@ on_handle_take_client (GrdDBusRemoteDesktopRdpHandover *interface, + g_clear_object (&remote_client->socket_connection); + g_clear_handle_id (&remote_client->abort_handover_source_id, g_source_remove); + ++ grd_dbus_remote_desktop_rdp_handover_set_handover_is_waiting (interface, ++ FALSE); ++ + return G_DBUS_METHOD_INVOCATION_HANDLED; + } + +@@ -511,6 +515,9 @@ handover_iface_new (const char *session_id, + g_signal_connect (handover->interface, "handle-get-system-credentials", + G_CALLBACK (on_handle_get_system_credentials), remote_client); + ++ grd_dbus_remote_desktop_rdp_handover_set_handover_is_waiting ( ++ handover->interface, remote_client->needs_handover); ++ + return handover; + } + +@@ -612,6 +619,7 @@ remote_client_new (GrdDaemonSystem *daemon_system, + remote_client = g_new0 (GrdRemoteClient, 1); + remote_client->id = get_next_available_id (daemon_system); + remote_client->daemon_system = daemon_system; ++ remote_client->needs_handover = session != NULL; + + if (!session) + return remote_client; +@@ -1007,8 +1015,8 @@ on_remote_display_remote_id_changed (GrdDBusGdmRemoteDisplay *remote_display, + + steal_handover_from_client (new_remote_client, remote_client); + +- grd_dbus_remote_desktop_rdp_handover_emit_restart_handover ( +- new_remote_client->handover_dst->interface); ++ grd_dbus_remote_desktop_rdp_handover_set_handover_is_waiting ( ++ new_remote_client->handover_dst->interface, TRUE); + + g_hash_table_remove (daemon_system->remote_clients, remote_client->id); + } +diff --git a/src/org.gnome.RemoteDesktop.xml b/src/org.gnome.RemoteDesktop.xml +index 3fda8ec..38b35a3 100644 +--- a/src/org.gnome.RemoteDesktop.xml ++++ b/src/org.gnome.RemoteDesktop.xml +@@ -194,12 +194,11 @@ + + + +- ++ Property to inform the dst server that a new handover is ready to start. ++ --> ++ + + + +-- +2.51.0 + + +From a030f48f43412f44f4c7c1b5d12c6763cd95cad4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Joan=20Torres=20L=C3=B3pez?= +Date: Wed, 25 Jun 2025 13:20:51 +0200 +Subject: [PATCH 10/12] rdp-server: Inform new connection in its dbus iface + +This will be used in next commits. +--- + src/grd-rdp-server.c | 5 +++++ + src/org.gnome.RemoteDesktop.xml | 7 +++++++ + 2 files changed, 12 insertions(+) + +diff --git a/src/grd-rdp-server.c b/src/grd-rdp-server.c +index b6d5234..0c7ebec 100644 +--- a/src/grd-rdp-server.c ++++ b/src/grd-rdp-server.c +@@ -166,7 +166,12 @@ static void + on_session_post_connect (GrdSessionRdp *session_rdp, + GrdRdpServer *rdp_server) + { ++ GrdDBusRemoteDesktopRdpServer *rdp_server_iface = ++ grd_context_get_rdp_server_interface (rdp_server->context); ++ + g_signal_emit (rdp_server, signals[INCOMING_NEW_CONNECTION], 0, session_rdp); ++ ++ grd_dbus_remote_desktop_rdp_server_emit_new_connection (rdp_server_iface); + } + + static void +diff --git a/src/org.gnome.RemoteDesktop.xml b/src/org.gnome.RemoteDesktop.xml +index 38b35a3..d42bf93 100644 +--- a/src/org.gnome.RemoteDesktop.xml ++++ b/src/org.gnome.RemoteDesktop.xml +@@ -46,6 +46,13 @@ + + + ++ ++ ++ + + +