terminal: proper cursor copy for alt screen

This commit is contained in:
Mitchell Hashimoto
2024-03-09 14:02:58 -08:00
parent a1e8a59aa3
commit 3154686f9e
2 changed files with 93 additions and 10 deletions

View File

@ -437,6 +437,32 @@ fn cursorDownOrScroll(self: *Screen) !void {
} }
} }
/// Copy another cursor. The cursor can be on any screen but the x/y
/// must be within our screen bounds.
pub fn cursorCopy(self: *Screen, other: Cursor) !void {
assert(other.x < self.pages.cols);
assert(other.y < self.pages.rows);
const old = self.cursor;
self.cursor = other;
errdefer self.cursor = old;
// We need to keep our old x/y because that is our cursorAbsolute
// will fix up our pointers.
//
// We keep our old page pin because we expect to be in the active
// page relative to our own screen.
self.cursor.page_pin = old.page_pin;
self.cursor.x = old.x;
self.cursor.y = old.y;
self.cursorAbsolute(other.x, other.y);
// We keep the old style ref so manualStyleUpdate can clean our old style up.
self.cursor.style_id = old.style_id;
self.cursor.style_ref = old.style_ref;
try self.manualStyleUpdate();
}
/// Options for scrolling the viewport of the terminal grid. The reason /// Options for scrolling the viewport of the terminal grid. The reason
/// we have this in addition to PageList.Scroll is because we have additional /// we have this in addition to PageList.Scroll is because we have additional
/// scroll behaviors that are not part of the PageList.Scroll enum. /// scroll behaviors that are not part of the PageList.Scroll enum.
@ -1730,6 +1756,68 @@ test "Screen read and write no scrollback large" {
} }
} }
test "Screen cursorCopy x/y" {
const testing = std.testing;
const alloc = testing.allocator;
var s = try Screen.init(alloc, 10, 10, 0);
defer s.deinit();
s.cursorAbsolute(2, 3);
try testing.expect(s.cursor.x == 2);
try testing.expect(s.cursor.y == 3);
var s2 = try Screen.init(alloc, 10, 10, 0);
defer s2.deinit();
try s2.cursorCopy(s.cursor);
try testing.expect(s2.cursor.x == 2);
try testing.expect(s2.cursor.y == 3);
try s2.testWriteString("Hello");
{
const str = try s2.dumpStringAlloc(alloc, .{ .screen = .{} });
defer alloc.free(str);
try testing.expectEqualStrings("\n\n\n Hello", str);
}
}
test "Screen cursorCopy style deref" {
const testing = std.testing;
const alloc = testing.allocator;
var s = try Screen.init(alloc, 10, 10, 0);
defer s.deinit();
var s2 = try Screen.init(alloc, 10, 10, 0);
defer s2.deinit();
const page = s2.cursor.page_pin.page.data;
// Bold should create our style
try s2.setAttribute(.{ .bold = {} });
try testing.expectEqual(@as(usize, 1), page.styles.count(page.memory));
try testing.expect(s2.cursor.style.flags.bold);
// Copy default style, should release our style
try s2.cursorCopy(s.cursor);
try testing.expect(!s2.cursor.style.flags.bold);
try testing.expectEqual(@as(usize, 0), page.styles.count(page.memory));
}
test "Screen cursorCopy style copy" {
const testing = std.testing;
const alloc = testing.allocator;
var s = try Screen.init(alloc, 10, 10, 0);
defer s.deinit();
try s.setAttribute(.{ .bold = {} });
var s2 = try Screen.init(alloc, 10, 10, 0);
defer s2.deinit();
const page = s2.cursor.page_pin.page.data;
try s2.cursorCopy(s.cursor);
try testing.expect(s2.cursor.style.flags.bold);
try testing.expectEqual(@as(usize, 1), page.styles.count(page.memory));
}
test "Screen style basics" { test "Screen style basics" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; const alloc = testing.allocator;

View File

@ -2142,19 +2142,13 @@ pub fn alternateScreen(
self.screen.kitty_images.dirty = true; self.screen.kitty_images.dirty = true;
// Bring our pen with us // Bring our pen with us
self.screen.cursor = old.cursor; self.screen.cursorCopy(old.cursor) catch |err| {
self.screen.cursor.style_id = 0; log.warn("cursor copy failed entering alt screen err={}", .{err});
self.screen.cursor.style_ref = null; };
self.screen.cursorAbsolute(old.cursor.x, old.cursor.y);
if (options.clear_on_enter) { if (options.clear_on_enter) {
self.eraseDisplay(.complete, false); self.eraseDisplay(.complete, false);
} }
// Update any style ref after we erase the display so we definitely have space
self.screen.manualStyleUpdate() catch |err| {
log.warn("style update failed entering alt screen err={}", .{err});
};
} }
/// Switch back to the primary screen (reset alternate screen mode). /// Switch back to the primary screen (reset alternate screen mode).
@ -6414,7 +6408,8 @@ test "Terminal: saveCursor with screen change" {
defer t.deinit(alloc); defer t.deinit(alloc);
try t.setAttribute(.{ .bold = {} }); try t.setAttribute(.{ .bold = {} });
t.screen.cursor.x = 2; t.setCursorPos(t.screen.cursor.y + 1, 3);
try testing.expect(t.screen.cursor.x == 2);
t.screen.charset.gr = .G3; t.screen.charset.gr = .G3;
t.modes.set(.origin, true); t.modes.set(.origin, true);
t.alternateScreen(.{ t.alternateScreen(.{