mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-14 16:31:03 +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> {
|
pub fn get_portal(conn: &SyncConnection) -> Proxy<&SyncConnection> {
|
||||||
conn.with_proxy(
|
conn.with_proxy(
|
||||||
"org.freedesktop.portal.Desktop",
|
"org.freedesktop.portal.Desktop",
|
||||||
@@ -632,13 +648,14 @@ pub fn request_remote_desktop(
|
|||||||
let failure_res = failure.clone();
|
let failure_res = failure.clone();
|
||||||
let session: Arc<Mutex<Option<dbus::Path>>> = Arc::new(Mutex::new(None));
|
let session: Arc<Mutex<Option<dbus::Path>>> = Arc::new(Mutex::new(None));
|
||||||
let session_res = session.clone();
|
let session_res = session.clone();
|
||||||
|
let create_session_handle_token = "u1";
|
||||||
args.insert(
|
args.insert(
|
||||||
"session_handle_token".to_string(),
|
"session_handle_token".to_string(),
|
||||||
Variant(Box::new("u1".to_string())),
|
Variant(Box::new(create_session_handle_token.to_string())),
|
||||||
);
|
);
|
||||||
args.insert(
|
args.insert(
|
||||||
"handle_token".to_string(),
|
"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;
|
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,
|
// 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
|
// a convention for Request object paths has been established that allows
|
||||||
// the caller to subscribe to the signal before making the method call.
|
// 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(
|
handle_response(
|
||||||
&conn,
|
&conn,
|
||||||
path,
|
get_request_path(&conn, create_session_handle_token)?,
|
||||||
on_create_session_response(
|
on_create_session_response(
|
||||||
fd.clone(),
|
fd.clone(),
|
||||||
streams.clone(),
|
streams.clone(),
|
||||||
@@ -673,6 +684,11 @@ pub fn request_remote_desktop(
|
|||||||
),
|
),
|
||||||
failure_res.clone(),
|
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
|
// wait 3 minutes for user interaction
|
||||||
for _ in 0..1800 {
|
for _ in 0..1800 {
|
||||||
@@ -751,9 +767,10 @@ fn on_create_session_response(
|
|||||||
// persist_mode may be configured by the user.
|
// persist_mode may be configured by the user.
|
||||||
args.insert("persist_mode".to_string(), Variant(Box::new(2u32)));
|
args.insert("persist_mode".to_string(), Variant(Box::new(2u32)));
|
||||||
}
|
}
|
||||||
|
let select_sources_handle_token = "u3";
|
||||||
args.insert(
|
args.insert(
|
||||||
"handle_token".to_string(),
|
"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
|
// https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.ScreenCast.html
|
||||||
if is_server_running() {
|
if is_server_running() {
|
||||||
@@ -769,42 +786,43 @@ fn on_create_session_response(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = portal.select_sources(ses.clone(), args)?;
|
|
||||||
handle_response(
|
handle_response(
|
||||||
c,
|
c,
|
||||||
path,
|
get_request_path(c, select_sources_handle_token)?,
|
||||||
on_select_sources_response(
|
on_select_sources_response(
|
||||||
fd.clone(),
|
fd.clone(),
|
||||||
streams.clone(),
|
streams.clone(),
|
||||||
failure.clone(),
|
failure.clone(),
|
||||||
ses,
|
ses.clone(),
|
||||||
is_support_restore_token,
|
is_support_restore_token,
|
||||||
),
|
),
|
||||||
failure.clone(),
|
failure.clone(),
|
||||||
)?;
|
)?;
|
||||||
|
let _ = portal.select_sources(ses.clone(), args)?;
|
||||||
} else {
|
} else {
|
||||||
// TODO: support persist_mode for remote_desktop_portal
|
// TODO: support persist_mode for remote_desktop_portal
|
||||||
// https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.RemoteDesktop.html
|
// https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.RemoteDesktop.html
|
||||||
|
|
||||||
|
let select_devices_handle_token = "u2";
|
||||||
args.insert(
|
args.insert(
|
||||||
"handle_token".to_string(),
|
"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)));
|
args.insert("types".to_string(), Variant(Box::new(7u32)));
|
||||||
|
|
||||||
let path = portal.select_devices(ses.clone(), args)?;
|
|
||||||
handle_response(
|
handle_response(
|
||||||
c,
|
c,
|
||||||
path,
|
get_request_path(c, select_devices_handle_token)?,
|
||||||
on_select_devices_response(
|
on_select_devices_response(
|
||||||
fd.clone(),
|
fd.clone(),
|
||||||
streams.clone(),
|
streams.clone(),
|
||||||
failure.clone(),
|
failure.clone(),
|
||||||
ses,
|
ses.clone(),
|
||||||
is_support_restore_token,
|
is_support_restore_token,
|
||||||
),
|
),
|
||||||
failure.clone(),
|
failure.clone(),
|
||||||
)?;
|
)?;
|
||||||
|
let _ = portal.select_devices(ses.clone(), args)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -825,9 +843,10 @@ fn on_select_devices_response(
|
|||||||
move |_: OrgFreedesktopPortalRequestResponse, c, _| {
|
move |_: OrgFreedesktopPortalRequestResponse, c, _| {
|
||||||
let portal = get_portal(c);
|
let portal = get_portal(c);
|
||||||
let mut args: PropMap = HashMap::new();
|
let mut args: PropMap = HashMap::new();
|
||||||
|
let select_sources_handle_token = "u3";
|
||||||
args.insert(
|
args.insert(
|
||||||
"handle_token".to_string(),
|
"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
|
// https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.ScreenCast.html
|
||||||
if is_server_running() {
|
if is_server_running() {
|
||||||
@@ -836,19 +855,19 @@ fn on_select_devices_response(
|
|||||||
args.insert("types".into(), Variant(Box::new(1u32))); //| 2u32)));
|
args.insert("types".into(), Variant(Box::new(1u32))); //| 2u32)));
|
||||||
|
|
||||||
let session = session.clone();
|
let session = session.clone();
|
||||||
let path = portal.select_sources(session.clone(), args)?;
|
|
||||||
handle_response(
|
handle_response(
|
||||||
c,
|
c,
|
||||||
path,
|
get_request_path(c, select_sources_handle_token)?,
|
||||||
on_select_sources_response(
|
on_select_sources_response(
|
||||||
fd.clone(),
|
fd.clone(),
|
||||||
streams.clone(),
|
streams.clone(),
|
||||||
failure.clone(),
|
failure.clone(),
|
||||||
session,
|
session.clone(),
|
||||||
is_support_restore_token,
|
is_support_restore_token,
|
||||||
),
|
),
|
||||||
failure.clone(),
|
failure.clone(),
|
||||||
)?;
|
)?;
|
||||||
|
let _ = portal.select_sources(session.clone(), args)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -868,19 +887,14 @@ fn on_select_sources_response(
|
|||||||
move |_: OrgFreedesktopPortalRequestResponse, c, _| {
|
move |_: OrgFreedesktopPortalRequestResponse, c, _| {
|
||||||
let portal = get_portal(c);
|
let portal = get_portal(c);
|
||||||
let mut args: PropMap = HashMap::new();
|
let mut args: PropMap = HashMap::new();
|
||||||
|
let start_handle_token = "u4";
|
||||||
args.insert(
|
args.insert(
|
||||||
"handle_token".to_string(),
|
"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(
|
handle_response(
|
||||||
c,
|
c,
|
||||||
path,
|
get_request_path(c, start_handle_token)?,
|
||||||
on_start_response(
|
on_start_response(
|
||||||
fd.clone(),
|
fd.clone(),
|
||||||
streams.clone(),
|
streams.clone(),
|
||||||
@@ -889,6 +903,11 @@ fn on_select_sources_response(
|
|||||||
),
|
),
|
||||||
failure.clone(),
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user