Commit 48d9a17

Vishal  ·  2026-03-19 08:35:37 -0400 EDT
parent 27bd9b8
fix: avoid replaying synchronized output on reattach (#95)
1 files changed,  +49, -0
+49, -0
......@@ -212,6 +212,17 @@ pub fn serializeTerminalState(alloc: std.mem.Allocator, term: *ghostty_vt.Termin
212212 var builder: std.Io.Writer.Allocating = .init(alloc);
213213 defer builder.deinit();
214214
215+ // Synchronized output (DECSET 2026) is a transient rendering handshake
216+ // between a program and its current terminal client. Replaying it to a
217+ // newly attached client can leave that client deferring renders until its
218+ // local timeout fires, so temporarily exclude it from restored state and
219+ // restore the original mode before returning.
220+ const had_synchronized_output = term.modes.get(.synchronized_output);
221+ if (had_synchronized_output) {
222+ term.modes.set(.synchronized_output, false);
223+ defer term.modes.set(.synchronized_output, true);
224+ }
225+
215226 var term_formatter = ghostty_vt.formatter.TerminalFormatter.init(term, .vt);
216227 term_formatter.content = .{ .selection = null };
217228 term_formatter.extra = .{
......@@ -528,3 +539,41 @@ test "isKittyCtrlBackslash" {
528539 try std.testing.expect(!isKittyCtrlBackslash("\x1b[92;1u"));
529540 try std.testing.expect(!isKittyCtrlBackslash("garbage"));
530541 }
542+
543+test "serializeTerminalState excludes synchronized output replay" {
544+ const alloc = std.testing.allocator;
545+
546+ var term = try ghostty_vt.Terminal.init(alloc, .{
547+ .cols = 80,
548+ .rows = 24,
549+ });
550+ defer term.deinit(alloc);
551+
552+ var stream = term.vtStream();
553+ defer stream.deinit();
554+
555+ stream.nextSlice("\x1b[?2004h"); // Bracketed paste
556+ stream.nextSlice("\x1b[?2026h"); // Synchronized output
557+ stream.nextSlice("hello");
558+
559+ try std.testing.expect(term.modes.get(.bracketed_paste));
560+ try std.testing.expect(term.modes.get(.synchronized_output));
561+
562+ const output = serializeTerminalState(alloc, &term) orelse return error.TestUnexpectedNull;
563+ defer alloc.free(output);
564+
565+ try std.testing.expect(term.modes.get(.synchronized_output));
566+
567+ var restored = try ghostty_vt.Terminal.init(alloc, .{
568+ .cols = 80,
569+ .rows = 24,
570+ });
571+ defer restored.deinit(alloc);
572+
573+ var restored_stream = restored.vtStream();
574+ defer restored_stream.deinit();
575+ restored_stream.nextSlice(output);
576+
577+ try std.testing.expect(restored.modes.get(.bracketed_paste));
578+ try std.testing.expect(!restored.modes.get(.synchronized_output));
579+}