Skip to content

feat(gui): VM GUI 執行中動態解析度——guest 側 resize watcher(vz/WHP 共用)#128

Merged
TimLai666 merged 2 commits into
mainfrom
feat/vm-gui-dynamic-resolution
Jul 11, 2026
Merged

feat(gui): VM GUI 執行中動態解析度——guest 側 resize watcher(vz/WHP 共用)#128
TimLai666 merged 2 commits into
mainfrom
feat/vm-gui-dynamic-resolution

Conversation

@TimLai666

@TimLai666 TimLai666 commented Jul 11, 2026

Copy link
Copy Markdown
Owner

摘要

讓 VM GUI(WHP 與 macOS vz 共用的 cage kiosk compositor)支援執行中動態解析度:host 視窗縮放 → guest 真正 re-modeset。補上 #127 實測後記載的 guest 端缺口(「cage 啟動後不跟模式變更;修法在 guest 側」「翻預設 gated on re-modeset + HiDPI output scale」的前半)。

本 PR 原與 #127 平行開發、重疊的四個修復(extract exec bit、initramfs busybox 架構、vz 埠代理搶埠、vz-smoke httpd/cargo fallback)已在 rebase 時去重,一律採 main 已合併的版本;vz 埠轉發沿用 #127 的「runtime proxy 在 macOS 讓位」設計。

實作

  • guest-agent resize watcher(新 resize.rs,零新依賴):netlink 直聽 drm uevent(不依賴 udevd、debounce 收斂)→ GETCONNECTOR count_modes=0 首呼叫觸發 kernel re-probe 取新 preferred mode(sysfs 只反映上次 probe)→ wlr-randr --custom-modewlr-output-management 要 cage 換模式(custom mode 繞過 cage 端過期 mode list)。同尺寸抑制;連續失敗自動停用;舊 overlay/舊 initramfs 相容(which() PATH 退路、缺 wlr-randr 即回到既有縮放行為)。
  • 為何是這條路:cage 0.2.0(Alpine 3.21+)已內建 wlr-output-management——「cage 沒有該協定」是 0.1.5 時代資訊;wlroots 上游 request_state 通知(MR 5182)仍是 draft。不換 compositor、不 fork、不 backport wlroots;等上游落地後 watcher 自動變冗餘。
  • GUI overlay:Alpine 3.20 → 3.22(cage 0.1.5 → 0.2.0),附 wlr-randr。
  • vz 預設不變(維持 feat: 容器內 DNS(全後端)——resolv.conf 注入 + WHP 10.0.2.3 DNS pivot;macOS vz 實機 follow-ups #127 定案的「點數 scanout + view 縮放」——Retina 像素模式讓 Linux guest UI/游標縮半);CHEFER_VZ_DYNAMIC_RESOLUTION=1 的 opt-in 因本 watcher 成為全鏈可用。WHP 無 opt-in 閘,拖拉即走 watcher。HiDPI output scale(翻預設的另一半)另開後續。
  • vz-helperCHEFER_VZ_GUI_TEST_RESIZE=<秒>:<W>x<H> 測試鉤子(程式化 setContentSize,與拖拉同路徑、僅動態解析度模式生效)→ vz-smoke 可免拖拉自動驗證。
  • 附帶修復:appliance init 未 export PATH(PID 1 環境無 PATH → guest-agent 找不到 overlay 工具,剪貼簿/動態解析度被靜默停用);watcher 子行程改自行收 stdout/stderr(supervisor waitpid(ANY) 會搶先收屍使 Command::output() 得 ECHILD 誤報失敗——實機 e2e 抓到)。

實機驗證(Apple Silicon,macOS 26)

  • CHEFER_VZ_DYNAMIC_RESOLUTION=1 + 測試鉤子:t+20s 縮放 1100×700pt → console gui: resize: Virtual-1 -> 2200x1400 → guest 內 xdpyinfo dimensions 2560×1600 → 2200×1400 跟隨(Retina 2×)✓
  • Alpine 3.22 overlay 的 cage 0.2.0 開機/算繪正常(xclock,多輪 GUI e2e)✓
  • vz-smoke 自動斷言(開機/exit code/服務常駐/TCP 轉發)在本批程式碼上全過 ✓
  • 單元測試:workspace 全綠 + guest-agent 全部測試於 aarch64 musl 實跑(Alpine 容器);clippy/fmt 乾淨 ✓

風險與待辦

  • WHP 實機回歸待下次 Windows 機會(guest 側共用本 watcher;驗法見 DESIGN M8-d)。
  • overlay 整包升 3.22(mesa 25.1、wlroots 0.18):vz 實測通過,QEMU/WHP 理論等價。
  • HiDPI output scale(修 opt-in 模式游標/UI 縮半、翻預設的前置)另開後續任務。

🤖 Generated with Claude Code

TimLai666 and others added 2 commits July 12, 2026 00:15
…ot found

kernel 給 PID 1 的環境沒有 PATH;init 只賦值不 export,guest-agent 及其子行程
的環境便沒有 PATH——以 PATH 尋找 GUI overlay 工具(wl-copy/wlr-randr)全部
「not found」,剪貼簿與動態解析度被靜默停用(實機 vz e2e 抓到)。
guest-agent 的 which() 另補預設路徑退路(隨 feat commit),讓舊 initramfs 也能運作。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
host 端早已把新尺寸推進 guest(WHP 的 SharedResize 中斷鏈、vz 的
automaticallyReconfiguresDisplay),但 cage 不會因「已連線 connector 的模式清單
變更」re-modeset——wlroots 的 preferred-mode-change → request_state 通知在上游
仍是未合併 draft(MR 5182,目標 0.20),Alpine 出貨版短期拿不到;replug 模擬
又會讓 cage 拆輸出、觸發 fail_fast(見 DESIGN M8-d)。

guest-agent 新增 resize watcher(resize.rs,零新依賴):
- netlink 直聽 kernel uevent(不依賴 udevd),drm change 事件 debounce 收斂;
- DRM_IOCTL_MODE_GETCONNECTOR 的 count_modes=0 首呼叫觸發 kernel re-probe 取
  新 preferred mode(sysfs modes 只反映上次 probe,讀不到新值);
- wlr-randr --custom-mode 經 wlr-output-management 要求 cage 換模式(custom
  mode 繞過 cage 端過期的 mode list);同尺寸抑制、連續失敗自動停用(舊
  overlay 相容:回到 host 端縮放的既有行為);
- 子行程不能用 Command::output():supervisor 以 waitpid(ANY) 監督服務,會把
  wlr-randr 搶先收屍讓 output() 得 ECHILD 誤報失敗(實機 e2e 抓到)——自行讀完
  stdout/stderr,wait 遇 ECHILD 以 stderr 是否為空判定;
- which() 在 PATH 未設時退回標準路徑(舊 initramfs 的 init 沒 export PATH)。

GUI overlay 升 Alpine 3.20 → 3.22:cage 0.2.0 才有 wlr-output-management
(0.1.5 沒有),另附 wlr-randr。vz 維持 main 既定的預設取捨(scanout=點數、
view 縮放——Retina 像素模式會讓 Linux guest UI/游標縮半,翻預設 gated on
HiDPI output scale);CHEFER_VZ_DYNAMIC_RESOLUTION=1 的 opt-in 因本 watcher
成為全鏈可用。vz-helper 加 CHEFER_VZ_GUI_TEST_RESIZE 測試鉤子(程式化
setContentSize,與拖拉同一條顯示重組態路徑,僅動態解析度模式生效),vz-smoke
--gui 據此可免拖拉驗證;另在有 Docker 時現建新 overlay、恢復動態解析度檢核項。

實機驗證(Apple Silicon,macOS 26):CHEFER_VZ_DYNAMIC_RESOLUTION=1 下
t+20s 程式化縮放 1100x700pt → console「gui: resize: Virtual-1 -> 2200x1400」
→ guest 內 xdpyinfo dimensions 2560x1600 → 2200x1400 跟隨(Retina 2x)。
WHP 側裝置/中斷鏈先前已實機驗證,guest 側共用本 watcher,回歸驗證待下次
Windows 實機(WHP 無 opt-in 閘,拖拉即觸發)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@TimLai666
TimLai666 force-pushed the feat/vm-gui-dynamic-resolution branch from 440800e to 5a12748 Compare July 11, 2026 16:16
@TimLai666 TimLai666 changed the title feat(gui): VM GUI 執行中動態解析度 + 實機 vz 驗證挖出的四個修復 feat(gui): VM GUI 執行中動態解析度——guest 側 resize watcher(vz/WHP 共用) Jul 11, 2026
@TimLai666
TimLai666 merged commit 95e3581 into main Jul 11, 2026
18 checks passed
@TimLai666
TimLai666 deleted the feat/vm-gui-dynamic-resolution branch July 11, 2026 17:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant