mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 00:06:09 +03:00
terminal: set left and right margins, left and right margin mode 69
This commit is contained in:
@ -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);
|
||||||
|
@ -134,12 +134,14 @@ 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 (comptime !entry.disabled) {
|
||||||
if (entry.value == v and entry.ansi == ansi) {
|
if (entry.value == v and entry.ansi == ansi) {
|
||||||
const tag: ModeTag = .{ .ansi = ansi, .value = entry.value };
|
const tag: ModeTag = .{ .ansi = ansi, .value = entry.value };
|
||||||
const int: ModeTag.Backing = @bitCast(tag);
|
const int: ModeTag.Backing = @bitCast(tag);
|
||||||
return @enumFromInt(int);
|
return @enumFromInt(int);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -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| {
|
||||||
|
@ -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,
|
||||||
|
Reference in New Issue
Block a user