Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions src/daemon.zig
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,11 @@ pub const Daemon = struct {
// repainted from terminal state.
var out_buf: [32 * 1024 + 32]u8 = undefined;
var writer = std.Io.Writer.fixed(&out_buf);
// The active screen before this chunk. A switch the filter does
// not see (a full reset, RIS, returns to the primary screen
// without a 47/1047/1049 toggle) still has to repaint so the
// client's `.screen` state stays authoritative.
const was_alt = win.onAltScreen();
const result = win.alt_filter.feed(chunk, &writer) catch
altscreen.Filter.Result{ .switched = true, .discard_start = 0 };

Expand All @@ -518,9 +523,12 @@ pub const Daemon = struct {

const filtered = writer.buffered();
if (filtered.len > 0) conn.send(.output, filtered);
if (result.switched) {
// Repaint when the filter stripped a toggle, or when the active
// screen changed by a path the filter cannot see (e.g. RIS), so
// a fresh repaint and `.screen` reach the client either way.
if (result.switched or win.onAltScreen() != was_alt) {
self.repaintTo(conn) catch |err| {
log.warn("repaint after screen switch failed: {}", .{err});
log.warn("repaint after screen change failed: {}", .{err});
};
}
}
Expand Down
37 changes: 37 additions & 0 deletions test/integration.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2514,6 +2514,43 @@ test "ui: wheel sends arrows to alternate-screen applications" {
alloc.free(peeked);
}

test "ui: wheel scrolls again after a session resets the terminal" {
const alloc = std.testing.allocator;
var h = try Harness.init(alloc);
defer h.deinit();

try h.startDetached("rst", &.{"bash"});

var ui = try PtyClient.spawn(&h, &.{"ui"}, 24, 100);
defer ui.deinit();
try ui.waitFor("rst");

// Enter the alternate screen while attached: the daemon strips the
// toggle, repaints, and sends `.screen` = alt, so the wheel would
// turn into arrow keys for the application.
try h.sendLine("rst", "printf '\\033[?1049hINALT'");
try ui.waitFor("INALT");

// A full reset (RIS, ESC c) returns the terminal to the primary
// screen without a 47/1047/1049 toggle, so the alt-screen filter
// never sees a switch. The daemon must still repaint and send
// `.screen` = primary, or the client keeps treating the session as
// alternate-screen and the wheel never pages local scrollback.
try h.sendLine("rst", "printf '\\033c'");
try h.sendLine("rst", "echo POSTRIS");
try ui.waitFor("POSTRIS");

// Fill the view's scrollback on the primary screen.
try h.sendLine("rst", "i=1; while [ $i -le 60 ]; do echo SCROLL-$i; i=$((i+1)); done");
try ui.waitFor("SCROLL-60");

// Wheel up pages local scrollback instead of sending arrows; the
// hint only renders while the viewport is scrolled off the bottom.
ui.clearOutput();
for (0..35) |_| try ui.send("\x1b[<64;50;10M");
try ui.waitFor(" scrollback");
}

test "ui: session titles render in the sidebar" {
const alloc = std.testing.allocator;
var h = try Harness.init(alloc);
Expand Down
Loading