mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-06 07:38:21 +00:00
libghostty: make headers C++ compatible (#11950)
The headers were not C++ compatible and would fail compiling before (see https://github.com/ghostty-org/ghostty/discussions/11878). The only reason is because our typedefs would conflict since we named them identically. This also adds a `c-vt-stream` example and a `cpp-vt-stream` example, the latter primarily to verify we can build in C++ mode.
This commit is contained in:
19
example/c-vt-stream/README.md
Normal file
19
example/c-vt-stream/README.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Example: VT Stream Processing in C
|
||||
|
||||
This contains a simple example of how to use `ghostty_terminal_vt_write`
|
||||
to parse and process VT sequences in C. This is the C equivalent of
|
||||
the `zig-vt-stream` example, ideal for read-only terminal applications
|
||||
such as replay tooling, CI log viewers, and PaaS builder output.
|
||||
|
||||
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
|
||||
```
|
||||
42
example/c-vt-stream/build.zig
Normal file
42
example/c-vt-stream/build.zig
Normal file
@@ -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_stream",
|
||||
.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);
|
||||
}
|
||||
24
example/c-vt-stream/build.zig.zon
Normal file
24
example/c-vt-stream/build.zig.zon
Normal file
@@ -0,0 +1,24 @@
|
||||
.{
|
||||
.name = .c_vt_stream,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0xd5bb3fc45e3f4dfc,
|
||||
.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",
|
||||
},
|
||||
}
|
||||
74
example/c-vt-stream/src/main.c
Normal file
74
example/c-vt-stream/src/main.c
Normal file
@@ -0,0 +1,74 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ghostty/vt.h>
|
||||
|
||||
int main(void) {
|
||||
//! [vt-stream-init]
|
||||
// Create a terminal
|
||||
GhosttyTerminal terminal;
|
||||
GhosttyTerminalOptions opts = {
|
||||
.cols = 80,
|
||||
.rows = 24,
|
||||
.max_scrollback = 0,
|
||||
};
|
||||
GhosttyResult result = ghostty_terminal_new(NULL, &terminal, opts);
|
||||
assert(result == GHOSTTY_SUCCESS);
|
||||
//! [vt-stream-init]
|
||||
|
||||
//! [vt-stream-write]
|
||||
// Feed VT data into the terminal
|
||||
const char *text = "Hello, World!\r\n";
|
||||
ghostty_terminal_vt_write(terminal, (const uint8_t *)text, strlen(text));
|
||||
|
||||
// ANSI color codes: ESC[1;32m = bold green, ESC[0m = reset
|
||||
text = "\x1b[1;32mGreen Text\x1b[0m\r\n";
|
||||
ghostty_terminal_vt_write(terminal, (const uint8_t *)text, strlen(text));
|
||||
|
||||
// Cursor positioning: ESC[1;1H = move to row 1, column 1
|
||||
text = "\x1b[1;1HTop-left corner\r\n";
|
||||
ghostty_terminal_vt_write(terminal, (const uint8_t *)text, strlen(text));
|
||||
|
||||
// Cursor movement: ESC[5B = move down 5 lines
|
||||
text = "\x1b[5B";
|
||||
ghostty_terminal_vt_write(terminal, (const uint8_t *)text, strlen(text));
|
||||
text = "Moved down!\r\n";
|
||||
ghostty_terminal_vt_write(terminal, (const uint8_t *)text, strlen(text));
|
||||
|
||||
// Erase line: ESC[2K = clear entire line
|
||||
text = "\x1b[2K";
|
||||
ghostty_terminal_vt_write(terminal, (const uint8_t *)text, strlen(text));
|
||||
text = "New content\r\n";
|
||||
ghostty_terminal_vt_write(terminal, (const uint8_t *)text, strlen(text));
|
||||
|
||||
// Multiple lines
|
||||
text = "Line A\r\nLine B\r\nLine C\r\n";
|
||||
ghostty_terminal_vt_write(terminal, (const uint8_t *)text, strlen(text));
|
||||
//! [vt-stream-write]
|
||||
|
||||
//! [vt-stream-read]
|
||||
// Get the final terminal state as a plain string using the formatter
|
||||
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);
|
||||
|
||||
uint8_t *buf = NULL;
|
||||
size_t len = 0;
|
||||
result = ghostty_formatter_format_alloc(formatter, NULL, &buf, &len);
|
||||
assert(result == GHOSTTY_SUCCESS);
|
||||
|
||||
fwrite(buf, 1, len, stdout);
|
||||
printf("\n");
|
||||
|
||||
ghostty_free(NULL, buf, len);
|
||||
ghostty_formatter_free(formatter);
|
||||
//! [vt-stream-read]
|
||||
|
||||
ghostty_terminal_free(terminal);
|
||||
return 0;
|
||||
}
|
||||
19
example/cpp-vt-stream/README.md
Normal file
19
example/cpp-vt-stream/README.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Example: VT Stream Processing in C++
|
||||
|
||||
This contains a simple example of how to use `ghostty_terminal_vt_write`
|
||||
to parse and process VT sequences in C++. This is a simplified C++ port
|
||||
of the `c-vt-stream` example that verifies libghostty compiles in C++
|
||||
mode.
|
||||
|
||||
> [!IMPORTANT]
|
||||
>
|
||||
> **`libghostty` is a C library.** This example is only here so our CI
|
||||
> verifies that the library can be built in used from C++ files.
|
||||
|
||||
## Usage
|
||||
|
||||
Run the program:
|
||||
|
||||
```shell-session
|
||||
zig build run
|
||||
```
|
||||
42
example/cpp-vt-stream/build.zig
Normal file
42
example/cpp-vt-stream/build.zig
Normal file
@@ -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.cpp"},
|
||||
});
|
||||
|
||||
// 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 = "cpp_vt_stream",
|
||||
.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);
|
||||
}
|
||||
24
example/cpp-vt-stream/build.zig.zon
Normal file
24
example/cpp-vt-stream/build.zig.zon
Normal file
@@ -0,0 +1,24 @@
|
||||
.{
|
||||
.name = .cpp_vt_stream,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x112f5d044ef8c2ac,
|
||||
.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",
|
||||
},
|
||||
}
|
||||
49
example/cpp-vt-stream/src/main.cpp
Normal file
49
example/cpp-vt-stream/src/main.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <ghostty/vt.h>
|
||||
|
||||
int main() {
|
||||
// Create a terminal
|
||||
GhosttyTerminal terminal;
|
||||
GhosttyTerminalOptions opts = {
|
||||
.cols = 80,
|
||||
.rows = 24,
|
||||
.max_scrollback = 0,
|
||||
};
|
||||
GhosttyResult result = ghostty_terminal_new(nullptr, &terminal, opts);
|
||||
assert(result == GHOSTTY_SUCCESS);
|
||||
|
||||
// Feed VT data into the terminal
|
||||
const char *text = "Hello from C++!\r\n";
|
||||
ghostty_terminal_vt_write(terminal, reinterpret_cast<const uint8_t *>(text), std::strlen(text));
|
||||
|
||||
text = "\x1b[1;32mGreen Text\x1b[0m\r\n";
|
||||
ghostty_terminal_vt_write(terminal, reinterpret_cast<const uint8_t *>(text), std::strlen(text));
|
||||
|
||||
text = "\x1b[1;1HTop-left corner\r\n";
|
||||
ghostty_terminal_vt_write(terminal, reinterpret_cast<const uint8_t *>(text), std::strlen(text));
|
||||
|
||||
// Get the final terminal state as a plain string
|
||||
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(nullptr, &formatter, terminal, fmt_opts);
|
||||
assert(result == GHOSTTY_SUCCESS);
|
||||
|
||||
uint8_t *buf = nullptr;
|
||||
size_t len = 0;
|
||||
result = ghostty_formatter_format_alloc(formatter, nullptr, &buf, &len);
|
||||
assert(result == GHOSTTY_SUCCESS);
|
||||
|
||||
std::fwrite(buf, 1, len, stdout);
|
||||
std::printf("\n");
|
||||
|
||||
ghostty_free(nullptr, buf, len);
|
||||
ghostty_formatter_free(formatter);
|
||||
ghostty_terminal_free(terminal);
|
||||
return 0;
|
||||
}
|
||||
@@ -111,7 +111,7 @@ typedef struct {
|
||||
*
|
||||
* @ingroup formatter
|
||||
*/
|
||||
typedef struct GhosttyFormatter* GhosttyFormatter;
|
||||
typedef struct GhosttyFormatterImpl* GhosttyFormatter;
|
||||
|
||||
/**
|
||||
* Options for creating a terminal formatter.
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
*
|
||||
* @ingroup key
|
||||
*/
|
||||
typedef struct GhosttyKeyEncoder *GhosttyKeyEncoder;
|
||||
typedef struct GhosttyKeyEncoderImpl *GhosttyKeyEncoder;
|
||||
|
||||
/**
|
||||
* Kitty keyboard protocol flags.
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
*
|
||||
* @ingroup key
|
||||
*/
|
||||
typedef struct GhosttyKeyEvent *GhosttyKeyEvent;
|
||||
typedef struct GhosttyKeyEventImpl *GhosttyKeyEvent;
|
||||
|
||||
/**
|
||||
* Keyboard input event types.
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
*
|
||||
* @ingroup mouse
|
||||
*/
|
||||
typedef struct GhosttyMouseEncoder *GhosttyMouseEncoder;
|
||||
typedef struct GhosttyMouseEncoderImpl *GhosttyMouseEncoder;
|
||||
|
||||
/**
|
||||
* Mouse tracking mode.
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
*
|
||||
* @ingroup mouse
|
||||
*/
|
||||
typedef struct GhosttyMouseEvent *GhosttyMouseEvent;
|
||||
typedef struct GhosttyMouseEventImpl *GhosttyMouseEvent;
|
||||
|
||||
/**
|
||||
* Mouse event action type.
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
*
|
||||
* @ingroup osc
|
||||
*/
|
||||
typedef struct GhosttyOscParser *GhosttyOscParser;
|
||||
typedef struct GhosttyOscParserImpl *GhosttyOscParser;
|
||||
|
||||
/**
|
||||
* Opaque handle to a single OSC command.
|
||||
@@ -31,7 +31,7 @@ typedef struct GhosttyOscParser *GhosttyOscParser;
|
||||
*
|
||||
* @ingroup osc
|
||||
*/
|
||||
typedef struct GhosttyOscCommand *GhosttyOscCommand;
|
||||
typedef struct GhosttyOscCommandImpl *GhosttyOscCommand;
|
||||
|
||||
/** @defgroup osc OSC Parser
|
||||
*
|
||||
|
||||
@@ -86,21 +86,21 @@ extern "C" {
|
||||
*
|
||||
* @ingroup render
|
||||
*/
|
||||
typedef struct GhosttyRenderState* GhosttyRenderState;
|
||||
typedef struct GhosttyRenderStateImpl* GhosttyRenderState;
|
||||
|
||||
/**
|
||||
* Opaque handle to a render-state row iterator.
|
||||
*
|
||||
* @ingroup render
|
||||
*/
|
||||
typedef struct GhosttyRenderStateRowIterator* GhosttyRenderStateRowIterator;
|
||||
typedef struct GhosttyRenderStateRowIteratorImpl* GhosttyRenderStateRowIterator;
|
||||
|
||||
/**
|
||||
* Opaque handle to render-state row cells.
|
||||
*
|
||||
* @ingroup render
|
||||
*/
|
||||
typedef struct GhosttyRenderStateRowCells* GhosttyRenderStateRowCells;
|
||||
typedef struct GhosttyRenderStateRowCellsImpl* GhosttyRenderStateRowCells;
|
||||
|
||||
/**
|
||||
* Dirty state of a render state after update.
|
||||
|
||||
@@ -55,7 +55,7 @@ extern "C" {
|
||||
*
|
||||
* @ingroup sgr
|
||||
*/
|
||||
typedef struct GhosttySgrParser* GhosttySgrParser;
|
||||
typedef struct GhosttySgrParserImpl* GhosttySgrParser;
|
||||
|
||||
/**
|
||||
* SGR attribute tags.
|
||||
|
||||
@@ -34,6 +34,10 @@ extern "C" {
|
||||
* Once a terminal session is up and running, you can configure a key encoder
|
||||
* to write keyboard input via ghostty_key_encoder_setopt_from_terminal().
|
||||
*
|
||||
* ### Example: VT stream processing
|
||||
* @snippet c-vt-stream/src/main.c vt-stream-init
|
||||
* @snippet c-vt-stream/src/main.c vt-stream-write
|
||||
*
|
||||
* ## Effects
|
||||
*
|
||||
* By default, the terminal sequence processing with ghostty_terminal_vt_write()
|
||||
@@ -155,7 +159,7 @@ extern "C" {
|
||||
*
|
||||
* @ingroup terminal
|
||||
*/
|
||||
typedef struct GhosttyTerminal* GhosttyTerminal;
|
||||
typedef struct GhosttyTerminalImpl* GhosttyTerminal;
|
||||
|
||||
/**
|
||||
* Terminal initialization options.
|
||||
|
||||
Reference in New Issue
Block a user