A
Air官方
【Air 系列 · 鸿蒙 PC 适配全记录】42 / 56
■ 应用:AirScore(AirScore)
■ 定位:MuseScore 移植,乐谱编辑与播放
■ 上游:MuseScore Studio | 路线:Qt6+QtQuick 软渲 | 版本:1.1.1
■ 开源: https://gitcode.com/Andy88/Harmony-MuseScore
■ 市场状态:已上架 AGC 应用页: https://appgallery.huawei.com/app/6917615798513182446
■ 官网(免费下载): https://www.shunidc.cn/
————————————————————
开源鸿蒙 PC 社区: https://harmonypc.csdn.net/
PC 社区项目申请: https://atomgit.com/OpenHarmonyPCDeveloper
适配开源地址:https://gitcode.com/Andy88/Harmony-MuseScore
环境搭建文章:https://blog.csdn.net/weixin_52908342/article/details/161343743
▍一、为什么要适配 AirScore
MuseScore Studio 是目前使用最广泛的开源乐谱制作软件,制谱、回放、导出构成了音乐教育与创作的基本工作流。它同时也是一个对桌面环境依赖极重的 Qt 应用:完整的菜单命令体系、大量非模态对话框、乐谱画布的多点缩放、中英文混排的输入法交互,以及随安装目录分发的模板、乐器定义与音色数据。把这样一类应用带到鸿蒙 PC 上,检验的就不只是"能不能编译",而是窗口、鼠标、键盘、IME、文件选择器这整套桌面交互是否真正可用。
鸿蒙工程 AirScore 对应的上游是 MuseScore Studio 5.0-dev(master 快照 2026-08-26,对应 GitHub commit b09df849895b)。工程包名为 com.airscore.app,版本 1.0.0,deviceTypes 覆盖 phone、tablet 与 2in1,仅声明 ohos.permission.INTERNET 一项权限,target 与 compatible SDK 均为 6.1.0(23)。移植手册 PORTING.md 记载的目标设备为 HUAWEI MatePad 11.5 与 MatePad Mini 悦读版,Native ABI 为 arm64-v8a。
技术路线上,这个工程有一个特别的位置:它消费的是 AirGis 工程沉淀的 Qt 6.11.1 预编译栈,其中 Quick/Qml 模块族此前从未上过真机——PORTING.md 依赖矩阵明确记载 QGIS 工程把 Qml 守卫掉了,AirScore 是这套栈的第一个真机消费者。除此之外,MuseScore 本体以源码交叉编译方式进入产物,Qt 层选 ohos QPA(AirCast/AirCad 同系),上游 app 用 qt_add_library(SHARED) 改造成 libmscoreshell.so。三层叠加,构成一个相当完整的"桌面 Qt 大型应用"移植样本。
▍二、先划清边界:不把编译通过当成适配完成
MuseScore 上游是一个面向桌面操作系统的完整发行版:自带更新器、云服务入口、推广轮播、MuseSampler 专有音源,以及对 X11/ALSA/DBus 等 Linux 桌面设施的直接依赖。交叉编译通过只意味着生成了一组 ELF,不等于乐谱界面可以渲染、菜单可以点击、文件可以保存。
PORTING.md 第 0 节把验收标准写得很具体:完整可用、菜单栏鸿蒙化(ArkUI 渲染)、选择器鸿蒙化(系统 Picker)、弹窗多实例治理、汉化,并且立了一条反向约束——原生 UI(Qt Quick)直接渲染,除菜单栏外绝不手绘 ArkTS。也就是说,ArkTS 侧只做系统粘合,不复制界面。这条边界决定了下表的形态:
层次 原项目实现 鸿蒙侧当前方案
应用入口 main() 可执行程序 Stage 模型 EntryAbility + 宿主线程 airscore_main
窗口承载 桌面 WM 管理的 QQuickWindow XComponent SURFACE + ohos QPA
菜单栏 QML 菜单与 AppMenuModel 直渲 ArkTS 顶栏 bindMenu,getMenuSnapshot 轮询 + triggerMenuAction 回注
对话框 Qt::Dialog 独立窗口 DialogHost 浮层/子窗双模式(P16)
文件对话框 QFileDialog + 桌面对话 系统 DocumentViewPicker(QPA qohosfiledialoghelper)
音频后端 平台音频 API + MuseSampler OHOS 音频驱动(P4,M1 节拍供给)+ FluidSynth 回退(R8)
数据文件 安装目录 share/ rawfile mscore-data.tar 首启部署到 files/share
保存链路 QFile::copy 手动读写循环替换(P106)
边界里还包括一批"主动移除":P107 至 P119 系统性摘除了启动动画、WelcomeDialog 广告轮播、向导与主页中的 MuseHub/MuseSounds 推广、云端发布入口、诊断菜单与打印等在鸿蒙上无后端或纯引流的功能。判断依据不是功能多少,而是每一条进入菜单的能力都必须在当前产物里真实可用。
▍三、鸿蒙版的整体架构
整体是 Air 系列的双层架构 Qt 变体:ArkTS 壳负责 Ability 生命周期、系统 Picker、IME 通道、顶栏菜单与对话框子窗;libscorebridge.so 做 NAPI 薄桥;libscore_core.so 在宿主线程上以拓扑序 dlopen 全部依赖并调起 airscore_main;最终由 libmscoreshell.so 里的 Qt Quick 界面渲染乐谱。PORTING.md 中的架构图如下:
text
ArkTS 壳 entry/src/main/ets(AirCast Index.ets 改造)
XComponent SURFACE → OHNativeWindow → QPA(QOhos_SetNativeWindow)
顶栏 ArkUI 菜单(getMenuSnapshot 轮询 + triggerMenuAction 回注)
系统文件 Picker(DocumentViewPicker → qohosfiledialoghelper TSFN 桥)
libscorebridge.so(NAPI 薄壳:只链系统库 ace_napi/native_window/hilog)
dlopen 拓扑:Qt 栈(含 Qml/Quick)→ 依赖栈 → libqohos.so(QPA) → libmscoreshell.so
libmscoreshell.so(MuseScore 整体 SHARED 化,PATCH-5/6,入口 airscore_main)
Qt Quick 渲染:QT_QUICK_BACKEND=software + QSG_RENDER_LOOP=basic
QML 导入树:entry/libs/arm64-v8a/qml/ + QML2_IMPORT_PATH
数据:share/(templates/instruments/styles/zh_CN.qm/fonts)→ rawfile tar → files/ 首启部署
仓库主要目录如下(真实 ls 结果):
text
AirScore/
├── AppScope/ # 应用级配置(app.json5)
├── PORTING.md # 移植手册:补丁清单/风险册/里程碑/接手审计
├── ai_log/ # 每轮真机回归与修复实录
├── native/
│ ├── env.sh # 交叉环境(NDK clang 15.0.4,梯队B brew llvm)
│ ├── sources/ # MuseScore-main / qtshadertools / qtnetworkauth(symlink)
│ ├── scripts/ # apply_patches.py、configure_musescore.sh、build_qt_modules.sh 等
│ └── install/ # 交叉构建安装前缀
└── entry/
├── libs/arm64-v8a/ # Qt 6.11.1 全量 .so 与 QML 导入树
└── src/main/
├── module.json5 # 权限、Ability 与设备类型
├── ets/
│ ├── pages/Index.ets # XComponent + 顶栏菜单 + IME/选择器回调
│ ├── pages/DialogPage.ets # 对话框子窗页面
│ ├── components/ # DialogHost、DialogTitleBar
│ ├── utils/ # SubWindowManager、FileManager
│ └── entryability/EntryAbility.ets
├── resources/rawfile/mscore-data.tar
└── cpp/
├── napi_init.cpp # NAPI 桥:回调注册、触摸/按键注入、TSFN
├── score_core.cpp # libscore_core.so:dlopen 拓扑 + 宿主线程
└── qt_platform/ # ohos QPA 插件
▍四、核心适配过程
### 1. GUI 改 SHARED 库,入口移进宿主线程
P5 在 src/app/CMakeLists.txt 为 OS_IS_OHOS 增加 qt_add_library(MuseScoreStudio SHARED) 分支,产出 libmscoreshell.so;P6 在 src/app/main.cpp 把 main 改名为 mscore_app_main 并导出 airscore_main(argc, argv)。之所以必须如此,是 PORTING.md 风险 R6 记载的 appfreeze 双看门狗:ArkTS 主线程不能被阻塞,airscore_main 要跑在 native 后台线程上,跨线程回调一律走 TSFN,Qt 事件循环整线程归属。entry/src/main/cpp/score_core.cpp 的职责注释把这层契约写得很直白:最前置环境变量(HOME/XDG 指向沙箱)、拓扑序 dlopen、宿主线程设置 QT_QPA_PLATFORM=ohos、QPA 接线与日志镜像。
### 2. Qt Quick 界面直渲与 QML 导入树
与 Notepad3 这类 Widgets 应用不同,MuseScore 的界面是 QML。交叉场景下 QML 头文件 include 路径需要显式化(P12/P13 补 uicomponents_qml 链 Qt::Quick 与 QML 模块链),运行时 QML 导入树落在 entry/libs/arm64-v8a/qml/,由 QML2_IMPORT_PATH 指认。渲染采用 QT_QUICK_BACKEND=software 加 QSG_RENDER_LOOP=basic,这是预编译 Quick 栈在真机上的基线路径;大谱表滚动性能在风险册 R3 中被明确接受为 M1 基线,备选方案是 QPA 已具备的 EGL 直连。配套的打包约束同样来自预编译栈:nativeLib 配 strip:false 与 compressNativeLibs:false(R5),否则 hvigor 的 strip 会破坏 .so。
### 3. 菜单栏鸿蒙化的 v2 演进
P7 是壳层与 native 之间最重的一条通道。native 侧 isGlobalMenuAvailable() 在 OHOS 返回 true,QML 菜单栏自动隐藏;PlatformAppMenuModel 把菜单树导出为 JSON 快照,经 airscore_menu_snapshot/serial/trigger 三个 C ABI 函数暴露;ArkTS 顶栏 Index.ets 轮询快照渲染 bindMenu,叶子项经 handleMenuItem(id) 回注。v2 修掉的三个缺陷很有代表性:attach 曾嵌在 #ifdef MUSE_MODULE_WORKSPACE 内,模块裁剪即整链静默失效;instance() 惰性创建会在 NAPI 线程构造 QObject/QTimer,导致快照挂一次即冻结;1 秒全树序列化轮询违反性能纪律——改为 itemsChanged 事件驱动加 20 秒兜底,getMenuSerial 轻查询加代次门控,稳态零 JSON 拷贝。
### 4. 对话框浮层化与子窗治理
P16 把 Qt::Dialog 类窗口整体接入 ArkTS 侧的 SubWindowManager/DialogPage/DialogHost:QPA 升级 AirCast 新版 dialog 链,core 桥 register_bridge 增加第 5 参 dialog_ops,NAPI 按 XComponent id 分流。真机实证记录显示向导、新建、样式、会话恢复四类对话框浮层化全通,样式开关反复开关零泄漏。后续 P115 又补了三个 PC 自由多窗下的子窗问题:离屏 IME 输入框最小尺寸把子窗顶短出小黑条、二次矫正闭包捕获旧几何回卷窗口、子窗需要 setWindowFocusable(true) 才能响应鼠标点击。
### 5. 系统能力接线:选择器、IME 与数据部署
文件选择走 QPA 内建 qohosfiledialoghelper,TSFN 桥到 ArkTS 的 DocumentViewPicker,结果按请求 id 回流;保存链路在 P106 之后由 index.ets 的中转 watcher 监视中转文件并拷回授权 uri。IME 采用离屏 1×1 TextInput 通道(Index.ets 注释明确记载),P103 把提交语义从"光标处插入"改为整字段替换后,中英文逐字精确。数据部署上,299 MB 的上游 share 数据(风险 R9)只保留 zh_CN.qm、模板、乐器与必需数据打进 mscore-data.tar,由壳层首启解压到 files/share,P8 补上 AIRSCORE_DATA_DIR 环境变量注入,使 appDataPath() 与解包目标对齐。
▍五、适配中遇到的几个难点
### 难点一:IME 拼接出的 "fflfluflutflute"
现象是输入 flute 得到 fflfluflutflute:ArkTS onImeChange 每键上送隐藏输入框的全量值,而 QPA 的 commit 语义是光标处插入,全量值被逐次累加。解法在 P103:提交前查询 ImSurroundingText 与 ImCursorPosition,改为 setCommitString(text, -cpos, surround.length()) 整字段替换,空串提交即清空,且 ArkTS 侧长度门禁与 QPA 空串早退同步放开。MatePad 11.5 与 Mini 双真机回归记录均确认英文与中文逐字精确。
### 难点二:键盘收起后画面定格
界面可见而帧不重绘,连续截图 md5 相同,一度把 S5 捏合缩放验证判为 BLOCKED。根因在 PATCH-99 的分析里:键盘避让是 ArkUI 布局平移,XComponent surface 尺寸不变,OnSurfaceChanged 不触发,Qt 对高度变化无感知也无标脏。解法是 IME 面板收起后 450 ms 对全部可见顶层窗投全量 Expose;对话框浮层关闭场景同源,由 PATCH-100 在 detach/destroy 后 50 ms 补投。这一组修复落定后,捏合缩放三源链路才在同一轮拿到像素级验证。
### 难点三:预编译 Qt 的 QFile::copy 在 OHOS 上普遍失败
保存链路里 autosave 与手动保存双场景都报 409,同目录 writeFile 原语却成功。P105 先尝试插桩 QFile::copy,三连败实证了 musl PLT 绑定优先对象自身 DT_NEEDED 作用域,外部插桩打不赢直接依赖链——这次失败被完整留档,QString ABI 与符号取证方法论写进了 ai_log。真正的根修是 P106:在 muse/filesystem.cpp 的 copyRecursively 用手动读写循环替换,各分支 errorString 上 LOGE。真机终验记录显示保存文件 29,679 字节经中转转正落到 Download,标签页无星号、零错误框。
### 难点四:入门向导标题渲染成 MuseScǒre
向导标题的字母 o 上多出附加符,双设备可复现。P101 定位的根因链是:默认 systemFont 经 QFreeTypeFontDatabase 扫描 /system/fonts 落到可变字体(NotoSans*[wght].ttf),自编 FreeType 引擎对可变字体与 shaping 的组合渲染出错。解法是在 QOhosTheme::font 里把 SystemFont/LabelFont 钉死为静态 HarmonyOS Sans,依赖 Qt 在 family 未注册时的自动回退。闭环验证按九个字符逐字核对通过。
### 难点五:日志后缀引发的写盘挂起
P14 记载了一个不典型的坑:设备安全服务会实时扫描 *.log 文件,扫描期写盘挂起曾造成全局死锁。工程把日志后缀统一改为 .txt,文件名模式、remover 断言与扫描 glob 三处同步,score_core.cpp 的日志镜像也按此纪律执行——仅 ERROR 上 hilog,其余写沙箱 files/airscore/log/*.txt。
▍六、编译、安装与启动
这是 native 与 ArkTS 双链工程,完整构建分交叉编译与 HAP 打包两段。PORTING.md 第 5 节的速查如下:
bash
source native/env.sh # 交叉环境(AIRSCORE_TIER=brew 切梯队B)
bash native/scripts/build_qt_modules.sh # shadertools+networkauth 新编 → AirGis 前缀
bash native/scripts/configure_musescore.sh # MuseScore CMake 配置(首次含 deps REBUILD)
cmake --build native/build/musescore --parallel 8
依赖侧由 muse_deps EXTDEPS_OVERRIDE_ALL=REBUILD 源码构建 zlib/freetype/harfbuzz、ogg/flac/lame/opus 等音频栈;Qt 缺失模块经 build_qt_modules.sh 从官方 v6.11.1 tag 新编。补丁唯一权威是 native/scripts/apply_patches.py,就地标记 PATCH-xx(AirScore);部署期用 elf_edit 原位修 NEEDED(patchelf 0.18.0 因段重排被禁用)。
ArkTS 与 HAP 段使用标准 hvigorw 流程:
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.airscore.app
PORTING.md 与 ai_log/ 记载了 2026-09-01 至 09-09 的多轮真机回归:MatePad Mini 上建谱全流程走通、保存链真机终验通过、IME 与键盘避让双轮 PASS,本文引用的验证结论均出自这些记录。
▍七、当前功能边界
按接手审计表与真机回归记录,当前产物已覆盖:
- Stage 壳、XComponent、ohos QPA 与 Qt Quick 乐谱界面真机直渲;
- 建谱向导、新建乐谱、样式、会话恢复四类对话框浮层化,多实例 id 递增零泄漏;
- ArkTS 顶栏菜单接管,文件/编辑菜单中文条目真机验证通过;
- 双指捏合缩放三源链路(uinput 注入到 Qt 手势的像素级验证);
- 中英文 IME 输入逐字精确,键盘避让下浮层重定位与逐像素复位;
- 保存全链(选择器、路径树、staging、uri 回拷到 Download)真机终验通过;
- 去广告与去云引流后的菜单、主页与向导形态。
尚未进入当前产物的能力包括:OHAudio 真实发声(当前 P4 音频驱动仅供给 M1 节拍,真实发声挂在 M6)、手写笔(审计 S4 未启动)、剪贴板跨应用粘贴(S9 未启动)、碰一碰与分享(S10 未启动)、混合自有菜单项注入(S1 缺口),以及偏好设置等 QML 深层界面的汉化遍历。MuseSampler 专有音源不可用,按 R8 回退 muse 内置 FluidSynth。
因此,当前版本更准确的定位是"Qt Quick 乐谱界面真机直跑、壳层系统能力完成接管"的中期阶段版本:渲染、输入、菜单、对话框、保存五条主链路有真机实证,发声与外设能力仍是里程碑上的待办。
▍八、总结
AirScore 的价值在于把三个层面的经验压进了一个工程:预编译 Qt 6.11.1 Quick 栈第一次完整消费并暴露了 QML 导入、软件渲染与字形回退问题;MuseScore 这种量级的上游(补丁登记到 P120)证明了 GUI 改 SHARED 加 ArkTS 壳接管的范式可以承载真实桌面软件;而 IME、键盘避让、QFile::copy 这类问题的根修过程,全部以"现象—根因—真机验证"的格式留档在 PORTING.md 与 ai_log/ 中,构成可审计的移植记录。
后续工作重心在 PORTING.md 里程碑与审计表里是明确的:M6 的真实乐谱全流程补齐 OHAudio 真实发声,审计表中 S4/S8/S9/S10 的手写笔、文件夹授权、剪贴板与分享逐项启动,S1 的混合菜单项与 S2 的深层汉化收尾。每一步仍以可构建的 HAP 与双设备真机回归作为验收线。
■ 应用:AirScore(AirScore)
■ 定位:MuseScore 移植,乐谱编辑与播放
■ 上游:MuseScore Studio | 路线:Qt6+QtQuick 软渲 | 版本:1.1.1
■ 开源: https://gitcode.com/Andy88/Harmony-MuseScore
■ 市场状态:已上架 AGC 应用页: https://appgallery.huawei.com/app/6917615798513182446
■ 官网(免费下载): https://www.shunidc.cn/
————————————————————
开源鸿蒙 PC 社区: https://harmonypc.csdn.net/
PC 社区项目申请: https://atomgit.com/OpenHarmonyPCDeveloper
适配开源地址:https://gitcode.com/Andy88/Harmony-MuseScore
环境搭建文章:https://blog.csdn.net/weixin_52908342/article/details/161343743
▍一、为什么要适配 AirScore
MuseScore Studio 是目前使用最广泛的开源乐谱制作软件,制谱、回放、导出构成了音乐教育与创作的基本工作流。它同时也是一个对桌面环境依赖极重的 Qt 应用:完整的菜单命令体系、大量非模态对话框、乐谱画布的多点缩放、中英文混排的输入法交互,以及随安装目录分发的模板、乐器定义与音色数据。把这样一类应用带到鸿蒙 PC 上,检验的就不只是"能不能编译",而是窗口、鼠标、键盘、IME、文件选择器这整套桌面交互是否真正可用。
鸿蒙工程 AirScore 对应的上游是 MuseScore Studio 5.0-dev(master 快照 2026-08-26,对应 GitHub commit b09df849895b)。工程包名为 com.airscore.app,版本 1.0.0,deviceTypes 覆盖 phone、tablet 与 2in1,仅声明 ohos.permission.INTERNET 一项权限,target 与 compatible SDK 均为 6.1.0(23)。移植手册 PORTING.md 记载的目标设备为 HUAWEI MatePad 11.5 与 MatePad Mini 悦读版,Native ABI 为 arm64-v8a。
技术路线上,这个工程有一个特别的位置:它消费的是 AirGis 工程沉淀的 Qt 6.11.1 预编译栈,其中 Quick/Qml 模块族此前从未上过真机——PORTING.md 依赖矩阵明确记载 QGIS 工程把 Qml 守卫掉了,AirScore 是这套栈的第一个真机消费者。除此之外,MuseScore 本体以源码交叉编译方式进入产物,Qt 层选 ohos QPA(AirCast/AirCad 同系),上游 app 用 qt_add_library(SHARED) 改造成 libmscoreshell.so。三层叠加,构成一个相当完整的"桌面 Qt 大型应用"移植样本。
▍二、先划清边界:不把编译通过当成适配完成
MuseScore 上游是一个面向桌面操作系统的完整发行版:自带更新器、云服务入口、推广轮播、MuseSampler 专有音源,以及对 X11/ALSA/DBus 等 Linux 桌面设施的直接依赖。交叉编译通过只意味着生成了一组 ELF,不等于乐谱界面可以渲染、菜单可以点击、文件可以保存。
PORTING.md 第 0 节把验收标准写得很具体:完整可用、菜单栏鸿蒙化(ArkUI 渲染)、选择器鸿蒙化(系统 Picker)、弹窗多实例治理、汉化,并且立了一条反向约束——原生 UI(Qt Quick)直接渲染,除菜单栏外绝不手绘 ArkTS。也就是说,ArkTS 侧只做系统粘合,不复制界面。这条边界决定了下表的形态:
层次 原项目实现 鸿蒙侧当前方案
应用入口 main() 可执行程序 Stage 模型 EntryAbility + 宿主线程 airscore_main
窗口承载 桌面 WM 管理的 QQuickWindow XComponent SURFACE + ohos QPA
菜单栏 QML 菜单与 AppMenuModel 直渲 ArkTS 顶栏 bindMenu,getMenuSnapshot 轮询 + triggerMenuAction 回注
对话框 Qt::Dialog 独立窗口 DialogHost 浮层/子窗双模式(P16)
文件对话框 QFileDialog + 桌面对话 系统 DocumentViewPicker(QPA qohosfiledialoghelper)
音频后端 平台音频 API + MuseSampler OHOS 音频驱动(P4,M1 节拍供给)+ FluidSynth 回退(R8)
数据文件 安装目录 share/ rawfile mscore-data.tar 首启部署到 files/share
保存链路 QFile::copy 手动读写循环替换(P106)
边界里还包括一批"主动移除":P107 至 P119 系统性摘除了启动动画、WelcomeDialog 广告轮播、向导与主页中的 MuseHub/MuseSounds 推广、云端发布入口、诊断菜单与打印等在鸿蒙上无后端或纯引流的功能。判断依据不是功能多少,而是每一条进入菜单的能力都必须在当前产物里真实可用。
▍三、鸿蒙版的整体架构
整体是 Air 系列的双层架构 Qt 变体:ArkTS 壳负责 Ability 生命周期、系统 Picker、IME 通道、顶栏菜单与对话框子窗;libscorebridge.so 做 NAPI 薄桥;libscore_core.so 在宿主线程上以拓扑序 dlopen 全部依赖并调起 airscore_main;最终由 libmscoreshell.so 里的 Qt Quick 界面渲染乐谱。PORTING.md 中的架构图如下:
text
ArkTS 壳 entry/src/main/ets(AirCast Index.ets 改造)
XComponent SURFACE → OHNativeWindow → QPA(QOhos_SetNativeWindow)
顶栏 ArkUI 菜单(getMenuSnapshot 轮询 + triggerMenuAction 回注)
系统文件 Picker(DocumentViewPicker → qohosfiledialoghelper TSFN 桥)
libscorebridge.so(NAPI 薄壳:只链系统库 ace_napi/native_window/hilog)
dlopen 拓扑:Qt 栈(含 Qml/Quick)→ 依赖栈 → libqohos.so(QPA) → libmscoreshell.so
libmscoreshell.so(MuseScore 整体 SHARED 化,PATCH-5/6,入口 airscore_main)
Qt Quick 渲染:QT_QUICK_BACKEND=software + QSG_RENDER_LOOP=basic
QML 导入树:entry/libs/arm64-v8a/qml/ + QML2_IMPORT_PATH
数据:share/(templates/instruments/styles/zh_CN.qm/fonts)→ rawfile tar → files/ 首启部署
仓库主要目录如下(真实 ls 结果):
text
AirScore/
├── AppScope/ # 应用级配置(app.json5)
├── PORTING.md # 移植手册:补丁清单/风险册/里程碑/接手审计
├── ai_log/ # 每轮真机回归与修复实录
├── native/
│ ├── env.sh # 交叉环境(NDK clang 15.0.4,梯队B brew llvm)
│ ├── sources/ # MuseScore-main / qtshadertools / qtnetworkauth(symlink)
│ ├── scripts/ # apply_patches.py、configure_musescore.sh、build_qt_modules.sh 等
│ └── install/ # 交叉构建安装前缀
└── entry/
├── libs/arm64-v8a/ # Qt 6.11.1 全量 .so 与 QML 导入树
└── src/main/
├── module.json5 # 权限、Ability 与设备类型
├── ets/
│ ├── pages/Index.ets # XComponent + 顶栏菜单 + IME/选择器回调
│ ├── pages/DialogPage.ets # 对话框子窗页面
│ ├── components/ # DialogHost、DialogTitleBar
│ ├── utils/ # SubWindowManager、FileManager
│ └── entryability/EntryAbility.ets
├── resources/rawfile/mscore-data.tar
└── cpp/
├── napi_init.cpp # NAPI 桥:回调注册、触摸/按键注入、TSFN
├── score_core.cpp # libscore_core.so:dlopen 拓扑 + 宿主线程
└── qt_platform/ # ohos QPA 插件
▍四、核心适配过程
### 1. GUI 改 SHARED 库,入口移进宿主线程
P5 在 src/app/CMakeLists.txt 为 OS_IS_OHOS 增加 qt_add_library(MuseScoreStudio SHARED) 分支,产出 libmscoreshell.so;P6 在 src/app/main.cpp 把 main 改名为 mscore_app_main 并导出 airscore_main(argc, argv)。之所以必须如此,是 PORTING.md 风险 R6 记载的 appfreeze 双看门狗:ArkTS 主线程不能被阻塞,airscore_main 要跑在 native 后台线程上,跨线程回调一律走 TSFN,Qt 事件循环整线程归属。entry/src/main/cpp/score_core.cpp 的职责注释把这层契约写得很直白:最前置环境变量(HOME/XDG 指向沙箱)、拓扑序 dlopen、宿主线程设置 QT_QPA_PLATFORM=ohos、QPA 接线与日志镜像。
### 2. Qt Quick 界面直渲与 QML 导入树
与 Notepad3 这类 Widgets 应用不同,MuseScore 的界面是 QML。交叉场景下 QML 头文件 include 路径需要显式化(P12/P13 补 uicomponents_qml 链 Qt::Quick 与 QML 模块链),运行时 QML 导入树落在 entry/libs/arm64-v8a/qml/,由 QML2_IMPORT_PATH 指认。渲染采用 QT_QUICK_BACKEND=software 加 QSG_RENDER_LOOP=basic,这是预编译 Quick 栈在真机上的基线路径;大谱表滚动性能在风险册 R3 中被明确接受为 M1 基线,备选方案是 QPA 已具备的 EGL 直连。配套的打包约束同样来自预编译栈:nativeLib 配 strip:false 与 compressNativeLibs:false(R5),否则 hvigor 的 strip 会破坏 .so。
### 3. 菜单栏鸿蒙化的 v2 演进
P7 是壳层与 native 之间最重的一条通道。native 侧 isGlobalMenuAvailable() 在 OHOS 返回 true,QML 菜单栏自动隐藏;PlatformAppMenuModel 把菜单树导出为 JSON 快照,经 airscore_menu_snapshot/serial/trigger 三个 C ABI 函数暴露;ArkTS 顶栏 Index.ets 轮询快照渲染 bindMenu,叶子项经 handleMenuItem(id) 回注。v2 修掉的三个缺陷很有代表性:attach 曾嵌在 #ifdef MUSE_MODULE_WORKSPACE 内,模块裁剪即整链静默失效;instance() 惰性创建会在 NAPI 线程构造 QObject/QTimer,导致快照挂一次即冻结;1 秒全树序列化轮询违反性能纪律——改为 itemsChanged 事件驱动加 20 秒兜底,getMenuSerial 轻查询加代次门控,稳态零 JSON 拷贝。
### 4. 对话框浮层化与子窗治理
P16 把 Qt::Dialog 类窗口整体接入 ArkTS 侧的 SubWindowManager/DialogPage/DialogHost:QPA 升级 AirCast 新版 dialog 链,core 桥 register_bridge 增加第 5 参 dialog_ops,NAPI 按 XComponent id 分流。真机实证记录显示向导、新建、样式、会话恢复四类对话框浮层化全通,样式开关反复开关零泄漏。后续 P115 又补了三个 PC 自由多窗下的子窗问题:离屏 IME 输入框最小尺寸把子窗顶短出小黑条、二次矫正闭包捕获旧几何回卷窗口、子窗需要 setWindowFocusable(true) 才能响应鼠标点击。
### 5. 系统能力接线:选择器、IME 与数据部署
文件选择走 QPA 内建 qohosfiledialoghelper,TSFN 桥到 ArkTS 的 DocumentViewPicker,结果按请求 id 回流;保存链路在 P106 之后由 index.ets 的中转 watcher 监视中转文件并拷回授权 uri。IME 采用离屏 1×1 TextInput 通道(Index.ets 注释明确记载),P103 把提交语义从"光标处插入"改为整字段替换后,中英文逐字精确。数据部署上,299 MB 的上游 share 数据(风险 R9)只保留 zh_CN.qm、模板、乐器与必需数据打进 mscore-data.tar,由壳层首启解压到 files/share,P8 补上 AIRSCORE_DATA_DIR 环境变量注入,使 appDataPath() 与解包目标对齐。
▍五、适配中遇到的几个难点
### 难点一:IME 拼接出的 "fflfluflutflute"
现象是输入 flute 得到 fflfluflutflute:ArkTS onImeChange 每键上送隐藏输入框的全量值,而 QPA 的 commit 语义是光标处插入,全量值被逐次累加。解法在 P103:提交前查询 ImSurroundingText 与 ImCursorPosition,改为 setCommitString(text, -cpos, surround.length()) 整字段替换,空串提交即清空,且 ArkTS 侧长度门禁与 QPA 空串早退同步放开。MatePad 11.5 与 Mini 双真机回归记录均确认英文与中文逐字精确。
### 难点二:键盘收起后画面定格
界面可见而帧不重绘,连续截图 md5 相同,一度把 S5 捏合缩放验证判为 BLOCKED。根因在 PATCH-99 的分析里:键盘避让是 ArkUI 布局平移,XComponent surface 尺寸不变,OnSurfaceChanged 不触发,Qt 对高度变化无感知也无标脏。解法是 IME 面板收起后 450 ms 对全部可见顶层窗投全量 Expose;对话框浮层关闭场景同源,由 PATCH-100 在 detach/destroy 后 50 ms 补投。这一组修复落定后,捏合缩放三源链路才在同一轮拿到像素级验证。
### 难点三:预编译 Qt 的 QFile::copy 在 OHOS 上普遍失败
保存链路里 autosave 与手动保存双场景都报 409,同目录 writeFile 原语却成功。P105 先尝试插桩 QFile::copy,三连败实证了 musl PLT 绑定优先对象自身 DT_NEEDED 作用域,外部插桩打不赢直接依赖链——这次失败被完整留档,QString ABI 与符号取证方法论写进了 ai_log。真正的根修是 P106:在 muse/filesystem.cpp 的 copyRecursively 用手动读写循环替换,各分支 errorString 上 LOGE。真机终验记录显示保存文件 29,679 字节经中转转正落到 Download,标签页无星号、零错误框。
### 难点四:入门向导标题渲染成 MuseScǒre
向导标题的字母 o 上多出附加符,双设备可复现。P101 定位的根因链是:默认 systemFont 经 QFreeTypeFontDatabase 扫描 /system/fonts 落到可变字体(NotoSans*[wght].ttf),自编 FreeType 引擎对可变字体与 shaping 的组合渲染出错。解法是在 QOhosTheme::font 里把 SystemFont/LabelFont 钉死为静态 HarmonyOS Sans,依赖 Qt 在 family 未注册时的自动回退。闭环验证按九个字符逐字核对通过。
### 难点五:日志后缀引发的写盘挂起
P14 记载了一个不典型的坑:设备安全服务会实时扫描 *.log 文件,扫描期写盘挂起曾造成全局死锁。工程把日志后缀统一改为 .txt,文件名模式、remover 断言与扫描 glob 三处同步,score_core.cpp 的日志镜像也按此纪律执行——仅 ERROR 上 hilog,其余写沙箱 files/airscore/log/*.txt。
▍六、编译、安装与启动
这是 native 与 ArkTS 双链工程,完整构建分交叉编译与 HAP 打包两段。PORTING.md 第 5 节的速查如下:
bash
source native/env.sh # 交叉环境(AIRSCORE_TIER=brew 切梯队B)
bash native/scripts/build_qt_modules.sh # shadertools+networkauth 新编 → AirGis 前缀
bash native/scripts/configure_musescore.sh # MuseScore CMake 配置(首次含 deps REBUILD)
cmake --build native/build/musescore --parallel 8
依赖侧由 muse_deps EXTDEPS_OVERRIDE_ALL=REBUILD 源码构建 zlib/freetype/harfbuzz、ogg/flac/lame/opus 等音频栈;Qt 缺失模块经 build_qt_modules.sh 从官方 v6.11.1 tag 新编。补丁唯一权威是 native/scripts/apply_patches.py,就地标记 PATCH-xx(AirScore);部署期用 elf_edit 原位修 NEEDED(patchelf 0.18.0 因段重排被禁用)。
ArkTS 与 HAP 段使用标准 hvigorw 流程:
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.airscore.app
PORTING.md 与 ai_log/ 记载了 2026-09-01 至 09-09 的多轮真机回归:MatePad Mini 上建谱全流程走通、保存链真机终验通过、IME 与键盘避让双轮 PASS,本文引用的验证结论均出自这些记录。
▍七、当前功能边界
按接手审计表与真机回归记录,当前产物已覆盖:
- Stage 壳、XComponent、ohos QPA 与 Qt Quick 乐谱界面真机直渲;
- 建谱向导、新建乐谱、样式、会话恢复四类对话框浮层化,多实例 id 递增零泄漏;
- ArkTS 顶栏菜单接管,文件/编辑菜单中文条目真机验证通过;
- 双指捏合缩放三源链路(uinput 注入到 Qt 手势的像素级验证);
- 中英文 IME 输入逐字精确,键盘避让下浮层重定位与逐像素复位;
- 保存全链(选择器、路径树、staging、uri 回拷到 Download)真机终验通过;
- 去广告与去云引流后的菜单、主页与向导形态。
尚未进入当前产物的能力包括:OHAudio 真实发声(当前 P4 音频驱动仅供给 M1 节拍,真实发声挂在 M6)、手写笔(审计 S4 未启动)、剪贴板跨应用粘贴(S9 未启动)、碰一碰与分享(S10 未启动)、混合自有菜单项注入(S1 缺口),以及偏好设置等 QML 深层界面的汉化遍历。MuseSampler 专有音源不可用,按 R8 回退 muse 内置 FluidSynth。
因此,当前版本更准确的定位是"Qt Quick 乐谱界面真机直跑、壳层系统能力完成接管"的中期阶段版本:渲染、输入、菜单、对话框、保存五条主链路有真机实证,发声与外设能力仍是里程碑上的待办。
▍八、总结
AirScore 的价值在于把三个层面的经验压进了一个工程:预编译 Qt 6.11.1 Quick 栈第一次完整消费并暴露了 QML 导入、软件渲染与字形回退问题;MuseScore 这种量级的上游(补丁登记到 P120)证明了 GUI 改 SHARED 加 ArkTS 壳接管的范式可以承载真实桌面软件;而 IME、键盘避让、QFile::copy 这类问题的根修过程,全部以"现象—根因—真机验证"的格式留档在 PORTING.md 与 ai_log/ 中,构成可审计的移植记录。
后续工作重心在 PORTING.md 里程碑与审计表里是明确的:M6 的真实乐谱全流程补齐 OHAudio 真实发声,审计表中 S4/S8/S9/S10 的手写笔、文件夹授权、剪贴板与分享逐项启动,S1 的混合菜单项与 S2 的深层汉化收尾。每一步仍以可构建的 HAP 与双设备真机回归作为验收线。
楼主
8 天前