mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
Merge pull request #646 from mitchellh/sm
xterm audit: DECALN, Top/Bot Margins, Left/Right Margins
This commit is contained in:
@ -866,16 +866,37 @@ pub fn decaln(self: *Terminal) !void {
|
|||||||
defer tracy.end();
|
defer tracy.end();
|
||||||
|
|
||||||
// Reset margins, also sets cursor to top-left
|
// Reset margins, also sets cursor to top-left
|
||||||
self.setScrollingRegion(0, 0);
|
self.scrolling_region = .{
|
||||||
|
.top = 0,
|
||||||
|
.bottom = self.rows - 1,
|
||||||
|
.left = 0,
|
||||||
|
.right = self.cols - 1,
|
||||||
|
};
|
||||||
|
|
||||||
// Fill with Es, does not move cursor. We reset fg/bg so we can just
|
// Origin mode is disabled
|
||||||
// optimize here by doing row copies.
|
self.modes.set(.origin, false);
|
||||||
const filled = self.screen.getRow(.{ .active = 0 });
|
|
||||||
filled.fill(.{ .char = 'E' });
|
|
||||||
|
|
||||||
var row: usize = 1;
|
// Move our cursor to the top-left
|
||||||
while (row < self.rows) : (row += 1) {
|
self.setCursorPos(1, 1);
|
||||||
try self.screen.getRow(.{ .active = row }).copyRow(filled);
|
|
||||||
|
// Clear our stylistic attributes
|
||||||
|
self.screen.cursor.pen = .{
|
||||||
|
.bg = self.screen.cursor.pen.bg,
|
||||||
|
.fg = self.screen.cursor.pen.fg,
|
||||||
|
.attrs = .{
|
||||||
|
.has_bg = self.screen.cursor.pen.attrs.has_bg,
|
||||||
|
.has_fg = self.screen.cursor.pen.attrs.has_fg,
|
||||||
|
.protected = self.screen.cursor.pen.attrs.protected,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Our pen has the letter E
|
||||||
|
var pen: Screen.Cell = .{ .char = 'E' };
|
||||||
|
|
||||||
|
// Fill with Es, does not move cursor.
|
||||||
|
for (0..self.rows) |y| {
|
||||||
|
const filled = self.screen.getRow(.{ .active = y });
|
||||||
|
filled.fill(pen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1767,19 +1788,33 @@ pub fn scrollViewport(self: *Terminal, behavior: ScrollViewport) !void {
|
|||||||
/// and bottom-most line of the screen.
|
/// and bottom-most line of the screen.
|
||||||
///
|
///
|
||||||
/// Top and bottom are 1-indexed.
|
/// Top and bottom are 1-indexed.
|
||||||
pub fn setScrollingRegion(self: *Terminal, top: usize, bottom: usize) void {
|
pub fn setTopAndBottomMargin(self: *Terminal, top_req: usize, bottom_req: usize) void {
|
||||||
const tracy = trace(@src());
|
const tracy = trace(@src());
|
||||||
defer tracy.end();
|
defer tracy.end();
|
||||||
|
|
||||||
var t = if (top == 0) 1 else top;
|
const top = @max(1, top_req);
|
||||||
var b = @min(bottom, self.rows);
|
const bottom = @min(self.rows, if (bottom_req == 0) self.rows else bottom_req);
|
||||||
if (t >= b) {
|
if (top >= bottom) return;
|
||||||
t = 1;
|
|
||||||
b = self.rows;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.scrolling_region.top = t - 1;
|
self.scrolling_region.top = top - 1;
|
||||||
self.scrolling_region.bottom = b - 1;
|
self.scrolling_region.bottom = bottom - 1;
|
||||||
|
self.setCursorPos(1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DECSLRM
|
||||||
|
pub fn setLeftAndRightMargin(self: *Terminal, left_req: usize, right_req: usize) void {
|
||||||
|
const tracy = trace(@src());
|
||||||
|
defer tracy.end();
|
||||||
|
|
||||||
|
// We must have this mode enabled to do anything
|
||||||
|
if (!self.modes.get(.enable_left_and_right_margin)) return;
|
||||||
|
|
||||||
|
const left = @max(1, left_req);
|
||||||
|
const right = @min(self.rows, if (right_req == 0) self.rows else right_req);
|
||||||
|
if (left >= right) return;
|
||||||
|
|
||||||
|
self.scrolling_region.left = left - 1;
|
||||||
|
self.scrolling_region.right = right - 1;
|
||||||
self.setCursorPos(1, 1);
|
self.setCursorPos(1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2513,7 +2548,7 @@ test "Terminal: setCursorPos (original test)" {
|
|||||||
try testing.expectEqual(@as(usize, 79), t.screen.cursor.y);
|
try testing.expectEqual(@as(usize, 79), t.screen.cursor.y);
|
||||||
|
|
||||||
// Set the scroll region
|
// Set the scroll region
|
||||||
t.setScrollingRegion(10, t.rows);
|
t.setTopAndBottomMargin(10, t.rows);
|
||||||
t.setCursorPos(0, 0);
|
t.setCursorPos(0, 0);
|
||||||
try testing.expectEqual(@as(usize, 0), t.screen.cursor.x);
|
try testing.expectEqual(@as(usize, 0), t.screen.cursor.x);
|
||||||
try testing.expectEqual(@as(usize, 9), t.screen.cursor.y);
|
try testing.expectEqual(@as(usize, 9), t.screen.cursor.y);
|
||||||
@ -2526,41 +2561,217 @@ test "Terminal: setCursorPos (original test)" {
|
|||||||
try testing.expectEqual(@as(usize, 0), t.screen.cursor.x);
|
try testing.expectEqual(@as(usize, 0), t.screen.cursor.x);
|
||||||
try testing.expectEqual(@as(usize, 79), t.screen.cursor.y);
|
try testing.expectEqual(@as(usize, 79), t.screen.cursor.y);
|
||||||
|
|
||||||
t.setScrollingRegion(10, 11);
|
t.setTopAndBottomMargin(10, 11);
|
||||||
t.setCursorPos(2, 0);
|
t.setCursorPos(2, 0);
|
||||||
try testing.expectEqual(@as(usize, 0), t.screen.cursor.x);
|
try testing.expectEqual(@as(usize, 0), t.screen.cursor.x);
|
||||||
try testing.expectEqual(@as(usize, 10), t.screen.cursor.y);
|
try testing.expectEqual(@as(usize, 10), t.screen.cursor.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "Terminal: setScrollingRegion" {
|
test "Terminal: setTopAndBottomMargin simple" {
|
||||||
var t = try init(testing.allocator, 80, 80);
|
const alloc = testing.allocator;
|
||||||
defer t.deinit(testing.allocator);
|
var t = try init(alloc, 5, 5);
|
||||||
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
// Initial value
|
try t.printString("ABC");
|
||||||
try testing.expectEqual(@as(usize, 0), t.scrolling_region.top);
|
t.carriageReturn();
|
||||||
try testing.expectEqual(@as(usize, t.rows - 1), t.scrolling_region.bottom);
|
try t.linefeed();
|
||||||
|
try t.printString("DEF");
|
||||||
|
t.carriageReturn();
|
||||||
|
try t.linefeed();
|
||||||
|
try t.printString("GHI");
|
||||||
|
t.setTopAndBottomMargin(0, 0);
|
||||||
|
try t.scrollDown(1);
|
||||||
|
|
||||||
// Move our cusor so we can verify we move it back
|
{
|
||||||
t.setCursorPos(5, 5);
|
var str = try t.plainString(testing.allocator);
|
||||||
t.setScrollingRegion(3, 7);
|
defer testing.allocator.free(str);
|
||||||
|
try testing.expectEqualStrings("\nABC\nDEF\nGHI", str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Cursor should move back to top-left
|
test "Terminal: setTopAndBottomMargin top only" {
|
||||||
try testing.expectEqual(@as(usize, 0), t.screen.cursor.x);
|
const alloc = testing.allocator;
|
||||||
try testing.expectEqual(@as(usize, 0), t.screen.cursor.y);
|
var t = try init(alloc, 5, 5);
|
||||||
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
// Scroll region is set
|
try t.printString("ABC");
|
||||||
try testing.expectEqual(@as(usize, 2), t.scrolling_region.top);
|
t.carriageReturn();
|
||||||
try testing.expectEqual(@as(usize, 6), t.scrolling_region.bottom);
|
try t.linefeed();
|
||||||
|
try t.printString("DEF");
|
||||||
|
t.carriageReturn();
|
||||||
|
try t.linefeed();
|
||||||
|
try t.printString("GHI");
|
||||||
|
t.setTopAndBottomMargin(2, 0);
|
||||||
|
try t.scrollDown(1);
|
||||||
|
|
||||||
// Scroll region invalid
|
{
|
||||||
t.setScrollingRegion(7, 3);
|
var str = try t.plainString(testing.allocator);
|
||||||
try testing.expectEqual(@as(usize, 0), t.scrolling_region.top);
|
defer testing.allocator.free(str);
|
||||||
try testing.expectEqual(@as(usize, t.rows - 1), t.scrolling_region.bottom);
|
try testing.expectEqualStrings("ABC\n\nDEF\nGHI", str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Scroll region with zero top and bottom
|
test "Terminal: setTopAndBottomMargin top and bottom" {
|
||||||
t.setScrollingRegion(0, 0);
|
const alloc = testing.allocator;
|
||||||
try testing.expectEqual(@as(usize, 0), t.scrolling_region.top);
|
var t = try init(alloc, 5, 5);
|
||||||
try testing.expectEqual(@as(usize, t.rows - 1), t.scrolling_region.bottom);
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
|
try t.printString("ABC");
|
||||||
|
t.carriageReturn();
|
||||||
|
try t.linefeed();
|
||||||
|
try t.printString("DEF");
|
||||||
|
t.carriageReturn();
|
||||||
|
try t.linefeed();
|
||||||
|
try t.printString("GHI");
|
||||||
|
t.setTopAndBottomMargin(1, 2);
|
||||||
|
try t.scrollDown(1);
|
||||||
|
|
||||||
|
{
|
||||||
|
var str = try t.plainString(testing.allocator);
|
||||||
|
defer testing.allocator.free(str);
|
||||||
|
try testing.expectEqualStrings("\nABC\nGHI", str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Terminal: setTopAndBottomMargin top equal to bottom" {
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var t = try init(alloc, 5, 5);
|
||||||
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
|
try t.printString("ABC");
|
||||||
|
t.carriageReturn();
|
||||||
|
try t.linefeed();
|
||||||
|
try t.printString("DEF");
|
||||||
|
t.carriageReturn();
|
||||||
|
try t.linefeed();
|
||||||
|
try t.printString("GHI");
|
||||||
|
t.setTopAndBottomMargin(2, 2);
|
||||||
|
try t.scrollDown(1);
|
||||||
|
|
||||||
|
{
|
||||||
|
var str = try t.plainString(testing.allocator);
|
||||||
|
defer testing.allocator.free(str);
|
||||||
|
try testing.expectEqualStrings("\nABC\nDEF\nGHI", str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Terminal: setLeftAndRightMargin simple" {
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var t = try init(alloc, 5, 5);
|
||||||
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
|
try t.printString("ABC");
|
||||||
|
t.carriageReturn();
|
||||||
|
try t.linefeed();
|
||||||
|
try t.printString("DEF");
|
||||||
|
t.carriageReturn();
|
||||||
|
try t.linefeed();
|
||||||
|
try t.printString("GHI");
|
||||||
|
t.modes.set(.enable_left_and_right_margin, true);
|
||||||
|
t.setLeftAndRightMargin(0, 0);
|
||||||
|
t.eraseChars(1);
|
||||||
|
|
||||||
|
{
|
||||||
|
var str = try t.plainString(testing.allocator);
|
||||||
|
defer testing.allocator.free(str);
|
||||||
|
try testing.expectEqualStrings(" BC\nDEF\nGHI", str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Terminal: setLeftAndRightMargin left only" {
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var t = try init(alloc, 5, 5);
|
||||||
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
|
try t.printString("ABC");
|
||||||
|
t.carriageReturn();
|
||||||
|
try t.linefeed();
|
||||||
|
try t.printString("DEF");
|
||||||
|
t.carriageReturn();
|
||||||
|
try t.linefeed();
|
||||||
|
try t.printString("GHI");
|
||||||
|
t.modes.set(.enable_left_and_right_margin, true);
|
||||||
|
t.setLeftAndRightMargin(2, 0);
|
||||||
|
t.setCursorPos(1, 2);
|
||||||
|
try t.insertLines(1);
|
||||||
|
|
||||||
|
{
|
||||||
|
var str = try t.plainString(testing.allocator);
|
||||||
|
defer testing.allocator.free(str);
|
||||||
|
try testing.expectEqualStrings("A\nDBC\nGEF\n HI", str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Terminal: setLeftAndRightMargin left and right" {
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var t = try init(alloc, 5, 5);
|
||||||
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
|
try t.printString("ABC");
|
||||||
|
t.carriageReturn();
|
||||||
|
try t.linefeed();
|
||||||
|
try t.printString("DEF");
|
||||||
|
t.carriageReturn();
|
||||||
|
try t.linefeed();
|
||||||
|
try t.printString("GHI");
|
||||||
|
t.modes.set(.enable_left_and_right_margin, true);
|
||||||
|
t.setLeftAndRightMargin(1, 2);
|
||||||
|
t.setCursorPos(1, 2);
|
||||||
|
try t.insertLines(1);
|
||||||
|
|
||||||
|
{
|
||||||
|
var str = try t.plainString(testing.allocator);
|
||||||
|
defer testing.allocator.free(str);
|
||||||
|
try testing.expectEqualStrings(" C\nABF\nDEI\nGH", str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Terminal: setLeftAndRightMargin left equal right" {
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var t = try init(alloc, 5, 5);
|
||||||
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
|
try t.printString("ABC");
|
||||||
|
t.carriageReturn();
|
||||||
|
try t.linefeed();
|
||||||
|
try t.printString("DEF");
|
||||||
|
t.carriageReturn();
|
||||||
|
try t.linefeed();
|
||||||
|
try t.printString("GHI");
|
||||||
|
t.modes.set(.enable_left_and_right_margin, true);
|
||||||
|
t.setLeftAndRightMargin(2, 2);
|
||||||
|
t.setCursorPos(1, 2);
|
||||||
|
try t.insertLines(1);
|
||||||
|
|
||||||
|
{
|
||||||
|
var str = try t.plainString(testing.allocator);
|
||||||
|
defer testing.allocator.free(str);
|
||||||
|
try testing.expectEqualStrings("\nABC\nDEF\nGHI", str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Terminal: setLeftAndRightMargin mode 69 unset" {
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var t = try init(alloc, 5, 5);
|
||||||
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
|
try t.printString("ABC");
|
||||||
|
t.carriageReturn();
|
||||||
|
try t.linefeed();
|
||||||
|
try t.printString("DEF");
|
||||||
|
t.carriageReturn();
|
||||||
|
try t.linefeed();
|
||||||
|
try t.printString("GHI");
|
||||||
|
t.modes.set(.enable_left_and_right_margin, false);
|
||||||
|
t.setLeftAndRightMargin(1, 2);
|
||||||
|
t.setCursorPos(1, 2);
|
||||||
|
try t.insertLines(1);
|
||||||
|
|
||||||
|
{
|
||||||
|
var str = try t.plainString(testing.allocator);
|
||||||
|
defer testing.allocator.free(str);
|
||||||
|
try testing.expectEqualStrings("\nABC\nDEF\nGHI", str);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test "Terminal: deleteLines" {
|
test "Terminal: deleteLines" {
|
||||||
@ -2615,7 +2826,7 @@ test "Terminal: deleteLines with scroll region" {
|
|||||||
try t.linefeed();
|
try t.linefeed();
|
||||||
try t.print('D');
|
try t.print('D');
|
||||||
|
|
||||||
t.setScrollingRegion(1, 3);
|
t.setTopAndBottomMargin(1, 3);
|
||||||
t.setCursorPos(1, 1);
|
t.setCursorPos(1, 1);
|
||||||
try t.deleteLines(1);
|
try t.deleteLines(1);
|
||||||
|
|
||||||
@ -2651,7 +2862,7 @@ test "Terminal: deleteLines with scroll region, large count" {
|
|||||||
try t.linefeed();
|
try t.linefeed();
|
||||||
try t.print('D');
|
try t.print('D');
|
||||||
|
|
||||||
t.setScrollingRegion(1, 3);
|
t.setTopAndBottomMargin(1, 3);
|
||||||
t.setCursorPos(1, 1);
|
t.setCursorPos(1, 1);
|
||||||
try t.deleteLines(5);
|
try t.deleteLines(5);
|
||||||
|
|
||||||
@ -2687,7 +2898,7 @@ test "Terminal: deleteLines with scroll region, cursor outside of region" {
|
|||||||
try t.linefeed();
|
try t.linefeed();
|
||||||
try t.print('D');
|
try t.print('D');
|
||||||
|
|
||||||
t.setScrollingRegion(1, 3);
|
t.setTopAndBottomMargin(1, 3);
|
||||||
t.setCursorPos(4, 1);
|
t.setCursorPos(4, 1);
|
||||||
try t.deleteLines(1);
|
try t.deleteLines(1);
|
||||||
|
|
||||||
@ -2820,7 +3031,7 @@ test "Terminal: insertLines outside of scroll region" {
|
|||||||
t.carriageReturn();
|
t.carriageReturn();
|
||||||
try t.linefeed();
|
try t.linefeed();
|
||||||
try t.printString("GHI");
|
try t.printString("GHI");
|
||||||
t.setScrollingRegion(3, 4);
|
t.setTopAndBottomMargin(3, 4);
|
||||||
t.setCursorPos(2, 2);
|
t.setCursorPos(2, 2);
|
||||||
try t.insertLines(1);
|
try t.insertLines(1);
|
||||||
|
|
||||||
@ -2846,7 +3057,7 @@ test "Terminal: insertLines top/bottom scroll region" {
|
|||||||
t.carriageReturn();
|
t.carriageReturn();
|
||||||
try t.linefeed();
|
try t.linefeed();
|
||||||
try t.printString("123");
|
try t.printString("123");
|
||||||
t.setScrollingRegion(1, 3);
|
t.setTopAndBottomMargin(1, 3);
|
||||||
t.setCursorPos(2, 2);
|
t.setCursorPos(2, 2);
|
||||||
try t.insertLines(1);
|
try t.insertLines(1);
|
||||||
|
|
||||||
@ -2944,7 +3155,7 @@ test "Terminal: insertLines with scroll region" {
|
|||||||
try t.linefeed();
|
try t.linefeed();
|
||||||
try t.print('E');
|
try t.print('E');
|
||||||
|
|
||||||
t.setScrollingRegion(1, 2);
|
t.setTopAndBottomMargin(1, 2);
|
||||||
t.setCursorPos(1, 1);
|
t.setCursorPos(1, 1);
|
||||||
try t.insertLines(1);
|
try t.insertLines(1);
|
||||||
|
|
||||||
@ -3089,7 +3300,7 @@ test "Terminal: reverseIndex top of scrolling region" {
|
|||||||
try t.linefeed();
|
try t.linefeed();
|
||||||
|
|
||||||
// Set our scroll region
|
// Set our scroll region
|
||||||
t.setScrollingRegion(2, 5);
|
t.setTopAndBottomMargin(2, 5);
|
||||||
t.setCursorPos(2, 1);
|
t.setCursorPos(2, 1);
|
||||||
try t.reverseIndex();
|
try t.reverseIndex();
|
||||||
try t.print('X');
|
try t.print('X');
|
||||||
@ -3141,7 +3352,7 @@ test "Terminal: index outside of scrolling region" {
|
|||||||
defer t.deinit(alloc);
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
try testing.expectEqual(@as(usize, 0), t.screen.cursor.y);
|
try testing.expectEqual(@as(usize, 0), t.screen.cursor.y);
|
||||||
t.setScrollingRegion(2, 5);
|
t.setTopAndBottomMargin(2, 5);
|
||||||
try t.index();
|
try t.index();
|
||||||
try testing.expectEqual(@as(usize, 1), t.screen.cursor.y);
|
try testing.expectEqual(@as(usize, 1), t.screen.cursor.y);
|
||||||
}
|
}
|
||||||
@ -3151,7 +3362,7 @@ test "Terminal: index from the bottom outside of scroll region" {
|
|||||||
var t = try init(alloc, 2, 5);
|
var t = try init(alloc, 2, 5);
|
||||||
defer t.deinit(alloc);
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
t.setScrollingRegion(1, 2);
|
t.setTopAndBottomMargin(1, 2);
|
||||||
t.setCursorPos(5, 1);
|
t.setCursorPos(5, 1);
|
||||||
try t.print('A');
|
try t.print('A');
|
||||||
try t.index();
|
try t.index();
|
||||||
@ -3228,7 +3439,7 @@ test "Terminal: index inside scroll region" {
|
|||||||
var t = try init(alloc, 5, 5);
|
var t = try init(alloc, 5, 5);
|
||||||
defer t.deinit(alloc);
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
t.setScrollingRegion(1, 3);
|
t.setTopAndBottomMargin(1, 3);
|
||||||
try t.print('A');
|
try t.print('A');
|
||||||
try t.index();
|
try t.index();
|
||||||
try t.print('X');
|
try t.print('X');
|
||||||
@ -3245,7 +3456,7 @@ test "Terminal: index bottom of scroll region" {
|
|||||||
var t = try init(alloc, 5, 5);
|
var t = try init(alloc, 5, 5);
|
||||||
defer t.deinit(alloc);
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
t.setScrollingRegion(1, 3);
|
t.setTopAndBottomMargin(1, 3);
|
||||||
t.setCursorPos(4, 1);
|
t.setCursorPos(4, 1);
|
||||||
try t.print('B');
|
try t.print('B');
|
||||||
t.setCursorPos(3, 1);
|
t.setCursorPos(3, 1);
|
||||||
@ -3265,7 +3476,7 @@ test "Terminal: index bottom of primary screen with scroll region" {
|
|||||||
var t = try init(alloc, 5, 5);
|
var t = try init(alloc, 5, 5);
|
||||||
defer t.deinit(alloc);
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
t.setScrollingRegion(1, 3);
|
t.setTopAndBottomMargin(1, 3);
|
||||||
t.setCursorPos(3, 1);
|
t.setCursorPos(3, 1);
|
||||||
try t.print('A');
|
try t.print('A');
|
||||||
t.setCursorPos(5, 1);
|
t.setCursorPos(5, 1);
|
||||||
@ -3286,7 +3497,7 @@ test "Terminal: index outside left/right margin" {
|
|||||||
var t = try init(alloc, 10, 5);
|
var t = try init(alloc, 10, 5);
|
||||||
defer t.deinit(alloc);
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
t.setScrollingRegion(1, 3);
|
t.setTopAndBottomMargin(1, 3);
|
||||||
t.scrolling_region.left = 3;
|
t.scrolling_region.left = 3;
|
||||||
t.scrolling_region.right = 5;
|
t.scrolling_region.right = 5;
|
||||||
t.setCursorPos(3, 3);
|
t.setCursorPos(3, 3);
|
||||||
@ -3307,7 +3518,7 @@ test "Terminal: index inside left/right margin" {
|
|||||||
var t = try init(alloc, 10, 5);
|
var t = try init(alloc, 10, 5);
|
||||||
defer t.deinit(alloc);
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
t.setScrollingRegion(1, 3);
|
t.setTopAndBottomMargin(1, 3);
|
||||||
t.scrolling_region.left = 3;
|
t.scrolling_region.left = 3;
|
||||||
t.scrolling_region.right = 5;
|
t.scrolling_region.right = 5;
|
||||||
t.setCursorPos(3, 3);
|
t.setCursorPos(3, 3);
|
||||||
@ -3344,6 +3555,50 @@ test "Terminal: DECALN" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "Terminal: decaln reset margins" {
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var t = try init(alloc, 3, 3);
|
||||||
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
|
// Initial value
|
||||||
|
t.modes.set(.origin, true);
|
||||||
|
t.setTopAndBottomMargin(2, 3);
|
||||||
|
try t.decaln();
|
||||||
|
try t.scrollDown(1);
|
||||||
|
|
||||||
|
{
|
||||||
|
var str = try t.plainString(testing.allocator);
|
||||||
|
defer testing.allocator.free(str);
|
||||||
|
try testing.expectEqualStrings("\nEEE\nEEE", str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Terminal: decaln preserves color" {
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var t = try init(alloc, 3, 3);
|
||||||
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
|
const pen: Screen.Cell = .{
|
||||||
|
.bg = .{ .r = 0xFF, .g = 0x00, .b = 0x00 },
|
||||||
|
.attrs = .{ .has_bg = true },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initial value
|
||||||
|
t.screen.cursor.pen = pen;
|
||||||
|
t.modes.set(.origin, true);
|
||||||
|
t.setTopAndBottomMargin(2, 3);
|
||||||
|
try t.decaln();
|
||||||
|
try t.scrollDown(1);
|
||||||
|
|
||||||
|
{
|
||||||
|
var str = try t.plainString(testing.allocator);
|
||||||
|
defer testing.allocator.free(str);
|
||||||
|
try testing.expectEqualStrings("\nEEE\nEEE", str);
|
||||||
|
const cell = t.screen.getCell(.active, 0, 0);
|
||||||
|
try testing.expectEqual(pen, cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "Terminal: insertBlanks" {
|
test "Terminal: insertBlanks" {
|
||||||
// NOTE: this is not verified with conformance tests, so these
|
// NOTE: this is not verified with conformance tests, so these
|
||||||
// tests might actually be verifying wrong behavior.
|
// tests might actually be verifying wrong behavior.
|
||||||
@ -5181,7 +5436,7 @@ test "Terminal: cursorDown above bottom scroll margin" {
|
|||||||
var t = try init(alloc, 5, 5);
|
var t = try init(alloc, 5, 5);
|
||||||
defer t.deinit(alloc);
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
t.setScrollingRegion(1, 3);
|
t.setTopAndBottomMargin(1, 3);
|
||||||
try t.print('A');
|
try t.print('A');
|
||||||
t.cursorDown(10);
|
t.cursorDown(10);
|
||||||
try t.print('X');
|
try t.print('X');
|
||||||
@ -5198,7 +5453,7 @@ test "Terminal: cursorDown below bottom scroll margin" {
|
|||||||
var t = try init(alloc, 5, 5);
|
var t = try init(alloc, 5, 5);
|
||||||
defer t.deinit(alloc);
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
t.setScrollingRegion(1, 3);
|
t.setTopAndBottomMargin(1, 3);
|
||||||
try t.print('A');
|
try t.print('A');
|
||||||
t.setCursorPos(4, 1);
|
t.setCursorPos(4, 1);
|
||||||
t.cursorDown(10);
|
t.cursorDown(10);
|
||||||
@ -5251,7 +5506,7 @@ test "Terminal: cursorUp below top scroll margin" {
|
|||||||
var t = try init(alloc, 5, 5);
|
var t = try init(alloc, 5, 5);
|
||||||
defer t.deinit(alloc);
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
t.setScrollingRegion(2, 4);
|
t.setTopAndBottomMargin(2, 4);
|
||||||
t.setCursorPos(3, 1);
|
t.setCursorPos(3, 1);
|
||||||
try t.print('A');
|
try t.print('A');
|
||||||
t.cursorUp(5);
|
t.cursorUp(5);
|
||||||
@ -5269,7 +5524,7 @@ test "Terminal: cursorUp above top scroll margin" {
|
|||||||
var t = try init(alloc, 5, 5);
|
var t = try init(alloc, 5, 5);
|
||||||
defer t.deinit(alloc);
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
t.setScrollingRegion(3, 5);
|
t.setTopAndBottomMargin(3, 5);
|
||||||
t.setCursorPos(3, 1);
|
t.setCursorPos(3, 1);
|
||||||
try t.print('A');
|
try t.print('A');
|
||||||
t.setCursorPos(2, 1);
|
t.setCursorPos(2, 1);
|
||||||
@ -5403,7 +5658,7 @@ test "Terminal: scrollDown outside of scroll region" {
|
|||||||
t.carriageReturn();
|
t.carriageReturn();
|
||||||
try t.linefeed();
|
try t.linefeed();
|
||||||
try t.printString("GHI");
|
try t.printString("GHI");
|
||||||
t.setScrollingRegion(3, 4);
|
t.setTopAndBottomMargin(3, 4);
|
||||||
t.setCursorPos(2, 2);
|
t.setCursorPos(2, 2);
|
||||||
const cursor = t.screen.cursor;
|
const cursor = t.screen.cursor;
|
||||||
try t.scrollDown(1);
|
try t.scrollDown(1);
|
||||||
|
@ -134,10 +134,12 @@ const ModeTag = packed struct(u16) {
|
|||||||
|
|
||||||
pub fn modeFromInt(v: u16, ansi: bool) ?Mode {
|
pub fn modeFromInt(v: u16, ansi: bool) ?Mode {
|
||||||
inline for (entries) |entry| {
|
inline for (entries) |entry| {
|
||||||
if (entry.value == v and entry.ansi == ansi) {
|
if (comptime !entry.disabled) {
|
||||||
const tag: ModeTag = .{ .ansi = ansi, .value = entry.value };
|
if (entry.value == v and entry.ansi == ansi) {
|
||||||
const int: ModeTag.Backing = @bitCast(tag);
|
const tag: ModeTag = .{ .ansi = ansi, .value = entry.value };
|
||||||
return @enumFromInt(int);
|
const int: ModeTag.Backing = @bitCast(tag);
|
||||||
|
return @enumFromInt(int);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,6 +162,7 @@ const ModeEntry = struct {
|
|||||||
value: comptime_int,
|
value: comptime_int,
|
||||||
default: bool = false,
|
default: bool = false,
|
||||||
ansi: bool = false,
|
ansi: bool = false,
|
||||||
|
disabled: bool = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The full list of available entries. For documentation see how
|
/// The full list of available entries. For documentation see how
|
||||||
@ -195,6 +198,10 @@ const entries: []const ModeEntry = &.{
|
|||||||
.{ .name = "bracketed_paste", .value = 2004 },
|
.{ .name = "bracketed_paste", .value = 2004 },
|
||||||
.{ .name = "synchronized_output", .value = 2026 },
|
.{ .name = "synchronized_output", .value = 2026 },
|
||||||
.{ .name = "grapheme_cluster", .value = 2027 },
|
.{ .name = "grapheme_cluster", .value = 2027 },
|
||||||
|
|
||||||
|
// Disabled for now until we ensure we get left/right margins working
|
||||||
|
// correctly in all sequences.
|
||||||
|
.{ .name = "enable_left_and_right_margin", .value = 69, .disabled = true },
|
||||||
};
|
};
|
||||||
|
|
||||||
test {
|
test {
|
||||||
|
@ -787,8 +787,20 @@ pub fn Stream(comptime Handler: type) type {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
||||||
// Save Mode
|
|
||||||
's' => switch (action.intermediates.len) {
|
's' => switch (action.intermediates.len) {
|
||||||
|
// DECSLRM
|
||||||
|
0 => if (@hasDecl(T, "setLeftAndRightMargin")) {
|
||||||
|
switch (action.params.len) {
|
||||||
|
0 => try self.handler.setLeftAndRightMargin(0, 0),
|
||||||
|
1 => try self.handler.setLeftAndRightMargin(action.params[0], 0),
|
||||||
|
2 => try self.handler.setLeftAndRightMargin(action.params[0], action.params[1]),
|
||||||
|
else => log.warn("invalid DECSLRM command: {}", .{action}),
|
||||||
|
}
|
||||||
|
} else log.warn(
|
||||||
|
"unimplemented CSI callback: {}",
|
||||||
|
.{action},
|
||||||
|
),
|
||||||
|
|
||||||
1 => switch (action.intermediates[0]) {
|
1 => switch (action.intermediates[0]) {
|
||||||
'?' => if (@hasDecl(T, "saveMode")) {
|
'?' => if (@hasDecl(T, "saveMode")) {
|
||||||
for (action.params) |mode_int| {
|
for (action.params) |mode_int| {
|
||||||
|
@ -1394,7 +1394,11 @@ const StreamHandler = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn setTopAndBottomMargin(self: *StreamHandler, top: u16, bot: u16) !void {
|
pub fn setTopAndBottomMargin(self: *StreamHandler, top: u16, bot: u16) !void {
|
||||||
self.terminal.setScrollingRegion(top, bot);
|
self.terminal.setTopAndBottomMargin(top, bot);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setLeftAndRightMargin(self: *StreamHandler, left: u16, right: u16) !void {
|
||||||
|
self.terminal.setLeftAndRightMargin(left, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setModifyKeyFormat(self: *StreamHandler, format: terminal.ModifyKeyFormat) !void {
|
pub fn setModifyKeyFormat(self: *StreamHandler, format: terminal.ModifyKeyFormat) !void {
|
||||||
@ -1460,6 +1464,13 @@ const StreamHandler = struct {
|
|||||||
// Origin resets cursor pos
|
// Origin resets cursor pos
|
||||||
.origin => self.terminal.setCursorPos(1, 1),
|
.origin => self.terminal.setCursorPos(1, 1),
|
||||||
|
|
||||||
|
.enable_left_and_right_margin => if (!enabled) {
|
||||||
|
// When we disable left/right margin mode we need to
|
||||||
|
// reset the left/right margins.
|
||||||
|
self.terminal.scrolling_region.left = 0;
|
||||||
|
self.terminal.scrolling_region.right = self.terminal.cols - 1;
|
||||||
|
},
|
||||||
|
|
||||||
.alt_screen_save_cursor_clear_enter => {
|
.alt_screen_save_cursor_clear_enter => {
|
||||||
const opts: terminal.Terminal.AlternateScreenOptions = .{
|
const opts: terminal.Terminal.AlternateScreenOptions = .{
|
||||||
.cursor_save = true,
|
.cursor_save = true,
|
||||||
|
45
website/app/vt/decaln/page.mdx
Normal file
45
website/app/vt/decaln/page.mdx
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import VTSequence from "@/components/VTSequence";
|
||||||
|
|
||||||
|
# Screen Alignment Test (DECALN)
|
||||||
|
|
||||||
|
<VTSequence sequence={["ESC", "#", "8"]} />
|
||||||
|
|
||||||
|
Reset margins, move cursor to the top left, and fill the screen with `E`.
|
||||||
|
|
||||||
|
Reset the top, bottom, left, and right margins and unset [origin mode](#TODO).
|
||||||
|
The cursor is moved to the top-left corner of the screen.
|
||||||
|
|
||||||
|
All stylistic SGR attributes are unset, such as bold, blink, etc.
|
||||||
|
SGR foreground and background colors are preserved.
|
||||||
|
The [protected attribute](#TODO) is not unset.
|
||||||
|
|
||||||
|
The entire screen is filled with the character `E`. The letter `E` ignores
|
||||||
|
the current SGR settings and is written with no styling.
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
### DECALN V-1: Simple Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
printf "\033#8"
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|EEEEEEEE|
|
||||||
|
|EEEEEEEE|
|
||||||
|
|EEEEEEEE|
|
||||||
|
```
|
||||||
|
|
||||||
|
### DECALN V-2: Reset Margins
|
||||||
|
|
||||||
|
```bash
|
||||||
|
printf "\033[2;3r" # scroll region top/bottom
|
||||||
|
printf "\033#8"
|
||||||
|
printf "\033[T"
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|c_______|
|
||||||
|
|EEEEEEEE|
|
||||||
|
|EEEEEEEE|
|
||||||
|
```
|
120
website/app/vt/decslrm/page.mdx
Normal file
120
website/app/vt/decslrm/page.mdx
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
import VTSequence from "@/components/VTSequence";
|
||||||
|
|
||||||
|
# Set Left and Right Margins (DECSLRM)
|
||||||
|
|
||||||
|
<VTSequence sequence={["CSI", "Pl", ";", "Pr", "s"]} />
|
||||||
|
|
||||||
|
Sets the left and right margins, otherwise known as the scroll region.
|
||||||
|
To learn more about scroll regions in general, see
|
||||||
|
[Set Top and Bottom Margins](/vt/decstbm).
|
||||||
|
|
||||||
|
Parameters `l` and `r` are integer values. If either value is zero the
|
||||||
|
value will be reset to default values. The default value for `l` is `1`
|
||||||
|
and the default value of `r` is the number of columns in the screen.
|
||||||
|
|
||||||
|
Values `l` and `r` can be omitted. If either value is omitted, their
|
||||||
|
default values will be used. Note that it is impossible to omit `l`
|
||||||
|
and not omit `r`.
|
||||||
|
|
||||||
|
This sequence requires [enable left and right margin (mode 69)](#TODO)
|
||||||
|
to be set. If mode 69 is not set, this sequence does nothing and left
|
||||||
|
and right margins will not be set.
|
||||||
|
|
||||||
|
This sequence conflicts with [save cursor (`CSI s`)](#TODO). If
|
||||||
|
mode 69 is disabled, save cursor will be invoked. If mode 69 is enabled,
|
||||||
|
the `CSI s` save cursor sequence will be disabled, but save cursor is always
|
||||||
|
also available as `ESC 7`.
|
||||||
|
|
||||||
|
If left is larger or equal to right, this sequence does nothing. A
|
||||||
|
scroll region must be at least two columns (`r` must be greater than `l`).
|
||||||
|
The rest of this sequence description assumes valid values for `l` and `r`.
|
||||||
|
|
||||||
|
This sequence unsets the pending wrap state and moves the cursor to
|
||||||
|
the top-left of the screen. If [origin mode](#TODO) is set, the cursor is
|
||||||
|
moved to the top-left of the scroll region.
|
||||||
|
|
||||||
|
To reset the left and right margins, call this sequence with both values set to
|
||||||
|
"0". This will force the default values for both `l` and `r` which is
|
||||||
|
the full screen. Unsetting mode 69 will also reset the left and right margins.
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
### DECSLRM V-1: Full Screen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
printf "\033[1;1H" # move to top-left
|
||||||
|
printf "\033[0J" # clear screen
|
||||||
|
printf "ABC\n"
|
||||||
|
printf "DEF\n"
|
||||||
|
printf "GHI\n"
|
||||||
|
printf "\033[?69h" # enable left/right margins
|
||||||
|
printf "\033[s" # scroll region left/right
|
||||||
|
printf "\033[X"
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|cBC_____|
|
||||||
|
|DEF_____|
|
||||||
|
|GHI_____|
|
||||||
|
```
|
||||||
|
|
||||||
|
### DECSLRM V-2: Left Only
|
||||||
|
|
||||||
|
```bash
|
||||||
|
printf "\033[1;1H" # move to top-left
|
||||||
|
printf "\033[0J" # clear screen
|
||||||
|
printf "ABC\n"
|
||||||
|
printf "DEF\n"
|
||||||
|
printf "GHI\n"
|
||||||
|
printf "\033[?69h" # enable left/right margins
|
||||||
|
printf "\033[2s" # scroll region left/right
|
||||||
|
printf "\033[2G" # move cursor to column 2
|
||||||
|
printf "\033[L"
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|Ac______|
|
||||||
|
|DBC_____|
|
||||||
|
|GEF_____|
|
||||||
|
| HI_____|
|
||||||
|
```
|
||||||
|
|
||||||
|
### DECSLRM V-3: Left And Right
|
||||||
|
|
||||||
|
```bash
|
||||||
|
printf "\033[1;1H" # move to top-left
|
||||||
|
printf "\033[0J" # clear screen
|
||||||
|
printf "ABC\n"
|
||||||
|
printf "DEF\n"
|
||||||
|
printf "GHI\n"
|
||||||
|
printf "\033[?69h" # enable left/right margins
|
||||||
|
printf "\033[1;2s" # scroll region left/right
|
||||||
|
printf "\033[2G" # move cursor to column 2
|
||||||
|
printf "\033[L"
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|_cC_____|
|
||||||
|
|ABF_____|
|
||||||
|
|DEI_____|
|
||||||
|
|GH______|
|
||||||
|
```
|
||||||
|
|
||||||
|
### DECSLRM V-4: Left Equal to Right
|
||||||
|
|
||||||
|
```bash
|
||||||
|
printf "\033[1;1H" # move to top-left
|
||||||
|
printf "\033[0J" # clear screen
|
||||||
|
printf "ABC\n"
|
||||||
|
printf "DEF\n"
|
||||||
|
printf "GHI\n"
|
||||||
|
printf "\033[?69h" # enable left/right margins
|
||||||
|
printf "\033[2;2s" # scroll region left/right
|
||||||
|
printf "\033[X"
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|cBC_____|
|
||||||
|
|DEF_____|
|
||||||
|
|GHI_____|
|
||||||
|
```
|
111
website/app/vt/decstbm/page.mdx
Normal file
111
website/app/vt/decstbm/page.mdx
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import VTSequence from "@/components/VTSequence";
|
||||||
|
|
||||||
|
# Set Top and Bottom Margins (DECSTBM)
|
||||||
|
|
||||||
|
<VTSequence sequence={["CSI", "Pt", ";", "Pb", "r"]} />
|
||||||
|
|
||||||
|
Sets the top and bottom margins, otherwise known as the scroll region.
|
||||||
|
|
||||||
|
Parameters `t` and `b` are integer values. If either value is zero the
|
||||||
|
value will be reset to default values. The default value for `t` is `1`
|
||||||
|
and the default value of `b` is the number of rows in the screen.
|
||||||
|
|
||||||
|
Values `t` and `b` can be omitted. If either value is omitted, their
|
||||||
|
default values will be used. Note that it is impossible to omit `t`
|
||||||
|
and not omit `b`. The only valid sequences are `CSI t ; b r`,
|
||||||
|
`CSI t r` and `CSI r`.
|
||||||
|
|
||||||
|
If top is larger or equal to bottom, this sequence does nothing. A
|
||||||
|
scroll region must be at least two rows (`b` must be greater than `t`).
|
||||||
|
The rest of this sequence description assumes valid values for `t` and `b`.
|
||||||
|
|
||||||
|
This sequence unsets the pending wrap state and moves the cursor to
|
||||||
|
the top-left of the screen. If [origin mode](#TODO) is set, the cursor is
|
||||||
|
moved to the top-left of the scroll region.
|
||||||
|
|
||||||
|
To reset the scroll region, call this sequence with both values set to
|
||||||
|
"0". This will force the default values for both `t` and `b` which is
|
||||||
|
the full screen.
|
||||||
|
|
||||||
|
The top and bottom margin constitute what is known as the _scroll region_.
|
||||||
|
The scroll region impacts the operation of many sequences such as
|
||||||
|
[insert line](/vt/il), [cursor down](/vt/cud), etc. Scroll regions are
|
||||||
|
an effective and efficient way to constraint terminal modifications to a
|
||||||
|
rectangular region of the screen.
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
### DECSTBM V-1: Full Screen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
printf "\033[1;1H" # move to top-left
|
||||||
|
printf "\033[0J" # clear screen
|
||||||
|
printf "ABC\n"
|
||||||
|
printf "DEF\n"
|
||||||
|
printf "GHI\n"
|
||||||
|
printf "\033[r" # scroll region top/bottom
|
||||||
|
printf "\033[T"
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|c_______|
|
||||||
|
|ABC_____|
|
||||||
|
|DEF_____|
|
||||||
|
|GHI_____|
|
||||||
|
```
|
||||||
|
|
||||||
|
### DECSTBM V-2: Top Only
|
||||||
|
|
||||||
|
```bash
|
||||||
|
printf "\033[1;1H" # move to top-left
|
||||||
|
printf "\033[0J" # clear screen
|
||||||
|
printf "ABC\n"
|
||||||
|
printf "DEF\n"
|
||||||
|
printf "GHI\n"
|
||||||
|
printf "\033[2r" # scroll region top/bottom
|
||||||
|
printf "\033[T"
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|ABC_____|
|
||||||
|
|________|
|
||||||
|
|DEF_____|
|
||||||
|
|GHI_____|
|
||||||
|
```
|
||||||
|
|
||||||
|
### DECSTBM V-3: Top and Bottom
|
||||||
|
|
||||||
|
```bash
|
||||||
|
printf "\033[1;1H" # move to top-left
|
||||||
|
printf "\033[0J" # clear screen
|
||||||
|
printf "ABC\n"
|
||||||
|
printf "DEF\n"
|
||||||
|
printf "GHI\n"
|
||||||
|
printf "\033[1;2r" # scroll region top/bottom
|
||||||
|
printf "\033[T"
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|________|
|
||||||
|
|ABC_____|
|
||||||
|
|GHI_____|
|
||||||
|
```
|
||||||
|
|
||||||
|
### DECSTBM V-4: Top Equal to Bottom
|
||||||
|
|
||||||
|
```bash
|
||||||
|
printf "\033[1;1H" # move to top-left
|
||||||
|
printf "\033[0J" # clear screen
|
||||||
|
printf "ABC\n"
|
||||||
|
printf "DEF\n"
|
||||||
|
printf "GHI\n"
|
||||||
|
printf "\033[2;2r" # scroll region top/bottom
|
||||||
|
printf "\033[T"
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|________|
|
||||||
|
|ABC_____|
|
||||||
|
|DEF_____|
|
||||||
|
|GHI_____|
|
||||||
|
```
|
Reference in New Issue
Block a user