A
Air官方
【Air 系列 · 鸿蒙 PC 适配全记录】49 / 56
■ 应用:AirTerm(AirTerm)
■ 定位:Remmina 移植,多协议远程桌面客户端
■ 上游:Remmina 1.4.41 | 路线:GTK3 gtk3-stack | 版本:1.0.1
■ 开源: https://gitcode.com/Andy88/Harmony-Remmina
■ 市场状态:开发中,即将上架
■ 官网(免费下载): https://www.shunidc.cn/
————————————————————
开源鸿蒙 PC 社区: https://harmonypc.csdn.net/
PC 社区项目申请: https://atomgit.com/OpenHarmonyPCDeveloper
适配开源地址:https://gitcode.com/Andy88/Harmony-Remmina
环境搭建文章:https://blog.csdn.net/weixin_52908342/article/details/161343743
▍一、为什么要适配 AirTerm
AirTerm 的上游是 Remmina——GTK3 技术栈上最常用的远程桌面客户端,一个连接管理器带一组协议插件,覆盖 RDP、VNC、SSH/SFTP 等主流远程方式。它的价值不在单个协议做到最深,而在把日常需要反复连机器的场景收敛成一个入口:保存连接、双击进入、断线重连。这类工具在平板与二合一设备上的需求同样成立,鸿蒙 PC 缺的正是这样统一的远程桌面入口。
工程原名 AirRemmina,2026-09-20 更名为 AirTerm,显示名与包名(com.airterm.app)同步调整并重新生成了签名材料。目标设备为 HUAWEI MatePad 11.5(arm64-v8a,HarmonyOS 6.1.0/API 23),应用版本 1.0.0,deviceTypes 覆盖 tablet、2in1、phone。用户明确要求多实例同时开多个远程会话,因此 app.json5 配置 multiAppMode(multiInstance,上限 5),EntryAbility 声明 launchType: multiton;权限仅申请 INTERNET。
这个工程移植的是 Remmina 本体而非重写:native/sources/remmina 下是完整的上游源码树,GTK 界面、连接管理器、插件体系全部保留,适配工作集中在"让 GTK3 应用在鸿蒙的窗口与输入体系上活起来"。
▍二、先划清边界:不把编译通过当成适配完成
桌面 Linux 上的 Remmina 依赖一整套运行时前提:GDK 的 X11/Wayland 后端、窗口管理器、系统插件目录、托盘协议、以及可执行文件加 -rdynamic 导出的服务函数表。这些前提在鸿蒙沙箱里一个都不存在。如果只把 Remmina 编译成动态库就算完成,得到的是无法出窗口、无法加载插件、无法断开连接的空壳。工程因此把边界划在"可安装、可连接、可操作"上,每一层都明确替代方案:
层次 桌面 Linux Remmina 鸿蒙侧当前方案
应用入口 C main + GtkApplication EntryAbility + 薄 NAPI libremminabridge.so
窗口承载 GDK X11/Wayland 后端 + WM 自研 OHOS GDK 后端(gtk 3.24.53)+ ArkUI XComponent
主程序形态 -rdynamic 可执行文件 add_library(SHARED) 改造为 libremmina.so,main 导出为 remmina_ohos_main
插件加载 系统插件目录 REMMINA_RUNTIME_PLUGINDIR 重定向到 bundle libs 绝对路径 + g_module_open
RDP 用户自备 FreeRDP FreeRDP 3.30 + winpr3(AirRDP/AirVNC 产物复用)
VNC libvncclient libvncclient 0.9.15(AirVNC 拷入 + elf_edit 重处理)
SSH/SFTP libssh + libgcrypt libssh 0.11.1 + libsodium 1.0.20 新编,gcrypt 旧密码兼容裁剪
托盘图标 GtkStatusIcon 无托盘环境降级禁用(补丁 P4)
裁剪同样写进了边界:SPICE、X2GO、VTE 终端、WWW 浏览器、Avahi 发现等特性在 CMake configure 期显式关闭,WITH_NEWS/STATS/TIP=OFF 砍掉需要联网的附属功能。哪些能力进入当前产物、哪些显式不承诺,在 PORTING.md 的裁剪开关一节列得清清楚楚。
▍三、鸿蒙版的整体架构
工程采用 Air 系列标准的双层模式:ArkTS 壳只负责 Ability 生命周期、rawfile 数据部署与 XComponent 宿主;一层薄 NAPI 桥按拓扑序 dlopen 整个依赖栈(RTLD_GLOBAL),再进入 core 宿主拉起 GTK 与 Remmina 本体。协议插件以独立 .so 形态在运行时由 g_module_open 加载。
text
ArkTS 壳(entry/src/main/ets:Index.ets XComponent + EntryAbility rawfile 部署/沉浸式)
│ NAPI: libremminabridge.so(薄桥,只链系统库;拓扑序 dlopen 依赖栈 RTLD_GLOBAL)
▼
libairremmina_core.so(宿主:env/loaders.cache/日志txt/SIGSEGV/事件注入 + 链 libremmina.so)
├── libremmina.so(Remmina src/ 全量,main→remmina_ohos_entry)
├── GTK3 栈(libgtk-3/libgdk-3 含 OHOS 后端 + glib 2.80.4 全家,native/install 共享复用)
├── 新编依赖:libsodium 1.0.20 / libssh 0.11.1 / curl 8.14.1+TLS / PCRE2
└── 插件(entry/libs/arm64-v8a,g_module_open dlopen):
remmina-plugin-vnc.so(libvncclient 0.9.15)
remmina-plugin-rdp.so(FreeRDP 3.30 + winpr3)
remmina-plugin-exec.so(零依赖,沙箱内受限)
主要目录如下:
text
AirTerm/
├── PORTING.md # 移植手册(活文档:策略/补丁/风险/里程碑)
├── entry/
│ ├── src/main/ets/ # Index.ets(XComponent/多实例)、EntryAbility.ets
│ ├── src/main/cpp/ # airremmina_core.cpp、napi_init.cpp、闭包基库 libs/
│ └── libs/arm64-v8a/ # 运行时 dlopen:插件、freerdp、vnc、loaders
├── native/
│ ├── sources/ # remmina 源码树 + curl/libsodium/libssh/libvncserver
│ ├── scripts/ # env.sh、build_m1_deps.sh、build_remmina.sh、
│ │ # deploy_remmina.sh、elf_edit.py、fix_elf_align.py
│ ├── cross/ohos-toolchain.cmake # CMake 交叉工具链
│ ├── install -> Frameworks/prebuilt/gtk3-stack # 共享 GTK3 栈(与 AirPhoto/AirRaw 同源)
│ └── logs/ # 各依赖构建日志
├── tools/ # mini_vnc_server.py、mini_ssh_server.py 测试设施
└── screenshots/ # 各里程碑真机截图存档
▍四、核心适配过程
### 1. 把可执行文件改造成共享库
Remmina 的主程序是带 -rdynamic 的可执行文件,插件通过 g_module_open 加载后回查主程序的服务函数表——这个模式在鸿蒙上不成立(OHOS 禁 exec,插件也拿不到可执行文件的符号表)。补丁 P1 将 src/CMakeLists.txt 的 add_executable 改为 add_library(remmina SHARED),去掉 -rdynamic 与 --no-undefined,把 main 改名导出为 remmina_ohos_main;桥与 core 以 RTLD_GLOBAL 方式 dlopen 整条链,插件加载时符号自然可解析。这一机制与 Air 系列其他工程处理 GEGL 算子的先例同源。
### 2. 三层依赖栈复用与新建
GTK3 栈不重复构建:native/install 指向共享 gtk3-stack(glib 2.80.4、gtk+ 3.24.53 含 OHOS GDK 后端、pango、cairo、rsvg 等,与 AirPhoto/AirRaw 同源同版本)。协议依赖分两类:FreeRDP 3.30/winpr3 与 libvncserver/libvncclient 0.9.15 从兄弟工程 AirVNC 的产物拷入并重跑 elf_edit(去版本号 NEEDED、去 libc.so);libsodium 1.0.20、libssh 0.11.1、curl 8.14.1 则由 native/scripts/build_m1_deps.sh 交叉新编,openssl 3.4.1 静态链入。
### 3. 双通道打包与 ELF 纪律
打包分两条通道:能进 hvigor 闭包的基础库放 entry/src/main/cpp/libs/arm64-v8a;运行时才 dlopen 的 loaders、协议插件、freerdp/vnc 库放模块级 entry/libs/arm64-v8a。两条通道配合两道保险:build-profile.json5 在 debug 与 release 都设 strip:false,module.json5 设 compressNativeLibs:false,防止 hvigor 的 DoNativeStrip 重排 PT_LOAD 对齐导致 musl dlopen 崩溃。REMMINA_RUNTIME_PLUGINDIR(补丁 P2)重定向到设备上 bundle libs 的绝对路径,插件才能被找到。
### 4. core 宿主与启动时序
entry/src/main/cpp/airremmina_core.cpp 承担 GTK 之外的全部环境自理:线程第一时间前置 HOME/XDG/GDK_BACKEND=ohos/GIO_USE_VFS=local 等环境变量;用 condvar 实现 notifyDatafilesReady 门控,保证 rawfile 数据部署完成后才执行 gtk_init(对应手册里 OnSurfaceCreated 早于数据就绪的经典竞态);幂等写 settings.ini 关闭 overlay 滚动条(触摸屏上不可见);无 WM 环境下用 gtk_header_bar_set_decoration_layout(":") 清掉主窗口 CSD 的最小化/最大化/关闭三钮(补丁 P7,对话框不受影响)。
### 5. 多实例远程会话
launchType: multiton 让每次启动都得到独立 Ability 实例,但平板上进程级 multiAppMode 不生效,多实例要靠应用层自己完成(补丁 P8/P9):实例 #2 起 XComponent id 带 airremmina-instance-N 后缀,native 侧按 id 分流开独立连接,以 UTILITY hint 分类子窗口并绑定 surface;输入事件按 local_to_root 分流到对应实例;关闭连接必须走 remmina_protocol_widget_close_connection(外部直接 destroy rcw 必崩);实例号由 native 分配——ArkTS 模块级状态在 multiton 实例间不共享。
▍五、适配中遇到的几个难点
### 难点1:构建成功不等于能 dlopen
M0 首轮构建曾出现 splash 卡在"准备 Remmina 数据...":桥的 dlopen 清单里包含 lcms2、json-c、potrace 等并未部署的库,LoadCoreLibraries 直接中止。这暴露了闭包打包的隐蔽性——不在闭包里的库被静默丢弃,只有把 dlopen 清单收敛到"已部署库"才能通过。相关对策固化在 PORTING.md 的踩坑表第 2 条。
### 难点2:ELF 处理必须在正确的前提上做
三件套脚本(elf_edit.py、fix_elf_align.py、unpatchelf.py)承担去 NEEDED 版本号、去 libc.so 依赖与对齐修复,但前提是拷对库:NDK sysroot 里的 libz.so 是 8KB 的 ret 桩,绝不能进部署清单,必须用 install 前缀的真库;从 AirVNC 拷来的 freerdp3/vnc 库必须重跑 elf_edit 而不是直接使用。deploy_remmina.sh 把这些规则固化成了流程。
### 难点3:签名材料的证书链校验
自建 CA 签发的 p7b 在 MatePad 上被拒装(错误码 9568257,fail to verify pkcs7)——新版系统会校验完整证书链,AirShark 时代可行的自建 CA 方案失效。M0 实录记录了最终路线:改用 devecocli 的云端调试签名一次通过。这也解释了更名后签名材料为何整套重新生成。
### 难点4:FreeRDP 3.30 与 Remmina 1.4.41 的版本差
风险登记册把这定为高等级风险:Remmina 的 freerdp3 适配写于 2023 年,而复用的是 AirRDP 的 3.30.0。实际结果好于预期——编译错误驱动排查后,M2 里程碑记录"FreeRDP 3.30 API 兼容零补丁",RDP 插件在 M5 完成重链(补 libc++ NEEDED)与客户端链路验证。
### 难点5:musl 沙箱的行为差异
pthread_cancel 在 Remmina 断开连接的 7 处调用中使用,musl 有实现但语义需真机回归;g_spawn 类调用(exec 插件、外部工具)在无 fork 沙箱内必然失败,处理方式是编译保留、运行时 fail-safe,并在文档中如实标注"exec 插件功能受限"。不做掩盖性修补,是这类边界处理的原则。
▍六、编译、安装与启动
native 侧按依赖顺序构建(依赖已就位时各脚本幂等),再走 hvigor 打包:
bash
source native/scripts/env.sh
bash native/scripts/build_m1_deps.sh # libsodium/libssh/libvncserver/curl
bash native/scripts/build_remmina.sh # libremmina.so + 插件
bash native/scripts/deploy_remmina.sh # elf_edit + 双通道部署
ArkTS 侧标准 hvigor 构建:
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(注意 compressNativeLibs:false 与 strip:false 是运行前提,不要移除)。安装启动:
bash
hdc install -r entry/build/default/outputs/default/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.airterm.app
PORTING.md 里程碑记录了各阶段真机验证:M0 GTK 空窗口渲染、M3 连接管理器全屏、M4 VNC 端到端(自写 mini RFB server 验证渲染与触摸回传)、M5 RDP 插件链路、M6 SFTP 实测(paramiko mini server)、M8 真实 VNC 服务器矩阵(TigerVNC/TightVNC/x11vnc 正向 4 用例加 TLSNone/错密码负向 2 用例,后补 TurboVNC/QEMU/wayvnc 矩阵),截图存于 screenshots/。
▍七、当前功能边界
当前版本已覆盖的能力(依据 PORTING.md 里程碑与补丁清单):
- 可安装 HAP:Stage 模型、XComponent 承载、GTK3 OHOS 后端渲染全屏窗口;
- Remmina 连接管理器完整 UI(真机 100% 铺满)与简中文化(fonts.conf + zh_CN);
- VNC 插件端到端:libvncclient 0.9.15,通过 TigerVNC/TightVNC/x11vnc/TurboVNC/QEMU 多服务器矩阵验证,含触摸回传与优雅超时负向用例;
- RDP 插件:FreeRDP 3.30 + winpr3 链路验证;
- SFTP 端到端:libssh 0.11.1 + quick-connect sftp:// 补丁(P6,修复上游无 sftp 分支的缺口);
- 多实例:multiton 启动、独立连接窗口、输入分流、安全关闭;
- 密码存储:libsodium 加密(gcrypt 旧格式兼容显式裁剪)。
尚未进入当前产物的能力:SPICE、X2GO、VTE、WWW、AVAHI、KF5Wallet 等特性按裁剪开关关闭;libsecret 密钥环插件因无依赖自动跳过;exec 插件受沙箱限制 spawn 会失败;托盘图标在无 tray 环境禁用。这些全部是文档中显式声明的取舍,而非遗漏。
因此,当前版本更准确的定位是"Remmina 鸿蒙远程桌面基础版":以 VNC 与 SSH/SFTP 为验证最充分的通道,RDP 链路已通但服务器矩阵覆盖仍在扩展。
▍八、总结
AirTerm 的适配过程验证了一条路线的成熟度:GTK3 加自研 OHOS GDK 后端,可以承载一个插件化、多协议、界面复杂的桌面应用,而不需要重写它的任何一层业务代码。工程里真正花力气的部分都在结合部——可执行改共享库后的符号回查、双通道打包的 ELF 纪律、rawfile 与窗口创建的时序、多实例的窗口治理。这些经验以踩坑表、风险登记册与里程碑实录的形式留在了 PORTING.md,后续 Air 系列 GTK 路线的工程可以直接继承。
后续工作的重心按风险登记册推进:RDP 对真实服务器的回归扩展、pthread_cancel 断开语义的长连接观察、触摸手势在远程画布上的体验打磨。每一步仍以"可构建的 HAP 加真机连接行为"为验收口径。
■ 应用:AirTerm(AirTerm)
■ 定位:Remmina 移植,多协议远程桌面客户端
■ 上游:Remmina 1.4.41 | 路线:GTK3 gtk3-stack | 版本:1.0.1
■ 开源: https://gitcode.com/Andy88/Harmony-Remmina
■ 市场状态:开发中,即将上架
■ 官网(免费下载): https://www.shunidc.cn/
————————————————————
开源鸿蒙 PC 社区: https://harmonypc.csdn.net/
PC 社区项目申请: https://atomgit.com/OpenHarmonyPCDeveloper
适配开源地址:https://gitcode.com/Andy88/Harmony-Remmina
环境搭建文章:https://blog.csdn.net/weixin_52908342/article/details/161343743
▍一、为什么要适配 AirTerm
AirTerm 的上游是 Remmina——GTK3 技术栈上最常用的远程桌面客户端,一个连接管理器带一组协议插件,覆盖 RDP、VNC、SSH/SFTP 等主流远程方式。它的价值不在单个协议做到最深,而在把日常需要反复连机器的场景收敛成一个入口:保存连接、双击进入、断线重连。这类工具在平板与二合一设备上的需求同样成立,鸿蒙 PC 缺的正是这样统一的远程桌面入口。
工程原名 AirRemmina,2026-09-20 更名为 AirTerm,显示名与包名(com.airterm.app)同步调整并重新生成了签名材料。目标设备为 HUAWEI MatePad 11.5(arm64-v8a,HarmonyOS 6.1.0/API 23),应用版本 1.0.0,deviceTypes 覆盖 tablet、2in1、phone。用户明确要求多实例同时开多个远程会话,因此 app.json5 配置 multiAppMode(multiInstance,上限 5),EntryAbility 声明 launchType: multiton;权限仅申请 INTERNET。
这个工程移植的是 Remmina 本体而非重写:native/sources/remmina 下是完整的上游源码树,GTK 界面、连接管理器、插件体系全部保留,适配工作集中在"让 GTK3 应用在鸿蒙的窗口与输入体系上活起来"。
▍二、先划清边界:不把编译通过当成适配完成
桌面 Linux 上的 Remmina 依赖一整套运行时前提:GDK 的 X11/Wayland 后端、窗口管理器、系统插件目录、托盘协议、以及可执行文件加 -rdynamic 导出的服务函数表。这些前提在鸿蒙沙箱里一个都不存在。如果只把 Remmina 编译成动态库就算完成,得到的是无法出窗口、无法加载插件、无法断开连接的空壳。工程因此把边界划在"可安装、可连接、可操作"上,每一层都明确替代方案:
层次 桌面 Linux Remmina 鸿蒙侧当前方案
应用入口 C main + GtkApplication EntryAbility + 薄 NAPI libremminabridge.so
窗口承载 GDK X11/Wayland 后端 + WM 自研 OHOS GDK 后端(gtk 3.24.53)+ ArkUI XComponent
主程序形态 -rdynamic 可执行文件 add_library(SHARED) 改造为 libremmina.so,main 导出为 remmina_ohos_main
插件加载 系统插件目录 REMMINA_RUNTIME_PLUGINDIR 重定向到 bundle libs 绝对路径 + g_module_open
RDP 用户自备 FreeRDP FreeRDP 3.30 + winpr3(AirRDP/AirVNC 产物复用)
VNC libvncclient libvncclient 0.9.15(AirVNC 拷入 + elf_edit 重处理)
SSH/SFTP libssh + libgcrypt libssh 0.11.1 + libsodium 1.0.20 新编,gcrypt 旧密码兼容裁剪
托盘图标 GtkStatusIcon 无托盘环境降级禁用(补丁 P4)
裁剪同样写进了边界:SPICE、X2GO、VTE 终端、WWW 浏览器、Avahi 发现等特性在 CMake configure 期显式关闭,WITH_NEWS/STATS/TIP=OFF 砍掉需要联网的附属功能。哪些能力进入当前产物、哪些显式不承诺,在 PORTING.md 的裁剪开关一节列得清清楚楚。
▍三、鸿蒙版的整体架构
工程采用 Air 系列标准的双层模式:ArkTS 壳只负责 Ability 生命周期、rawfile 数据部署与 XComponent 宿主;一层薄 NAPI 桥按拓扑序 dlopen 整个依赖栈(RTLD_GLOBAL),再进入 core 宿主拉起 GTK 与 Remmina 本体。协议插件以独立 .so 形态在运行时由 g_module_open 加载。
text
ArkTS 壳(entry/src/main/ets:Index.ets XComponent + EntryAbility rawfile 部署/沉浸式)
│ NAPI: libremminabridge.so(薄桥,只链系统库;拓扑序 dlopen 依赖栈 RTLD_GLOBAL)
▼
libairremmina_core.so(宿主:env/loaders.cache/日志txt/SIGSEGV/事件注入 + 链 libremmina.so)
├── libremmina.so(Remmina src/ 全量,main→remmina_ohos_entry)
├── GTK3 栈(libgtk-3/libgdk-3 含 OHOS 后端 + glib 2.80.4 全家,native/install 共享复用)
├── 新编依赖:libsodium 1.0.20 / libssh 0.11.1 / curl 8.14.1+TLS / PCRE2
└── 插件(entry/libs/arm64-v8a,g_module_open dlopen):
remmina-plugin-vnc.so(libvncclient 0.9.15)
remmina-plugin-rdp.so(FreeRDP 3.30 + winpr3)
remmina-plugin-exec.so(零依赖,沙箱内受限)
主要目录如下:
text
AirTerm/
├── PORTING.md # 移植手册(活文档:策略/补丁/风险/里程碑)
├── entry/
│ ├── src/main/ets/ # Index.ets(XComponent/多实例)、EntryAbility.ets
│ ├── src/main/cpp/ # airremmina_core.cpp、napi_init.cpp、闭包基库 libs/
│ └── libs/arm64-v8a/ # 运行时 dlopen:插件、freerdp、vnc、loaders
├── native/
│ ├── sources/ # remmina 源码树 + curl/libsodium/libssh/libvncserver
│ ├── scripts/ # env.sh、build_m1_deps.sh、build_remmina.sh、
│ │ # deploy_remmina.sh、elf_edit.py、fix_elf_align.py
│ ├── cross/ohos-toolchain.cmake # CMake 交叉工具链
│ ├── install -> Frameworks/prebuilt/gtk3-stack # 共享 GTK3 栈(与 AirPhoto/AirRaw 同源)
│ └── logs/ # 各依赖构建日志
├── tools/ # mini_vnc_server.py、mini_ssh_server.py 测试设施
└── screenshots/ # 各里程碑真机截图存档
▍四、核心适配过程
### 1. 把可执行文件改造成共享库
Remmina 的主程序是带 -rdynamic 的可执行文件,插件通过 g_module_open 加载后回查主程序的服务函数表——这个模式在鸿蒙上不成立(OHOS 禁 exec,插件也拿不到可执行文件的符号表)。补丁 P1 将 src/CMakeLists.txt 的 add_executable 改为 add_library(remmina SHARED),去掉 -rdynamic 与 --no-undefined,把 main 改名导出为 remmina_ohos_main;桥与 core 以 RTLD_GLOBAL 方式 dlopen 整条链,插件加载时符号自然可解析。这一机制与 Air 系列其他工程处理 GEGL 算子的先例同源。
### 2. 三层依赖栈复用与新建
GTK3 栈不重复构建:native/install 指向共享 gtk3-stack(glib 2.80.4、gtk+ 3.24.53 含 OHOS GDK 后端、pango、cairo、rsvg 等,与 AirPhoto/AirRaw 同源同版本)。协议依赖分两类:FreeRDP 3.30/winpr3 与 libvncserver/libvncclient 0.9.15 从兄弟工程 AirVNC 的产物拷入并重跑 elf_edit(去版本号 NEEDED、去 libc.so);libsodium 1.0.20、libssh 0.11.1、curl 8.14.1 则由 native/scripts/build_m1_deps.sh 交叉新编,openssl 3.4.1 静态链入。
### 3. 双通道打包与 ELF 纪律
打包分两条通道:能进 hvigor 闭包的基础库放 entry/src/main/cpp/libs/arm64-v8a;运行时才 dlopen 的 loaders、协议插件、freerdp/vnc 库放模块级 entry/libs/arm64-v8a。两条通道配合两道保险:build-profile.json5 在 debug 与 release 都设 strip:false,module.json5 设 compressNativeLibs:false,防止 hvigor 的 DoNativeStrip 重排 PT_LOAD 对齐导致 musl dlopen 崩溃。REMMINA_RUNTIME_PLUGINDIR(补丁 P2)重定向到设备上 bundle libs 的绝对路径,插件才能被找到。
### 4. core 宿主与启动时序
entry/src/main/cpp/airremmina_core.cpp 承担 GTK 之外的全部环境自理:线程第一时间前置 HOME/XDG/GDK_BACKEND=ohos/GIO_USE_VFS=local 等环境变量;用 condvar 实现 notifyDatafilesReady 门控,保证 rawfile 数据部署完成后才执行 gtk_init(对应手册里 OnSurfaceCreated 早于数据就绪的经典竞态);幂等写 settings.ini 关闭 overlay 滚动条(触摸屏上不可见);无 WM 环境下用 gtk_header_bar_set_decoration_layout(":") 清掉主窗口 CSD 的最小化/最大化/关闭三钮(补丁 P7,对话框不受影响)。
### 5. 多实例远程会话
launchType: multiton 让每次启动都得到独立 Ability 实例,但平板上进程级 multiAppMode 不生效,多实例要靠应用层自己完成(补丁 P8/P9):实例 #2 起 XComponent id 带 airremmina-instance-N 后缀,native 侧按 id 分流开独立连接,以 UTILITY hint 分类子窗口并绑定 surface;输入事件按 local_to_root 分流到对应实例;关闭连接必须走 remmina_protocol_widget_close_connection(外部直接 destroy rcw 必崩);实例号由 native 分配——ArkTS 模块级状态在 multiton 实例间不共享。
▍五、适配中遇到的几个难点
### 难点1:构建成功不等于能 dlopen
M0 首轮构建曾出现 splash 卡在"准备 Remmina 数据...":桥的 dlopen 清单里包含 lcms2、json-c、potrace 等并未部署的库,LoadCoreLibraries 直接中止。这暴露了闭包打包的隐蔽性——不在闭包里的库被静默丢弃,只有把 dlopen 清单收敛到"已部署库"才能通过。相关对策固化在 PORTING.md 的踩坑表第 2 条。
### 难点2:ELF 处理必须在正确的前提上做
三件套脚本(elf_edit.py、fix_elf_align.py、unpatchelf.py)承担去 NEEDED 版本号、去 libc.so 依赖与对齐修复,但前提是拷对库:NDK sysroot 里的 libz.so 是 8KB 的 ret 桩,绝不能进部署清单,必须用 install 前缀的真库;从 AirVNC 拷来的 freerdp3/vnc 库必须重跑 elf_edit 而不是直接使用。deploy_remmina.sh 把这些规则固化成了流程。
### 难点3:签名材料的证书链校验
自建 CA 签发的 p7b 在 MatePad 上被拒装(错误码 9568257,fail to verify pkcs7)——新版系统会校验完整证书链,AirShark 时代可行的自建 CA 方案失效。M0 实录记录了最终路线:改用 devecocli 的云端调试签名一次通过。这也解释了更名后签名材料为何整套重新生成。
### 难点4:FreeRDP 3.30 与 Remmina 1.4.41 的版本差
风险登记册把这定为高等级风险:Remmina 的 freerdp3 适配写于 2023 年,而复用的是 AirRDP 的 3.30.0。实际结果好于预期——编译错误驱动排查后,M2 里程碑记录"FreeRDP 3.30 API 兼容零补丁",RDP 插件在 M5 完成重链(补 libc++ NEEDED)与客户端链路验证。
### 难点5:musl 沙箱的行为差异
pthread_cancel 在 Remmina 断开连接的 7 处调用中使用,musl 有实现但语义需真机回归;g_spawn 类调用(exec 插件、外部工具)在无 fork 沙箱内必然失败,处理方式是编译保留、运行时 fail-safe,并在文档中如实标注"exec 插件功能受限"。不做掩盖性修补,是这类边界处理的原则。
▍六、编译、安装与启动
native 侧按依赖顺序构建(依赖已就位时各脚本幂等),再走 hvigor 打包:
bash
source native/scripts/env.sh
bash native/scripts/build_m1_deps.sh # libsodium/libssh/libvncserver/curl
bash native/scripts/build_remmina.sh # libremmina.so + 插件
bash native/scripts/deploy_remmina.sh # elf_edit + 双通道部署
ArkTS 侧标准 hvigor 构建:
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(注意 compressNativeLibs:false 与 strip:false 是运行前提,不要移除)。安装启动:
bash
hdc install -r entry/build/default/outputs/default/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.airterm.app
PORTING.md 里程碑记录了各阶段真机验证:M0 GTK 空窗口渲染、M3 连接管理器全屏、M4 VNC 端到端(自写 mini RFB server 验证渲染与触摸回传)、M5 RDP 插件链路、M6 SFTP 实测(paramiko mini server)、M8 真实 VNC 服务器矩阵(TigerVNC/TightVNC/x11vnc 正向 4 用例加 TLSNone/错密码负向 2 用例,后补 TurboVNC/QEMU/wayvnc 矩阵),截图存于 screenshots/。
▍七、当前功能边界
当前版本已覆盖的能力(依据 PORTING.md 里程碑与补丁清单):
- 可安装 HAP:Stage 模型、XComponent 承载、GTK3 OHOS 后端渲染全屏窗口;
- Remmina 连接管理器完整 UI(真机 100% 铺满)与简中文化(fonts.conf + zh_CN);
- VNC 插件端到端:libvncclient 0.9.15,通过 TigerVNC/TightVNC/x11vnc/TurboVNC/QEMU 多服务器矩阵验证,含触摸回传与优雅超时负向用例;
- RDP 插件:FreeRDP 3.30 + winpr3 链路验证;
- SFTP 端到端:libssh 0.11.1 + quick-connect sftp:// 补丁(P6,修复上游无 sftp 分支的缺口);
- 多实例:multiton 启动、独立连接窗口、输入分流、安全关闭;
- 密码存储:libsodium 加密(gcrypt 旧格式兼容显式裁剪)。
尚未进入当前产物的能力:SPICE、X2GO、VTE、WWW、AVAHI、KF5Wallet 等特性按裁剪开关关闭;libsecret 密钥环插件因无依赖自动跳过;exec 插件受沙箱限制 spawn 会失败;托盘图标在无 tray 环境禁用。这些全部是文档中显式声明的取舍,而非遗漏。
因此,当前版本更准确的定位是"Remmina 鸿蒙远程桌面基础版":以 VNC 与 SSH/SFTP 为验证最充分的通道,RDP 链路已通但服务器矩阵覆盖仍在扩展。
▍八、总结
AirTerm 的适配过程验证了一条路线的成熟度:GTK3 加自研 OHOS GDK 后端,可以承载一个插件化、多协议、界面复杂的桌面应用,而不需要重写它的任何一层业务代码。工程里真正花力气的部分都在结合部——可执行改共享库后的符号回查、双通道打包的 ELF 纪律、rawfile 与窗口创建的时序、多实例的窗口治理。这些经验以踩坑表、风险登记册与里程碑实录的形式留在了 PORTING.md,后续 Air 系列 GTK 路线的工程可以直接继承。
后续工作的重心按风险登记册推进:RDP 对真实服务器的回归扩展、pthread_cancel 断开语义的长连接观察、触摸手势在远程画布上的体验打磨。每一步仍以"可构建的 HAP 加真机连接行为"为验收口径。
楼主
8 天前