@cueloop/client 0.1.0-alpha.41 → 0.1.0-alpha.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/native/build.sh +44 -18
- package/native/darwin-arm64/libcueloopvt.dylib +0 -0
- package/native/src/shim.zig +114 -0
- package/package.json +5 -5
- package/src/components/terminal-pane.ts +1 -1
- package/src/ghostty-terminal.test.ts +37 -0
- package/src/ghostty-terminal.ts +2 -2
- package/native/src/shim.c +0 -107
package/native/build.sh
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
2
|
# Rebuild libcueloopvt.dylib: libghostty-vt (Ghostty's VT core) statically linked
|
|
3
|
-
# behind the flat FFI shim
|
|
4
|
-
# Output lands in native/<os>-<arch>/libcueloopvt.dylib. Requires curl
|
|
3
|
+
# behind the flat FFI shim. Pinned to a Ghostty commit + Zig 0.16.0.
|
|
4
|
+
# Output lands in native/<os>-<arch>/libcueloopvt.dylib. Requires curl and git.
|
|
5
5
|
set -eu
|
|
6
6
|
|
|
7
7
|
GHOSTTY_COMMIT="5834a0e3df621802e9578e4562d88b0c2ad4ada8"
|
|
@@ -10,47 +10,73 @@ ZIG_VERSION="0.16.0"
|
|
|
10
10
|
here="$(cd "$(dirname "$0")" && pwd)"
|
|
11
11
|
work="${TMPDIR:-/tmp}/cueloop-libghostty-build"
|
|
12
12
|
mkdir -p "$work"
|
|
13
|
+
export ZIG_GLOBAL_CACHE_DIR="$work/zig-gcache"
|
|
14
|
+
export ZIG_LOCAL_CACHE_DIR="$work/zig-lcache"
|
|
13
15
|
cd "$work"
|
|
14
16
|
|
|
15
|
-
#
|
|
17
|
+
# Host triple for the Bun prebuilt dir and Zig download. The two tools use
|
|
18
|
+
# different architecture names.
|
|
16
19
|
arch="$(uname -m)"; os="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
|
17
20
|
case "$os" in darwin) zig_os="macos"; dylib="libcueloopvt.dylib";; linux) zig_os="linux"; dylib="libcueloopvt.so";; *) echo "unsupported os $os" >&2; exit 1;; esac
|
|
18
|
-
|
|
21
|
+
case "$arch-$os" in
|
|
22
|
+
arm64-darwin|aarch64-linux) zig_arch="aarch64"; bun_arch="arm64";;
|
|
23
|
+
x86_64-darwin|x86_64-linux) zig_arch="x86_64"; bun_arch="x64";;
|
|
24
|
+
*) echo "unsupported architecture $arch on $os" >&2; exit 1;;
|
|
25
|
+
esac
|
|
26
|
+
case "$zig_arch-$zig_os" in
|
|
27
|
+
aarch64-macos) zig_sha="b23d70deaa879b5c2d486ed3316f7eaa53e84acf6fc9cc747de152450d401489";;
|
|
28
|
+
x86_64-macos) zig_sha="0387557ed1877bc6a2e1802c8391953baddba76081876301c522f52977b52ba7";;
|
|
29
|
+
aarch64-linux) zig_sha="ea4b09bfb22ec6f6c6ceac57ab63efb6b46e17ab08d21f69f3a48b38e1534f17";;
|
|
30
|
+
x86_64-linux) zig_sha="70e49664a74374b48b51e6f3fdfbf437f6395d42509050588bd49abe52ba3d00";;
|
|
31
|
+
esac
|
|
32
|
+
outdir="$here/${os}-${bun_arch}"
|
|
33
|
+
zig_archive="zig-$zig_arch-$zig_os-$ZIG_VERSION.tar.xz"
|
|
19
34
|
|
|
20
|
-
if [ ! -
|
|
35
|
+
if [ ! -f "$zig_archive" ]; then
|
|
21
36
|
echo "== fetching zig $ZIG_VERSION =="
|
|
22
|
-
curl -
|
|
23
|
-
tar -xf zig.tar.xz
|
|
37
|
+
curl -fsSL -o "$zig_archive" "https://ziglang.org/download/$ZIG_VERSION/$zig_archive"
|
|
24
38
|
fi
|
|
25
|
-
|
|
39
|
+
printf '%s %s\n' "$zig_sha" "$zig_archive" | shasum -a 256 -c -
|
|
26
40
|
|
|
27
41
|
if [ ! -d ghostty ]; then
|
|
28
42
|
echo "== cloning ghostty @ $GHOSTTY_COMMIT =="
|
|
29
43
|
mkdir ghostty && cd ghostty && git init -q
|
|
30
44
|
git remote add origin https://github.com/ghostty-org/ghostty.git
|
|
31
|
-
git fetch -q --depth 1 origin "$GHOSTTY_COMMIT"
|
|
32
|
-
git checkout -q FETCH_HEAD
|
|
33
45
|
cd ..
|
|
34
46
|
fi
|
|
47
|
+
if ! git -C ghostty cat-file -e "$GHOSTTY_COMMIT^{commit}" 2>/dev/null; then
|
|
48
|
+
git -C ghostty fetch -q --depth 1 origin "$GHOSTTY_COMMIT"
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# Build only from fresh trees materialized from the verified archive and exact
|
|
52
|
+
# Git object. Cached working-tree edits and stale install outputs cannot enter.
|
|
53
|
+
build_root="$(mktemp -d "$work/native-build.XXXXXX")"
|
|
54
|
+
trap 'rm -rf "$build_root"' EXIT HUP INT TERM
|
|
55
|
+
tar -xf "$zig_archive" -C "$build_root"
|
|
56
|
+
zig="$build_root/zig-$zig_arch-$zig_os-$ZIG_VERSION/zig"
|
|
57
|
+
ghostty_source="$build_root/ghostty"
|
|
58
|
+
mkdir "$ghostty_source"
|
|
59
|
+
git -C ghostty archive "$GHOSTTY_COMMIT" | tar -x -C "$ghostty_source"
|
|
35
60
|
|
|
36
61
|
echo "== building libghostty-vt (ReleaseFast) =="
|
|
37
|
-
cd
|
|
62
|
+
cd "$ghostty_source"
|
|
38
63
|
# The final xcframework packaging step can fail while the .a we need is produced,
|
|
39
64
|
# so tolerate a non-zero exit here but assert the static lib exists below.
|
|
40
|
-
|
|
41
|
-
|
|
65
|
+
vt_install="$build_root/vt-install"
|
|
66
|
+
"$zig" build -Demit-lib-vt -Doptimize=ReleaseFast --prefix "$vt_install" || true
|
|
42
67
|
cd ..
|
|
43
|
-
staticlib="$
|
|
68
|
+
staticlib="$vt_install/lib/libghostty-vt.a"
|
|
44
69
|
[ -f "$staticlib" ] || { echo "build failed: $staticlib not produced" >&2; exit 1; }
|
|
45
70
|
|
|
46
71
|
echo "== linking shim -> $outdir/$dylib =="
|
|
47
72
|
mkdir -p "$outdir"
|
|
48
73
|
if [ "$os" = "darwin" ]; then
|
|
49
|
-
|
|
50
|
-
-
|
|
74
|
+
"$zig" build-lib -dynamic -OReleaseFast -lc -femit-bin="$outdir/$dylib" \
|
|
75
|
+
-install_name "@rpath/$dylib" \
|
|
76
|
+
-I"$vt_install/include" "$here/src/shim.zig" "$staticlib" \
|
|
51
77
|
-framework CoreFoundation -framework CoreText -framework CoreGraphics
|
|
52
78
|
else
|
|
53
|
-
|
|
54
|
-
-I"$
|
|
79
|
+
"$zig" build-lib -dynamic -OReleaseFast -lc -femit-bin="$outdir/$dylib" \
|
|
80
|
+
-I"$vt_install/include" "$here/src/shim.zig" "$staticlib" -lm -lpthread
|
|
55
81
|
fi
|
|
56
82
|
echo "== done: $outdir/$dylib =="
|
|
Binary file
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// Flat FFI wrapper over libghostty-vt. It hides the allocator vtable, the
|
|
2
|
+
// by-value GhosttyPoint that bun:ffi cannot pass, and packed-cell decoding.
|
|
3
|
+
const std = @import("std");
|
|
4
|
+
const ghostty = @cImport({
|
|
5
|
+
@cInclude("ghostty/vt/types.h");
|
|
6
|
+
@cInclude("ghostty/vt/allocator.h");
|
|
7
|
+
@cInclude("ghostty/vt/terminal.h");
|
|
8
|
+
@cInclude("ghostty/vt/point.h");
|
|
9
|
+
@cInclude("ghostty/vt/grid_ref.h");
|
|
10
|
+
@cInclude("ghostty/vt/screen.h");
|
|
11
|
+
@cInclude("ghostty/vt/style.h");
|
|
12
|
+
@cInclude("ghostty/vt/color.h");
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const CvtCell = extern struct {
|
|
16
|
+
codepoint: u32,
|
|
17
|
+
fg_r: u8,
|
|
18
|
+
fg_g: u8,
|
|
19
|
+
fg_b: u8,
|
|
20
|
+
fg_kind: u8,
|
|
21
|
+
bg_r: u8,
|
|
22
|
+
bg_g: u8,
|
|
23
|
+
bg_b: u8,
|
|
24
|
+
bg_kind: u8,
|
|
25
|
+
width: u8,
|
|
26
|
+
flags: u8,
|
|
27
|
+
_pad: u16,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
comptime {
|
|
31
|
+
std.debug.assert(@sizeOf(CvtCell) == 16);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export fn cvt_new(cols: u16, rows: u16) ?*anyopaque {
|
|
35
|
+
var terminal: ghostty.GhosttyTerminal = null;
|
|
36
|
+
if (ghostty.ghostty_terminal_new(null, &terminal, cols, rows) != ghostty.GHOSTTY_SUCCESS) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
return terminal;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export fn cvt_free(terminal: ?*anyopaque) void {
|
|
43
|
+
if (terminal) |handle| ghostty.ghostty_terminal_free(@ptrCast(handle));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export fn cvt_write(terminal: *anyopaque, data: [*]const u8, len: usize) void {
|
|
47
|
+
ghostty.ghostty_terminal_vt_write(@ptrCast(terminal), data, len);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export fn cvt_resize(terminal: *anyopaque, cols: u16, rows: u16) void {
|
|
51
|
+
// Cell pixel size does not affect the character grid.
|
|
52
|
+
_ = ghostty.ghostty_terminal_resize(@ptrCast(terminal), cols, rows, 1, 1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Writes [col u16 LE, row u16 LE, visible u8] into the caller's eight bytes.
|
|
56
|
+
export fn cvt_cursor(terminal: *anyopaque, out: [*]u8) void {
|
|
57
|
+
var x: u16 = 0;
|
|
58
|
+
var y: u16 = 0;
|
|
59
|
+
var visible = false;
|
|
60
|
+
_ = ghostty.ghostty_terminal_get(@ptrCast(terminal), ghostty.GHOSTTY_TERMINAL_DATA_CURSOR_X, &x);
|
|
61
|
+
_ = ghostty.ghostty_terminal_get(@ptrCast(terminal), ghostty.GHOSTTY_TERMINAL_DATA_CURSOR_Y, &y);
|
|
62
|
+
_ = ghostty.ghostty_terminal_get(@ptrCast(terminal), ghostty.GHOSTTY_TERMINAL_DATA_CURSOR_VISIBLE, &visible);
|
|
63
|
+
out[0] = @truncate(x);
|
|
64
|
+
out[1] = @truncate(x >> 8);
|
|
65
|
+
out[2] = @truncate(y);
|
|
66
|
+
out[3] = @truncate(y >> 8);
|
|
67
|
+
out[4] = @intFromBool(visible);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
fn decodeColor(color: *const ghostty.GhosttyStyleColor, red: *u8, green: *u8, blue: *u8, kind: *u8) void {
|
|
71
|
+
if (color.tag == ghostty.GHOSTTY_STYLE_COLOR_RGB) {
|
|
72
|
+
kind.* = 2;
|
|
73
|
+
red.* = color.value.rgb.r;
|
|
74
|
+
green.* = color.value.rgb.g;
|
|
75
|
+
blue.* = color.value.rgb.b;
|
|
76
|
+
} else if (color.tag == ghostty.GHOSTTY_STYLE_COLOR_PALETTE) {
|
|
77
|
+
kind.* = 1;
|
|
78
|
+
red.* = @intCast(color.value.palette);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export fn cvt_cell(terminal: *anyopaque, x: u16, y: u32, out: *CvtCell) c_int {
|
|
83
|
+
out.* = std.mem.zeroes(CvtCell);
|
|
84
|
+
|
|
85
|
+
var point: ghostty.GhosttyPoint = undefined;
|
|
86
|
+
point.tag = ghostty.GHOSTTY_POINT_TAG_VIEWPORT;
|
|
87
|
+
point.value.coordinate.x = x;
|
|
88
|
+
point.value.coordinate.y = y;
|
|
89
|
+
|
|
90
|
+
var reference: ghostty.GhosttyGridRef = undefined;
|
|
91
|
+
if (ghostty.ghostty_terminal_grid_ref(@ptrCast(terminal), point, &reference) != ghostty.GHOSTTY_SUCCESS) return -1;
|
|
92
|
+
|
|
93
|
+
var cell: ghostty.GhosttyCell = undefined;
|
|
94
|
+
if (ghostty.ghostty_grid_ref_cell(&reference, &cell) != ghostty.GHOSTTY_SUCCESS) return -1;
|
|
95
|
+
|
|
96
|
+
_ = ghostty.ghostty_cell_get(cell, ghostty.GHOSTTY_CELL_DATA_CODEPOINT, &out.codepoint);
|
|
97
|
+
var width: u32 = 0;
|
|
98
|
+
_ = ghostty.ghostty_cell_get(cell, ghostty.GHOSTTY_CELL_DATA_WIDE, &width);
|
|
99
|
+
out.width = @intCast(width);
|
|
100
|
+
|
|
101
|
+
var style: ghostty.GhosttyStyle = undefined;
|
|
102
|
+
ghostty.ghostty_style_default(&style);
|
|
103
|
+
if (ghostty.ghostty_grid_ref_style(&reference, &style) == ghostty.GHOSTTY_SUCCESS) {
|
|
104
|
+
decodeColor(&style.fg_color, &out.fg_r, &out.fg_g, &out.fg_b, &out.fg_kind);
|
|
105
|
+
decodeColor(&style.bg_color, &out.bg_r, &out.bg_g, &out.bg_b, &out.bg_kind);
|
|
106
|
+
if (style.bold) out.flags |= 1;
|
|
107
|
+
if (style.italic) out.flags |= 2;
|
|
108
|
+
if (style.underline != 0) out.flags |= 4;
|
|
109
|
+
if (style.inverse) out.flags |= 8;
|
|
110
|
+
if (style.faint) out.flags |= 16;
|
|
111
|
+
if (style.strikethrough) out.flags |= 32;
|
|
112
|
+
}
|
|
113
|
+
return 0;
|
|
114
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cueloop/client",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.42",
|
|
4
4
|
"description": "The cueloop review UI: projection renderer, keyboard grammar, review rail",
|
|
5
5
|
"homepage": "https://github.com/mmurakaru/cueloop#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -26,10 +26,10 @@
|
|
|
26
26
|
"access": "public"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@cueloop/daemon": "0.1.0-alpha.
|
|
30
|
-
"@cueloop/extension-api": "0.1.0-alpha.
|
|
31
|
-
"@cueloop/integration-obsidian": "0.1.0-alpha.
|
|
32
|
-
"@cueloop/schema": "0.1.0-alpha.
|
|
29
|
+
"@cueloop/daemon": "0.1.0-alpha.42",
|
|
30
|
+
"@cueloop/extension-api": "0.1.0-alpha.42",
|
|
31
|
+
"@cueloop/integration-obsidian": "0.1.0-alpha.42",
|
|
32
|
+
"@cueloop/schema": "0.1.0-alpha.42",
|
|
33
33
|
"@opentui/core": "^0.5.2",
|
|
34
34
|
"@opentui/keymap": "0.5.2",
|
|
35
35
|
"@opentui/react": "^0.5.2",
|
|
@@ -120,7 +120,7 @@ export class TerminalPaneRenderable extends Renderable {
|
|
|
120
120
|
for (let y = 0; y < this.height; y++) {
|
|
121
121
|
for (let x = 0; x < this.width; x++) {
|
|
122
122
|
const cell = this.vt.readCell(x, y);
|
|
123
|
-
//
|
|
123
|
+
// Skip an empty normal-width cell; the buffer already starts blank.
|
|
124
124
|
if (cell && cell.width === 0 && cell.codepoint === 0) continue;
|
|
125
125
|
const char = cell && cell.codepoint ? String.fromCodePoint(cell.codepoint) : " ";
|
|
126
126
|
let fg = cell ? resolveColor(cell.fg, DEFAULT_FG) : DEFAULT_FG;
|
|
@@ -42,4 +42,41 @@ describe("ghostty-terminal FFI", () => {
|
|
|
42
42
|
expect(cursor.x).toBe(3);
|
|
43
43
|
term.free();
|
|
44
44
|
});
|
|
45
|
+
|
|
46
|
+
test.skipIf(!factory)(
|
|
47
|
+
"preserves the native screen contract across resize and styled text",
|
|
48
|
+
() => {
|
|
49
|
+
// Arrange
|
|
50
|
+
const term = factory!.create(8, 2)!;
|
|
51
|
+
|
|
52
|
+
// Act - resize first, then write RGB colors, every supported style, and a wide glyph
|
|
53
|
+
term.resize(6, 3);
|
|
54
|
+
term.write(
|
|
55
|
+
new TextEncoder().encode(
|
|
56
|
+
"\x1b[38;2;12;34;56;48;2;78;90;123;1;3;4;7;9mA\x1b[0m\x1b[2mF\x1b[0m\u754c",
|
|
57
|
+
),
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
// Assert - the flat native ABI retains cell data and rejects coordinates outside the viewport
|
|
61
|
+
expect(term.readCell(0, 0)).toEqual({
|
|
62
|
+
codepoint: 0x41,
|
|
63
|
+
fg: { kind: "rgb", r: 12, g: 34, b: 56 },
|
|
64
|
+
bg: { kind: "rgb", r: 78, g: 90, b: 123 },
|
|
65
|
+
width: 0,
|
|
66
|
+
bold: true,
|
|
67
|
+
italic: true,
|
|
68
|
+
underline: true,
|
|
69
|
+
inverse: true,
|
|
70
|
+
faint: false,
|
|
71
|
+
strikethrough: true,
|
|
72
|
+
});
|
|
73
|
+
expect(term.readCell(1, 0)?.faint).toBe(true);
|
|
74
|
+
expect(term.readCell(2, 0)?.codepoint).toBe("\u754c".codePointAt(0));
|
|
75
|
+
expect(term.readCell(2, 0)?.width).toBe(1);
|
|
76
|
+
expect(term.readCell(3, 0)?.width).toBe(2);
|
|
77
|
+
expect(term.readCell(6, 0)).toBeNull();
|
|
78
|
+
expect(term.readCell(0, 3)).toBeNull();
|
|
79
|
+
term.free();
|
|
80
|
+
},
|
|
81
|
+
);
|
|
45
82
|
});
|
package/src/ghostty-terminal.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A live in-process terminal emulator backed by Ghostty's VT core (libghostty-vt)
|
|
3
|
-
* through a flat FFI shim (native/src/shim.
|
|
3
|
+
* through a flat FFI shim (native/src/shim.zig). Feed it a child process's PTY
|
|
4
4
|
* output with `write`, then read the rendered screen cell-by-cell with `readCell`
|
|
5
5
|
* to paint it anywhere - this is what lets a real `cc`/shell render inside an
|
|
6
6
|
* OpenTUI box. Load with `loadGhosttyTerminals`, which returns null when no
|
|
@@ -30,7 +30,7 @@ export interface GhosttyCell {
|
|
|
30
30
|
codepoint: number;
|
|
31
31
|
fg: GhosttyColor;
|
|
32
32
|
bg: GhosttyColor;
|
|
33
|
-
/**
|
|
33
|
+
/** Ghostty width tag: 0 = normal, 1 = double-width lead, 2 = trailing cell. */
|
|
34
34
|
width: number;
|
|
35
35
|
bold: boolean;
|
|
36
36
|
italic: boolean;
|
package/native/src/shim.c
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
// Flat, FFI-trivial wrapper over libghostty-vt: hides the allocator vtable, the
|
|
2
|
-
// by-value GhosttyPoint (which bun:ffi cannot pass), and the packed-cell decode,
|
|
3
|
-
// exposing exactly what an OpenTUI paint loop needs - new/write/resize/cell/free.
|
|
4
|
-
#include <stdint.h>
|
|
5
|
-
#include <stddef.h>
|
|
6
|
-
#include <stdbool.h>
|
|
7
|
-
#include <string.h>
|
|
8
|
-
#include "ghostty/vt/types.h"
|
|
9
|
-
#include "ghostty/vt/allocator.h"
|
|
10
|
-
#include "ghostty/vt/terminal.h"
|
|
11
|
-
#include "ghostty/vt/point.h"
|
|
12
|
-
#include "ghostty/vt/grid_ref.h"
|
|
13
|
-
#include "ghostty/vt/screen.h"
|
|
14
|
-
#include "ghostty/vt/style.h"
|
|
15
|
-
#include "ghostty/vt/color.h"
|
|
16
|
-
|
|
17
|
-
typedef struct {
|
|
18
|
-
uint32_t codepoint; // 0 = blank
|
|
19
|
-
uint8_t fg_r, fg_g, fg_b, fg_kind; // kind: 0 default, 1 palette (index in fg_r), 2 rgb
|
|
20
|
-
uint8_t bg_r, bg_g, bg_b, bg_kind;
|
|
21
|
-
uint8_t width; // 0 (wide trailing), 1, 2
|
|
22
|
-
uint8_t flags; // bit0 bold, 1 italic, 2 underline, 3 inverse, 4 faint, 5 strikethrough
|
|
23
|
-
uint16_t _pad;
|
|
24
|
-
} CvtCell;
|
|
25
|
-
|
|
26
|
-
void *cvt_new(uint16_t cols, uint16_t rows) {
|
|
27
|
-
GhosttyTerminal t = NULL;
|
|
28
|
-
// NULL allocator selects libghostty's default (libc malloc/free when linked).
|
|
29
|
-
if (ghostty_terminal_new(NULL, &t, cols, rows) != GHOSTTY_SUCCESS) return NULL;
|
|
30
|
-
return (void *)t;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
void cvt_free(void *t) {
|
|
34
|
-
if (t) ghostty_terminal_free((GhosttyTerminal)t);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
void cvt_write(void *t, const uint8_t *data, size_t len) {
|
|
38
|
-
ghostty_terminal_vt_write((GhosttyTerminal)t, data, len);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
void cvt_resize(void *t, uint16_t cols, uint16_t rows) {
|
|
42
|
-
// cell pixel size is irrelevant to the character grid; pass a nominal 1x1.
|
|
43
|
-
ghostty_terminal_resize((GhosttyTerminal)t, cols, rows, 1, 1);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Writes cursor state into out8: [0..1] col (u16), [2..3] row (u16), [4] visible (u8).
|
|
47
|
-
void cvt_cursor(void *t, uint8_t *out8) {
|
|
48
|
-
uint16_t cx = 0, cy = 0;
|
|
49
|
-
bool vis = false;
|
|
50
|
-
ghostty_terminal_get((GhosttyTerminal)t, GHOSTTY_TERMINAL_DATA_CURSOR_X, &cx);
|
|
51
|
-
ghostty_terminal_get((GhosttyTerminal)t, GHOSTTY_TERMINAL_DATA_CURSOR_Y, &cy);
|
|
52
|
-
ghostty_terminal_get((GhosttyTerminal)t, GHOSTTY_TERMINAL_DATA_CURSOR_VISIBLE, &vis);
|
|
53
|
-
memcpy(out8, &cx, 2);
|
|
54
|
-
memcpy(out8 + 2, &cy, 2);
|
|
55
|
-
out8[4] = vis ? 1 : 0;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
static void decode_color(const GhosttyStyleColor *c, uint8_t *r, uint8_t *g, uint8_t *b,
|
|
59
|
-
uint8_t *kind) {
|
|
60
|
-
if (c->tag == GHOSTTY_STYLE_COLOR_RGB) {
|
|
61
|
-
*kind = 2;
|
|
62
|
-
*r = c->value.rgb.r;
|
|
63
|
-
*g = c->value.rgb.g;
|
|
64
|
-
*b = c->value.rgb.b;
|
|
65
|
-
} else if (c->tag == GHOSTTY_STYLE_COLOR_PALETTE) {
|
|
66
|
-
*kind = 1;
|
|
67
|
-
*r = (uint8_t)c->value.palette; // palette index in r; JS maps 0-255 -> RGB
|
|
68
|
-
*g = 0;
|
|
69
|
-
*b = 0;
|
|
70
|
-
} else {
|
|
71
|
-
*kind = 0;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Reads the viewport cell at (x, y). Returns 0 on success, -1 otherwise.
|
|
76
|
-
int cvt_cell(void *t, uint16_t x, uint32_t y, CvtCell *out) {
|
|
77
|
-
memset(out, 0, sizeof(*out));
|
|
78
|
-
GhosttyPoint p;
|
|
79
|
-
p.tag = GHOSTTY_POINT_TAG_VIEWPORT;
|
|
80
|
-
p.value.coordinate.x = x;
|
|
81
|
-
p.value.coordinate.y = y;
|
|
82
|
-
GhosttyGridRef ref;
|
|
83
|
-
if (ghostty_terminal_grid_ref((GhosttyTerminal)t, p, &ref) != GHOSTTY_SUCCESS) return -1;
|
|
84
|
-
GhosttyCell cell;
|
|
85
|
-
if (ghostty_grid_ref_cell(&ref, &cell) != GHOSTTY_SUCCESS) return -1;
|
|
86
|
-
uint32_t cp = 0;
|
|
87
|
-
ghostty_cell_get(cell, GHOSTTY_CELL_DATA_CODEPOINT, &cp);
|
|
88
|
-
out->codepoint = cp;
|
|
89
|
-
uint32_t wide = 0;
|
|
90
|
-
ghostty_cell_get(cell, GHOSTTY_CELL_DATA_WIDE, &wide);
|
|
91
|
-
out->width = (uint8_t)wide;
|
|
92
|
-
GhosttyStyle st;
|
|
93
|
-
ghostty_style_default(&st);
|
|
94
|
-
if (ghostty_grid_ref_style(&ref, &st) == GHOSTTY_SUCCESS) {
|
|
95
|
-
decode_color(&st.fg_color, &out->fg_r, &out->fg_g, &out->fg_b, &out->fg_kind);
|
|
96
|
-
decode_color(&st.bg_color, &out->bg_r, &out->bg_g, &out->bg_b, &out->bg_kind);
|
|
97
|
-
uint8_t f = 0;
|
|
98
|
-
if (st.bold) f |= 1;
|
|
99
|
-
if (st.italic) f |= 2;
|
|
100
|
-
if (st.underline) f |= 4;
|
|
101
|
-
if (st.inverse) f |= 8;
|
|
102
|
-
if (st.faint) f |= 16;
|
|
103
|
-
if (st.strikethrough) f |= 32;
|
|
104
|
-
out->flags = f;
|
|
105
|
-
}
|
|
106
|
-
return 0;
|
|
107
|
-
}
|