Flutter 终端模拟器 - 写一个 Termux,开源篇

4,968 阅读7分钟

废话

最近社区输出越来越少了,实习回到家,一般8.30多吧,收拾收拾,电脑一开,有可能就写到凌晨一两点了。整个人状态也不怎么好,感觉这样时间长了,估计得直接转行。

这个终端的灵感主要来自 termux 和 termius 还有很早的 Android Terminal,起初想做一个和 termius 相似的产品,估计也是心有余而力不足了。(有生之年一定弄个!)

该终端涉及到的各部分细节以及相关技术难点,个人还是觉得比较多,我就不挨个将其中的东西拿来解析了,这篇主要是介绍这个使用 Flutter 框架开发的终端模拟器以及开源相关的内容。

还有很多得之后才能写上,大家感兴趣的先随便玩玩。

系列文章

  1. Flutter 终端模拟器探索篇(一)| 简易终端模拟器

  2. Flutter 终端模拟器探索篇(二)| 完整终端模拟器

  3. Flutter 终端模拟器探索篇(三)| 原理解析与集成

  4. Flutter 终端模拟器组件 - 开源篇

运行截图

部分设置还没有具体的响应逻辑

主要难点

1.终端序列的解析:这部分要做得很好实际是极难的,目前仅仅参考 xterm.js 进行了相关序列的解析,而从 xterm 序列 来看,Linux 终端涉及到的序列真的太太太多了,全部实现真就得有生之年系列了,而且解析的速度、正确率,都直接跟笔者的技术相关。感觉还是对很多技术的理解不够,写起来很吃力。但如果只实现 xterm.js 实现到的序列的话,经过后续的迭代还是没太大问题。

2.上层组件的渲染:整个终端组件是完全通过 canvas 根据缓存流计算然后绘制的,内部逻辑也相对复杂,需要确保在一帧的时间内完成整个终端可视窗口的绘制。

3.ffi 的适配:我期望的是各个平台都不需要单独编译 C 语言作为 so library,这样就不会存在很多 c 语言代码,每次都要编译到各个平台。从 ffigen 的出现以来,我尝试将所有的 c 语言代码完全由 dart 改写,但最后在 android 平台上表现不佳,在 dart_pty 的 readme 末尾有提到,所以目前还是采用引入 so 库的方式。

4.android 端源的搭建:这部分也比较麻烦,可躺了不少坑,主要是通过 termux-package 来交叉编译,有相关文章在编写中了。

核心仓库

不是本项目所在的仓库哦,只是核心依赖到的仓库。 termare_view:终端的上层组件,是一个 Widget,使用 canvas 进行绘制。

dart_pty:创建一个伪终端并执行一个子进程,可对进程进行交互。

软件源相关

集成了 apt 作为包管理器,就像使用 ubuntu 一样。

同步软件源

apt udpate

更新软件包

apt upgrade

安装软件包

apt install $PKG

不能使用 pkg install,pkg 其实就是 termux 封装的一个 shell 壳,内部去执行了 update ,但是 pkg 这个脚本文件内涉及到的源是 termux 的,不能应用于本软件。

安装 ZSH

跟PC一样,但是在修改字体上会有一点问题。zsh 很多库其实已经兼容了 termux,字体的重载是执行的 termux-reload-settings,这就是一个简单的 shell,调用 am 命令发送广播到 termux,termux 再进行更改,如果要适配到 termare,咱改改 shell 内广播发送到的包名,也监听一个广播,然后主动调用 Flutter 端代码吧(后续,不难的)。

也可以自己粘贴命令,但是设置内部也是自动键入命令的方法。

点击右上角设置->点击安装ZSH->选择安装源->完成安装

安装完成后,会询问是否将 zsh 设置为默认的 shell,这个时候输入 y 是不生效的,只有键入以下代码。

chsh

然后交互键入 zsh 即可实现切换默认的 shell。

安装 ZSH powerlevel10k 主题

点击右上角设置->点击安装ZSH->选择安装源->完成安装

安装代码补全

点击右上角设置->安装 zsh-autosuggestions->完成安装

遗留问题

以下列出目前主要的一些问题,当然还有很多细节的处理是没有做的。

性能相关

这部分跟上层组件 termare_view 和 dart_pty 两个仓库都有关系。

反复对比过 termare 相对 termux,在大量输出的情况,例如find /cmatrix -r,termare 性能较低,并且find /命令更耗时,已经尝试过定位耗时状况,与获取终端的轮询和页面构建的时间都有关系,主要在前者。例如 find / 命令,这个命令执行后会有大量的输出写入到终端文件描述符,在这个命令执行完成后,dart 端还在拼命去将内容读出来,解析序列,最后渲染,所以就让使用者感觉是命令本身执行耗时。

页面渲染部分目前尝试用Stopwatch ,在 canvas 内觉得耗时的代码后都打印了一个时间,从一次 build 的开始到结束基本是小于 11 ms的(根据 90 fps刷新率的设备),已经做了很多优化,所以在执行有大量输出的命令时,页面的构建其实并不是主要影响命令执行时间的因素,有点难解决,希望我能再厉害点吧!

序列相关

仅跟 termare_view 有关

按照 xterm.js 的 序列文档 适配了其中 90% 的序列,但个别序列实现比较复杂,例如 CSI ps;ps r,目前能正常解析,但在序列的行为表现上还有问题,还有个别序列没获取到具体的行为,例如ESC \

一些命令如 vim,htop,nano,在运行的时候使用到了较多的序列,主要表现在 vim 编写文本时,内容超过终端高度时涉及到的序列。而 htop 输出的终端序列实在太多,不好定位是哪个序列的问题导致表现上的差异。

Android 端依赖源相关

目前从 termux 组织 fork 了一份 termux-package 仓库到了 termare,改了关键的配置文件,来单独编译 termare 需要的源。

在 termux-package 内有接近900个包,由于执行 build-all.sh 的时候,有几个依赖的编译报错实在太难解决(个人技术原因),就把编译失败的都跳过了,但是 termux 内涉及到的99%都编译下来了,目前有832个包,如果你需要一些未编译的包,可以直接私信我,我如果能够成功编译的话会及时同步上去。

输入问题

在 PC 上不能输入中文,估计得细读 TextField 源码才成,还有按键等支持也比较少🤯。 在安卓端没有特殊按键的输入😩。

平台支持情况

目前只编译了 macOS 和 Android 这两个平台的包。实际是支持3个 PC 平台和 Android 平台的。

后续计划

这将会是我长期维护的开源项目,包括整个组织的其它项目,目前主要是保证在 Android 端的正常运行,自己的其他项目也依赖了这个终端模拟器。

  1. 完善 termare_view 的终端序列

  2. 对 PC 端的按键适配

  3. 将底层 c 语言部分除 setNonblock 的所有代码用 dart 重写。(其实已经实现了,还没时间整合)

  4. 性能优化

  5. 尝试为 android 平台编译更多的源

下载地址

大学在学的方向是前端,但是大部分的时间在学 Flutter,所以前端只学了一些基础,用 web 基础开发写了这个官网,顺便交了作业,在移动端的布局适配问题很大。

PC 平台的包可能会存在打不开的情况,我之前在一台 mac 编译后另一台打开直接显示损坏,搜到的一些方法也没用上,然后在另一台本地编译就行🤣

Termare下载地址

开源地址

termare_app

觉得好玩的话给颗✨吧!

草稿箱还有放了接近一年的文章都有,有终端源相关的,scrcpy、aqueduct、其他在做的一些项目的文章。也不知道还有没精力弄出来。Enjoy coding!