基于 Go、cgo、GTK3 和 VTE 的 Linux 优先终端模拟器项目。
本文档是项目的长期方案、架构约束和开发进度记录。开发过程中需要持续更新,代码实现与本文档不一致时,应优先修正文档或明确记录偏差。
MothTerm 首先实现一个 Linux 桌面终端应用,后续预留 macOS 和 Windows 支持能力。
- GTK3 主窗口
- VTE 终端控件
- 启动默认 Shell
- 启动指定命令
- PTY 窗口大小同步
- 输入、输出、颜色、光标和滚动
- 复制、粘贴、全选
- 字体、前景色和背景色配置
- 新建、关闭和切换标签页
- Shell 标题同步到标签页
- Shell 退出状态处理
- 基本菜单和快捷键
- Linux 打包和安装
- Windows/macOS 的完整实现
- 分屏
- 配置图形界面
- 搜索
- URL 检测
- 多 Profile
- 插件系统
- 图片协议
- 自己实现 ANSI/VT100 解析器
- 自己实现 PTY
- 自己绘制完整终端网格
- Linux 优先:先完成可用的 Linux 版本,再验证其他平台。
- 跨平台预留,不提前过度实现:公共应用层和终端抽象层保持平台无关,但具体后端先只实现 Linux。
- VTE 优先复用:第一版使用 VTE 提供终端模拟能力,不重复实现终端协议和 PTY。
- 隔离 cgo:GTK、GLib、VTE 的原始 C API 集中在 Bridge 层,不扩散到 Go 业务代码。
- GTK 主线程原则:所有 GTK 对象操作都在 GTK 主循环线程执行。
- 明确生命周期:所有 GTK/GObject/VTE 句柄必须有明确的创建、持有、释放路径。
- 小步验证:先验证 GTK3/VTE 环境,再接入 cgo,最后增加应用功能。
Go 应用层
├── App 生命周期
├── Window 和 Tab 管理
├── 配置
├── 快捷键和 Action
└── TerminalSession 抽象
│
│ cgo
▼
C Bridge 层
├── GTK3
├── GLib/GObject
├── VTE
├── 回调桥接
└── 对象生命周期管理
│
▼
Linux 平台
├── POSIX PTY
├── Shell
└── X11/Wayland
GTK3 + VTE + Linux POSIX PTY
VTE 负责:
- 终端转义序列解析
- PTY 集成
- 光标和屏幕状态
- 颜色和字体渲染
- 滚动历史
- 选择、复制和粘贴
- 鼠标事件和终端输入协议
Go 负责:
- 应用生命周期
- 标签页和窗口模型
- 参数解析
- 配置读写
- Action 和快捷键
- 日志和状态管理
后续目标平台和终端后端预计如下:
| 平台 | GUI 后端 | 终端进程后端 | 当前状态 |
|---|---|---|---|
| Linux | GTK3 + X11/Wayland | POSIX PTY | 首先实现 |
| macOS | GTK3 + Quartz | POSIX PTY | 预留 |
| Windows | GTK3 + Win32 | ConPTY | 预留 |
Go 应用层不得直接依赖 VteTerminal*。应依赖抽象接口:
type TerminalSession interface {
Spawn(spec CommandSpec) error
Write(data []byte) error
Resize(cols, rows int) error
Close() error
}命令描述:
type CommandSpec struct {
Program string
Args []string
Dir string
Env []string
}Linux 实现可以内部使用 VTE;未来 Windows 可以增加 ConPTY 实现,不应影响 App、Window、Tab 和 Config 层。
mothterm/
├── AGENTS.md
├── README.md
├── LICENSE
├── CONTRIBUTING.md
├── SECURITY.md
├── CODE_OF_CONDUCT.md
├── go.mod
├── Makefile
├── docs/
│ ├── README.md
│ └── PLAN.md
├── scripts/
│ └── check-deps.sh
├── examples/
│ └── m0_demo.c
│
├── cmd/
│ └── mothterm/
│ └── main.go
│
├── internal/
│ ├── app/
│ ├── window/
│ ├── tab/
│ ├── config/
│ └── terminal/
│ ├── session.go
│ ├── backend.go
│ ├── events.go
│ ├── command.go
│ └── linux_session.go
│
├── bridge/
│ ├── bridge.h
│ ├── bridge.c
│ ├── gtk_app.c
│ ├── gtk_window.c
│ ├── vte_terminal.c
│ └── callbacks.c
│
├── ui/
│ └── window.ui
│
├── data/
│ └── mothterm.desktop
│
└── packaging/
└── linux/
暂时不创建 bridge/windows 和 bridge/macos,等 Linux MVP 稳定后再添加。
Go 只通过 bridge.h 中的封装函数使用 GTK/VTE。
typedef void* MothAppHandle;
typedef void* MothWindowHandle;
typedef void* MothTerminalHandle;
MothAppHandle moth_app_new(const char *app_id);
int moth_app_run(MothAppHandle app);
MothTerminalHandle moth_terminal_new(void);
int moth_terminal_spawn(MothTerminalHandle terminal,
const char *cwd,
const char *program,
char **argv);
void moth_terminal_resize(MothTerminalHandle terminal,
int columns,
int rows);
void moth_terminal_destroy(MothTerminalHandle terminal);C 代码不长期保存 Go 指针。回调关联使用整数 ID 或 runtime/cgo.Handle。
C callback data: uint64 callback_id
Go: map[uint64]*Terminal
对象销毁时必须同时注销回调并释放关联 ID。
- GTK 初始化和主循环在指定的 GTK 主线程执行。
- Go 后台 goroutine 不直接操作 GTK 对象。
- 跨线程 UI 更新通过 GLib 主循环调度。
- C 回调进入 Go 时不执行耗时业务,必要时投递到 Go 事件队列。
状态:已完成(2026-08-19)
目标:确认系统依赖和最小 C GTK/VTE 程序可运行。
检查结果:
GTK3: 3.24.49
VTE runtime: 0.80.1
Go: go1.26.1 linux/amd64
GCC: 14.2.0
显示后端: X11(DISPLAY=:0.0)
当前开发环境没有安装 libvte-2.91-dev,但已安装 VTE runtime。由于当前用户无法通过 sudo 安装系统包,本阶段使用 apt-get download 下载开发包并解压到项目的 .deps/vte-root,通过本地头文件和系统 runtime 完成编译验证。
已创建:
examples/m0_demo.c
.deps/vte-root/
编译验证通过,Demo 能够启动 GTK3 窗口、显示 VTE、异步启动默认 Shell,并打印 Shell PID。由于终端命令环境没有主动关闭窗口,使用 timeout 4s ./examples/m0_demo 结束验证进程,退出码 124 是 timeout 的预期结果,不代表 Demo 启动失败。
已知事项:
- 当前本地开发包是临时解压方式,后续应优先安装系统级
libvte-2.91-dev。 - 初始 Demo 仅用于环境验证,后续 M1 会重构为正式 C Bridge,不直接复用 Demo 的应用结构。
验收:纯 C Demo 可以创建 GTK3 窗口、显示 VTE,并启动 Shell。通过。
状态:已完成(2026-08-19)
已创建 Go module、C Bridge 和 Go 启动程序。Bridge 封装 GTK3/VTE,启动默认 Shell 或 -e 指定命令,支持窗口创建、VTE 终端、Shell 标题/退出状态回调和基础标签页容器。
验收:运行程序后能够执行 bash、vim、top 等交互式程序。
状态:已完成(2026-08-19)
已实现字体、前景/背景色、光标色、选区色、光标闪烁、响铃和滚动历史配置,以及 VTE 自带的输入输出、PTY、选择、复制粘贴和窗口尺寸能力;已增加 Reset、缩放和菜单 Action。
状态:已完成(2026-08-19)
已实现 Notebook 标签页容器、独立 VTE Shell、标题更新、退出状态显示、Ctrl+Shift+T/W、Ctrl+PageUp/PageDown、关闭时 PTY 回收,以及独立菜单操作。最后一个标签页关闭时退出应用。
状态:已完成(2026-08-19)
已实现 JSON 配置默认值、读取、校验、原子保存 API,并接入启动读取;已实现新建/关闭、复制/粘贴/全选、Reset、缩放和退出菜单 Action。
状态:已完成(2026-08-19)
已添加 Makefile、README、desktop 文件、依赖检查脚本、Debug/Release 构建和安装目标;已完成本地构建与 smoke 验证。
本轮实现新增 internal/terminal 抽象(CommandSpec 与 Session)、配置校验和原子保存、GTK 菜单操作、标签页标题/状态和 PTY 关闭回收、依赖检查脚本以及 Debug/Release 构建目标。实际 Bridge 仍集中在单一 bridge.c 文件中,与规划中的多文件拆分存在差异,但 Go 应用层未直接使用 GTK/VTE 类型。
验收结果:go test ./...、make build、make release、make check-deps 均通过;timeout 3s ./mothterm -e 'printf ready' 返回预期 124,stderr 为空。构建仍有 VTE deprecated API 警告,功能不受影响,后续可迁移到非 deprecated API。
已补齐 GTK size-allocate 到 VTE 字符网格的同步:根据终端控件分配尺寸和字符宽高调用 vte_terminal_set_size,由 VTE 负责对应 PTY 的 rows/columns 更新。标签页销毁先清除关联数据、断开回调并解绑 PTY,避免异步 spawn/child-exited 回调访问已释放的 Tab。
CLI 启动语义现已覆盖 --cwd、重复 --env NAME=VALUE、-e 与 positional arguments 互斥,以及环境变量覆盖/新增;Go 侧增加了环境合并和 CommandSpec 字段测试。字体缩放通过缩放 Action 原子保存到配置文件。
已添加真实 GTK/VTE 集成脚本 tests/gtk_vte_integration.sh,在 X11 显示服务器下验证 --cwd、--empty-env、--env、PTY 子进程退出、窗口出现和 Ctrl+Q 应用退出。为修复 Go cgo 指针规则,Bridge 现在由 C 分配并管理 argv/env 字符串数组。
跨平台边界:当前正式实现仍是 Linux GTK3 + VTE + POSIX PTY;macOS、Windows 仅保留 Go 抽象接口和规划,不宣称已实现。
Linux MVP 完成后再单独验证:
- macOS GTK3 + Quartz + PTY
- Windows GTK3 + Win32 + ConPTY
- 是否可以继续复用 VTE
- 如果不能,是否需要独立 TerminalView
| 功能 | 快捷键 |
|---|---|
| 新建标签页 | Ctrl+Shift+T |
| 关闭标签页 | Ctrl+Shift+W |
| 下一个标签页 | Ctrl+PageDown |
| 上一个标签页 | Ctrl+PageUp |
| 复制 | Ctrl+Shift+C |
| 粘贴 | Ctrl+Shift+V |
| 全选 | Ctrl+Shift+A |
| 放大字体 | Ctrl+plus |
| 缩小字体 | Ctrl+minus |
| 重置字体 | Ctrl+0 |
| 退出 | Ctrl+Q |
快捷键后续应统一迁移到 GTK Action 层,不要散落在 Widget 回调中。
推荐先使用 TOML 或 JSON,第一版暂不做配置界面。
font = "Monospace 12"
scrollback_lines = 10000
cursor_blink = true
audible_bell = false
copy_on_select = false
window_width = 1000
window_height = 700
shell = ""
foreground = "#d8dee9"
background = "#20242d"
cursor = "#ffffff"
selection_background = "#4c566a"空的 shell 表示自动读取 $SHELL,失败后回退到 /bin/sh。
程序能够启动 GTK3 窗口。
窗口中显示 VTE Terminal。
能够启动默认 Shell。
能够运行交互式命令。
关闭窗口后子进程不会遗留。
可以创建多个标签页。
每个标签页有独立 Shell。
标签页标题能够更新。
关闭标签页会正确结束对应 Shell。
关闭最后一个标签页会按照应用策略退出或创建新标签页。
Linux 上可以从源码构建 Release 版本。
可以通过 desktop 文件启动。
依赖缺失时能够给出明确错误。
常见终端程序可以正常使用。
每完成一个功能,需要同步修改本文件:
- 更新“开发进度”状态;
- 记录实际实现与原方案的差异;
- 添加遇到的问题和解决方式;
- 更新验收结果;
- 如果架构发生变化,修改对应章节;
- 重要决策追加到“决策记录”。
| 阶段 | 状态 | 说明 |
|---|---|---|
| M0 环境验证 | 已完成 | GTK3/VTE/Go/GCC 检查通过;纯 C Demo 已编译并启动 Shell |
| M1 Go + cgo 最小窗口 | 已完成 | Go 应用已接入集中式 C Bridge |
| M2 终端基础能力 | 已完成 | 配置、PTY、复制粘贴、缩放和终端尺寸同步已实现 |
| M3 标签页 | 已完成 | Notebook、多 Shell、标题同步和关闭回收已实现 |
| M4 配置和 Action | 已完成 | JSON 配置、原子保存和 GTK Action 已实现 |
| M5 Linux 发布 | 已完成 | Makefile、依赖检查、desktop 文件和安装目标已实现 |
| M6 跨平台验证 | 暂缓 | Linux MVP 完成后进行 |
决定先实现 Linux 版本,使用 GTK3 + VTE + POSIX PTY。Windows 和 macOS 只保留抽象接口,不在 MVP 阶段实现。
决定 Go 应用层不直接依赖 VteTerminal*,通过 TerminalSession 和 C Bridge 隔离 VTE。这样后续可以接入 Windows ConPTY 或其他终端渲染实现。
决定第一版不自行实现 ANSI/VT100 解析、PTY 和终端渲染,优先复用 VTE。
- 继续维护 Linux MVP 的稳定性和兼容性;
- 迁移 VTE deprecated API,减少编译警告;
- 完善 Linux 打包和发布流程;
- 在 Linux MVP 稳定后,单独评估 macOS 和 Windows 后端。
本方案文档位于 docs/PLAN.md。根目录 README.md 负责项目介绍和快速开始;贡献、安全与行为准则等仓库治理文档保留在根目录,便于 GitHub 和贡献者发现。