Air 系列应用交流/AirSCAD(from OpenSCAD)|AirSCAD · 鸿蒙 PC 适配全记录

AirSCAD(from OpenSCAD)|AirSCAD · 鸿蒙 PC 适配全记录

【Air 系列 · 鸿蒙 PC 适配全记录】38 / 56

■ 应用:AirSCAD(from OpenSCAD)(AirSCAD)
■ 定位:OpenSCAD 移植,代码式 3D 建模
■ 上游:OpenSCAD 1.0.0-master | 路线:Qt6+gl4es | 版本:1.4.0
■ 开源: https://gitcode.com/Andy88/Harmony-OpenSCAD
■ 市场状态:已上架 AGC 应用页: https://appgallery.huawei.com/app/6917615344438092131
■ 官网(免费下载): https://www.shunidc.cn/

————————————————————
开源鸿蒙 PC 社区: https://harmonypc.csdn.net/

PC 社区项目申请: https://atomgit.com/OpenHarmonyPCDeveloper

适配开源地址:https://gitcode.com/Andy88/Harmony-OpenSCAD

环境搭建文章:https://blog.csdn.net/weixin_52908342/article/details/161343743

▍一、为什么要适配 AirSCAD

AirSCAD 的移植对象是 OpenSCAD(1.0.0-master)——程序员圈子里的经典 constructive solid geometry 建模工具:左侧用 QScintilla 编辑 .scad 脚本,右侧 3D 视口实时渲染几何体,F5 预览、F6 出最终几何,导出 STL/OFF/AMF/WRL/SVG/PDF。它是"代码即建模"理念的代表作,也是对移植工程要求最高的一类应用:GUI、文本编辑、OpenGL 渲染、几何计算内核、gettext 国际化一个都不能少。

选择它上鸿蒙 PC 的理由很直接:CAD 是 2in1 形态下键鼠 + 触控双模最有价值的场景之一,而 OpenSCAD 的 Qt 技术栈与系列工程验证过的 Qt6 QPA 路线天然契合。工程依赖矩阵显示,Qt 6.11.1、libqohos QPA、gl4es(Maleoon GPU 的 GLES 翻译层)、QScintilla 2.14.1 均复用共享安装栈,本仓库只新编 manifold、OpenCSG、Clipper2 与 airlibintl;CGAL 头文件与 lib3mf 明确不编,几何内核以 Manifold 为默认。

当前工程包名 com.airscad.app,版本 1.1.0,deviceTypes 覆盖 phone、tablet 与 2in1,Ability 声明 launchType: singleton 防止双实例双 Qt 线程,权限仅 READ_PASTEBOARD(剪贴板入向门控轮询用)。PORTING.md 记载主验机为 MateBook Pro(2in1),另有 MatePad 11.5 与 MatePad Mini 悦读版,M0 至 M4 五轮会话的全部修复均以真机实证收口。

▍二、先划清边界:不把编译通过当成适配完成

OpenSCAD 原版是一个桌面 GUI 程序:main() 直接拉起 QApplication 与主窗口,渲染走桌面 OpenGL,对话框、菜单、剪贴板全部直连系统能力。鸿蒙侧不能给它进程与桌面环境,工程的做法是把 GUI 入口改造成共享库(GUI→SHARED),由 ArkTS 壳经 NAPI 拉起;桌面系统服务则逐个换成 DocumentViewPicker、ArkTS 菜单栏与 pasteboard 桥。换不动或刻意不做的,在 PORTING.md 遗留节如实登记。

层次 OpenSCAD 原版(桌面) 鸿蒙侧当前方案
应用入口 桌面 main() 进程 Stage EntryAbility + ArkTS 壳(singleton)
窗口/视口 Qt 主窗口 + 桌面 OpenGL XComponent(TEXTURE) + libqohos QPA + gl4es
菜单系统 QMenuBar 原生渲染 菜单树导出 JSON → ArkTS 单级拍平分组渲染回注
代码编辑 QScintilla 直连键盘输入 同一 QScintilla + ArkTS 1×1 IME 落点 + 影子 diff
几何内核 CGAL / Manifold 双内核 Manifold 内核(CGAL 不编),预览 Throwntogether 回退
文件对话框 QFileDialog 原生 DocumentViewPicker 全模式分流 + staging 回拷
汉化 gettext 运行时 + .mo airlibintl + LOG 中央钩子,po 共 729 条已译
剪贴板 系统剪贴板直连 Qt↔pasteboard 双向桥(READ_PASTEBOARD 门控)

这条边界里最容易被误读的是 F5 预览:本设备构建以 Throwntogether 渲染器回退(OpenCSG 桌面 GLSL 着色器经 gl4es 翻译层会触发 SIGSEGV,遗留节登记),几何体真实上屏但差集对象以 CUTOUT 高亮叠加,无 z-buffer CSG。工程没有假装 F5 与桌面完全等价。

▍三、鸿蒙版的整体架构

PORTING.md 记载的总体架构是标准 Air 双层:ArkTS 壳负责窗口、菜单栏、IME 落点与暗黑钉死;NAPI 薄壳按拓扑序 dlopen 全部动态库后把控制权交给 core;core 在 Qt 宿主线程组装 QApplication 与 OpenSCAD 主窗口。

text
ArkTS 壳(entry/src/main/ets)
│ EntryAbility:setColorMode(DARK) 钉死暗黑(onConfigurationUpdate 判空回环防递归)
│ 顶栏 ArkTS 菜单栏:QMenuBar 树导出 JSON → 单级拍平分组渲染
│ 激活经 activateMenuAction 回注 QAction(GUI 线程 BlockingQueued 导出防竞态)
│ XComponent(TEXTURE) —— Qt/OpenSCAD 原生窗口
│ 1×1 透明 TextInput —— IME 落点(增量 diff 提交 op:0 文本/1 退格/2 回车)
│ NAPI:libscadbridge.so(薄壳,仅链系统库)
▼ OnSurfaceCreated 按拓扑序 dlopen 全部 .so(RTLD_GLOBAL)→ dlsym core API
libairscad_core.so(hvigor CMake 编译:Qt 宿主线程 + 菜单导出/回注 + 输入转发 + 沙箱目录)
├── Qt 6.11.1 栈(libQt6Core/Gui/Widgets/... + libqohos.so QPA + libGL.so gl4es)
├── libopenscad_gui.so(GUI→SHARED:openscad_ohos_main → QApplication + MainWindow)
└── 运行时数据(examples、color-schemes、shaders、zh_CN.mo → rawfile 部署)


主要目录如下:

text
AirSCAD/
├── AppScope/app.json5 # 包名 com.airscad.app、版本 1.1.0
├── PORTING.md # M0–M4 交付记录、依赖矩阵、标准对照、遗留登记
├── ai_log/ # 各轮排障实录(M0 12 条根因等)
├── entry/src/main/
│ ├── ets/pages/Index.ets # ArkTS 壳:菜单栏、XComponent、IME 落点、picker 桥
│ └── cpp/
│ ├── napi_init.cpp # NAPI 桥:dlopen 拓扑序、输入/选择器/剪贴板透传
│ ├── airscad_core.cpp # Qt 宿主线程、菜单导出/回注、沙箱目录
│ └── qt_platform/ # QPA 相关构建输入
└── native/
├── scripts/ # env.sh、build_openscad.sh、deploy.sh
└── sources/ # openscad、manifold、OpenCSG、Clipper2、airlibintl、glu_ohos


▍四、核心适配过程

### 1. GUI→SHARED 与构建流水线

OpenSCAD 的 GUI 入口被改造成 libopenscad_gui.so,暴露 openscad_ohos_main;native/scripts/build_openscad.sh 负责编出它并应用 P1–P5 补丁,deploy.sh 收集 .so 栈与 rawfile(examples 整目录递归、zh_CN.po 现场_msgfmt_)并跑 ELF 三件套;entry/src/main/cpp 经 hvigor CMake 编出 libscadbridge 与 libairscad_core。流水线有个以血泪换来的细节:build 产物必须再跑 deploy 才会落进 entry/libs/arm64-v8a,否则 hvigor 打包旧库——PORTING.md 明文记录了"因此白烧一轮回归"的教训,且 build_openscad.sh 加了 pipefail,防止 ninja 失败被 tee 管道吞掉后旧产物照样部署。

### 2. ArkTS 菜单栏与硬件键盘全链

Qt 侧把 QMenuBar 树导出为 JSON,任意深度子树拍平为「前缀 ▸ 组头」单级分组(规避 ArkUI Menu 嵌套点击死区),激活经 activateMenuAction 回注 QAction,导出整体搬 Qt 主线程 BlockingQueued 防竞态。PC 硬件键盘链路则打通了四层根因:napi 侧修饰键自跟踪生成 metaState(Ctrl/Shift/Alt 位),有硬件键盘时跳过软键盘落点焦点,TEXTURE 型 XComponent 收不到硬件键就改走页面 focusable+defaultFocus+onKeyEvent 焦点链转发,QPA 的 update() 门控改为无条件重申。MateBook Pro 实证:直打上屏并触发代码补全,Ctrl+A/C/V 跨进程剪贴板往返(775/774 字符)完整。

### 3. IME 落点与文本提交链

软键盘输入经一个 1×1 透明 TextInput 作 IME 落点,增量 diff 提交(文本/退格/回车三类 op)到 Qt。这条链在 M4 被大粘贴打穿:QScintilla 的 GetMonitorRect 空指针导致补全弹窗闪退(faultlog 定案);修掉闪退后,QInputMethodEvent 长提交又被逐字处理成 CHARADDED 弹窗风暴,解法是提交超 32 字符改走"写剪贴板 + 向焦点对象发 Ctrl+V"的粘贴语义,整块原子插入;影子文本 diff 从"全删重插" fallback 重写为公共前后缀最小编辑,删除路径同步收缩影子防"已删字符复活";IME 落点也从屏外坐标改为屏上 1×1(MatePad Mini 实证系统拒绑屏外组件),并去掉 imeVisible 闩锁。

### 4. 渲染:gl4es 与 CSG 几何链

QGLView/OpenCSG/napi 三处的 glad 装载改走 gl4es(桌面 GL 符号劫持根因),坐标轴与 CSG 几何体得以在 Maleoon GPU 上屏。F6 走 Manifold 内核,验收记录为三组 CSG 几何体 1090 顶点/2188 面、真实孔洞实体与流形统计;F5 预览在 M3 修复了"csgRoot 求值被 ENABLE_OPENCSG 守卫、Throwntogether 恒拿空产品表"的根因,探针 16416 顶点、0 GL 错误,视觉与探针双通道验证。桌面 GLSL 的 OpenCSG 双算法路径因 gl4es 翻译层 SIGSEGV 保持 Throwntogether 回退,属登记遗留。

### 5. 选择器、保存与剪贴板系统桥

文件选择以 5 参契约(reqId+mode+filters+suggest+ud)全链贯通,mode 0/2/3/4 分流打开/保存/选目录/多选,nameFilters 映射 fileSuffixFilters。保存链路是 ArkTS staging 方案:保存型 picker 给出系统 URI,Qt 写沙箱中转文件,watcher 轮询稳定后回拷并解除中转;上游 TabManager 的 QSaveFile 在沙箱 commit 阶段被拒,改为 QFile 直写保留状态检查。剪贴板双向桥:出向 setMimeData 直达系统粘贴板,入向 pasteboard.on('update') 加轮询,且必须过 checkAccessToken 权限门控——未门控的 2 秒轮询曾造成 READ_PASTEBOARD 弹窗风暴。

▍五、适配中遇到的几个难点

### 难点一:输入状态码契约错位,鼠标 press 从未真正工作

QPA 的 QOhos_InjectMouse state 契约是 0=MOVE/1=DOWN/2=UP,触摸是 0=Pressed/1=Released/2=Updated,而 napi 侧是 0=DOWN/1=MOVE/2=UP,core 直通导致按下被当移动、移动被当按下。此前所有依赖 Qt 鼠标 press 的功能——点击定位光标、拖拽、消息框按钮——从未真正工作过,早期验收恰好走 ArkTS 菜单与 IME 链才没暴露。修复是 core 显式映射表,验证用"点击第 7 行定位光标 + 键盘弹出 + 分隔条拖拽 940→1400"三重视觉证据。

### 难点二:DPI 断链让界面小到不可用

用户报告"界面太小"的根因是 QPA 默认 dpr 恒 1.0——AirCad 里负责注入显示指标的 QOhos_SetDisplayMetrics 单点通道在本工程断链,Qt 逻辑像素等于物理像素,280ppi 屏上文字不足 1 毫米。修复是 Index.ets 读 display.densityPixels/densityDPI 经 napi 透传 QPA(guiStart 前),dpr 以 densityPixels 单源,视觉验证 Qt 文字从 15px 到 25-30px,与 ArkTS 菜单近 1:1。

### 难点三:大粘贴的闪退、冻结与复活三连

一次 9.6KB 粘贴暴露三层独立缺陷:QScintilla 平台层 GetMonitorRect 无 screenAt 判空直接 SIGSEGV;改用输入法事件提交后,长文本被逐字处理触发 CHARADDED 弹窗风暴冻结界面;影子文本的"全删重插"fallback 又让光标不在文末时整段历史重复插入。最终形态是三段式修复:判空、超长提交走剪贴板+Ctrl+V 粘贴语义、公共前后缀最小编辑加删除路径影子同步。验收是同一份 9.6KB 注入单事件落盘零截断,粘贴后连输四个右括号每击只追加一字符。

### 难点四:沙箱里 fork/exec 被禁

core 初始化用 system("mkdir -p") 建沙箱目录,在 OHOS 上静默失败——应用沙箱禁 fork/exec——HOME/.local/share 不存在导致 backupPath/userPath 返回空串,F5 阶段 create_directories("") 报 ERROR。改为 std::filesystem::create_directories 根治。同族问题还有 musl 无 gettext:airlibintl 承接 AirPS 的 libintl 方案,且上游 LOG() 不经 gettext,控制台汉化另做中央钩子加 254 条词条,解析器/求值器警告错误再批量补 207 条,po 累计 729 条已译,译文中的 printf 格式符(%s/%d 等)经脚本级全检。

### 难点五:启动即弹键盘盖住半个建模视口

用户报告"建模页面异常"实为软键盘整幅盖屏:MainWindow 初始焦点触发 QPA 请求开键盘,基线截图里控制台与编辑器底部全被遮住。修复是启动期 5 秒时窗丢弃 IME 开面板请求,点击编辑器仍正常弹;配合 DPI 与沉浸式修复后,启动态是编辑器 + 3D 视口 + 控制台三栏完整可见的暗黑界面。

▍六、编译、安装与启动

Native 侧按 PORTING.md 记载的流水线执行,再走标准 hvigor 打包:

bash
cd native/scripts
bash env.sh # OHOS NDK(AirChat-SDK24)交叉环境

bash build_openscad.sh # libopenscad.so(P1~P5 补丁,pipefail)
bash deploy.sh # .so 栈 + rawfile(examples 递归 + 源 po 现场 msgfmt)+ ELF 三件套
cd ../..


ArkTS/HAP 侧:

bash
export JAVA_HOME=/Applications/DevEco-Studio.app/Contents/jbr/Contents/Home
export DEVECO_SDK_HOME=/Applications/DevEco-Studio.app/Contents/sdk

hvigorw --mode module -p module=entry assembleHap --no-daemon


产物位于 entry/build/default/outputs/default/entry-default-signed.hap。安装启动:

bash
hdc install -r entry/build/default/outputs/default/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.airscad.app


注意 deploy.sh 必须在 hvigor 之前执行完整,否则打包的是旧 .so。PORTING.md 验收清单记载 M3 在 MateBook Pro 上完成 F5/F6 渲染、控制台汉化、状态栏、PC 标题栏隐藏等全项通过,双机(MatePad 11.5 + Mini)完成 M2/M4 回归。

▍七、当前功能边界

当前版本已覆盖的能力:

- 双层架构可安装 HAP:ArkTS 壳 + libscadbridge/libairscad_core + libopenscad_gui.so;
- 编辑器(QScintilla 语法高亮、代码补全)+ 3D 视口 + 控制台三栏暗黑界面;
- F5 预览(Throwntogether 回退)与 F6 Manifold 渲染真实出几何,导出 STL 端到端落盘;
- ArkTS 菜单栏全量汉化(6 顶级 + 全部二级),菜单回注触发 F5/F6/新建;
- PC 硬件键盘全链(修饰键 metaState、Ctrl+A/C/V 剪贴板往返)与触控双指捏合/平移;
- 软键盘 IME 链:增量 diff、大粘贴原子插入、域光标归位、启动键盘抑制;
- 系统选择器全模式(打开/保存/目录/多选)+ staging 回拷,另存为端到端;
- 剪贴板文本双向系统桥(权限门控),光标样式 QPA→PointerStyle 映射;
- 示例菜单(基础/高级/函数/旧版/参数化)、color-schemes、单实例启动。

尚未覆盖或已登记的边界:

- OpenCSG 双算法预览保持 Throwntogether 回退(gl4es 翻译层 SIGSEGV 待修,F6 不受影响);
- 鼠标拾取/测量随 OpenCSG 关闭一并禁用(依赖 MouseSelector+FBO);
- 3MF 导入导出禁用(lib3mf 未编);CGAL 内核不编,几何以 Manifold 为准;
- 剪贴板图片双向未做(文本已全链验证);碰一碰/分享未适配(.scad 非标准 utd 类型);
- 控制台冷门词条(CGAL 专用错误路径、调试 trace)按需增量补译。

因此,当前版本更准确的定位是"OpenSCAD 鸿蒙 PC 可用建模基础版":编辑、预览、渲染、导出、键鼠触控双模的主链路在 2in1 真机闭环,渲染深水区与长尾集成项按遗留清单逐项清账。

▍八、总结

AirSCAD 是 Air 系工程里验证 Qt 路线上限的一个:它把 GUI→SHARED 范式推到了"桌面 GUI 程序整体变共享库"的完整形态,也把 OpenGL 渲染、QScintilla 编辑、gettext 汉化、几何内核这些桌面依赖逐个落到了鸿蒙的地基上。PORTING.md 里 M0 到 M4 五轮、每条修复带真机证据的记录方式,让"F5 预览无几何体""大粘贴闪退"这类深水区问题都有可追溯的根因链。

后续工作的重心在遗留清单里是明确的:修复 gl4es 翻译层以恢复 OpenCSG 双算法预览与鼠标拾取测量,补齐剪贴板图片双向与分享通道,控制台长尾词条随用随补。这个工程已经证明键鼠加触控的 2in1 形态撑得起一款 CAD 工具的日常操作,剩下的就是把登记在册的每一项按同样的实证口径逐个闭环。
楼主 8 天前
想参与讨论?登录 或 30 秒注册。