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

AirSky(from Stellarium)|AirSky · 鸿蒙 PC 适配全记录

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

■ 应用:AirSky(from Stellarium)(AirSky)
■ 定位:Stellarium 移植,星空天文模拟
■ 上游:Stellarium | 路线:Qt6 6.11.1+QPA | 版本:1.1.0
■ 开源: https://gitcode.com/Andy88/Harmony-Stellarium
■ 市场状态:已上架 AGC 应用页: https://appgallery.huawei.com/app/6917616691988772910
■ 官网(免费下载): https://www.shunidc.cn/

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

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

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

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

▍一、为什么要适配 AirSky

Stellarium 是开源天文软件中历史最长、数据最全的项目之一:真实星表、大气与地景模拟、星座连线与星空文化、目镜与望远镜控制、天文历算与脚本引擎,构成了一个完整的观星与天文教学工具箱。把这样的软件带到鸿蒙 PC 上,价值不在复刻一个星空壁纸,而在于让一套成熟的科学计算与渲染体系直接服务鸿蒙设备的观星、教学与科普场景。

AirSky 是 Stellarium 26.2.230-dev(Qt6 构建线)在鸿蒙侧的完整原生移植工程,采用 ArkTS 薄壳加 XComponent(TEXTURE)、Qt for OpenHarmony QPA 与 libstelMain.so 全量交叉编译的路线。工程档案中记录了从模拟器验证到多台真机端到端跑通的过程:MatePad 11.5、MatePad Mini、Pura X 手机、Mate 60 Pro 与 MateBook Pro 上,星空渲染、触摸选星、拖拽视角、全中文对话框与长时稳定运行均有留痕记录。

当前工程包名 com.airsky.app,版本 1.0.0,deviceTypes 覆盖 phone、tablet 与 2in1,申请 ohos.permission.INTERNET 权限;ability 锁定横屏(orientation: landscape)并声明 fullscreen、split、floating 三种窗口模式。Native ABI 为 arm64-v8a,随包数据 stel_data.zip 约 477MB,覆盖星表、星空文化、地景、星云纹理与翻译资源。

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

Stellarium 体量大、Qt 依赖深,移植工程从第一天起就明确了"可运行产物"与"上游全功能"的差异:引擎全量编进 libstelMain.so 不等于每个编译开关都点亮,对话框能弹出不等于每条输入链路都正确。工程的做法是把每个能力、每个编译开关的状态都登记在案,用数据决定取舍,而不是用源码存在性代替功能交付。

层次 原项目实现(桌面 Linux/Windows) 鸿蒙侧当前方案
应用入口 桌面 main() 进程 Stage 模型 EntryAbility + ArkTS 薄壳
窗口承载 桌面窗口系统与 GLX/WGL XComponent(TEXTURE) + Qt6.11.1 ohos QPA
引擎核心 Stellarium 主程序进程内直连 libstelMain.so 全量引擎(38MB 级),拓扑序 dlopen 装载
翻译体系 gettext 运行时 Qt .qm 方案:lconvert 生成 8 域 zh_CN.qm 随包分发,零 gettext 依赖
数据分发 安装目录共享数据 stel_data.zip(477M STORED)随 HAP 携带,首启解压至沙箱
视频与语音 Qt5Multimedia 系 ENABLE_MEDIA=0,Qt 栈无 Qt6Multimedia/TextToSpeech,维持关闭
在线数据 SIMBAD 查询、插件在线更新 QtNetwork 无 TLS 后端,联网功能记录为已知边界,纯离线功能不受影响

这张表的右列全部有工程档案对应:编译开关的清算记录(ENABLE_SCRIPTING/XLSX 开启并验证、SHOWMYSKY 定性不可行、MEDIA 维持关闭)写明了对上游源码的判断依据。边界意识在这里具体化为一条纪律:每个"关掉的东西"都要有定性与留档,而不是悄悄消失。

▍三、鸿蒙版的整体架构

工程分四层:ArkTS 壳负责应用生命周期、数据部署与系统服务桥接;薄 NAPI 模块 libskybridge.so 承接 XComponent 回调、按拓扑序 dlopen 全部依赖并分发输入;core 宿主 libstellarsky_core.so 复刻上游 main()(StelFileMgr、翻译、字体、config 与 QApplication + StelMainView + exec);最底层是 libstelMain.so 全量引擎与 Qt6 12 模块、qohos QPA 插件。GL 路线上,Stellarium 的绘制全部经 QOpenGLFunctions/QOpenGLExtraFunctions 包装,Qt GLES3 构建直接对接 Maleoon 驱动,无需 gl4es 一类的转译层。

text
EntryAbility / Index.ets(ArkTS 壳)
├── 数据部署:rawfile/stel_data.zip → files/stel-data(zlib 解压 + verifyData 守门)
├── IME 桥:离屏 TextInput diff 协议 → TSFN → native
└── XComponent(TEXTURE, libraryname=skybridge)
▼
libskybridge.so(薄 NAPI:拓扑序 dlopen + 输入分发 + 就绪协议)
▼ dlopen(RTLD_GLOBAL)
libstellarsky_core.so(core 宿主:复刻 main() + QApplication + StelMainView + exec)
├── libstelMain.so(Stellarium 引擎全量:core/gui/modules + 23 个静态插件)
├── libqohos.so(Qt6 ohos QPA:字体/IME/文件选择器/SVG 插件内置)
└── Qt6 6.11.1 × 12 模块(AirGis 预编译栈 + 补编 Charts/Positioning/SerialPort/Declarative)


主要目录如下:

text
AirSky/
├── AppScope/app.json5 # com.airsky.app、版本
├── PORTING.md # 移植档案:决策表、补丁清单、各轮验证记录
├── stellarium/ # 上游源码树(PATCH-AIRSKY-* 就地标记)
├── native/
│ ├── scripts/ # env.sh、build_qt_modules、build_stellarium、pack_data、deploy
│ ├── sources/ # qtcharts/qtdeclarative/qtpositioning/qtserialport 6.11.1 补编源码
│ └── install/ # 交叉编译安装前缀
└── entry/src/main/
├── module.json5 # 横屏、三窗口模式、INTERNET 权限
├── ets/pages/Index.ets # XComponent 宿主、数据部署、TUNING 注入、dumpStelLog
├── ets/entryability/EntryAbility.ets
├── resources/rawfile/stel_data.zip # 477M 数据包
└── cpp/
├── napi_init.cpp # 薄 NAPI:回调、dlopen 清单、输入注入
├── sky_core.cpp # main() 复刻、拖拽直控链、环境设置
└── libs/ # deploy.sh 归一产物


▍四、核心适配过程

### 1. Qt 模块族补编与依赖闭包

Stellarium 的根 CMakeLists 对 Qt6Charts、Qt6Positioning 等模块是无条件 REQUIRED,而可复用的预编译栈里没有它们。工程从官方 submodule 源码补编 6.11.1(build_qt_modules.sh),过程中处理了 Qt6DBus cmake config 残缺(移出配置路径加 geoclue 关闭)、Qt 6.11 SBOM 机制对非标准 prefix 不兼容(QT_GENERATE_SBOM=OFF)等具体障碍,产物装入既有前缀。最终 12 个 Qt 模块加上 qohos QPA、libstelMain.so 依依赖闭包由 deploy.sh 归一(NEEDED 去版本号、patch_libs),再硬链接进 entry/libs/arm64-v8a/ 打包通道。数据包则由 pack_data.sh 打成 477M 的 STORED zip,配合 DATA_VERSION 版本戳实现旧安装自动重解压。

### 2. 上游就地补丁的最小集合

上游源码树内的补丁保持克制并逐一登记:StelOpenGL.hpp 把 Android GLES 分支扩展为兼容 __OHOS__(对齐 QOpenGLExtraFunctions 与 GLES3 头,解决 GL_R16F/GL_RED 未定义);src/CMakeLists.txt 把 stelMain 从 STATIC 改 SHARED(上游 GENERATE_STELMAINLIB 仅在 WIN32 分支生效);StelFileMgr 新增手动泵式 copyFile 并全仓 18 处调用点替换(含 8 个插件文件),修复 QFile::copy 在 hmdfs 沙箱静默失败的问题。后续汉化走 lconvert 生成 zh_CN.qm 的 Qt 翻译链路,splash 与 Help 对话框等上游信息按需移除,每一处都有补丁编号可回溯。

### 3. 输入模型:从触摸直控到物理键盘落点

触屏无 hover 语义,工程为 SkyGui 工具栏注入 auto_hide=false 常显;拖拽视角不走 Qt 事件合成,而是 napi 触摸 MOVE 经 GUI 线程排队后直调 StelMovementMgr::skyBeginDrag/skyEndDrag 与 StelApp::skyMove 的直控链,tap 选星则 scene 直投保证坐标正确。物理键盘链修复了投递落点:注入目标从主窗口改为 QApplication::focusWidget()->window(),对话框打开后按键正确进入 QLineEdit;metaState 经 skycore_inject_key 透传恢复 Shift/Ctrl 组合;CapsLock 用 NDK 独立 API 注入自定义位并在 QPA 侧按 Shift 异或 Caps 决定大小写。滚轮链按源归一(鼠标 15 度每格、触控板 50px 每步)并接入捏合轴,凑齐三源缩放。

### 4. 渲染提交与帧率工程

QPA 层把 Qt raster 层的脏区从每帧全屏上传改为按脏矩形纹理上传(对齐 scaledDirtyRegion 契约,配纹理归属守卫),真机统计 9000 帧中 8999 帧零上传,空闲帧率约翻倍。surface 销毁与重建路径引入原子渲染暂停闸:flush 与 re-present 两条呈现源在 surface 失效时全部早退,恢复后自然续帧。息屏楔死问题通过 setWindowKeepScreenOn 与暂停闸双路关闭,并用后台/锁屏信号矩阵实验把楔死前提收窄验证。帧率瓶颈最终被数据定案为 Qt 单线程串行管线(PC 同管线 36.71 帧、平板 15 帧左右,GPU 占用仅约 4%),既定的 FBO 直通与分辨率缩放方案因攻错方向而明确不实施。

### 5. 汉化与编译开关清算

中文界面不依赖 gettext:构建期用 lconvert 把 po 转 8 域 zh_CN.qm 随包分发,状态栏、信息面板、设置对话框、天文计算器全部中文生效。编译开关逐一实证:ENABLE_SCRIPTING 补编 qtdeclarative 点亮 .ssc 脚本引擎与 RemoteControl 插件;ENABLE_XLSX 在网络恢复后交叉编译 QXlsx 并端到端验证天文计算器导出 positions.xlsx 的完整链路;ENABLE_SHOWMYSKY 因上游硬依赖桌面 OpenGL 3.3 定性为不可行;ENABLE_MEDIA 因 Qt 栈缺 Multimedia/TextToSpeech 维持关闭。

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

### 难点1:真机黑屏的连环根因

模拟器验证通过后,真机首启黑屏。第一层是包内 libQt6Gui 的 NEEDED 依赖 libQt6DBus.so 从未打包(补编 Qt 模块后前缀中的 Gui 换成了带 DBus 依赖的版本,deploy 清单未跟进),补齐后引擎装配阶段又出现 LandscapeMgr 空指针崩溃。深层根因是 Qt 的 QFileSystemEngine::cloneFile 用 copy_file_range 且 errno 白名单不认 hmdfs 的失败码,QFile::copy 在真机沙箱静默失败:config.ini 未落盘、QSettings 读出空串、地景 ID 为空串直接置空。同一缺陷随后打击了全部 8 个插件的 qrc 资源首启复制。工程以手动读写泵替代(StelFileMgr::copyFile)并全仓 18 处替换后,真机全功能点亮。这轮的价值在方法论:黑屏必须沿 dlopen、崩溃栈、沙箱文件系统三层逐级取证,不能停在构建成功。

### 难点2:整体输入失联的多重根因叠加

真机一度出现"无法点击任何按钮、无法移动"的全面失联。逐层破案出四个独立问题:qpa_sym 模板的函数级 static 按签名实例化,InjectTouch 与 InjectMouse 共享同一缓存指针,鼠标实参喂进触摸函数;napi 与 core 的触摸参数序约定错位,MOVE 被解释成新触点按下;复刻 main() 时漏掉 SplashScreen::finish,不可见的启动页置顶吞掉全部点击;QWSI 队列正常的鼠标事件被 Qt 内部静默丢弃。工程分别以去缓存 dlsym、参数序对齐、补 finish 调用、改经 invokeMethod 直投 QWindow 解决,并用窗口枚举与事件探针逐一实证。

### 难点3:息屏场景的 GL 楔死

闲置约十分钟后画面定格且唤醒不自愈。三通道取证(慢路径探针、线程采样、截图)定位到机制:息屏后显示管线停止、surface 缓冲队列无人消费,GL 调用阻塞在驱动等 fence 且全程持有互斥锁,surface 销毁重建路径无法接手。修复分两路:观星属规范常亮场景,壳层 setWindowKeepScreenOn 关闭自动息屏;QPA 层加原子暂停闸,surface 销毁即停帧、有效句柄复位恢复。后续用后台/锁屏信号矩阵实验修正了风险面认知:进程被系统冻结的场景不构成楔死,风险收窄到前台息屏与 GL 途中的 surface 失效两种,各自有对应修复覆盖。

### 难点4:PC 物理键盘与手机形态的适配落差

同一 HAP 覆盖 2in1、平板、手机三种形态带来不同问题。PC 上搜索框打字零进账,根因是注入落点恒为主窗口,按键被主视图快捷键消费(搜索框内敲 x 视场变了);平板上 XComponent 未声明 focusable 时物理键全落空。手机侧,Pura X 竖屏逻辑宽 660 装不下约 1100 逻辑宽的底部工具栏,工程锁定横屏(观星主姿态本就横屏)并按设备条件注入按钮缩放;首启 TUNING 缺口导致工具栏永不显现的问题则通过 config 预生成与三元组注入(缺失即插入段内)解决。四设备形态最终全部跑通,验证矩阵覆盖到键码表级细节(G 键键码 2023 而非 2033 的脚本错误由 QPA 探针定案)。

### 难点5:帧率优化的方向纠偏

帧率提升的既定方案是 FBO 直通与渲染分辨率缩放,但量化链否定了这个方向:脏区上传优化后 9000 帧中 8999 帧零上传、GPU 占用约 4%、raster 层恒静,瓶颈在 CPU 侧的 Qt 单线程串行管线(update 加 GL 提交加 swap)。同一管线在 PC 上 36.71 帧、平板上 15 帧左右的对照数据说明差异来自设备算力而非渲染面。工程据此明确不实施缩放类方案,把下一步指向 update/GL 提交多线程化这一上游级工程,并用 minimum_fps 节流档作为续航取舍。这是用数据否决直觉方案的实例。

▍六、编译、安装与启动

Native 侧按 native/scripts/ 的固定顺序执行:env.sh 载入交叉编译环境(NDK、Qt 前缀与宿主工具),build_qt_modules.sh 补编 Charts/Positioning 等模块,build_stellarium.sh 产出 libstelMain.so,pack_data.sh 打数据包并生成翻译,deploy.sh 完成依赖闭包归一并硬链接进 entry/libs/arm64-v8a。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


签名产物位于:

text
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.airsky.app


工程档案中的真机验证记录(MatePad 11.5、Mini、Pura X、Mate 60 Pro、MateBook Pro)均基于上述链路的产物:星空与地景渲染、中文界面、搜索与设置对话框、拖拽与缩放、天文计算器导出等均有截图与数据留痕。

▍七、当前功能边界

当前鸿蒙版已覆盖的能力:

- 可安装 HAP 与四层原生架构(ArkTS 壳、薄 NAPI、core 宿主、libstelMain 全量引擎);
- 星空、大气、地景、行星实时渲染,触摸选星(信息面板与选中圈)、拖拽视角、捏合与滚轮缩放;
- 全中文界面:状态栏、信息面板、搜索对话框(五标签页)、设置对话框、天文计算器;
- .ssc 脚本引擎与 RemoteControl 插件(ENABLE_SCRIPTING)、天文计算器导出 positions.xlsx(ENABLE_XLSX);
- 串口望远镜与 NMEA 定位编译开关(qtserialport/qtserialport 补编),IME 中文输入全链、物理键盘快捷键与修饰键、CapsLock 语义;
- 全屏沉浸、锁横屏、三窗口模式、多设备形态(2in1/平板/手机)适配与息屏稳定性防护。

尚未覆盖或明确关闭的能力(均有档案依据):视频与语音(ENABLE_MEDIA=0,Qt 栈缺 Qt6Multimedia/TextToSpeech);ShowMySky 大气模型(上游硬依赖桌面 OpenGL 3.3,定性不可行留档);SIMBAD 在线查询与插件在线更新(QtNetwork 无 TLS 后端,已知边界);INDI 协议客户端(CPM 网络受限期关闭);平板帧率下一档(需 update/GL 提交多线程化的上游级工程);MatePad 11.5 含 XLSX 引擎最终版的完整长跑回归(档案记载待设备回线补跑)。

因此,当前版本更准确的定位是"Stellarium 鸿蒙原生移植的多设备可用版":观星主链路在四种设备形态上端到端验证,联网与多媒体能力如实登记为边界,帧率架构级优化列为后续方向。

▍八、总结

AirSky 的移植过程展示了一个高依赖度 Qt6 上游在鸿蒙侧落地的完整形态:用补编 Qt 模块族补齐 REQUIRED 依赖,用四级 so 链与拓扑序 dlopen 建立装载通道,用直控链与注入落点修复重建输入模型,用数据包版本化与 TUNING 键值注入支撑快速迭代。相比编译本身,真正决定可用性的是 hmdfs 文件语义、息屏渲染生命周期、hover 缺失、多形态窗口差异这类平台细节,工程档案对每一处都保留了根因与验证记录。

后续工作的重心已由数据锚定:渲染管线的多线程化是帧率下一档的正解,属于上游级工程;TLS 后端补齐将解锁 SIMBAD 与插件在线更新;长跑稳定性回归与落盘文件的人工核验在档案中挂账。这些方向延续工程的既有方法:每个结论以探针与真机数据定案,每个边界以文档留档,不把未验证的能力计入围栏之内。
楼主 8 天前
想参与讨论?登录 或 30 秒注册。