mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 08:46:08 +03:00
6
.github/workflows/release-tip.yml
vendored
6
.github/workflows/release-tip.yml
vendored
@ -96,6 +96,12 @@ jobs:
|
|||||||
- name: Build Ghostty.app
|
- name: Build Ghostty.app
|
||||||
run: cd macos && xcodebuild -configuration Release
|
run: cd macos && xcodebuild -configuration Release
|
||||||
|
|
||||||
|
# Copy the resources we build during zig build into the final Ghostty.app
|
||||||
|
- name: Copy Resources
|
||||||
|
run: |
|
||||||
|
# Terminfo
|
||||||
|
cp -R zig-out/Ghostty.app/Contents/Resources/terminfo macos/build/Release/Ghostty.app/Contents/Resources/terminfo
|
||||||
|
|
||||||
# We inject the "build number" as simply the number of commits since HEAD.
|
# We inject the "build number" as simply the number of commits since HEAD.
|
||||||
# This will be a monotonically always increasing build number that we use.
|
# This will be a monotonically always increasing build number that we use.
|
||||||
- name: Inject Build Number
|
- name: Inject Build Number
|
||||||
|
89
build.zig
89
build.zig
@ -3,7 +3,16 @@ const builtin = @import("builtin");
|
|||||||
const fs = std.fs;
|
const fs = std.fs;
|
||||||
const LibExeObjStep = std.build.LibExeObjStep;
|
const LibExeObjStep = std.build.LibExeObjStep;
|
||||||
const RunStep = std.build.RunStep;
|
const RunStep = std.build.RunStep;
|
||||||
|
|
||||||
const apprt = @import("src/apprt.zig");
|
const apprt = @import("src/apprt.zig");
|
||||||
|
const font = @import("src/font/main.zig");
|
||||||
|
const terminfo = @import("src/terminfo/main.zig");
|
||||||
|
const WasmTarget = @import("src/os/wasm/target.zig").Target;
|
||||||
|
const LibtoolStep = @import("src/build/LibtoolStep.zig");
|
||||||
|
const LipoStep = @import("src/build/LipoStep.zig");
|
||||||
|
const XCFrameworkStep = @import("src/build/XCFrameworkStep.zig");
|
||||||
|
const Version = @import("src/build/Version.zig");
|
||||||
|
|
||||||
const glfw = @import("vendor/mach-glfw/build.zig");
|
const glfw = @import("vendor/mach-glfw/build.zig");
|
||||||
const fontconfig = @import("pkg/fontconfig/build.zig");
|
const fontconfig = @import("pkg/fontconfig/build.zig");
|
||||||
const freetype = @import("pkg/freetype/build.zig");
|
const freetype = @import("pkg/freetype/build.zig");
|
||||||
@ -21,12 +30,6 @@ const utf8proc = @import("pkg/utf8proc/build.zig");
|
|||||||
const zlib = @import("pkg/zlib/build.zig");
|
const zlib = @import("pkg/zlib/build.zig");
|
||||||
const tracylib = @import("pkg/tracy/build.zig");
|
const tracylib = @import("pkg/tracy/build.zig");
|
||||||
const system_sdk = @import("vendor/mach-glfw/system_sdk.zig");
|
const system_sdk = @import("vendor/mach-glfw/system_sdk.zig");
|
||||||
const font = @import("src/font/main.zig");
|
|
||||||
const WasmTarget = @import("src/os/wasm/target.zig").Target;
|
|
||||||
const LibtoolStep = @import("src/build/LibtoolStep.zig");
|
|
||||||
const LipoStep = @import("src/build/LipoStep.zig");
|
|
||||||
const XCFrameworkStep = @import("src/build/XCFrameworkStep.zig");
|
|
||||||
const Version = @import("src/build/Version.zig");
|
|
||||||
|
|
||||||
// Do a comptime Zig version requirement. The required Zig version is
|
// Do a comptime Zig version requirement. The required Zig version is
|
||||||
// somewhat arbitrary: it is meant to be a version that we feel works well,
|
// somewhat arbitrary: it is meant to be a version that we feel works well,
|
||||||
@ -271,6 +274,80 @@ pub fn build(b: *std.Build) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Terminfo
|
||||||
|
{
|
||||||
|
// Encode our terminfo
|
||||||
|
var str = std.ArrayList(u8).init(b.allocator);
|
||||||
|
defer str.deinit();
|
||||||
|
try terminfo.ghostty.encode(str.writer());
|
||||||
|
|
||||||
|
// Write it
|
||||||
|
var wf = b.addWriteFiles();
|
||||||
|
const src_source = wf.add("share/terminfo/ghostty.terminfo", str.items);
|
||||||
|
const src_install = b.addInstallFile(src_source, "share/terminfo/ghostty.terminfo");
|
||||||
|
b.getInstallStep().dependOn(&src_install.step);
|
||||||
|
if (target.isDarwin()) {
|
||||||
|
const mac_src_install = b.addInstallFile(
|
||||||
|
src_source,
|
||||||
|
"Ghostty.app/Contents/Resources/terminfo/ghostty.terminfo",
|
||||||
|
);
|
||||||
|
b.getInstallStep().dependOn(&mac_src_install.step);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to termcap source format if thats helpful to people and
|
||||||
|
// install it. The resulting value here is the termcap source in case
|
||||||
|
// that is used for other commands.
|
||||||
|
{
|
||||||
|
const run_step = RunStep.create(b, "infotocap");
|
||||||
|
run_step.addArg("infotocap");
|
||||||
|
run_step.addFileSourceArg(src_source);
|
||||||
|
const out_source = run_step.captureStdOut();
|
||||||
|
_ = run_step.captureStdErr(); // so we don't see stderr
|
||||||
|
|
||||||
|
const cap_install = b.addInstallFile(out_source, "share/terminfo/ghostty.termcap");
|
||||||
|
b.getInstallStep().dependOn(&cap_install.step);
|
||||||
|
|
||||||
|
if (target.isDarwin()) {
|
||||||
|
const mac_cap_install = b.addInstallFile(
|
||||||
|
out_source,
|
||||||
|
"Ghostty.app/Contents/Resources/terminfo/ghostty.termcap",
|
||||||
|
);
|
||||||
|
b.getInstallStep().dependOn(&mac_cap_install.step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile the terminfo source into a terminfo database
|
||||||
|
{
|
||||||
|
const run_step = RunStep.create(b, "tic");
|
||||||
|
run_step.addArgs(&.{ "tic", "-x", "-o" });
|
||||||
|
const path = run_step.addOutputFileArg("terminfo");
|
||||||
|
run_step.addFileSourceArg(src_source);
|
||||||
|
_ = run_step.captureStdErr(); // so we don't see stderr
|
||||||
|
|
||||||
|
// Depend on the terminfo source install step so that Zig build
|
||||||
|
// creates the "share" directory for us.
|
||||||
|
run_step.step.dependOn(&src_install.step);
|
||||||
|
|
||||||
|
{
|
||||||
|
const copy_step = RunStep.create(b, "copy terminfo db");
|
||||||
|
copy_step.addArgs(&.{ "cp", "-R" });
|
||||||
|
copy_step.addFileSourceArg(path);
|
||||||
|
copy_step.addArg(b.fmt("{s}/share", .{b.install_prefix}));
|
||||||
|
b.getInstallStep().dependOn(©_step.step);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target.isDarwin()) {
|
||||||
|
const copy_step = RunStep.create(b, "copy terminfo db");
|
||||||
|
copy_step.addArgs(&.{ "cp", "-R" });
|
||||||
|
copy_step.addFileSourceArg(path);
|
||||||
|
copy_step.addArg(
|
||||||
|
b.fmt("{s}/Ghostty.app/Contents/Resources", .{b.install_prefix}),
|
||||||
|
);
|
||||||
|
b.getInstallStep().dependOn(©_step.step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// App (Linux)
|
// App (Linux)
|
||||||
if (target.isLinux()) {
|
if (target.isLinux()) {
|
||||||
// https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html
|
// https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
, flatpak-builder
|
, flatpak-builder
|
||||||
, gdb
|
, gdb
|
||||||
, glxinfo
|
, glxinfo
|
||||||
|
, ncurses
|
||||||
, nodejs
|
, nodejs
|
||||||
, parallel
|
, parallel
|
||||||
, pkg-config
|
, pkg-config
|
||||||
@ -67,6 +68,7 @@ in mkShell rec {
|
|||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
# For builds
|
# For builds
|
||||||
llvmPackages_latest.llvm
|
llvmPackages_latest.llvm
|
||||||
|
ncurses
|
||||||
pkg-config
|
pkg-config
|
||||||
scdoc
|
scdoc
|
||||||
zig
|
zig
|
||||||
|
@ -190,6 +190,7 @@ test {
|
|||||||
// Libraries
|
// Libraries
|
||||||
_ = @import("segmented_pool.zig");
|
_ = @import("segmented_pool.zig");
|
||||||
_ = @import("terminal/main.zig");
|
_ = @import("terminal/main.zig");
|
||||||
|
_ = @import("terminfo/main.zig");
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
_ = @import("blocking_queue.zig");
|
_ = @import("blocking_queue.zig");
|
||||||
|
@ -382,15 +382,7 @@ fn doAction(self: *Parser, action: TransitionAction, c: u8) ?Action {
|
|||||||
self.params_idx += 1;
|
self.params_idx += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We only allow the colon separator for the 'm' command.
|
const result: Action = .{
|
||||||
switch (self.params_sep) {
|
|
||||||
.none => {},
|
|
||||||
.semicolon => {},
|
|
||||||
.colon => if (c != 'm') break :csi_dispatch null,
|
|
||||||
.mixed => break :csi_dispatch null,
|
|
||||||
}
|
|
||||||
|
|
||||||
break :csi_dispatch Action{
|
|
||||||
.csi_dispatch = .{
|
.csi_dispatch = .{
|
||||||
.intermediates = self.intermediates[0..self.intermediates_idx],
|
.intermediates = self.intermediates[0..self.intermediates_idx],
|
||||||
.params = self.params[0..self.params_idx],
|
.params = self.params[0..self.params_idx],
|
||||||
@ -398,10 +390,35 @@ fn doAction(self: *Parser, action: TransitionAction, c: u8) ?Action {
|
|||||||
.sep = switch (self.params_sep) {
|
.sep = switch (self.params_sep) {
|
||||||
.none, .semicolon => .semicolon,
|
.none, .semicolon => .semicolon,
|
||||||
.colon => .colon,
|
.colon => .colon,
|
||||||
.mixed => unreachable,
|
|
||||||
|
// This should never happen because of the checks below
|
||||||
|
// but we have to exhaustively handle the switch.
|
||||||
|
.mixed => .semicolon,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// We only allow the colon separator for the 'm' command.
|
||||||
|
switch (self.params_sep) {
|
||||||
|
.none => {},
|
||||||
|
.semicolon => {},
|
||||||
|
.colon => if (c != 'm') {
|
||||||
|
log.warn(
|
||||||
|
"CSI colon separator only allowed for 'm' command, got: {}",
|
||||||
|
.{result},
|
||||||
|
);
|
||||||
|
break :csi_dispatch null;
|
||||||
|
},
|
||||||
|
.mixed => {
|
||||||
|
log.warn(
|
||||||
|
"CSI command had mixed colons and semicolons, got: {}",
|
||||||
|
.{result},
|
||||||
|
);
|
||||||
|
break :csi_dispatch null;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
break :csi_dispatch result;
|
||||||
},
|
},
|
||||||
.esc_dispatch => Action{
|
.esc_dispatch => Action{
|
||||||
.esc_dispatch = .{
|
.esc_dispatch = .{
|
||||||
@ -418,6 +435,7 @@ fn doAction(self: *Parser, action: TransitionAction, c: u8) ?Action {
|
|||||||
fn clear(self: *Parser) void {
|
fn clear(self: *Parser) void {
|
||||||
self.intermediates_idx = 0;
|
self.intermediates_idx = 0;
|
||||||
self.params_idx = 0;
|
self.params_idx = 0;
|
||||||
|
self.params_sep = .none;
|
||||||
self.param_acc = 0;
|
self.param_acc = 0;
|
||||||
self.param_acc_idx = 0;
|
self.param_acc_idx = 0;
|
||||||
}
|
}
|
||||||
@ -531,6 +549,64 @@ test "csi: SGR ESC [ 38 : 2 m" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "csi: SGR colon followed by semicolon" {
|
||||||
|
var p = init();
|
||||||
|
_ = p.next(0x1B);
|
||||||
|
for ("[48:2") |c| {
|
||||||
|
const a = p.next(c);
|
||||||
|
try testing.expect(a[0] == null);
|
||||||
|
try testing.expect(a[1] == null);
|
||||||
|
try testing.expect(a[2] == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const a = p.next('m');
|
||||||
|
try testing.expect(p.state == .ground);
|
||||||
|
try testing.expect(a[0] == null);
|
||||||
|
try testing.expect(a[1].? == .csi_dispatch);
|
||||||
|
try testing.expect(a[2] == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = p.next(0x1B);
|
||||||
|
_ = p.next('[');
|
||||||
|
{
|
||||||
|
const a = p.next('H');
|
||||||
|
try testing.expect(p.state == .ground);
|
||||||
|
try testing.expect(a[0] == null);
|
||||||
|
try testing.expect(a[1].? == .csi_dispatch);
|
||||||
|
try testing.expect(a[2] == null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "csi: SGR ESC [ 48 : 2 m" {
|
||||||
|
var p = init();
|
||||||
|
_ = p.next(0x1B);
|
||||||
|
for ("[48:2:240:143:104") |c| {
|
||||||
|
const a = p.next(c);
|
||||||
|
try testing.expect(a[0] == null);
|
||||||
|
try testing.expect(a[1] == null);
|
||||||
|
try testing.expect(a[2] == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const a = p.next('m');
|
||||||
|
try testing.expect(p.state == .ground);
|
||||||
|
try testing.expect(a[0] == null);
|
||||||
|
try testing.expect(a[1].? == .csi_dispatch);
|
||||||
|
try testing.expect(a[2] == null);
|
||||||
|
|
||||||
|
const d = a[1].?.csi_dispatch;
|
||||||
|
try testing.expect(d.final == 'm');
|
||||||
|
try testing.expect(d.sep == .colon);
|
||||||
|
try testing.expect(d.params.len == 5);
|
||||||
|
try testing.expectEqual(@as(u16, 48), d.params[0]);
|
||||||
|
try testing.expectEqual(@as(u16, 2), d.params[1]);
|
||||||
|
try testing.expectEqual(@as(u16, 240), d.params[2]);
|
||||||
|
try testing.expectEqual(@as(u16, 143), d.params[3]);
|
||||||
|
try testing.expectEqual(@as(u16, 104), d.params[4]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "csi: SGR ESC [4:3m colon" {
|
test "csi: SGR ESC [4:3m colon" {
|
||||||
var p = init();
|
var p = init();
|
||||||
_ = p.next(0x1B);
|
_ = p.next(0x1B);
|
||||||
|
@ -467,6 +467,16 @@ test "sgr: 256 color" {
|
|||||||
try testing.expect(p.next().? == .@"256_bg");
|
try testing.expect(p.next().? == .@"256_bg");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "sgr: 24-bit bg color" {
|
||||||
|
{
|
||||||
|
const v = testParseColon(&[_]u16{ 48, 2, 1, 2, 3 });
|
||||||
|
try testing.expect(v == .direct_color_bg);
|
||||||
|
try testing.expectEqual(@as(u8, 1), v.direct_color_bg.r);
|
||||||
|
try testing.expectEqual(@as(u8, 2), v.direct_color_bg.g);
|
||||||
|
try testing.expectEqual(@as(u8, 3), v.direct_color_bg.b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "sgr: underline color" {
|
test "sgr: underline color" {
|
||||||
{
|
{
|
||||||
const v = testParseColon(&[_]u16{ 58, 2, 1, 2, 3 });
|
const v = testParseColon(&[_]u16{ 58, 2, 1, 2, 3 });
|
||||||
|
@ -48,7 +48,7 @@ pub fn Stream(comptime Handler: type) type {
|
|||||||
tracy.value(@intCast(u64, c));
|
tracy.value(@intCast(u64, c));
|
||||||
defer tracy.end();
|
defer tracy.end();
|
||||||
|
|
||||||
//log.debug("char: {x}", .{c});
|
// log.debug("char: {c}", .{c});
|
||||||
const actions = self.parser.next(c);
|
const actions = self.parser.next(c);
|
||||||
for (actions) |action_opt| {
|
for (actions) |action_opt| {
|
||||||
// if (action_opt) |action| {
|
// if (action_opt) |action| {
|
||||||
|
98
src/terminfo/Source.zig
Normal file
98
src/terminfo/Source.zig
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
//! Terminfo source format. This can be used to encode terminfo files.
|
||||||
|
//! This cannot parse terminfo source files yet because it isn't something
|
||||||
|
//! I need to do but this can be added later.
|
||||||
|
//!
|
||||||
|
//! Background: https://invisible-island.net/ncurses/man/terminfo.5.html
|
||||||
|
|
||||||
|
const Source = @This();
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
/// The set of names for the terminal. These match the TERM environment variable
|
||||||
|
/// and are used to look up this terminal. Historically, the final name in the
|
||||||
|
/// list was the most common name for the terminal and contains spaces and
|
||||||
|
/// other characters. See terminfo(5) for details.
|
||||||
|
names: []const []const u8,
|
||||||
|
|
||||||
|
/// The set of capabilities in this terminfo file.
|
||||||
|
capabilities: []const Capability,
|
||||||
|
|
||||||
|
/// A capability in a terminfo file. This also includes any "use" capabilities
|
||||||
|
/// since they behave just like other capabilities as documented in terminfo(5).
|
||||||
|
pub const Capability = struct {
|
||||||
|
/// The name of capability. This is the "Cap-name" value in terminfo(5).
|
||||||
|
name: []const u8,
|
||||||
|
value: Value,
|
||||||
|
|
||||||
|
pub const Value = union(enum) {
|
||||||
|
/// Canceled value, i.e. suffixed with @
|
||||||
|
canceled: void,
|
||||||
|
|
||||||
|
/// Boolean values are always true if they exist so there is no value.
|
||||||
|
boolean: void,
|
||||||
|
|
||||||
|
/// Numeric values are always "unsigned decimal integers". The size
|
||||||
|
/// of the integer is unspecified in terminfo(5). I chose 32-bits
|
||||||
|
/// because it is a common integer size but this may be wrong.
|
||||||
|
numeric: u32,
|
||||||
|
|
||||||
|
string: []const u8,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Encode as a terminfo source file. The encoding is always done in a
|
||||||
|
/// human-readable format with whitespace. Fields are always written in the
|
||||||
|
/// order of the slices on this struct; this will not do any reordering.
|
||||||
|
pub fn encode(self: Source, writer: anytype) !void {
|
||||||
|
// Encode the names in the order specified
|
||||||
|
for (self.names, 0..) |name, i| {
|
||||||
|
if (i != 0) try writer.writeAll("|");
|
||||||
|
try writer.writeAll(name);
|
||||||
|
}
|
||||||
|
try writer.writeAll(",\n");
|
||||||
|
|
||||||
|
// Encode each of the capabilities in the order specified
|
||||||
|
for (self.capabilities) |cap| {
|
||||||
|
try writer.writeAll("\t");
|
||||||
|
try writer.writeAll(cap.name);
|
||||||
|
switch (cap.value) {
|
||||||
|
.canceled => try writer.writeAll("@"),
|
||||||
|
.boolean => {},
|
||||||
|
.numeric => |v| try writer.print("#{d}", .{v}),
|
||||||
|
.string => |v| try writer.print("={s}", .{v}),
|
||||||
|
}
|
||||||
|
try writer.writeAll(",\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "encode" {
|
||||||
|
const src: Source = .{
|
||||||
|
.names = &.{
|
||||||
|
"ghostty",
|
||||||
|
"xterm-ghostty",
|
||||||
|
"Ghostty",
|
||||||
|
},
|
||||||
|
|
||||||
|
.capabilities = &.{
|
||||||
|
.{ .name = "am", .value = .{ .boolean = {} } },
|
||||||
|
.{ .name = "ccc", .value = .{ .canceled = {} } },
|
||||||
|
.{ .name = "colors", .value = .{ .numeric = 256 } },
|
||||||
|
.{ .name = "bel", .value = .{ .string = "^G" } },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Encode
|
||||||
|
var buf: [1024]u8 = undefined;
|
||||||
|
var buf_stream = std.io.fixedBufferStream(&buf);
|
||||||
|
try src.encode(buf_stream.writer());
|
||||||
|
|
||||||
|
const expected =
|
||||||
|
\\ghostty|xterm-ghostty|Ghostty,
|
||||||
|
\\ am,
|
||||||
|
\\ ccc@,
|
||||||
|
\\ colors#256,
|
||||||
|
\\ bel=^G,
|
||||||
|
\\
|
||||||
|
;
|
||||||
|
try std.testing.expectEqualStrings(@as([]const u8, expected), buf_stream.getWritten());
|
||||||
|
}
|
334
src/terminfo/ghostty.zig
Normal file
334
src/terminfo/ghostty.zig
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const Source = @import("Source.zig");
|
||||||
|
|
||||||
|
/// Ghostty's terminfo entry.
|
||||||
|
pub const ghostty: Source = .{
|
||||||
|
.names = &.{
|
||||||
|
// The preferred name
|
||||||
|
"ghostty",
|
||||||
|
|
||||||
|
// We support the "xterm-" prefix because some poorly behaved programs
|
||||||
|
// use this to detect if the terminal supports 256 colors and other
|
||||||
|
// features.
|
||||||
|
"xterm-ghostty",
|
||||||
|
|
||||||
|
// Our "formal" name
|
||||||
|
"Ghostty",
|
||||||
|
},
|
||||||
|
|
||||||
|
// NOTE: These capabilities are super underdocumented and I'm not 100%
|
||||||
|
// I've got the list or my understanding of any in this list fully correct.
|
||||||
|
// As we learn more, please update the comments to better explain what
|
||||||
|
// anything means.
|
||||||
|
//
|
||||||
|
// I've marked some capabilities as "???" if I don't understand what they
|
||||||
|
// mean but I just assume I support since other modern terminals do. In
|
||||||
|
// this case, I'd love if anyone could help explain what this means and
|
||||||
|
// verify that Ghostty does indeed support it and if not we can fix it.
|
||||||
|
.capabilities = &.{
|
||||||
|
// automatic right margin -- when reaching the end of a line, text is
|
||||||
|
// wrapped to the next line.
|
||||||
|
.{ .name = "am", .value = .{ .boolean = {} } },
|
||||||
|
|
||||||
|
// background color erase -- screen is erased with the background color
|
||||||
|
.{ .name = "bce", .value = .{ .boolean = {} } },
|
||||||
|
|
||||||
|
// terminal can change color definitions, i.e. we can change the color
|
||||||
|
// palette. TODO: this may require implementing CSI 4 which we don't
|
||||||
|
// at the time of writing this comment.
|
||||||
|
.{ .name = "ccc", .value = .{ .boolean = {} } },
|
||||||
|
|
||||||
|
// supports changing the window title.
|
||||||
|
.{ .name = "hs", .value = .{ .boolean = {} } },
|
||||||
|
|
||||||
|
// terminal has a meta key
|
||||||
|
.{ .name = "km", .value = .{ .boolean = {} } },
|
||||||
|
|
||||||
|
// terminal will not echo input on the screen on its own
|
||||||
|
.{ .name = "mc5i", .value = .{ .boolean = {} } },
|
||||||
|
|
||||||
|
// safe to move (move what?) while in insert/standout mode. (???)
|
||||||
|
.{ .name = "mir", .value = .{ .boolean = {} } },
|
||||||
|
.{ .name = "msgr", .value = .{ .boolean = {} } },
|
||||||
|
|
||||||
|
// no pad character (???)
|
||||||
|
.{ .name = "npc", .value = .{ .boolean = {} } },
|
||||||
|
|
||||||
|
// newline ignored after 80 cols (???)
|
||||||
|
.{ .name = "xenl", .value = .{ .boolean = {} } },
|
||||||
|
|
||||||
|
// Tmux "truecolor" mode. Other programs also use this to detect
|
||||||
|
// if the terminal supports "truecolor". This means that the terminal
|
||||||
|
// can display 24-bit RGB colors.
|
||||||
|
.{ .name = "Tc", .value = .{ .boolean = {} } },
|
||||||
|
|
||||||
|
// Colored underlines. https://sw.kovidgoyal.net/kitty/underlines/
|
||||||
|
.{ .name = "Su", .value = .{ .boolean = {} } },
|
||||||
|
|
||||||
|
// Full keyboard support using Kitty's keyboard protocol:
|
||||||
|
// https://sw.kovidgoyal.net/kitty/keyboard-protocol/
|
||||||
|
// Commented out because we don't yet support this.
|
||||||
|
// .{ .name = "fullkbd", .value = .{ .boolean = {} } },
|
||||||
|
|
||||||
|
// Number of colors in the color palette.
|
||||||
|
.{ .name = "colors", .value = .{ .numeric = 256 } },
|
||||||
|
|
||||||
|
// Number of columns in a line. Our terminal is variable width on
|
||||||
|
// Window resize but this appears to just be the value set by most
|
||||||
|
// terminals.
|
||||||
|
.{ .name = "cols", .value = .{ .numeric = 80 } },
|
||||||
|
|
||||||
|
// Initial tabstop interval.
|
||||||
|
.{ .name = "it", .value = .{ .numeric = 8 } },
|
||||||
|
|
||||||
|
// Number of lines on a page. Similar to cols this is variable width
|
||||||
|
// but this appears to be the value set by most terminals.
|
||||||
|
.{ .name = "lines", .value = .{ .numeric = 24 } },
|
||||||
|
|
||||||
|
// Number of color pairs on the screen.
|
||||||
|
.{ .name = "pairs", .value = .{ .numeric = 32767 } },
|
||||||
|
|
||||||
|
// Alternate character set. This is the VT100 alternate character set.
|
||||||
|
// I don't know what the value means, I copied this from Kitty and
|
||||||
|
// verified with some other terminals (looks similar).
|
||||||
|
.{ .name = "acsc", .value = .{ .string = "++\\,\\,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~" } },
|
||||||
|
|
||||||
|
// These are all capabilities that should be pretty straightforward
|
||||||
|
// and map to input sequences.
|
||||||
|
.{ .name = "bel", .value = .{ .string = "^G" } },
|
||||||
|
.{ .name = "blink", .value = .{ .string = "\\E[5m" } },
|
||||||
|
.{ .name = "bold", .value = .{ .string = "\\E[1m" } },
|
||||||
|
.{ .name = "cbt", .value = .{ .string = "\\E[Z" } },
|
||||||
|
.{ .name = "civis", .value = .{ .string = "\\E[?25l" } },
|
||||||
|
.{ .name = "clear", .value = .{ .string = "\\E[H\\E[2J" } },
|
||||||
|
.{ .name = "cnorm", .value = .{ .string = "\\E[?12l\\E[?25h" } },
|
||||||
|
.{ .name = "cr", .value = .{ .string = "\\r" } },
|
||||||
|
.{ .name = "csr", .value = .{ .string = "\\E[%i%p1%d;%p2%dr" } },
|
||||||
|
.{ .name = "cub", .value = .{ .string = "\\E[%p1%dD" } },
|
||||||
|
.{ .name = "cub1", .value = .{ .string = "^H" } },
|
||||||
|
.{ .name = "cud", .value = .{ .string = "\\E[%p1%dB" } },
|
||||||
|
.{ .name = "cud1", .value = .{ .string = "^J" } },
|
||||||
|
.{ .name = "cuf", .value = .{ .string = "\\E[%p1%dC" } },
|
||||||
|
.{ .name = "cuf1", .value = .{ .string = "\\E[C" } },
|
||||||
|
.{ .name = "cup", .value = .{ .string = "\\E[%i%p1%d;%p2%dH" } },
|
||||||
|
.{ .name = "cuu", .value = .{ .string = "\\E[%p1%dA" } },
|
||||||
|
.{ .name = "cuu1", .value = .{ .string = "\\E[A" } },
|
||||||
|
.{ .name = "cvvis", .value = .{ .string = "\\E[?12;25h" } },
|
||||||
|
.{ .name = "dch", .value = .{ .string = "\\E[%p1%dP" } },
|
||||||
|
.{ .name = "dch1", .value = .{ .string = "\\E[P" } },
|
||||||
|
.{ .name = "dim", .value = .{ .string = "\\E[2m" } },
|
||||||
|
.{ .name = "dl", .value = .{ .string = "\\E[%p1%dM" } },
|
||||||
|
.{ .name = "dl1", .value = .{ .string = "\\E[M" } },
|
||||||
|
.{ .name = "dsl", .value = .{ .string = "\\E]2;\\007" } },
|
||||||
|
.{ .name = "ech", .value = .{ .string = "\\E[%p1%dX" } },
|
||||||
|
.{ .name = "ed", .value = .{ .string = "\\E[J" } },
|
||||||
|
.{ .name = "el", .value = .{ .string = "\\E[K" } },
|
||||||
|
.{ .name = "el1", .value = .{ .string = "\\E[1K" } },
|
||||||
|
.{ .name = "flash", .value = .{ .string = "\\E[?5h$<100/>\\E[?5l" } },
|
||||||
|
.{ .name = "fsl", .value = .{ .string = "^G" } },
|
||||||
|
.{ .name = "home", .value = .{ .string = "\\E[H" } },
|
||||||
|
.{ .name = "hpa", .value = .{ .string = "\\E[%i%p1%dG" } },
|
||||||
|
.{ .name = "ht", .value = .{ .string = "^I" } },
|
||||||
|
.{ .name = "hts", .value = .{ .string = "\\EH" } },
|
||||||
|
.{ .name = "ich", .value = .{ .string = "\\E[%p1%d@" } },
|
||||||
|
.{ .name = "il", .value = .{ .string = "\\E[%p1%dL" } },
|
||||||
|
.{ .name = "il1", .value = .{ .string = "\\E[L" } },
|
||||||
|
.{ .name = "ind", .value = .{ .string = "\\n" } },
|
||||||
|
.{ .name = "indn", .value = .{ .string = "\\E[%p1%dS" } },
|
||||||
|
.{ .name = "initc", .value = .{ .string = "\\E]4;%p1%d;rgb\\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\\E\\\\" } },
|
||||||
|
.{ .name = "invis", .value = .{ .string = "\\E[8m" } },
|
||||||
|
.{ .name = "oc", .value = .{ .string = "\\E]104\\007" } },
|
||||||
|
.{ .name = "op", .value = .{ .string = "\\E[39;49m" } },
|
||||||
|
.{ .name = "rc", .value = .{ .string = "\\E8" } },
|
||||||
|
.{ .name = "rep", .value = .{ .string = "%p1%c\\E[%p2%{1}%-%db" } },
|
||||||
|
.{ .name = "rev", .value = .{ .string = "\\E[7m" } },
|
||||||
|
.{ .name = "ri", .value = .{ .string = "\\EM" } },
|
||||||
|
.{ .name = "rin", .value = .{ .string = "\\E[%p1%dT" } },
|
||||||
|
.{ .name = "ritm", .value = .{ .string = "\\E[23m" } },
|
||||||
|
.{ .name = "rmacs", .value = .{ .string = "\\E(B" } },
|
||||||
|
.{ .name = "rmam", .value = .{ .string = "\\E[?7l" } },
|
||||||
|
.{ .name = "rmcup", .value = .{ .string = "\\E[?1049l" } },
|
||||||
|
.{ .name = "rmir", .value = .{ .string = "\\E[4l" } },
|
||||||
|
.{ .name = "rmkx", .value = .{ .string = "\\E[?1l" } },
|
||||||
|
.{ .name = "rmso", .value = .{ .string = "\\E[27m" } },
|
||||||
|
.{ .name = "rmul", .value = .{ .string = "\\E[24m" } },
|
||||||
|
.{ .name = "rmxx", .value = .{ .string = "\\E[29m" } },
|
||||||
|
.{ .name = "setab", .value = .{ .string = "\\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m" } },
|
||||||
|
.{ .name = "setaf", .value = .{ .string = "\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m" } },
|
||||||
|
.{ .name = "setrgbb", .value = .{ .string = "\\E[48:2:%p1%d:%p2%d:%p3%dm" } },
|
||||||
|
.{ .name = "setrgbf", .value = .{ .string = "\\E[38:2:%p1%d:%p2%d:%p3%dm" } },
|
||||||
|
.{ .name = "sgr", .value = .{ .string = "%?%p9%t\\E(0%e\\E(B%;\\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m" } },
|
||||||
|
.{ .name = "sgr0", .value = .{ .string = "\\E(B\\E[m" } },
|
||||||
|
.{ .name = "sitm", .value = .{ .string = "\\E[3m" } },
|
||||||
|
.{ .name = "smacs", .value = .{ .string = "\\E(0" } },
|
||||||
|
.{ .name = "smam", .value = .{ .string = "\\E[?7h" } },
|
||||||
|
.{ .name = "smcup", .value = .{ .string = "\\E[?1049h" } },
|
||||||
|
.{ .name = "smir", .value = .{ .string = "\\E[4h" } },
|
||||||
|
.{ .name = "smkx", .value = .{ .string = "\\E[?1h" } },
|
||||||
|
.{ .name = "smso", .value = .{ .string = "\\E[7m" } },
|
||||||
|
.{ .name = "smul", .value = .{ .string = "\\E[4m" } },
|
||||||
|
.{ .name = "smxx", .value = .{ .string = "\\E[9m" } },
|
||||||
|
.{ .name = "tbc", .value = .{ .string = "\\E[3g" } },
|
||||||
|
.{ .name = "tsl", .value = .{ .string = "\\E]2;" } },
|
||||||
|
.{ .name = "u6", .value = .{ .string = "\\E[%i%d;%dR" } },
|
||||||
|
.{ .name = "u7", .value = .{ .string = "\\E[6n" } },
|
||||||
|
.{ .name = "u8", .value = .{ .string = "\\E[?%[;0123456789]c" } },
|
||||||
|
.{ .name = "u9", .value = .{ .string = "\\E[c" } },
|
||||||
|
.{ .name = "vpa", .value = .{ .string = "\\E[%i%p1%dd" } },
|
||||||
|
|
||||||
|
//-----------------------------------------------------------
|
||||||
|
// Completely unvalidated entries that are blindly copied from
|
||||||
|
// other terminals (Kitty, Wezterm, Alacritty) and may or may not
|
||||||
|
// actually work with Ghostty. todo is to validate these!
|
||||||
|
|
||||||
|
.{ .name = "kDC", .value = .{ .string = "\\E[3;2~" } },
|
||||||
|
.{ .name = "kDC3", .value = .{ .string = "\\E[3;3~" } },
|
||||||
|
.{ .name = "kDC4", .value = .{ .string = "\\E[3;4~" } },
|
||||||
|
.{ .name = "kDC5", .value = .{ .string = "\\E[3;5~" } },
|
||||||
|
.{ .name = "kDC6", .value = .{ .string = "\\E[3;6~" } },
|
||||||
|
.{ .name = "kDC7", .value = .{ .string = "\\E[3;7~" } },
|
||||||
|
.{ .name = "kDN", .value = .{ .string = "\\E[1;2B" } },
|
||||||
|
.{ .name = "kDN3", .value = .{ .string = "\\E[1;3B" } },
|
||||||
|
.{ .name = "kDN4", .value = .{ .string = "\\E[1;4B" } },
|
||||||
|
.{ .name = "kDN5", .value = .{ .string = "\\E[1;5B" } },
|
||||||
|
.{ .name = "kDN6", .value = .{ .string = "\\E[1;6B" } },
|
||||||
|
.{ .name = "kDN7", .value = .{ .string = "\\E[1;7B" } },
|
||||||
|
.{ .name = "kEND", .value = .{ .string = "\\E[1;2F" } },
|
||||||
|
.{ .name = "kEND3", .value = .{ .string = "\\E[1;3F" } },
|
||||||
|
.{ .name = "kEND4", .value = .{ .string = "\\E[1;4F" } },
|
||||||
|
.{ .name = "kEND5", .value = .{ .string = "\\E[1;5F" } },
|
||||||
|
.{ .name = "kEND6", .value = .{ .string = "\\E[1;6F" } },
|
||||||
|
.{ .name = "kEND7", .value = .{ .string = "\\E[1;7F" } },
|
||||||
|
.{ .name = "kHOM", .value = .{ .string = "\\E[1;2H" } },
|
||||||
|
.{ .name = "kHOM3", .value = .{ .string = "\\E[1;3H" } },
|
||||||
|
.{ .name = "kHOM4", .value = .{ .string = "\\E[1;4H" } },
|
||||||
|
.{ .name = "kHOM5", .value = .{ .string = "\\E[1;5H" } },
|
||||||
|
.{ .name = "kHOM6", .value = .{ .string = "\\E[1;6H" } },
|
||||||
|
.{ .name = "kHOM7", .value = .{ .string = "\\E[1;7H" } },
|
||||||
|
.{ .name = "kIC", .value = .{ .string = "\\E[2;2~" } },
|
||||||
|
.{ .name = "kIC3", .value = .{ .string = "\\E[2;3~" } },
|
||||||
|
.{ .name = "kIC4", .value = .{ .string = "\\E[2;4~" } },
|
||||||
|
.{ .name = "kIC5", .value = .{ .string = "\\E[2;5~" } },
|
||||||
|
.{ .name = "kIC6", .value = .{ .string = "\\E[2;6~" } },
|
||||||
|
.{ .name = "kIC7", .value = .{ .string = "\\E[2;7~" } },
|
||||||
|
.{ .name = "kLFT", .value = .{ .string = "\\E[1;2D" } },
|
||||||
|
.{ .name = "kLFT3", .value = .{ .string = "\\E[1;3D" } },
|
||||||
|
.{ .name = "kLFT4", .value = .{ .string = "\\E[1;4D" } },
|
||||||
|
.{ .name = "kLFT5", .value = .{ .string = "\\E[1;5D" } },
|
||||||
|
.{ .name = "kLFT6", .value = .{ .string = "\\E[1;6D" } },
|
||||||
|
.{ .name = "kLFT7", .value = .{ .string = "\\E[1;7D" } },
|
||||||
|
.{ .name = "kNXT", .value = .{ .string = "\\E[6;2~" } },
|
||||||
|
.{ .name = "kNXT3", .value = .{ .string = "\\E[6;3~" } },
|
||||||
|
.{ .name = "kNXT4", .value = .{ .string = "\\E[6;4~" } },
|
||||||
|
.{ .name = "kNXT5", .value = .{ .string = "\\E[6;5~" } },
|
||||||
|
.{ .name = "kNXT6", .value = .{ .string = "\\E[6;6~" } },
|
||||||
|
.{ .name = "kNXT7", .value = .{ .string = "\\E[6;7~" } },
|
||||||
|
.{ .name = "kPRV", .value = .{ .string = "\\E[5;2~" } },
|
||||||
|
.{ .name = "kPRV3", .value = .{ .string = "\\E[5;3~" } },
|
||||||
|
.{ .name = "kPRV4", .value = .{ .string = "\\E[5;4~" } },
|
||||||
|
.{ .name = "kPRV5", .value = .{ .string = "\\E[5;5~" } },
|
||||||
|
.{ .name = "kPRV6", .value = .{ .string = "\\E[5;6~" } },
|
||||||
|
.{ .name = "kPRV7", .value = .{ .string = "\\E[5;7~" } },
|
||||||
|
.{ .name = "kRIT", .value = .{ .string = "\\E[1;2C" } },
|
||||||
|
.{ .name = "kRIT3", .value = .{ .string = "\\E[1;3C" } },
|
||||||
|
.{ .name = "kRIT4", .value = .{ .string = "\\E[1;4C" } },
|
||||||
|
.{ .name = "kRIT5", .value = .{ .string = "\\E[1;5C" } },
|
||||||
|
.{ .name = "kRIT6", .value = .{ .string = "\\E[1;6C" } },
|
||||||
|
.{ .name = "kRIT7", .value = .{ .string = "\\E[1;7C" } },
|
||||||
|
.{ .name = "kUP", .value = .{ .string = "\\E[1;2A" } },
|
||||||
|
.{ .name = "kUP3", .value = .{ .string = "\\E[1;3A" } },
|
||||||
|
.{ .name = "kUP4", .value = .{ .string = "\\E[1;4A" } },
|
||||||
|
.{ .name = "kUP5", .value = .{ .string = "\\E[1;5A" } },
|
||||||
|
.{ .name = "kUP6", .value = .{ .string = "\\E[1;6A" } },
|
||||||
|
.{ .name = "kUP7", .value = .{ .string = "\\E[1;7A" } },
|
||||||
|
.{ .name = "kbs", .value = .{ .string = "^?" } },
|
||||||
|
.{ .name = "kcbt", .value = .{ .string = "\\E[Z" } },
|
||||||
|
.{ .name = "kcub1", .value = .{ .string = "\\EOD" } },
|
||||||
|
.{ .name = "kcud1", .value = .{ .string = "\\EOB" } },
|
||||||
|
.{ .name = "kcuf1", .value = .{ .string = "\\EOC" } },
|
||||||
|
.{ .name = "kcuu1", .value = .{ .string = "\\EOA" } },
|
||||||
|
.{ .name = "kdch1", .value = .{ .string = "\\E[3~" } },
|
||||||
|
.{ .name = "kend", .value = .{ .string = "\\EOF" } },
|
||||||
|
.{ .name = "kent", .value = .{ .string = "\\EOM" } },
|
||||||
|
.{ .name = "kf1", .value = .{ .string = "\\EOP" } },
|
||||||
|
.{ .name = "kf10", .value = .{ .string = "\\E[21~" } },
|
||||||
|
.{ .name = "kf11", .value = .{ .string = "\\E[23~" } },
|
||||||
|
.{ .name = "kf12", .value = .{ .string = "\\E[24~" } },
|
||||||
|
.{ .name = "kf13", .value = .{ .string = "\\E[1;2P" } },
|
||||||
|
.{ .name = "kf14", .value = .{ .string = "\\E[1;2Q" } },
|
||||||
|
.{ .name = "kf15", .value = .{ .string = "\\E[1;2R" } },
|
||||||
|
.{ .name = "kf16", .value = .{ .string = "\\E[1;2S" } },
|
||||||
|
.{ .name = "kf17", .value = .{ .string = "\\E[15;2~" } },
|
||||||
|
.{ .name = "kf18", .value = .{ .string = "\\E[17;2~" } },
|
||||||
|
.{ .name = "kf19", .value = .{ .string = "\\E[18;2~" } },
|
||||||
|
.{ .name = "kf2", .value = .{ .string = "\\EOQ" } },
|
||||||
|
.{ .name = "kf20", .value = .{ .string = "\\E[19;2~" } },
|
||||||
|
.{ .name = "kf21", .value = .{ .string = "\\E[20;2~" } },
|
||||||
|
.{ .name = "kf22", .value = .{ .string = "\\E[21;2~" } },
|
||||||
|
.{ .name = "kf23", .value = .{ .string = "\\E[23;2~" } },
|
||||||
|
.{ .name = "kf24", .value = .{ .string = "\\E[24;2~" } },
|
||||||
|
.{ .name = "kf25", .value = .{ .string = "\\E[1;5P" } },
|
||||||
|
.{ .name = "kf26", .value = .{ .string = "\\E[1;5Q" } },
|
||||||
|
.{ .name = "kf27", .value = .{ .string = "\\E[1;5R" } },
|
||||||
|
.{ .name = "kf28", .value = .{ .string = "\\E[1;5S" } },
|
||||||
|
.{ .name = "kf29", .value = .{ .string = "\\E[15;5~" } },
|
||||||
|
.{ .name = "kf3", .value = .{ .string = "\\EOR" } },
|
||||||
|
.{ .name = "kf30", .value = .{ .string = "\\E[17;5~" } },
|
||||||
|
.{ .name = "kf31", .value = .{ .string = "\\E[18;5~" } },
|
||||||
|
.{ .name = "kf32", .value = .{ .string = "\\E[19;5~" } },
|
||||||
|
.{ .name = "kf33", .value = .{ .string = "\\E[20;5~" } },
|
||||||
|
.{ .name = "kf34", .value = .{ .string = "\\E[21;5~" } },
|
||||||
|
.{ .name = "kf35", .value = .{ .string = "\\E[23;5~" } },
|
||||||
|
.{ .name = "kf36", .value = .{ .string = "\\E[24;5~" } },
|
||||||
|
.{ .name = "kf37", .value = .{ .string = "\\E[1;6P" } },
|
||||||
|
.{ .name = "kf38", .value = .{ .string = "\\E[1;6Q" } },
|
||||||
|
.{ .name = "kf39", .value = .{ .string = "\\E[1;6R" } },
|
||||||
|
.{ .name = "kf4", .value = .{ .string = "\\EOS" } },
|
||||||
|
.{ .name = "kf40", .value = .{ .string = "\\E[1;6S" } },
|
||||||
|
.{ .name = "kf41", .value = .{ .string = "\\E[15;6~" } },
|
||||||
|
.{ .name = "kf42", .value = .{ .string = "\\E[17;6~" } },
|
||||||
|
.{ .name = "kf43", .value = .{ .string = "\\E[18;6~" } },
|
||||||
|
.{ .name = "kf44", .value = .{ .string = "\\E[19;6~" } },
|
||||||
|
.{ .name = "kf45", .value = .{ .string = "\\E[20;6~" } },
|
||||||
|
.{ .name = "kf46", .value = .{ .string = "\\E[21;6~" } },
|
||||||
|
.{ .name = "kf47", .value = .{ .string = "\\E[23;6~" } },
|
||||||
|
.{ .name = "kf48", .value = .{ .string = "\\E[24;6~" } },
|
||||||
|
.{ .name = "kf49", .value = .{ .string = "\\E[1;3P" } },
|
||||||
|
.{ .name = "kf5", .value = .{ .string = "\\E[15~" } },
|
||||||
|
.{ .name = "kf50", .value = .{ .string = "\\E[1;3Q" } },
|
||||||
|
.{ .name = "kf51", .value = .{ .string = "\\E[1;3R" } },
|
||||||
|
.{ .name = "kf52", .value = .{ .string = "\\E[1;3S" } },
|
||||||
|
.{ .name = "kf53", .value = .{ .string = "\\E[15;3~" } },
|
||||||
|
.{ .name = "kf54", .value = .{ .string = "\\E[17;3~" } },
|
||||||
|
.{ .name = "kf55", .value = .{ .string = "\\E[18;3~" } },
|
||||||
|
.{ .name = "kf56", .value = .{ .string = "\\E[19;3~" } },
|
||||||
|
.{ .name = "kf57", .value = .{ .string = "\\E[20;3~" } },
|
||||||
|
.{ .name = "kf58", .value = .{ .string = "\\E[21;3~" } },
|
||||||
|
.{ .name = "kf59", .value = .{ .string = "\\E[23;3~" } },
|
||||||
|
.{ .name = "kf6", .value = .{ .string = "\\E[17~" } },
|
||||||
|
.{ .name = "kf60", .value = .{ .string = "\\E[24;3~" } },
|
||||||
|
.{ .name = "kf61", .value = .{ .string = "\\E[1;4P" } },
|
||||||
|
.{ .name = "kf62", .value = .{ .string = "\\E[1;4Q" } },
|
||||||
|
.{ .name = "kf63", .value = .{ .string = "\\E[1;4R" } },
|
||||||
|
.{ .name = "kf7", .value = .{ .string = "\\E[18~" } },
|
||||||
|
.{ .name = "kf8", .value = .{ .string = "\\E[19~" } },
|
||||||
|
.{ .name = "kf9", .value = .{ .string = "\\E[20~" } },
|
||||||
|
.{ .name = "khome", .value = .{ .string = "\\EOH" } },
|
||||||
|
.{ .name = "kich1", .value = .{ .string = "\\E[2~" } },
|
||||||
|
.{ .name = "kind", .value = .{ .string = "\\E[1;2B" } },
|
||||||
|
.{ .name = "kmous", .value = .{ .string = "\\E[M" } },
|
||||||
|
.{ .name = "knp", .value = .{ .string = "\\E[6~" } },
|
||||||
|
.{ .name = "kpp", .value = .{ .string = "\\E[5~" } },
|
||||||
|
.{ .name = "kri", .value = .{ .string = "\\E[1;2A" } },
|
||||||
|
.{ .name = "rs1", .value = .{ .string = "\\E]\\E\\\\\\Ec" } },
|
||||||
|
.{ .name = "sc", .value = .{ .string = "\\E7" } },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
test "encode" {
|
||||||
|
// Encode
|
||||||
|
var buf: [4096]u8 = undefined;
|
||||||
|
var buf_stream = std.io.fixedBufferStream(&buf);
|
||||||
|
try ghostty.encode(buf_stream.writer());
|
||||||
|
try std.testing.expect(buf_stream.getWritten().len > 0);
|
||||||
|
}
|
13
src/terminfo/main.zig
Normal file
13
src/terminfo/main.zig
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
//! Package terminfo provides functionality related to terminfo/termcap files.
|
||||||
|
//!
|
||||||
|
//! At the time of writing this comment, the focus is on generating terminfo
|
||||||
|
//! files so that we can maintain our terminfo in Zig instead of hand-writing
|
||||||
|
//! the archaic (imo) terminfo format by hand. But eventually we may want to
|
||||||
|
//! extract this into a more full-featured library on its own.
|
||||||
|
|
||||||
|
pub const ghostty = @import("ghostty.zig").ghostty;
|
||||||
|
pub const Source = @import("Source.zig");
|
||||||
|
|
||||||
|
test {
|
||||||
|
@import("std").testing.refAllDecls(@This());
|
||||||
|
}
|
@ -491,8 +491,27 @@ const Subprocess = struct {
|
|||||||
break :env try std.process.getEnvMap(alloc);
|
break :env try std.process.getEnvMap(alloc);
|
||||||
};
|
};
|
||||||
errdefer env.deinit();
|
errdefer env.deinit();
|
||||||
try env.put("TERM", "xterm-256color");
|
|
||||||
try env.put("COLORTERM", "truecolor");
|
// Set our TERM var. This is a bit complicated because we want to use
|
||||||
|
// the ghostty TERM value but we want to only do that if we have
|
||||||
|
// ghostty in the TERMINFO database.
|
||||||
|
//
|
||||||
|
// For now, we just look up a bundled dir but in the future we should
|
||||||
|
// also load the terminfo database and look for it.
|
||||||
|
if (try terminfoDir(alloc)) |dir| {
|
||||||
|
try env.put("TERM", "xterm-ghostty");
|
||||||
|
try env.put("COLORTERM", "truecolor");
|
||||||
|
try env.put("TERMINFO", dir);
|
||||||
|
} else {
|
||||||
|
if (comptime builtin.target.isDarwin()) {
|
||||||
|
log.warn("ghostty terminfo not found, using xterm-256color", .{});
|
||||||
|
log.warn("the terminfo SHOULD exist on macos, please ensure", .{});
|
||||||
|
log.warn("you're using a valid app bundle.", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
try env.put("TERM", "xterm-256color");
|
||||||
|
try env.put("COLORTERM", "truecolor");
|
||||||
|
}
|
||||||
|
|
||||||
// When embedding in macOS and running via XCode, XCode injects
|
// When embedding in macOS and running via XCode, XCode injects
|
||||||
// a bunch of things that break our shell process. We remove those.
|
// a bunch of things that break our shell process. We remove those.
|
||||||
@ -745,6 +764,41 @@ const Subprocess = struct {
|
|||||||
fn killCommandFlatpak(command: *FlatpakHostCommand) !void {
|
fn killCommandFlatpak(command: *FlatpakHostCommand) !void {
|
||||||
try command.signal(c.SIGHUP, true);
|
try command.signal(c.SIGHUP, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the directory to the terminfo database, if it can be detected.
|
||||||
|
/// The memory returned can't be easily freed so the alloc should be
|
||||||
|
/// an arena or something similar.
|
||||||
|
fn terminfoDir(alloc: Allocator) !?[]const u8 {
|
||||||
|
// We only support Mac lookups right now because the terminfo
|
||||||
|
// DB can be embedded directly in the App bundle.
|
||||||
|
if (comptime !builtin.target.isDarwin()) return null;
|
||||||
|
|
||||||
|
// Get the path to our running binary
|
||||||
|
var exe_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
|
||||||
|
var exe: []const u8 = std.fs.selfExePath(&exe_buf) catch return null;
|
||||||
|
|
||||||
|
// We have an exe path! Climb the tree looking for the terminfo
|
||||||
|
// bundle as we expect it.
|
||||||
|
while (std.fs.path.dirname(exe)) |dir| {
|
||||||
|
exe = dir;
|
||||||
|
|
||||||
|
var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
|
||||||
|
const path = try std.fmt.bufPrint(
|
||||||
|
&buf,
|
||||||
|
"{s}/Contents/Resources/terminfo",
|
||||||
|
.{dir},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (std.fs.accessAbsolute(path, .{})) {
|
||||||
|
return try alloc.dupe(u8, path);
|
||||||
|
} else |_| {
|
||||||
|
// Folder doesn't exist. If a different error happens its okay
|
||||||
|
// we just ignore it and move on.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The read thread sits in a loop doing the following pseudo code:
|
/// The read thread sits in a loop doing the following pseudo code:
|
||||||
|
Reference in New Issue
Block a user