terminal: handle SCOSC/SCORC

Fixes #1401

SCOSC is ambiguous with regards to DECSLRM. This commit copies the logic
of xterm: if left/right mode is enabled, then CSI S is always DECSLRM.
But if left/right mode is disabled then CSI S empty always uses SCOSC.

SCORC always works.
This commit is contained in:
Mitchell Hashimoto
2024-01-28 08:04:14 -08:00
parent 06ff385e0c
commit c871140419
2 changed files with 56 additions and 2 deletions

View File

@ -805,7 +805,11 @@ pub fn Stream(comptime Handler: type) type {
// DECSLRM // DECSLRM
0 => if (@hasDecl(T, "setLeftAndRightMargin")) { 0 => if (@hasDecl(T, "setLeftAndRightMargin")) {
switch (action.params.len) { switch (action.params.len) {
0 => try self.handler.setLeftAndRightMargin(0, 0), // CSI S is ambiguous with zero params so we defer
// to our handler to do the proper logic. If mode 69
// is set, then we should invoke DECSLRM, otherwise
// we should invoke SC.
0 => try self.handler.setLeftAndRightMarginAmbiguous(),
1 => try self.handler.setLeftAndRightMargin(action.params[0], 0), 1 => try self.handler.setLeftAndRightMargin(action.params[0], 0),
2 => try self.handler.setLeftAndRightMargin(action.params[0], action.params[1]), 2 => try self.handler.setLeftAndRightMargin(action.params[0], action.params[1]),
else => log.warn("invalid DECSLRM command: {}", .{action}), else => log.warn("invalid DECSLRM command: {}", .{action}),
@ -865,8 +869,13 @@ pub fn Stream(comptime Handler: type) type {
), ),
}, },
// Kitty keyboard protocol
'u' => switch (action.intermediates.len) { 'u' => switch (action.intermediates.len) {
0 => if (@hasDecl(T, "restoreCursor"))
try self.handler.restoreCursor()
else
log.warn("unimplemented CSI callback: {}", .{action}),
// Kitty keyboard protocol
1 => switch (action.intermediates[0]) { 1 => switch (action.intermediates[0]) {
'?' => if (@hasDecl(T, "queryKittyKeyboard")) { '?' => if (@hasDecl(T, "queryKittyKeyboard")) {
try self.handler.queryKittyKeyboard(); try self.handler.queryKittyKeyboard();
@ -1676,6 +1685,43 @@ test "stream: insert characters" {
try testing.expect(!s.handler.called); try testing.expect(!s.handler.called);
} }
test "stream: SCOSC" {
const H = struct {
const Self = @This();
called: bool = false,
pub fn setLeftAndRightMargin(self: *Self, left: u16, right: u16) !void {
_ = self;
_ = left;
_ = right;
@panic("bad");
}
pub fn setLeftAndRightMarginAmbiguous(self: *Self) !void {
self.called = true;
}
};
var s: Stream(H) = .{ .handler = .{} };
for ("\x1B[s") |c| try s.next(c);
try testing.expect(s.handler.called);
}
test "stream: SCORC" {
const H = struct {
const Self = @This();
called: bool = false,
pub fn restoreCursor(self: *Self) !void {
self.called = true;
}
};
var s: Stream(H) = .{ .handler = .{} };
for ("\x1B[u") |c| try s.next(c);
try testing.expect(s.handler.called);
}
test "stream: too many csi params" { test "stream: too many csi params" {
const H = struct { const H = struct {
pub fn setCursorRight(self: *@This(), v: u16) !void { pub fn setCursorRight(self: *@This(), v: u16) !void {

View File

@ -2044,6 +2044,14 @@ const StreamHandler = struct {
self.terminal.setTopAndBottomMargin(top, bot); self.terminal.setTopAndBottomMargin(top, bot);
} }
pub fn setLeftAndRightMarginAmbiguous(self: *StreamHandler) !void {
if (self.terminal.modes.get(.enable_left_and_right_margin)) {
try self.setLeftAndRightMargin(0, 0);
} else {
try self.saveCursor();
}
}
pub fn setLeftAndRightMargin(self: *StreamHandler, left: u16, right: u16) !void { pub fn setLeftAndRightMargin(self: *StreamHandler, left: u16, right: u16) !void {
self.terminal.setLeftAndRightMargin(left, right); self.terminal.setLeftAndRightMargin(left, right);
} }