mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-18 18:31:02 +03:00
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
Derived from https://github.com/quadrupleslap/scrap
scrap
Scrap records your screen! At least it does if you're on Windows, macOS, or Linux.
Usage
[dependencies]
scrap = "0.5"
Its API is as simple as it gets!
struct Display; /// A screen.
struct Frame; /// An array of the pixels that were on-screen.
struct Capturer; /// A recording instance.
impl Capturer {
/// Begin recording.
pub fn new(display: Display) -> io::Result<Capturer>;
/// Try to get a frame.
/// Returns WouldBlock if it's not ready yet.
pub fn frame<'a>(&'a mut self) -> io::Result<Frame<'a>>;
pub fn width(&self) -> usize;
pub fn height(&self) -> usize;
}
impl Display {
/// The primary screen.
pub fn primary() -> io::Result<Display>;
/// All the screens.
pub fn all() -> io::Result<Vec<Display>>;
pub fn width(&self) -> usize;
pub fn height(&self) -> usize;
}
impl<'a> ops::Deref for Frame<'a> {
/// A frame is just an array of bytes.
type Target = [u8];
}
The Frame Format
- The frame format is guaranteed to be packed BGRA.
- The width and height are guaranteed to remain constant.
- The stride might be greater than the width, and it may also vary between frames.
System Requirements
| OS | Minimum Requirements |
|---|---|
| macOS | macOS 10.8 |
| Linux | XCB + SHM + RandR |
| Windows | DirectX 11.1 |