Skip to content

Commit 5292e8c

Browse files
committed
Do not send unsolicited DSR 997 on mode 2031 enable
DECSET 2031 (ColorPaletteUpdates) should notify applications only when the color scheme changes, not immediately on enable. The unsolicited initial report leaks into applications that enable the mode without arranging to consume a response, surfacing as literal `?997;1n` text in the shell input buffer. Mirrors the fix applied to DECSET 1004 focus reporting in #40. Fixes manaflow-ai/cmux#2636
1 parent cec20c5 commit 5292e8c

1 file changed

Lines changed: 58 additions & 0 deletions

File tree

src/termio/stream_handler.zig

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,16 @@ pub const StreamHandler = struct {
797797
.mouse_format_urxvt => self.terminal.flags.mouse_format = if (enabled) .urxvt else .x10,
798798
.mouse_format_sgr_pixels => self.terminal.flags.mouse_format = if (enabled) .sgr_pixels else .x10,
799799

800+
// Mode 2031 (ColorPaletteUpdates) notifies applications when the
801+
// color scheme *changes*. Do not synthesize an immediate DSR 997
802+
// report when the mode is enabled, otherwise applications that
803+
// enable DECSET 2031 during startup without arranging to consume
804+
// a response (for example, Claude Code) receive a spurious CSI
805+
// sequence that leaks into the shell input buffer as literal
806+
// text. Applications that want the current scheme can query it
807+
// explicitly via DSR 996.
808+
.report_color_scheme => {},
809+
800810
else => {},
801811
}
802812
}
@@ -1554,3 +1564,51 @@ pub const StreamHandler = struct {
15541564
self.surfaceMessageWriter(.{ .progress_report = report });
15551565
}
15561566
};
1567+
1568+
test "enabling mode 2031 does not emit an immediate color scheme report" {
1569+
const testing = std.testing;
1570+
1571+
var term = try terminal.Terminal.init(testing.allocator, .{
1572+
.cols = 10,
1573+
.rows = 5,
1574+
});
1575+
defer term.deinit(testing.allocator);
1576+
1577+
var mailbox = try termio.Mailbox.initSPSC(testing.allocator);
1578+
defer mailbox.deinit(testing.allocator);
1579+
1580+
var mutex: std.Thread.Mutex = .{};
1581+
var renderer_state: renderer.State = .{
1582+
.mutex = &mutex,
1583+
.terminal = &term,
1584+
};
1585+
var size: renderer.Size = .{
1586+
.screen = .{ .width = 800, .height = 600 },
1587+
.cell = .{ .width = 8, .height = 16 },
1588+
.padding = .{},
1589+
};
1590+
var handler = StreamHandler{
1591+
.alloc = testing.allocator,
1592+
.size = &size,
1593+
.terminal = &term,
1594+
.termio_mailbox = &mailbox,
1595+
.surface_mailbox = undefined,
1596+
.renderer_state = &renderer_state,
1597+
.renderer_mailbox = undefined,
1598+
.renderer_wakeup = undefined,
1599+
.default_cursor_style = .block,
1600+
.default_cursor_blink = null,
1601+
.enquiry_response = "",
1602+
.osc_color_report_format = .none,
1603+
.clipboard_write = .deny,
1604+
};
1605+
defer handler.deinit();
1606+
1607+
mutex.lock();
1608+
defer mutex.unlock();
1609+
try handler.setMode(.report_color_scheme, true);
1610+
1611+
try testing.expect(term.modes.get(.report_color_scheme));
1612+
try testing.expect(!handler.termio_messaged);
1613+
try testing.expect(mailbox.spsc.queue.pop() == null);
1614+
}

0 commit comments

Comments
 (0)