vdi-cloud/docs/linux-native-cursor-20261105.md

67 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Ubuntu / 麒麟原生独立光标实施(2026-11-05)
本轮按用户要求保留系统原生光标,降低指针移动对视频更新的依赖。两台Linux都使用X11、真实2560×1440、30FPS请求、30Mbps和原有软件编码;Windows仍为原硬编路径。Ubuntu光标主题已从此前的DMZ-White恢复Adwaita/24px,鼠标加速恢复default,关闭动画的性能设置保留。
## 调研与取舍
当前Moonlight v6.2.0的普通本地光标快捷键不会同步服务端形状,不能用于本任务。[上游使用说明](https://github.com/moonlight-stream/moonlight-docs/wiki/Setup-Guide)和[维护者说明](https://github.com/moonlight-stream/moonlight-qt/issues/1386)与固定版本源码一致。Linux Sunshine X11捕获会通过XFixes读取光标后混入视频;[v0.21.0捕获源码](https://github.com/LizardByte/Sunshine/blob/v0.21.0/src/platform/linux/x11grab.cpp)已实际下载核对。
继续调编码参数无法消除指针位置对软件编码、网络、解码的等待。换SPICE/VNC/RDP虽然可采用协议原生光标,但会改变现有桌面入口、音视频和部署路径;本轮没有把更换协议当成已验证优化。两台又有不同Sunshine版本,因此采用**已认证SSH光标通道 + 精确版本固定的进程级捕获适配器 + SDL主线程原生光标**,保留现有Moonlight视频和可靠鼠标输入通道。
这与11月3日的正式控制协议设计不同:不做运行中切换,而是在新视频连接前启动无光标捕获,退出时整个捕获会话结束后才恢复普通服务。这样无需用未关联的视频帧ACK或固定延时来猜测旧光标何时消失。SSH使用每台主机独立的受限强制命令密钥和固定主机公钥;没有新增未认证UDP端口。实现、安装、生命周期和回退详见[模块说明](../terminal/native-cursor/README.md)。
[SDL光标API](https://wiki.libsdl.org/SDL2/SDL_CreateColorCursor)提供真实彩色形状和热点。[SDL2 X11源码](https://github.com/libsdl-org/SDL/blob/release-2.30.0/src/video/x11/SDL_x11mouse.c)还暴露两个适配点:Xcursor像素直接复制,必须预乘alpha;启用指针边界时窗口内warp可能不执行,因此主动位置校正使用窗口范围内的global warp。没有更换SDL系统库。
## 实现及边界
- 0005客户端补丁由构建器在现有补丁后应用;Linux主机JSON才启用功能,Windows无该JSON。profile中的客户端SHA256在远端租约前校验,旧备份客户端实测被拒绝且麒麟原服务PID未改变。fork监督进程后exec保留kiosk预期的Moonlight PID,实际测试从正常列表进入。
- 主机guard核验私有Sunshine副本和adapter SHA256;adapter仅在该进程的XFixes捕获结果中清除光标像素。原系统二进制、主题、原配置保留;租约结束删除/run中的临时服务drop-in并恢复原服务。
- 形状、热点、动画、透明隐藏、单屏几何、序号和随机租约均有边界检查;原子状态文件位于盒子0700私有目录。SDL资源只在主线程创建/切换/释放。
- 最新本地输入位置先由服务器实际指针确认;本地静止500ms且没有按钮按下时才接受服务器主动warp,避免拿迟到的输入回显回拉光标。
- 当前仅支持已配置的单座席X11/2560×1440/绝对鼠标、最多128×128像素形状。短暂停顿保留上次形状,本地位置继续移动;未知形状、源分辨率改变、同步状态超过2秒或SSH故障会结束当前串流,恢复普通服务后可从列表重连。主机租约4秒,不允许无限期保留旧状态。
- 不宣称通用多座席、相对鼠标游戏、Wayland、任意显示设备或运行中平滑降级已完成。启用模式内阻止切换为相对鼠标或固定本地箭头的快捷键;其他主机保持原逻辑。
- 原视频保留2K H.264 4:2:0及30Mbps,没有用降分辨率提高指针反馈。独立指针解决位置移动,点击后的页面和拖窗内容继续受视频链路限制。
## 联调与证据
原始证据位于[evidence/native-cursor-20261105/](evidence/native-cursor-20261105/)。最终SHA为 `5d4330…` 的两系统复测为 `ubuntu-release-probe.json`、`kylin-release-probe.json`,点击与源事件核对见 `release-click-verification.json` 及对应 `*-release-source.log`。较早 `ubuntu-v4-final-probe.json`、`kylin-final-probe.json` 等保留作修正历史,不混同最终二进制证据。
| 验证项目 | Ubuntu | 麒麟 |
| --- | --- | --- |
| 原生箭头、文字、横向缩放、忙碌、隐藏、十字、自定义共7种 | 几何/热点一致,采样像素最大差0 | 几何/热点一致,采样像素最大差0 |
| 光标附近100×100均匀视频区域 | 七种均无非均匀像素;隐藏0可见像素 | 七种均无非均匀像素;隐藏0可见像素 |
| 唯一编号点击与源事件核对 | 28/28,最大坐标误差1.055px | 28/28,最大误差0.829px |
| 合成按下至编号出现在解码画面 | P50 188.38ms / P95 522.42ms | P50 134.37ms / P95 165.56ms |
| 稳定会话短按/双击补测 | 16次40ms按住全部收到,双击计数+2 | 16次40ms按住全部收到,双击计数+2 |
| 暂停Moonlight进程时移动本地指针 | 约250ms内4个位置读回正确,形状保留 | 约250ms内4个位置读回正确,形状保留 |
| 服务端主动warp至(400,600) | 本地读回(400,600) | 本地读回(400,600) |
| 主机agent / guard被SIGKILL后恢复原服务 | 2.724s / 0.771s检查恢复 | 3.220s / 0.893s检查恢复 |
这些点击数字测量的是**页面/视频响应**,不表示本地指针移动延迟;坐标查询耗时也不是物理显示延迟。暂停视频生产者仍可移动本地指针证明了指针位置不再等待视频更新。没有测量物理USB输入、面板扫描或与Windows做相同仪器对照,因此不能宣称物理零延迟或定量等同Windows。
Ubuntu冻结盒子bridge后状态超时退出,3.289s检查无残留客户端且原服务恢复;麒麟实际改为1920×1080后自动结束会话,3.555s检查恢复原服务和2560×1440,之后从正常入口重连2K通过。焦点探针仅确认原生形状重新出现;kiosk键盘抓取下没有证据证明XSetInputFocus(None)触发了真正SDL失焦,**完整失焦场景尚未验收**。详见对应 `*-recovery.json`、`ubuntu-stale-state.json`、`kylin-resolution-change.json`、`kylin-focus.json` 和warp记录。
4项本地协议/边界/旧客户端拒绝单测通过;固定基线按顺序应用0001~0005(0002在common-c子模块)的检查通过,构建器规范化补丁CRLF后仍严格检查上下文。现场客户端保留原先源码修改并加入0005,本轮未把其他任务的0004重新构建部署;不得仅据构建器清洁应用成功宣称现场已包含0004。
最终2秒等待策略的Ubuntu持续测试(`ubuntu-final-soak.json`)完成360.003秒、65,960次实际状态读取,接收器持续存活。观察到1次“包时间戳晚于读前时间、但不晚于读后时间”的真实并发更新,支持修正校验顺序;读后未来时间0次。测试中主动暂停主机代理1.1秒,本地指针移动至(650,900),会话存活并恢复同步(`ubuntu-short-stall.json`);最大状态年龄1109.79ms包含这次故障注入,不是鼠标移动延迟。之后同一会话的七种光标和28点击复测全部匹配;再冻结bridge 2.6秒,2.904秒检查客户端已退出,原Sunshine路径和guard inactive核对通过,见 `ubuntu-final-stale-state.json`、`final-stale-recovery-services.json`。这仍是有界回归,长期稳定性未验收。
## 故障与修正
1. Ubuntu系统Sunshine带file capabilities,加载器忽略LD_PRELOAD。没有清除原文件能力;只在租约内使用相同SHA256、无额外能力的私有副本,正常退出恢复原文件。适配器映射和dlsym receipt均确认后才返回READY。
2. 麒麟KYSEC拒绝未认证新脚本;仅认证host.py、私有Sunshine副本和adapter。没有关闭全局安全策略。
3. GTK blank cursor实测是1×1、热点(1,1)。初版按可见光标边界拒绝并安全结束会话;修正为全透明图像的规范1×1/热点(0,0)。可见图像仍严格拒绝越界热点。
4. 第一轮原生边缘像素差异大,发现SDL2 X11复制未预乘像素;修正后逐像素复测,不把初版当通过。初版28次点击P50约180ms/P95约475ms,只是探索数据。
5. Ubuntu两次“强制暂停客户端→恢复→第一点击”探针首击未回显(40ms和80ms各一次),根因未确定。最终将正常输入测量与SIGSTOP实验分开,在稳定会话核对28个唯一编号及源端事件,再单独暂停客户端;稳定短按/双击补测也通过。没有证据把失败归因于GTK/窗口焦点,不得据后续通过抹去失败,或宣称任意高速输入绝不丢失。
6. 主动warp第一轮未移动,确认是SDL2在pointer barrier存在时忽略窗口内warp;改为有界global warp后再复测。
7. 最终解锁Ubuntu时弹出历史Sunshine崩溃提示。`/var/crash`中报告为11:12:07、原`/usr/bin/sunshine`、SIGTRAP;同秒服务日志为“10 seconds passed, yet Sunshine's still running: Forcing shutdown”,发生在故障恢复测试期间原服务停止的阶段,随后11:12:14恢复。该停止看门狗问题未作上游修复;不能据后续恢复称所有服务退出都无异常。原报告保留,未向外发送;元数据与日志在证据目录。
8. 普通Ubuntu桌面连续运行约4分42秒后触发“stale or mismatched lease”退出。复核发现客户端先取当前时间再打开状态文件,可与写入进程原子更新文件竞态,合法新时间戳可能大于先前读数;修正为读取完成后再取校验时间。该轮错误未区分未来时间/实际过期,不能断言该竞态是退出的唯一原因。此前详细形状/点击证据来自修正前构建。
9. 仅修正读时间顺序的构建先完成6分钟/65,658次状态读取,最大状态年龄308.10ms、无未来时间;但之后会话9分32秒时发生真实heartbeat timeout。故障发生前有持续忙碌动画和桌面应用启动,尚未确定是X11读取、调度还是SSH发送阻塞,不能只凭CPU软件编码推断唯一原因。最终将客户端/bridge等待上限调整为2秒,主机租约为4秒,并增加超过100ms的X11读取耗时诊断;本地指针位置仍立即显示,状态最多保留2秒后结束整次会话。修正版需按新阈值验证短暂停顿容忍和持续失效恢复;不把前一轮6分钟通过当成长时间稳定证明。
## 配置、备份与回退
现场客户端SHA256 `5d4330f703d7f8e053bc3795ca42bdaf231c9e9dd8ebb00a612013adbcccee03`,两个Linux profile均固定该值。私有副本:Ubuntu SHA256 `61e1ecbc8db2df786b9760c652aa4b9885dff36c57d4af09616344314cb7c34c`;麒麟 `1c3099bf924db3aee759b3d7c05e012569ef8650c400d1479144398795decc71`。adapter `cff4b7532aee69f0f205fa42946f45a89c1330486adc6358b90d13d2ccf1a0ea`。精确manifest在证据目录。未重启VM/盒子、未改PVE显卡或Windows服务端。
最终正常入口Windows为2K45/RKMPP/EGL、无原生光标环境;麒麟为2K30、原生形状恢复后正常退出,原服务active且无临时drop-in。盒子随后保留Ubuntu2K30会话,PID299860/父235482、原生Adwaita/24及default加速,实际视频源仍2K。两台GTK源程序和盒子持续探针均停止;活动Ubuntu会话的guard按设计保持active。见 `windows-final.json`、`kylin-normal-client.json`、`ubuntu-normal-client.json`、`final-services.json` 及普通桌面截图。XWD截图不含盒子原生指针,不能根据截图中没有箭头判定无光标。
盒子备份 `~/vdi-terminal/backups/native-cursor-20261105/` 有旧客户端和wrapper。回退先退出Linux会话并确认guard inactive/原Sunshine active,再把盒子对应 `~/.config/vdi-terminal/native-cursor/<IP>.json` 改名为.disabled;重连即使用原视频光标。若要恢复旧客户端,必须先停用两个JSON。Ubuntu主题原值备份仍在 `~/vdi-terminal/backups/ubuntu-quality-20261105/gsettings.json`;本轮恢复原生主题符合最新用户要求。