开源CAD软件LibreCAD完整移植到浏览器:如何用JSPI解决对话框嵌套难题

开源CAD软件LibreCAD完整移植到浏览器:如何用JSPI解决对话框嵌套难题

_

LibreCAD是一个免费开源的2D CAD工具,支持读写DXF和DWG文件,具备图层、块、标注、填充等常用功能。它基于C++和Qt构建,原本只能作为桌面应用运行。如今有人将它完整移植到了浏览器里——不是阉割版或网页视图,而是真正的桌面程序在网页标签页中运行。首次加载约需18MB(经Brotli压缩),之后会缓存在浏览器中。

为什么移植这么难

LibreCAD这类传统桌面应用依赖QDialog::exec()这样的阻塞调用:打开对话框后,主程序会停在那一行,等待用户关闭对话框才继续往下走。这种模式在桌面上天经地义,但在浏览器里行不通——JavaScript是单线程的,主线程被“卡住”就意味着整个页面冻结。

最初Emscripten提供的解决方案是Asyncify(异步化),它能“暂停”正在等待的函数,把控制权交还给浏览器,稍后再恢复。Qt官方也推荐这种方式。然而Asyncify只能处理单层嵌套——第一个对话框能正常工作,但一旦你在对话框里再点开一个下拉菜单或颜色选择器,第二层暂停就无处可去,整个应用直接卡死。

JSPI才是关键

真正解决问题的技术叫JSPI(WebAssembly JavaScript Promise Integration),这是浏览器原生支持的挂起机制,与Asyncify不同,它允许任意深度的嵌套调用。Qt 6.9版本开始正式支持JSPI,但默认预编译包并未启用。移植者需要从源码编译Qt,开启-fwasm-exceptions和设备选项QT_EMSCRIPTEN_ASYNCIFY=2。

但这又带来了新问题:JSPI只允许通过“promising”入口函数进入的栈帧挂起,而默认只有main()被标记为promising。一旦main()返回(网页应用必须如此),所有后续事件都运行在普通栈上,尝试挂起就会报错。

最终的解决方案需要三个改动协同配合:将Qt的DOM事件处理器注册为emscripten::async()函数,让每个鼠标和键盘事件都运行在promising栈帧内;用同样方式包装Qt的定时器和事件队列回调;把main()重构为异步形式——创建应用后直接返回,由浏览器驱动整个事件循环。完成这些调整后,任意深度的对话框嵌套都能正常工作。

对普通用户意味着什么

现在可以直接打开浏览器标签页使用完整的LibreCAD,无需安装任何软件,对临时性绘图需求非常方便。不过目前仅支持Chrome 137+或Edge 137+等Chromium内核浏览器,Firefox和Safari尚未支持JSPI特性。如果你在使用Mac或手机,短期内可能还需要等待浏览器更新。

编注:信源为技术博客(magik.net),作者详细记录了移植过程中的技术挑战与解决方案,重点在于WebAssembly异步机制的技术细节,未涉及LibreCAD功能本身或用户使用体验。


开源自建扫地机器人:OOMWOO 用模块化设计打破商业封闭 2026-07-02
Fable 5重新上线当天即被绕过:简单提示即可诱导AI协助网络攻击 2026-07-02