diff --git a/example/c-vt-formatter/README.md b/example/c-vt-formatter/README.md new file mode 100644 index 000000000..f416c8dbd --- /dev/null +++ b/example/c-vt-formatter/README.md @@ -0,0 +1,18 @@ +# Example: `ghostty-vt` Terminal Formatter + +This contains a simple example of how to use the `ghostty-vt` terminal and +formatter APIs to create a terminal, write VT-encoded content into it, and +format the screen contents as plain text. + +This uses a `build.zig` and `Zig` to build the C program so that we +can reuse a lot of our build logic and depend directly on our source +tree, but Ghostty emits a standard C library that can be used with any +C tooling. + +## Usage + +Run the program: + +```shell-session +zig build run +``` diff --git a/example/c-vt-formatter/build.zig b/example/c-vt-formatter/build.zig new file mode 100644 index 000000000..637b48f13 --- /dev/null +++ b/example/c-vt-formatter/build.zig @@ -0,0 +1,42 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const run_step = b.step("run", "Run the app"); + + const exe_mod = b.createModule(.{ + .target = target, + .optimize = optimize, + }); + exe_mod.addCSourceFiles(.{ + .root = b.path("src"), + .files = &.{"main.c"}, + }); + + // You'll want to use a lazy dependency here so that ghostty is only + // downloaded if you actually need it. + if (b.lazyDependency("ghostty", .{ + // Setting simd to false will force a pure static build that + // doesn't even require libc, but it has a significant performance + // penalty. If your embedding app requires libc anyway, you should + // always keep simd enabled. + // .simd = false, + })) |dep| { + exe_mod.linkLibrary(dep.artifact("ghostty-vt")); + } + + // Exe + const exe = b.addExecutable(.{ + .name = "c_vt_formatter", + .root_module = exe_mod, + }); + b.installArtifact(exe); + + // Run + const run_cmd = b.addRunArtifact(exe); + run_cmd.step.dependOn(b.getInstallStep()); + if (b.args) |args| run_cmd.addArgs(args); + run_step.dependOn(&run_cmd.step); +} diff --git a/example/c-vt-formatter/build.zig.zon b/example/c-vt-formatter/build.zig.zon new file mode 100644 index 000000000..a14f0aedb --- /dev/null +++ b/example/c-vt-formatter/build.zig.zon @@ -0,0 +1,24 @@ +.{ + .name = .c_vt_formatter, + .version = "0.0.0", + .fingerprint = 0x9e3758265677a0c4, + .minimum_zig_version = "0.15.1", + .dependencies = .{ + // Ghostty dependency. In reality, you'd probably use a URL-based + // dependency like the one showed (and commented out) below this one. + // We use a path dependency here for simplicity and to ensure our + // examples always test against the source they're bundled with. + .ghostty = .{ .path = "../../" }, + + // Example of what a URL-based dependency looks like: + // .ghostty = .{ + // .url = "https://github.com/ghostty-org/ghostty/archive/COMMIT.tar.gz", + // .hash = "N-V-__8AAMVLTABmYkLqhZPLXnMl-KyN38R8UVYqGrxqO36s", + // }, + }, + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + }, +} diff --git a/example/c-vt-formatter/src/main.c b/example/c-vt-formatter/src/main.c new file mode 100644 index 000000000..5d408b172 --- /dev/null +++ b/example/c-vt-formatter/src/main.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include + +int main() { + // Create a terminal with a small grid + GhosttyTerminal terminal; + GhosttyTerminalOptions opts = { + .cols = 80, + .rows = 24, + .max_scrollback = 0, + }; + GhosttyResult result = ghostty_terminal_new(NULL, &terminal, opts); + assert(result == GHOSTTY_SUCCESS); + + // Write VT-encoded content into the terminal to exercise various + // cursor movement and styling sequences. + const char *commands[] = { + "Line 1: Hello World!\r\n", // Simple text on row 1 + "Line 2: \033[1mBold\033[0m and " // Bold text on row 2 + "\033[4mUnderline\033[0m\r\n", + "Line 3: placeholder\r\n", // Will be overwritten below + "\033[3;1H", // CUP: move cursor back to row 3, col 1 + "\033[2K", // EL: erase the entire line + "Line 3: Overwritten!\r\n", // Rewrite row 3 with new content + "\033[5;10H", // CUP: jump to row 5, col 10 + "Placed at (5,10)", // Write at that position + "\033[1;72H", // CUP: jump to row 1, col 72 + "RIGHT->", // Near the right edge of row 1 + }; + for (size_t i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) { + ghostty_terminal_vt_write(terminal, (const uint8_t *)commands[i], + strlen(commands[i])); + } + + // Create a plain-text formatter for the terminal + GhosttyFormatterTerminalOptions fmt_opts = GHOSTTY_INIT_SIZED(GhosttyFormatterTerminalOptions); + fmt_opts.emit = GHOSTTY_FORMATTER_FORMAT_PLAIN; + fmt_opts.trim = true; + + GhosttyFormatter formatter; + result = ghostty_formatter_terminal_new(NULL, &formatter, terminal, fmt_opts); + assert(result == GHOSTTY_SUCCESS); + + // Format into an allocated buffer + uint8_t *buf = NULL; + size_t len = 0; + result = ghostty_formatter_format_alloc(formatter, NULL, &buf, &len); + assert(result == GHOSTTY_SUCCESS); + + // Print the formatted output + printf("Formatted output (%zu bytes):\n", len); + fwrite(buf, 1, len, stdout); + printf("\n"); + + // Clean up + free(buf); + ghostty_formatter_free(formatter); + ghostty_terminal_free(terminal); + return 0; +} diff --git a/include/ghostty/vt.h b/include/ghostty/vt.h index 26ecf7889..42cd73e37 100644 --- a/include/ghostty/vt.h +++ b/include/ghostty/vt.h @@ -43,6 +43,7 @@ * - @ref c-vt-key-encode/src/main.c - Key encoding example * - @ref c-vt-paste/src/main.c - Paste safety check example * - @ref c-vt-sgr/src/main.c - SGR parser example + * - @ref c-vt-formatter/src/main.c - Terminal formatter example * */ @@ -66,6 +67,12 @@ * styling sequences and extract text attributes like colors and underline styles. */ +/** @example c-vt-formatter/src/main.c + * This example demonstrates how to use the terminal and formatter APIs to + * create a terminal, write VT-encoded content into it, and format the screen + * contents as plain text. + */ + #ifndef GHOSTTY_VT_H #define GHOSTTY_VT_H diff --git a/include/ghostty/vt/formatter.h b/include/ghostty/vt/formatter.h index 7f0519649..4beb5fc77 100644 --- a/include/ghostty/vt/formatter.h +++ b/include/ghostty/vt/formatter.h @@ -186,7 +186,9 @@ GhosttyResult ghostty_formatter_format_buf(GhosttyFormatter formatter, * * Each call formats the current terminal state. The buffer is allocated * using the provided allocator (or the default allocator if NULL). - * The caller is responsible for freeing the returned buffer using the + * The caller is responsible for freeing the returned buffer. When using + * the default allocator (NULL), the buffer can be freed with `free()`. + * When using a custom allocator, the buffer must be freed using the * same allocator. * * @param formatter The formatter handle (must not be NULL)