From a19aa0a398f2dcd0494482981f604d8113cd7f27 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 12 Sep 2025 21:08:52 -0700 Subject: [PATCH] terminal: OSC 104 with no semicolon should parse as reset palette https://github.com/ghostty-org/ghostty/pull/8590#issuecomment-3287418867 --- src/terminal/Parser.zig | 32 ++++++++++++++++++++++++++++++++ src/terminal/osc.zig | 16 +++++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/terminal/Parser.zig b/src/terminal/Parser.zig index 0c814ff68..1f2e814f6 100644 --- a/src/terminal/Parser.zig +++ b/src/terminal/Parser.zig @@ -930,6 +930,38 @@ test "osc: 112 incomplete sequence" { } } +test "osc: 104 empty" { + var p: Parser = init(); + defer p.deinit(); + p.osc_parser.alloc = std.testing.allocator; + + _ = p.next(0x1B); + _ = p.next(']'); + _ = p.next('1'); + _ = p.next('0'); + _ = p.next('4'); + + { + const a = p.next(0x07); + try testing.expect(p.state == .ground); + try testing.expect(a[0].? == .osc_dispatch); + try testing.expect(a[1] == null); + try testing.expect(a[2] == null); + + const cmd = a[0].?.osc_dispatch; + try testing.expect(cmd == .color_operation); + try testing.expectEqual(cmd.color_operation.terminator, .bel); + try testing.expect(cmd.color_operation.op == .osc_104); + try testing.expect(cmd.color_operation.requests.count() == 1); + var it = cmd.color_operation.requests.constIterator(0); + { + const op = it.next().?; + try testing.expect(op.* == .reset_palette); + } + try std.testing.expect(it.next() == null); + } +} + test "csi: too many params" { var p = init(); _ = p.next(0x1B); diff --git a/src/terminal/osc.zig b/src/terminal/osc.zig index 72efc949e..028fcdf0a 100644 --- a/src/terminal/osc.zig +++ b/src/terminal/osc.zig @@ -557,7 +557,11 @@ pub const Parser = struct { self.buf_start = self.buf_idx; self.complete = true; }, - '4' => self.state = .@"104", + '4' => { + self.state = .@"104"; + // If we have an allocator, then we can complete the OSC104 + if (self.alloc != null) self.complete = true; + }, else => self.state = .invalid, }, @@ -1584,6 +1588,16 @@ pub const Parser = struct { .kitty_color_protocol_value => self.endKittyColorProtocolOption(.key_and_value, true), .osc_color => self.endOscColor(), + // 104 abruptly ended turns into a reset palette command. + .@"104" => { + self.command = .{ .color_operation = .{ + .op = .osc_104, + } }; + self.state = .osc_color; + self.buf_start = self.buf_idx; + self.endOscColor(); + }, + // We received OSC 9;X ST, but nothing else, finish off as a // desktop notification with "X" as the body. .conemu_sleep,