From e0c5bdc53b157a7adb08dcc48528f5af3b053ab0 Mon Sep 17 00:00:00 2001 From: Rick Calixte <10281587+rcalixte@users.noreply.github.com> Date: Sun, 20 Oct 2024 00:03:54 -0400 Subject: [PATCH] Terminal: Reinitialize screens on scrollback reset When resetting the terminal screen, the memory buffer allocated for the scrollback is now cleared by reinitializing the screen and falling back to the current method if any of the attempts to reinitialize fail. Closes #2464 --- src/terminal/Terminal.zig | 59 +++++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 8 deletions(-) diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index 882ef41c0..a2bf6d50e 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -2621,12 +2621,59 @@ pub fn plainStringUnwrapped(self: *Terminal, alloc: Allocator) ![]const u8 { return try self.screen.dumpStringAllocUnwrapped(alloc, .{ .viewport = .{} }); } -/// Full reset +/// Full reset. +/// +/// This will attempt to free the existing screen memory and allocate +/// new screens but if that fails this will reuse the existing memory +/// from the prior screens. In the latter case, memory may be wasted +/// (since its unused) but it isn't leaked. pub fn fullReset(self: *Terminal) void { - // Switch back to primary screen and clear it. We do not restore cursor - // because see the next step... - self.primaryScreen(.{ .clear_on_exit = true, .cursor_save = false }); + // Attempt to initialize new screens. + var new_primary = Screen.init( + self.screen.alloc, + self.cols, + self.rows, + self.screen.pages.explicit_max_size, + ) catch |err| { + log.warn("failed to allocate new primary screen, reusing old memory err={}", .{err}); + self.fallbackReset(); + return; + }; + const new_secondary = Screen.init( + self.secondary_screen.alloc, + self.cols, + self.rows, + 0, + ) catch |err| { + log.warn("failed to allocate new secondary screen, reusing old memory err={}", .{err}); + new_primary.deinit(); + self.fallbackReset(); + return; + }; + // If we got here, both new screens were successfully allocated + // and we can deinitialize the old screens. + self.screen.deinit(); + self.secondary_screen.deinit(); + + // Replace with the newly allocated screens. + self.screen = new_primary; + self.secondary_screen = new_secondary; + + self.resetCommonState(); +} + +fn fallbackReset(self: *Terminal) void { + // Clear existing screens without reallocation + self.primaryScreen(.{ .clear_on_exit = true, .cursor_save = false }); + self.screen.clearSelection(); + self.eraseDisplay(.scrollback, false); + self.eraseDisplay(.complete, false); + self.screen.cursorAbsolute(0, 0); + self.resetCommonState(); +} + +fn resetCommonState(self: *Terminal) void { // We set the saved cursor to null and then restore. This will force // our cursor to go back to the default which will also move the cursor // to the top-left. @@ -2640,7 +2687,6 @@ pub fn fullReset(self: *Terminal) void { self.modes = .{}; self.flags = .{}; self.tabstops.reset(TABSTOP_INTERVAL); - self.screen.clearSelection(); self.screen.kitty_keyboard = .{}; self.secondary_screen.kitty_keyboard = .{}; self.screen.protected_mode = .off; @@ -2651,9 +2697,6 @@ pub fn fullReset(self: *Terminal) void { .right = self.cols - 1, }; self.previous_char = null; - self.eraseDisplay(.scrollback, false); - self.eraseDisplay(.complete, false); - self.screen.cursorAbsolute(0, 0); self.pwd.clearRetainingCapacity(); self.status_display = .main; }