RemoteDesk 客户端核心
client/crates/client-core 是 Windows 主程序与原生 Helper 共用的纯策略层。它不创建设备、不读取 Windows Credential Manager,也不持有远程帧或密码。
多显示器布局
ClientDisplayPlanner 接收协议层已经验证过的 DisplayLayout 和绑定同一远端 topology generation 的 DisplaySelection:
DisplaySelection::single选择一个远端显示器。DisplaySelection::all选择完整远端桌面。DisplaySelection::custom按用户顺序选择一个子集。DisplayResolution::{SourceNative, FollowWindow, Fixed}控制合成画布尺寸。
远端显示器允许负坐标。规划器保留原始 source_rect,同时生成从 (0, 0) 开始的 normalized_source_rect 和缩放后的 output_rect。混合 DPI 的 DisplayScale 按显示器保留;若选择未包含远端主屏,第一个被选显示器成为该计划的有效主屏。
规划前必须提供 DisplayPlanLimits。显示器数量、源像素总量和缩放后像素总量超过限制都会失败,不会让 Helper 先分配大表面再报错。
同一标准化布局会生成明确的后端命令:
RdpDisplayControlPlan将主屏放在数组首项并以主屏(0, 0)生成允许负坐标的 RDP Dynamic Monitor Layout。LinuxNativeDisplayPlan指定 Agent 捕获显示器、主屏和编码画布;它只调整编码/合成尺寸,不修改 Wayland/Xorg 实体显示模式。
规划器的 client generation 从 1 开始。远端 topology/选择、分辨率、本地窗口 output/DPI、GPU pipeline 或后端发生变化时 generation 递增并返回 DisplayRevalidationReason;完全相同的请求复用 generation。失败请求不推进 generation。Helper 回调和 path report 必须绑定当前 generation,旧结果应丢弃。
严格零拷贝继续由 GpuPolicy::RequiredEndToEnd 控制。本地窗口所在 output 的 DXGI Adapter 是 display adapter;decoder、renderer 或手动 Adapter 与它不一致时,RDP 和 Linux native 计划都拒绝。Compatibility 才允许生成带 cross-adapter copy 标记的计划。RDP 服务端路径是否零拷贝仍需真实 IronRDP/ETW 验证,客户端计划本身不能作此证明。
RDP 凭据
HostProfile 只能保存 HostProfileCredentialBinding,其中只有 CredentialRef 和非秘密状态,不包含用户名或密码。引用必须位于 RemoteDesk/RDP/<profile-key> 命名空间,profile key 仅允许 ASCII 字母、数字及 -_.:@,空值、外部命名空间、控制字符和超长值会被拒绝。
client/helpers/credential-store 使用 Windows Credential Manager generic credential,并通过 CredentialRef::target() 精确指定应用条目。独立本地控制台遮罩录入账号和密码;React/HTTP、控制服务和 viewer 命令行只传引用。IronRDP viewer 直接读取凭据并在进程内构造 NLA 配置,密码不会写回 client-core 状态、日志、命令行或序列化 HostProfile。
CredentialRef 和 HostProfileCredentialBinding 的 Debug 输出会隐藏 target。RdpCredentialStatus 只表达 Unchecked、Missing、Ready 或 NeedsUserUpdate。
验证
在仓库根目录运行:
$taskTemp = (Resolve-Path 'target\tmp').Path
$env:TEMP = $taskTemp
$env:TMP = $taskTemp
cargo test -p remotedesk-client-core
cargo clippy -p remotedesk-client-core --all-targets -- -D warnings