mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-09-19 20:18:07 +00:00
terminal: decrqm don't truncate 16-bit mode requests (#14237)
DECRQM requests can use a full 16-bit number but we truncated to 15-bits for the reply (cause thats all we know about). Preserve, the full 16-bits for requests so we give the proper response.
This commit is contained in:
@@ -24,7 +24,7 @@ pub fn report_encode(
|
||||
) callconv(lib.calling_conv) Result {
|
||||
const mode_tag: modes.ModeTag = @bitCast(tag);
|
||||
const report: modes.Report = .{
|
||||
.tag = mode_tag,
|
||||
.tag = .{ .value = mode_tag.value, .ansi = mode_tag.ansi },
|
||||
.state = state.toZig() orelse return .invalid_value,
|
||||
};
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ pub const ModeState = struct {
|
||||
|
||||
/// Return a DECRPM report for the given mode tag. If the tag does
|
||||
/// not correspond to a known mode, the report state is .not_recognized.
|
||||
pub fn getReport(self: *const ModeState, tag: ModeTag) Report {
|
||||
pub fn getReport(self: *const ModeState, tag: Report.Tag) Report {
|
||||
// DECECM (Erase Color Mode, DEC private mode 117) controls whether erasing
|
||||
// and scrolling use the default background or the active background color.
|
||||
// Ghostty's behavior is fixed equivalent to DECECM reset, and DECRQM has a
|
||||
@@ -191,14 +191,27 @@ pub fn modeFromInt(v: u16, ansi: bool) ?Mode {
|
||||
|
||||
/// A DECRPM mode report response.
|
||||
pub const Report = struct {
|
||||
tag: ModeTag,
|
||||
tag: Tag,
|
||||
state: State,
|
||||
|
||||
/// A query identifier can use the full parser parameter range. Keep it
|
||||
/// separate from ModeTag, which packs supported modes and the ANSI flag
|
||||
/// into a u16 and is also used by the public C API.
|
||||
pub const Tag = struct {
|
||||
value: u16,
|
||||
ansi: bool = false,
|
||||
|
||||
pub fn fromMode(mode: Mode) Tag {
|
||||
const tag = ModeTag.fromMode(mode);
|
||||
return .{ .value = tag.value, .ansi = tag.ansi };
|
||||
}
|
||||
};
|
||||
|
||||
pub const max_size = max_size: {
|
||||
// Construct the largest possible report in terms of values.
|
||||
const report: Report = .{
|
||||
.tag = .{
|
||||
.value = std.math.maxInt(u15),
|
||||
.value = std.math.maxInt(u16),
|
||||
.ansi = false,
|
||||
},
|
||||
.state = .permanently_reset,
|
||||
@@ -460,3 +473,25 @@ test "Report.encode not recognized" {
|
||||
try report.encode(&writer);
|
||||
try testing.expectEqualStrings("\x1B[?9999;0$y", writer.buffered());
|
||||
}
|
||||
|
||||
test "getReport large unknown modes" {
|
||||
const state: ModeState = .{};
|
||||
// These would alias ANSI insert mode and fixed-status DEC mode 117.
|
||||
for ([_]Report.Tag{
|
||||
.{ .value = 32772, .ansi = true },
|
||||
.{ .value = 32885 },
|
||||
}) |tag| {
|
||||
const report = state.getReport(tag);
|
||||
try testing.expectEqual(Report.State.not_recognized, report.state);
|
||||
try testing.expectEqualDeep(tag, report.tag);
|
||||
}
|
||||
}
|
||||
|
||||
test "Report.encode maximum size" {
|
||||
var buf: [Report.max_size]u8 = undefined;
|
||||
var writer: std.Io.Writer = .fixed(&buf);
|
||||
const report: Report = .{ .tag = .{ .value = 65535 }, .state = .permanently_reset };
|
||||
try report.encode(&writer);
|
||||
try testing.expectEqualStrings("\x1b[?65535;4$y", writer.buffered());
|
||||
try testing.expectEqual(buf.len, writer.buffered().len);
|
||||
}
|
||||
|
||||
@@ -1517,7 +1517,7 @@ pub const Handler = struct {
|
||||
|
||||
fn requestModeUnknown(self: *Handler, mode_raw: u16, ansi: bool) void {
|
||||
const report = self.terminal.modes.getReport(.{
|
||||
.value = @truncate(mode_raw),
|
||||
.value = mode_raw,
|
||||
.ansi = ansi,
|
||||
});
|
||||
self.sendModeReport(report);
|
||||
@@ -4606,6 +4606,12 @@ test "request mode DECRQM with write_pty callback" {
|
||||
s.nextSlice("\x1B[?7$p");
|
||||
try testing.expectEqualStrings("\x1B[?7;2$y", S.last_response.?);
|
||||
|
||||
// A large unknown mode must not alias wraparound mode 7.
|
||||
const before = t.modes;
|
||||
s.nextSlice("\x1B[?32775$p");
|
||||
try testing.expectEqualStrings("\x1B[?32775;0$y", S.last_response.?);
|
||||
try testing.expectEqualDeep(before, t.modes);
|
||||
|
||||
// Query an unknown mode
|
||||
s.nextSlice("\x1B[?9999$p");
|
||||
try testing.expectEqualStrings("\x1B[?9999;0$y", S.last_response.?);
|
||||
|
||||
@@ -583,7 +583,7 @@ pub const StreamHandler = struct {
|
||||
}
|
||||
|
||||
fn requestModeUnknown(self: *StreamHandler, mode_raw: u16, ansi: bool) !void {
|
||||
self.sendModeReport(self.terminal.modes.getReport(.{ .value = @truncate(mode_raw), .ansi = ansi }));
|
||||
self.sendModeReport(self.terminal.modes.getReport(.{ .value = mode_raw, .ansi = ansi }));
|
||||
}
|
||||
|
||||
fn sendModeReport(self: *StreamHandler, report: terminal.modes.Report) void {
|
||||
|
||||
Reference in New Issue
Block a user