* test: remove tests that do not pin user-visible behavior Apply the AGENTS.md test rules to the existing Rust and Flutter suites. A test is kept when it is the only coverage of user- or API-visible behavior, the regression test of a reported or fixed bug, a security boundary (one test per rejection class), or a check against an external reference. Tests that assert private state or the order of internal steps, pin review-round corner cases, or repeat what a kept test already covers are removed. Rust: 679 -> 298 tests. Dart: 118 -> 45 tests. Test-only helpers left without callers go with them (applyLastOnlineStateForTesting, terminalKeyboardRowWidth, debugBufferedInputCount, tokio's test-util dev feature), and cfg gates and visibility that existed only for the deleted tests are narrowed. Runtime behavior is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WS1HTjp6UQuG43KEcQPq1R * test: keep tests that pin security and user-visible behavior Restore six tests that review showed are the only coverage of their behavior: - update metadata rejects an artifact file name with path components; - ICE candidates from another session are dropped; - KCP delivers every multi-segment frame queued before a close; - set_files rejects an empty name in a multi-file transfer; - a lopsided elongated cursor keeps its centre hotspot (#16242); - toggling Show hidden during a pending same-path read issues a new read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WS1HTjp6UQuG43KEcQPq1R * test: keep offline update and key-exchange downgrade rejections Restore three tests that are the only coverage of their rejection class: - offline update metadata rejects a signed artifact with an http or credential-bearing URL; - offline verification rejects a renamed local file and a version that does not match the release; - a key-exchange pick lowered in transit leaves both sides unable to decrypt, so a v0 fallback after a failed v1 decrypt cannot pass unnoticed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WS1HTjp6UQuG43KEcQPq1R * test: keep the decomposed-grapheme and reconnect-cancel tests Each catches a plausible wrong change that passes every kept test: mapping the first scalar under a locked Ctrl turns é into \x05, and a reconnect that does not fail pending reads leaves them hanging until the 30 s timeout. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WS1HTjp6UQuG43KEcQPq1R --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
clipboard
Copy files and text through network. Main low-level logic from FreeRDP.
To enjoy file copy and paste feature on Linux/OSX,
please build with unix-file-copy-paste feature.
TODO: Move this lib to a separate project.
How it works
Terminologies:
- cliprdr: this module
- local: the endpoint which initiates a file copy events
- remote: the endpoint which paste the file copied from
local
The main algorithm of copying and pasting files is from Remote Desktop Protocol: Clipboard Virtual Channel Extension, and could be concluded as:
-
local and remote notify each other that it's ready.
-
local subscribes/listening to the system's clipboard for file copy
-
local once got file copy event, notice the remote
-
remote confirms receive and try pulls the file list
-
local updates its file-list, the remote flushes pulled file list to the clipboard
-
remote OS or desktop manager initiates a paste, making other programs reading clipboard files. Convert those reading requests to RPCs
- on Windows, all file reading will go through the stream file API
- on Linux/OSX, FUSE is used for converting reading requests to RPCs
- in case of local clipboard been transferred back and leading to a dead loop, all file copy event pointing at the FUSE directory will be ignored
-
finishing pasting all files one by one.
In a perspective of network data transferring:
sequenceDiagram
participant l as Local
participant r as Remote
note over l, r: Initialize
l ->> r: Monitor Ready
r ->> l: Monitor Ready
loop Get clipboard update
l ->> r: Format List (I got update)
r ->> l: Format List Response (notified)
r ->> l: Format Data Request (requests file list)
activate l
note left of l: Retrieve file list from system clipboard
l ->> r: Format Data Response (containing file list)
deactivate l
note over r: Update system clipboard with received file list
end
loop Some application requests copied files
note right of r: application reads file from x to x+y
note over r: the file is the a-th file on list
r ->> l: File Contents Request (read file a offset x size y)
activate l
note left of l: Find a-th file on list, read from x to x+y
l ->> r: File Contents Response (contents of file a offset x size y)
deactivate l
end
Note: In actual implementation, both sides could play send clipboard update and request file contents. There is no such limitation that only local can update clipboard and copy files to remote.
impl
windows
The protocol was originally designed as an extension of the Windows RDP, so the specific message packages fits windows well.
When starting cliprdr, a thread is spawned to create an invisible window
and to subscribe to OLE clipboard events.
The window's callback (see cliprdr_proc in src/windows/wf_cliprdr.c) was
set to handle a variety of events.
Detailed implementation is shown in pictures above.
Linux/OSX
The Cliprdr Server implementation has mainly 3 parts:
- Clipboard Client
- Local File list
- FUSE server
Clipboard Client
The clipboard client has a thread polling for file urls on clipboard.
If the client found any updates of file urls, after filtering out those pointing to our FUSE directory or duplicated, send format list directly to remote.
The cliprdr server also uses clipboard client for setting clipboard, or retrieve paths from system.
Local File List
The local file list is a temporary list of file metadata. When receiving file contents PDU from peer, the server picks out the file requested and open it for reading if necessary.
Also when receiving Format Data Request PDU from remote asking for file list, the local file list should be rebuilt from file list retrieved from Clipboard Client.
Some caching and preloading could be done on it since applications are likely to read on the list sequentially.
FUSE server
The FUSE server could convert POSIX file reading request to File Contents Request/Response RPCs.
When received file list from remote, the FUSE server will figure out the file system tree and rearrange its content.
Groceries
-
The protocol was originally implemented for windows, so paths in PDU will all be converted to DOS formats in UTF-16 LE encoding, and datetimes will be converted to LDAP timestamp instead of unix timestamp
UNIX /usr/bin/rustdesk -> DOS \usr\bin\rustdesk -
To better fit for preserving permissions on unix-like platforms, a reserved area of FileDescriptor PDU
-
you may notice the mountpoint is still occupied after the application quits. That's because the FUSE server was not mounted with
AUTO_UNMOUNT.- It's hard to implement graceful shutdown for a multi-processed program
AUTO_UNMOUNTwas not enabled by default and requires enableuser_allow_otherin configure. Letting users edit such global configuration to use this feature might not be a good idea.- use
umount()syscall to unmount will also require that option. - we currently directly call
umountprogram to unmount dangling FUSE server. It worked perfectly for now.


