feat(vt-capi): port the libghostty-vt C API
Ports ZIG/src/lib_vt.zig and terminal/c/* as crates/ghostty-vt-capi (cdylib + staticlib + rlib, soname libghostty-vt.so) implementing the unchanged include/ghostty/vt.h + vt/*.h ABI: OSC parser, SGR parser, key events and key encoding, paste safety, colors, result codes - all 38 native symbols, plus the ghostty_wasm_* helpers on wasm32. Verification: - tests/abi.rs generates a C probe from the headers, compiled with cc, that checks sizeof/alignof/offsetof of every struct/union, every enumerator and #define against Rust, and links every declared symbol; nm -D exports equal the declared functions. - Ghostty's example/c-vt* programs compile unchanged against the Rust library (shared and static) and produce the expected output. - The wasm32 build runs Ghostty's wasm-key-encode/wasm-sgr example scripts unmodified under node (upstream's wasm-sgr tag table is stale vs sgr.h; a regenerated copy checks full output). Test Plan: - cargo test -p ghostty-vt-capi (22 unit + 2 ABI + 5 C + 3 wasm tests)
This commit is contained in:
1 parent
5b9a0723a9
commit
75252a7651
35 files changed
+6911
No files matched your search
@@ -0,0 +1,94 @@
|
||||
//! Builds Ghostty's unchanged C examples (examples/c/*.c, from
|
||||
//! ZIG/example/c-vt*) against the Rust `libghostty-vt` and checks their output.
|
||||
#![cfg(unix)]
|
||||
#![allow(clippy::unwrap_used)]
|
||||
|
||||
mod common;
|
||||
|
||||
use std::process::Command;
|
||||
|
||||
fn run_example(name: &str) -> String {
|
||||
let lib = common::install_native_lib();
|
||||
let scratch = common::scratch_dir("c_examples");
|
||||
let src = common::manifest_dir()
|
||||
.join("examples/c")
|
||||
.join(format!("{name}.c"));
|
||||
let exe = scratch.join(name);
|
||||
common::compile_c(&lib, &[&src], &exe, &["-Wall"]);
|
||||
let out = Command::new(&exe).output().expect("run example");
|
||||
assert!(out.status.success(), "{name} exited with {:?}", out.status);
|
||||
String::from_utf8(out.stdout).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn c_vt_osc() {
|
||||
assert_eq!(
|
||||
run_example("c-vt"),
|
||||
"Command type: 1\nExtracted title: hello\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn c_vt_key_encode() {
|
||||
assert_eq!(
|
||||
run_example("c-vt-key-encode"),
|
||||
"Encoding event: left ctrl release with all Kitty flags enabled\n\
|
||||
Required buffer size: 12 bytes\n\
|
||||
Encoded 12 bytes\n\
|
||||
Hex: 1b 5b 35 37 34 34 32 3b 35 3a 33 75 \n\
|
||||
String: \\x1b[57442;5:3u\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn c_vt_paste() {
|
||||
assert_eq!(
|
||||
run_example("c-vt-paste"),
|
||||
"'hello world' is safe to paste\n\
|
||||
'rm -rf /\n' is UNSAFE - contains newline\n\
|
||||
Data with escape sequence is UNSAFE\n\
|
||||
Empty data is safe\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn c_vt_sgr() {
|
||||
assert_eq!(
|
||||
run_example("c-vt-sgr"),
|
||||
"Parsing Kakoune SGR sequence:\n\
|
||||
ESC[4:3;38;2;51;51;51;48;2;170;170;170;58;2;255;97;136m\n\
|
||||
\n\
|
||||
Attribute 1: Underline style = curly\n\
|
||||
Attribute 2: Foreground RGB = (51, 51, 51)\n\
|
||||
Attribute 3: Background RGB = (170, 170, 170)\n\
|
||||
Attribute 4: Underline color RGB = (255, 97, 136)\n\
|
||||
\n\
|
||||
Total attributes parsed: 4\n"
|
||||
);
|
||||
}
|
||||
|
||||
/// The static archive links too (what embedders of `libghostty-vt.a` use).
|
||||
#[test]
|
||||
fn c_vt_links_statically() {
|
||||
let lib = common::install_native_lib();
|
||||
let scratch = common::scratch_dir("c_examples_static");
|
||||
let src = common::manifest_dir().join("examples/c/c-vt-paste.c");
|
||||
let exe = scratch.join("paste-static");
|
||||
let out = Command::new("cc")
|
||||
.arg("-I")
|
||||
.arg(common::include_dir())
|
||||
.arg(&src)
|
||||
.arg(lib.join("libghostty-vt.a"))
|
||||
.args(["-lpthread", "-ldl", "-lm"])
|
||||
.arg("-o")
|
||||
.arg(&exe)
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(
|
||||
out.status.success(),
|
||||
"{}",
|
||||
String::from_utf8_lossy(&out.stderr)
|
||||
);
|
||||
let run = Command::new(&exe).output().unwrap();
|
||||
assert!(String::from_utf8_lossy(&run.stdout).contains("Empty data is safe"));
|
||||
}
|
||||
Reference in new issue
Block a user