Compare commits

..

2 Commits

Author SHA1 Message Date
rustdesk
2f342e7730 wayland: lang keys for the staged portal failures
Five keys, appended to `template.rs` and to every `src/lang/*.rs`. `it.rs` gets
empty values, as AGENTS.md requires -- it is maintained by hand by its
translator. No `en.rs` entries: each key is already its own English display
text, which is also what an older peer falls back to.

Two carry a `{}`: the portal's response code, and the name of the GStreamer
element that could not be created. `lang.rs`'s `extract_placeholder` resolves a
key by replacing the first `{...}` with `{}`, which is why the server sends the
value still inside the braces and why the scrap side strips braces out of any
detail before it gets there.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab
2026-09-08 22:47:13 +08:00
rustdesk
9efe392ca2 wayland: say which step of the portal handshake failed
The XDG portal handshake is four sequential requests, and every way it can end
badly -- the user declining, nobody answering, the portal being absent or
ending it or dying mid-handshake, the stream list coming back empty -- left
`request_remote_desktop` through one `bail!` carrying one string.
`map_err_scrap` then guessed a cause by looking for "dbus" or "pipewire" in
that string. Since that string always mentions "PipeWire library", a decline
and a three-minute timeout both came out as "Wayland requires higher version of
linux distro. Please try X11 desktop or change your OS." On Ubuntu 21+, where
the mapping passes the text through untouched, they came out as raw English
pointing at an unrelated GitHub issue.

The response code and the D-Bus error were in hand at the moment of failure and
were being dropped: `handle_response` collapsed all of it into one
`AtomicBool`. Record it instead, tagged with the stage that produced it, and
let the app side look the tag up. `map_err_scrap` gains one leading branch;
anything untagged -- which is everything the capture loop reports -- takes the
existing path unchanged.

The tag decides between five new keys and the existing `xdp-portal-unavailable`:

- A decline, nobody answering, the portal ending the request with its response
  code, an empty stream list, and a missing GStreamer element each get their
  own message. Sentence-case English, so a peer that has never heard of them
  falls back to the key and still reads as a sentence rather than showing a
  slug like `x11 expected`.
- Everything else is the portal failing to deliver, which is what
  `xdp-portal-unavailable` already says -- it is already translated everywhere
  and carries the one remedy a user can act on, `systemctl --user restart
  xdg-desktop-portal`. The D-Bus error name and message go to the log.
- The Ubuntu-before-21 branch keeps every outcome that says something about the
  machine and yields the two that say what a person did.

`fill_displays` needs the tag resolved at its own call site: it opens a second
portal session for cursor-based display disambiguation, and its error returns
straight up `check_init` without passing through `map_err_scrap`, so a tag
would otherwise reach the peer verbatim.

Two existing paths change, both necessarily:

- `check_init` no longer wraps `Capturer::new` in `with_context`. The peer is
  shown `format!("{}", err)` (connection.rs), which renders only the outermost
  layer, so that context was replacing the mapped code with "Failed to create
  capturer for display 0".
- The `std::process::exit(-1)` on libdbus' no-reply text is now reached only by
  the capture loop, which is what that self-heal was written for. Every D-Bus
  call in the handshake -- opening the session bus, `get_request_path`, the
  `add_match` inside `handle_response`, `create_session`, and `conn.process` in
  the wait loop -- carries a tag, so a no-reply there is reported rather than
  fatal. It is worth saying plainly what that branch did before: the portal
  proxy has a one-second timeout, so a portal slow to activate could take the
  whole service down.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab
2026-09-08 22:47:13 +08:00
3 changed files with 18 additions and 12 deletions

View File

@@ -516,14 +516,17 @@ fn stage_err(stage: &str, kind: &str, detail: &str) -> String {
format!("wl-stage:{}:{}:{}", stage, kind, detail.trim())
}
fn dbus_detail(err: &dbus::Error) -> String {
match (err.name(), err.message()) {
// The name alone is usually the generic `org.freedesktop.DBus.Error.Failed`; the message is
// where a backend says what it objected to. This ends up in the log, so carry both.
fn dbus_stage_err(stage: &str, err: &dbus::Error) -> String {
let detail = match (err.name(), err.message()) {
(Some(name), Some(message)) if !name.is_empty() && !message.is_empty() => {
format!("{}: {}", name, message)
}
(Some(name), _) if !name.is_empty() => name.to_owned(),
(_, message) => message.unwrap_or_default().to_owned(),
}
};
stage_err(stage, "dbus", &detail)
}
#[derive(Clone)]
@@ -746,8 +749,8 @@ pub fn request_remote_desktop(
INIT = true;
}
}
let conn = SyncConnection::new_session()
.map_err(|e| anyhow!(stage_err("session-bus", "dbus", &dbus_detail(&e))))?;
let conn =
SyncConnection::new_session().map_err(|e| anyhow!(dbus_stage_err("session-bus", &e)))?;
let portal = get_portal(&conn);
let mut args: PropMap = HashMap::new();
let fd: Arc<Mutex<Option<OwnedFd>>> = Arc::new(Mutex::new(None));
@@ -783,7 +786,8 @@ pub fn request_remote_desktop(
// the caller to subscribe to the signal before making the method call.
handle_response(
&conn,
get_request_path(&conn, create_session_handle_token)?,
get_request_path(&conn, create_session_handle_token)
.map_err(|e| anyhow!(dbus_stage_err("create-session", &e)))?,
on_create_session_response(
fd.clone(),
streams.clone(),
@@ -794,18 +798,20 @@ pub fn request_remote_desktop(
),
trace.clone(),
PortalStage::CreateSession,
)?;
)
.map_err(|e| anyhow!(dbus_stage_err("create-session", &e)))?;
if is_server_running() {
let _ = screencast_portal::create_session(&portal, args)
.map_err(|e| anyhow!(stage_err("create-session", "dbus", &dbus_detail(&e))))?;
.map_err(|e| anyhow!(dbus_stage_err("create-session", &e)))?;
} else {
let _ = remote_desktop_portal::create_session(&portal, args)
.map_err(|e| anyhow!(stage_err("create-session", "dbus", &dbus_detail(&e))))?;
.map_err(|e| anyhow!(dbus_stage_err("create-session", &e)))?;
}
// wait 3 minutes for user interaction
for _ in 0..1800 {
conn.process(Duration::from_millis(100))?;
conn.process(Duration::from_millis(100))
.map_err(|e| anyhow!(dbus_stage_err(trace_res.waiting_stage().as_str(), &e)))?;
// Once we got a file descriptor we are done!
if fd_res.lock().unwrap().is_some() {
break;

View File

@@ -770,7 +770,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("port-forward-mux-tip", "同一条端口转发规则上的所有连接共用一条到对方的连接,而不是每条连接都重新连接并登录一次。"),
("Enable WebRTC P2P connection", "启用 WebRTC P2P 连接"),
("Enable TCP hole punching", "启用 TCP 打洞"),
("The screen sharing request was declined on the remote device", "远程设备上拒绝了屏幕共享请求"),
("The screen sharing request was declined on the remote device", "远程设备上的用户拒绝了屏幕共享请求"),
("No one responded to the screen sharing request on the remote device", "远程设备上无人响应屏幕共享请求"),
("The XDG Desktop Portal ended the screen sharing request ({})", "XDG Desktop Portal 结束了屏幕共享请求 ({})"),
("The XDG Desktop Portal returned no screen to capture, the PipeWire library may be too old", "XDG Desktop Portal 未返回可捕获的屏幕PipeWire 库可能过旧"),

View File

@@ -770,7 +770,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("port-forward-mux-tip", "同一條連接埠轉送規則上的所有連線共用一條到對方的連線,而不是每條連線都重新連線並登入一次。"),
("Enable WebRTC P2P connection", "啟用 WebRTC P2P 連線"),
("Enable TCP hole punching", "啟用 TCP 打洞"),
("The screen sharing request was declined on the remote device", "遠端裝置上拒絕了螢幕分享要求"),
("The screen sharing request was declined on the remote device", "遠端裝置上的使用者拒絕了螢幕分享要求"),
("No one responded to the screen sharing request on the remote device", "遠端裝置上無人回應螢幕分享要求"),
("The XDG Desktop Portal ended the screen sharing request ({})", "XDG Desktop Portal 結束了螢幕分享要求 ({})"),
("The XDG Desktop Portal returned no screen to capture, the PipeWire library may be too old", "XDG Desktop Portal 未傳回可擷取的螢幕PipeWire 函式庫可能過舊"),