自动化与脚本
在 C 中用 libghostty-vt + Raylib…
需要把 Ghostty 的 VT 终端能力嵌入自有 C 应用,或用单文件最小示例学习 libghostty-vt 的终端状态与 render-state API 时适用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
在 C 中用 libghostty-vt特定
解决思路
- 获取项目:`git clone https://github.com/ghostty-org/ghostling.git`,然后 `cd ghostling`。
- Debug 构建:`cmake -B build -G Ninja`,`cmake --build build`,`./build/ghostling`;首次 configure 后只需 `cmake --build build`。
- Release 构建:`cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release`,`cmake --build build`,`./build/ghostling`;Debug 因 Ghostty 安全/正确性断言很慢,基准测试始终用 Release。
- 准备依赖:CMake 3.19+、Ninja、clang/gcc、Zig 0.15.x 在 PATH;macOS 安装 Xcode CLT 或 Xcode。
- 项目组织:把整个实现放在 `main.c`;`CMakeLists.txt` 负责通过 FetchContent 拉取 libghostty-vt 和 Raylib。
- 明确边界:libghostty-vt 提供 VT 解析(SIMD 优化)、终端状态(cursor/styles/reflow/scrollback)、render state、Unicode/多码点 grapheme、Kitty keyboard、鼠标跟踪、焦点报告,零依赖且兼容 WASM;不提供窗口、渲染、PTY、tabs、splits、配置。
- 初始化终端:包含 `#include "ghostty.h"`;用 `ghostty_terminal_new(&(ghostty_terminal_config_t){.cols=cols,.rows=rows})` 创建;检查返回 NULL 并处理错误。
- 从 PTY 写入终端:用 `read(pty_fd, buf, sizeof(buf))` 读原始字节,当 `n>0` 时调用 `ghostty_terminal_write(terminal, buf, (size_t)n)`。
- 发送键盘输入:构造 `ghostty_key_event_t`,设置 `.key=GHOSTTY_KEY_A`、`.mods=GHOSTTY_MODS_CTRL`、`.action=GHOSTTY_ACTION_PRESS`、`.composing=false`;调用 `ghostty_terminal_key(terminal,&ev,out,sizeof(out),&out_len)` 得到转义序列;若 `out_len>0` 写入 `pty_fd`。
- 发送鼠标事件:构造 `ghostty_mouse_event_t`,设置 `.x=cell_col`、`.y=cell_row`、`.button=GHOSTTY_MOUSE_LEFT`、`.action=GHOSTTY_MOUSE_PRESS`、`.mods=GHOSTTY_MODS_NONE`;调用 `ghostty_terminal_mouse(terminal,&mev,out,sizeof(out),&out_len)`;若有输出写入 `pty_fd`。
- 调整终端大小:调用 `ghostty_terminal_resize(terminal,new_cols,new_rows)`,让 libghostty-vt 自动 reflow;随后设置 `struct winsize ws={.ws_col=new_cols,.ws_row=new_rows}`,`ioctl(pty_fd,TIOCSWINSZ,&ws)`,并 `kill(child_pid,SIGWINCH)`。
- 渲染状态:用 `ghostty_terminal_render_state(terminal)` 取 `ghostty_render_state_t *rs`;渲染前 `ghostty_render_state_begin(rs)`。
- 遍历脏单元格:用 `ghostty_render_cell_iter_t iter={0}` 和 `ghostty_render_cell_t cell`;循环 `while (ghostty_render_state_next_cell(rs,&iter,&cell))`,只重绘变化单元格。
- 读取单元格字段:`cell.col`、`cell.row` 网格位置;`cell.codepoint` Unicode(0 为空);`cell.fg.r/g/b` 与 `cell.bg.r/g/b` RGB;`cell.attrs.bold/italic/reverse` 属性。
- 用 Raylib 绘制单元格:构造 `Color fg`/`Color bg`;按 `cell.col*cell_width`、`cell.row*cell_height` 构造 `Rectangle`;`DrawRectangleRec(rect,bg)`;若 `cell.codepoint!=0`,将 codepoint 编码为 UTF-8 后 `DrawText`。
- 结束渲染:遍历后调用 `ghostty_render_state_end(rs)`,把单元格标记为 clean。
- 滚动回看:用 `ghostty_terminal_scroll(terminal,-3)` 向上滚 3 行,`ghostty_terminal_scroll(terminal,3)` 向下滚 3 行;用 `ghostty_terminal_scroll_bottom(terminal)` 滚到底部。
- 获取光标:用 `ghostty_cursor_t cursor; ghostty_terminal_cursor(terminal,&cursor);` 读取 `cursor.col`、`cursor.row`、`cursor.visible`、`cursor.shape`(`GHOSTTY_CURSOR_BLOCK/_UNDERLINE/_BAR`)。
- 清理:调用 `ghostty_terminal_free(terminal)`。
- 自己管理 PTY:用 `<pty.h>` 的 `openpty(&master_fd,&slave_fd,NULL,NULL,&ws)` 创建 PTY,`ws` 设置行列;`fork()` 子进程。
- 子进程 PTY 设置:子进程调用 `setsid()`,`ioctl(slave_fd,TIOCSCTTY,0)`,`dup2(slave_fd,STDIN_FILENO/STDOUT_FILENO/STDERR_FILENO)`,关闭 `master_fd` 和 `slave_fd`,读取 `$SHELL`(无则 `/bin/sh`),`execl(shell,shell,NULL)`,失败 `_exit(1)`。
- 父进程 PTY 设置:父进程关闭 `slave_fd`,用 `master_fd` 读写。
- CMake 拉取 libghostty-vt:`include(FetchContent)`,`FetchContent_Declare(libghostty URL https://release.files.ghostty.org/tip/libghostty-vt-<platform>.tar.gz)`,`FetchContent_MakeAvailable(libghostty)`。
- CMake 拉取 Raylib:`FetchContent_Declare(raylib GIT_REPOSITORY https://github.com/raysan5/raylib.git GIT_TAG 5.0)`,`FetchContent_MakeAvailable(raylib)`。
- CMake 目标与链接:`add_executable(ghostling main.c)`,`target_link_libraries(ghostling PRIVATE ghostty-vt raylib)`。
- 常用枚举:键 `GHOSTTY_KEY_A…Z`、方向键、`ENTER/BACKSPACE/ESCAPE/TAB`、`F1…F12`;修饰符 `GHOSTTY_MODS_NONE/SHIFT/CTRL/ALT/SUPER`;鼠标 `GHOSTTY_MOUSE_LEFT/RIGHT/MIDDLE/WHEEL_UP/WHEEL_DOWN`;光标形状 `GHOSTTY_CURSOR_BLOCK/UNDERLINE/BAR`。
- 非阻塞 PTY 读循环:`fcntl(master_fd,F_SETFL,O_NONBLOCK)`;主循环中 `while ((n=read(master_fd,buf,sizeof(buf)))>0) ghostty_terminal_write(terminal,buf,(size_t)n);`;`EAGAIN` 表示暂时无数据,不是错误。
- Raylib 键映射:实现 `raylib_key_to_ghostty(int rl_key)`,用 `switch` 把 `KEY_A`→`GHOSTTY_KEY_A`、`KEY_ENTER`→`GHOSTTY_KEY_ENTER`、`KEY_BACKSPACE`→`GHOSTTY_KEY_BACKSPACE`、`KEY_UP/DOWN`→对应项,默认 `GHOSTTY_KEY_INVALID`。
- 滚动条绘制:取 `total_rows=ghostty_terminal_total_rows(terminal)`、`viewport_rows=rows`、`scroll_offset=ghostty_terminal_scroll_offset(terminal)`;计算 `bar_h=(float)viewport_rows/total_rows*window_height`、`bar_y=(float)scroll_offset/total_rows*window_height`;用 `DrawRectangle(window_width-SCROLLBAR_W,(int)bar_y,SCROLLBAR_W,(int)bar_h,GRAY)`。
- 排查 zig not found:安装 Zig 0.15.x 并确保在 `$PATH`。
- 排查 Debug 慢:用 Release:`cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release`。
- 排查终端渲染乱码:确认迭代单元格前调用了 `ghostty_render_state_begin`,之后调用了 `ghostty_render_state_end`。
- 排查子进程收不到 resize:在 `ghostty_terminal_resize` 后同时调用 `ioctl(pty_fd,TIOCSWINSZ,&ws)` 和 `kill(child_pid,SIGWINCH)`。
- 排查 Kitty keyboard 协议异常:这是已知 Raylib/GLFW 限制;libghostty-vt 本身支持正确,但需要更丰富的输入事件。
- 排查颜色不对:检查 `cell.fg`/`cell.bg`,libghostty-vt 已把调色板解析为 RGB,直接使用这些值。
- 排查 `ghostty_terminal_write` 崩溃:确保传入 buffer 有效且 `len>0`;绝不要传 NULL。
- 必须自己实现:PTY 创建和进程管理、窗口创建和事件循环、字体加载和字形渲染、剪贴板读写(OSC 52 字节由库提供,但处理归你)、tabs/splits/多窗口、配置 UI。
- 查完整 API:`https://libghostty.tip.ghostty.org/group__render.html`。
适用边界 · 注意事项
- 如果你希望开箱即用的完整终端应用(含 tabs/splits/配置/剪贴板处理),不要只用 libghostty-vt 实现,它不提供这些。
- 如果无法安装 Zig 0.15.x、CMake 3.19+、Ninja 或 C 编译器,或不能使用 Raylib/OS PTY,则不适合。
- 不要把 Debug 构建用于性能基准或流畅交互;Ghostty 断言会让它很慢,应使用 Release。