mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-17 17:26:09 +03:00
Merge pull request #1703 from mitchellh/terminfo
terminfo: for XTGETTCAP, non-parameterized string values return encoded
This commit is contained in:
@ -81,17 +81,39 @@ pub fn xtgettcapMap(comptime self: Source) type {
|
|||||||
kvs[1] = .{ "Co", "256" };
|
kvs[1] = .{ "Co", "256" };
|
||||||
kvs[2] = .{ "RGB", "8" };
|
kvs[2] = .{ "RGB", "8" };
|
||||||
for (self.capabilities, 3..) |cap, i| {
|
for (self.capabilities, 3..) |cap, i| {
|
||||||
kvs[i] = .{ cap.name, switch (cap.value) {
|
kvs[i] = .{
|
||||||
|
cap.name, switch (cap.value) {
|
||||||
.canceled => @compileError("canceled not handled yet"),
|
.canceled => @compileError("canceled not handled yet"),
|
||||||
.boolean => "",
|
.boolean => "",
|
||||||
.string => |v| v,
|
.string => |v| string: {
|
||||||
|
@setEvalBranchQuota(100_000);
|
||||||
|
// If a string contains parameters, then we do not escape
|
||||||
|
// anything within the string. I BELIEVE the history here is
|
||||||
|
// xterm initially only supported specific capabilities and none
|
||||||
|
// had parameters so it returned the tcap encoded form. Later,
|
||||||
|
// Kitty added support for more capabilities some of which
|
||||||
|
// have parameters. But Kitty returned them in terminfo source
|
||||||
|
// format. So we need to handle both cases.
|
||||||
|
if (std.mem.indexOfScalar(u8, v, '%') != null) break :string v;
|
||||||
|
// No-parameters. Encode and return.
|
||||||
|
// First replace \E with the escape char (0x1B)
|
||||||
|
var result = comptimeReplace(v, "\\E", "\x1b");
|
||||||
|
// Replace '^' with the control char version of that char.
|
||||||
|
while (std.mem.indexOfScalar(u8, result, '^')) |idx| {
|
||||||
|
if (idx > 0) @compileError("handle control-char in middle of string");
|
||||||
|
const c = result[idx + 1];
|
||||||
|
result = comptimeReplace(result, result[idx .. idx + 2], &.{c - 64});
|
||||||
|
}
|
||||||
|
break :string result;
|
||||||
|
},
|
||||||
.numeric => |v| numeric: {
|
.numeric => |v| numeric: {
|
||||||
var buf: [10]u8 = undefined;
|
var buf: [10]u8 = undefined;
|
||||||
const num_len = std.fmt.formatIntBuf(&buf, v, 10, .upper, .{});
|
const num_len = std.fmt.formatIntBuf(&buf, v, 10, .upper, .{});
|
||||||
const final = buf;
|
const final = buf;
|
||||||
break :numeric final[0..num_len];
|
break :numeric final[0..num_len];
|
||||||
},
|
},
|
||||||
} };
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now go through and convert them all to hex-encoded strings.
|
// Now go through and convert them all to hex-encoded strings.
|
||||||
@ -123,6 +145,22 @@ fn hexencode(comptime input: []const u8) []const u8 {
|
|||||||
return comptime &(std.fmt.bytesToHex(input, .upper));
|
return comptime &(std.fmt.bytesToHex(input, .upper));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// std.mem.replace but comptime-only so we can return the string
|
||||||
|
/// allocated in comptime memory.
|
||||||
|
fn comptimeReplace(
|
||||||
|
input: []const u8,
|
||||||
|
needle: []const u8,
|
||||||
|
replacement: []const u8,
|
||||||
|
) []const u8 {
|
||||||
|
comptime {
|
||||||
|
const len = std.mem.replacementSize(u8, input, needle, replacement);
|
||||||
|
var buf: [len]u8 = undefined;
|
||||||
|
_ = std.mem.replace(u8, input, needle, replacement, &buf);
|
||||||
|
const final = buf;
|
||||||
|
return &final;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "xtgettcap map" {
|
test "xtgettcap map" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
|
|
||||||
@ -136,6 +174,8 @@ test "xtgettcap map" {
|
|||||||
.capabilities = &.{
|
.capabilities = &.{
|
||||||
.{ .name = "am", .value = .{ .boolean = {} } },
|
.{ .name = "am", .value = .{ .boolean = {} } },
|
||||||
.{ .name = "colors", .value = .{ .numeric = 256 } },
|
.{ .name = "colors", .value = .{ .numeric = 256 } },
|
||||||
|
.{ .name = "kbs", .value = .{ .string = "^H" } },
|
||||||
|
.{ .name = "kf1", .value = .{ .string = "\\EOP" } },
|
||||||
.{ .name = "Smulx", .value = .{ .string = "\\E[4:%p1%dm" } },
|
.{ .name = "Smulx", .value = .{ .string = "\\E[4:%p1%dm" } },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -145,6 +185,14 @@ test "xtgettcap map" {
|
|||||||
"\x1bP1+r616D\x1b\\",
|
"\x1bP1+r616D\x1b\\",
|
||||||
map.get(hexencode("am")).?,
|
map.get(hexencode("am")).?,
|
||||||
);
|
);
|
||||||
|
try testing.expectEqualStrings(
|
||||||
|
"\x1bP1+r6B6273=08\x1b\\",
|
||||||
|
map.get(hexencode("kbs")).?,
|
||||||
|
);
|
||||||
|
try testing.expectEqualStrings(
|
||||||
|
"\x1bP1+r6B6631=1B4F50\x1b\\",
|
||||||
|
map.get(hexencode("kf1")).?,
|
||||||
|
);
|
||||||
try testing.expectEqualStrings(
|
try testing.expectEqualStrings(
|
||||||
"\x1bP1+r536D756C78=5C455B343A25703125646D\x1b\\",
|
"\x1bP1+r536D756C78=5C455B343A25703125646D\x1b\\",
|
||||||
map.get(hexencode("Smulx")).?,
|
map.get(hexencode("Smulx")).?,
|
||||||
|
@ -304,7 +304,7 @@ pub const ghostty: Source = .{
|
|||||||
.{ .name = "kUP5", .value = .{ .string = "\\E[1;5A" } },
|
.{ .name = "kUP5", .value = .{ .string = "\\E[1;5A" } },
|
||||||
.{ .name = "kUP6", .value = .{ .string = "\\E[1;6A" } },
|
.{ .name = "kUP6", .value = .{ .string = "\\E[1;6A" } },
|
||||||
.{ .name = "kUP7", .value = .{ .string = "\\E[1;7A" } },
|
.{ .name = "kUP7", .value = .{ .string = "\\E[1;7A" } },
|
||||||
.{ .name = "kbs", .value = .{ .string = "^?" } },
|
.{ .name = "kbs", .value = .{ .string = "^H" } },
|
||||||
.{ .name = "kcbt", .value = .{ .string = "\\E[Z" } },
|
.{ .name = "kcbt", .value = .{ .string = "\\E[Z" } },
|
||||||
.{ .name = "kcub1", .value = .{ .string = "\\EOD" } },
|
.{ .name = "kcub1", .value = .{ .string = "\\EOD" } },
|
||||||
.{ .name = "kcud1", .value = .{ .string = "\\EOB" } },
|
.{ .name = "kcud1", .value = .{ .string = "\\EOB" } },
|
||||||
|
Reference in New Issue
Block a user