mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-08 05:20:59 +03:00
fix(wayland): subscribe to portal Response before making the request (#15726)
`request_remote_desktop` and its response handlers call the portal method first and only then subscribe to the resulting Request's `Response` signal, using the object path returned by the call. The comment above `create_session` already describes why that is wrong: > To avoid a race condition between the caller subscribing to the signal > after receiving the reply for the method call and the signal getting > emitted, a convention for Request object paths has been established that > allows the caller to subscribe to the signal before making the method > call. The code then does the opposite of what the comment says. When the portal emits `Response` before our match rule is installed, the signal is dropped and the flow stalls: `request_remote_desktop` spins its 3-minute wait loop and gives up, so the user sees the screen picker again (or a failure) even when a valid restore token would have restored the session silently. Build the request path from our unique bus name plus the `handle_token` we pass in the call arguments, per the Request documentation, and subscribe before calling. Applied to all five portal calls: CreateSession, SelectSources (both the ScreenCast and post-SelectDevices paths), SelectDevices, and Start. The `handle_token` values are unchanged; they are now named locals so the path and the argument cannot drift apart. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -507,6 +507,22 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
// The request object path a portal method call will use, derived from our unique
|
||||
// bus name and the `handle_token` we pass in the call arguments. Knowing it up
|
||||
// front lets us subscribe to the `Response` signal *before* making the call.
|
||||
// https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Request.html
|
||||
fn get_request_path(
|
||||
conn: &SyncConnection,
|
||||
handle_token: &str,
|
||||
) -> Result<dbus::Path<'static>, dbus::Error> {
|
||||
let sender = conn.unique_name().trim_start_matches(':').replace('.', "_");
|
||||
dbus::Path::new(format!(
|
||||
"/org/freedesktop/portal/desktop/request/{}/{}",
|
||||
sender, handle_token
|
||||
))
|
||||
.map_err(|_| dbus::Error::new_failed("Failed to construct portal request path"))
|
||||
}
|
||||
|
||||
pub fn get_portal(conn: &SyncConnection) -> Proxy<&SyncConnection> {
|
||||
conn.with_proxy(
|
||||
"org.freedesktop.portal.Desktop",
|
||||
@@ -632,13 +648,14 @@ pub fn request_remote_desktop(
|
||||
let failure_res = failure.clone();
|
||||
let session: Arc<Mutex<Option<dbus::Path>>> = Arc::new(Mutex::new(None));
|
||||
let session_res = session.clone();
|
||||
let create_session_handle_token = "u1";
|
||||
args.insert(
|
||||
"session_handle_token".to_string(),
|
||||
Variant(Box::new("u1".to_string())),
|
||||
Variant(Box::new(create_session_handle_token.to_string())),
|
||||
);
|
||||
args.insert(
|
||||
"handle_token".to_string(),
|
||||
Variant(Box::new("u1".to_string())),
|
||||
Variant(Box::new(create_session_handle_token.to_string())),
|
||||
);
|
||||
|
||||
let mut is_support_restore_token = false;
|
||||
@@ -654,15 +671,9 @@ pub fn request_remote_desktop(
|
||||
// between the caller subscribing to the signal after receiving the reply for the method call and the signal getting emitted,
|
||||
// a convention for Request object paths has been established that allows
|
||||
// the caller to subscribe to the signal before making the method call.
|
||||
let path;
|
||||
if is_server_running() {
|
||||
path = screencast_portal::create_session(&portal, args)?;
|
||||
} else {
|
||||
path = remote_desktop_portal::create_session(&portal, args)?;
|
||||
}
|
||||
handle_response(
|
||||
&conn,
|
||||
path,
|
||||
get_request_path(&conn, create_session_handle_token)?,
|
||||
on_create_session_response(
|
||||
fd.clone(),
|
||||
streams.clone(),
|
||||
@@ -673,6 +684,11 @@ pub fn request_remote_desktop(
|
||||
),
|
||||
failure_res.clone(),
|
||||
)?;
|
||||
if is_server_running() {
|
||||
let _ = screencast_portal::create_session(&portal, args)?;
|
||||
} else {
|
||||
let _ = remote_desktop_portal::create_session(&portal, args)?;
|
||||
}
|
||||
|
||||
// wait 3 minutes for user interaction
|
||||
for _ in 0..1800 {
|
||||
@@ -751,9 +767,10 @@ fn on_create_session_response(
|
||||
// persist_mode may be configured by the user.
|
||||
args.insert("persist_mode".to_string(), Variant(Box::new(2u32)));
|
||||
}
|
||||
let select_sources_handle_token = "u3";
|
||||
args.insert(
|
||||
"handle_token".to_string(),
|
||||
Variant(Box::new("u3".to_string())),
|
||||
Variant(Box::new(select_sources_handle_token.to_string())),
|
||||
);
|
||||
// https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.ScreenCast.html
|
||||
if is_server_running() {
|
||||
@@ -769,42 +786,43 @@ fn on_create_session_response(
|
||||
});
|
||||
}
|
||||
|
||||
let path = portal.select_sources(ses.clone(), args)?;
|
||||
handle_response(
|
||||
c,
|
||||
path,
|
||||
get_request_path(c, select_sources_handle_token)?,
|
||||
on_select_sources_response(
|
||||
fd.clone(),
|
||||
streams.clone(),
|
||||
failure.clone(),
|
||||
ses,
|
||||
ses.clone(),
|
||||
is_support_restore_token,
|
||||
),
|
||||
failure.clone(),
|
||||
)?;
|
||||
let _ = portal.select_sources(ses.clone(), args)?;
|
||||
} else {
|
||||
// TODO: support persist_mode for remote_desktop_portal
|
||||
// https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.RemoteDesktop.html
|
||||
|
||||
let select_devices_handle_token = "u2";
|
||||
args.insert(
|
||||
"handle_token".to_string(),
|
||||
Variant(Box::new("u2".to_string())),
|
||||
Variant(Box::new(select_devices_handle_token.to_string())),
|
||||
);
|
||||
args.insert("types".to_string(), Variant(Box::new(7u32)));
|
||||
|
||||
let path = portal.select_devices(ses.clone(), args)?;
|
||||
handle_response(
|
||||
c,
|
||||
path,
|
||||
get_request_path(c, select_devices_handle_token)?,
|
||||
on_select_devices_response(
|
||||
fd.clone(),
|
||||
streams.clone(),
|
||||
failure.clone(),
|
||||
ses,
|
||||
ses.clone(),
|
||||
is_support_restore_token,
|
||||
),
|
||||
failure.clone(),
|
||||
)?;
|
||||
let _ = portal.select_devices(ses.clone(), args)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -825,9 +843,10 @@ fn on_select_devices_response(
|
||||
move |_: OrgFreedesktopPortalRequestResponse, c, _| {
|
||||
let portal = get_portal(c);
|
||||
let mut args: PropMap = HashMap::new();
|
||||
let select_sources_handle_token = "u3";
|
||||
args.insert(
|
||||
"handle_token".to_string(),
|
||||
Variant(Box::new("u3".to_string())),
|
||||
Variant(Box::new(select_sources_handle_token.to_string())),
|
||||
);
|
||||
// https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.ScreenCast.html
|
||||
if is_server_running() {
|
||||
@@ -836,19 +855,19 @@ fn on_select_devices_response(
|
||||
args.insert("types".into(), Variant(Box::new(1u32))); //| 2u32)));
|
||||
|
||||
let session = session.clone();
|
||||
let path = portal.select_sources(session.clone(), args)?;
|
||||
handle_response(
|
||||
c,
|
||||
path,
|
||||
get_request_path(c, select_sources_handle_token)?,
|
||||
on_select_sources_response(
|
||||
fd.clone(),
|
||||
streams.clone(),
|
||||
failure.clone(),
|
||||
session,
|
||||
session.clone(),
|
||||
is_support_restore_token,
|
||||
),
|
||||
failure.clone(),
|
||||
)?;
|
||||
let _ = portal.select_sources(session.clone(), args)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -868,19 +887,14 @@ fn on_select_sources_response(
|
||||
move |_: OrgFreedesktopPortalRequestResponse, c, _| {
|
||||
let portal = get_portal(c);
|
||||
let mut args: PropMap = HashMap::new();
|
||||
let start_handle_token = "u4";
|
||||
args.insert(
|
||||
"handle_token".to_string(),
|
||||
Variant(Box::new("u4".to_string())),
|
||||
Variant(Box::new(start_handle_token.to_string())),
|
||||
);
|
||||
let path;
|
||||
if is_server_running() {
|
||||
path = screencast_portal::start(&portal, session.clone(), "", args)?;
|
||||
} else {
|
||||
path = remote_desktop_portal::start(&portal, session.clone(), "", args)?;
|
||||
}
|
||||
handle_response(
|
||||
c,
|
||||
path,
|
||||
get_request_path(c, start_handle_token)?,
|
||||
on_start_response(
|
||||
fd.clone(),
|
||||
streams.clone(),
|
||||
@@ -889,6 +903,11 @@ fn on_select_sources_response(
|
||||
),
|
||||
failure.clone(),
|
||||
)?;
|
||||
if is_server_running() {
|
||||
let _ = screencast_portal::start(&portal, session.clone(), "", args)?;
|
||||
} else {
|
||||
let _ = remote_desktop_portal::start(&portal, session.clone(), "", args)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user