terminal: set left and right margins, left and right margin mode 69

This commit is contained in:
Mitchell Hashimoto
2023-10-09 21:15:03 -07:00
parent be99d8ffe1
commit 2354454907
4 changed files with 172 additions and 5 deletions

View File

@ -1801,6 +1801,23 @@ pub fn setTopAndBottomMargin(self: *Terminal, top_req: usize, bottom_req: usize)
self.setCursorPos(1, 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);
}
/// Mark the current semantic prompt information. Current escape sequences /// Mark the current semantic prompt information. Current escape sequences
/// (OSC 133) only allow setting this for wherever the current active cursor /// (OSC 133) only allow setting this for wherever the current active cursor
/// is located. /// is located.
@ -2637,6 +2654,126 @@ test "Terminal: setTopAndBottomMargin top equal to bottom" {
try testing.expectEqualStrings("\nABC\nDEF\nGHI", 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" {
const alloc = testing.allocator; const alloc = testing.allocator;
var t = try init(alloc, 80, 80); var t = try init(alloc, 80, 80);

View File

@ -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 {

View File

@ -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| {

View File

@ -1397,6 +1397,10 @@ const StreamHandler = struct {
self.terminal.setTopAndBottomMargin(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 {
self.terminal.flags.modify_other_keys_2 = false; self.terminal.flags.modify_other_keys_2 = false;
switch (format) { switch (format) {
@ -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,