terminal: parse and handle set modify key format (ESC[>{a};{b}m)

This commit is contained in:
Mitchell Hashimoto
2023-08-12 22:27:46 -07:00
parent d94474463b
commit 66aa1d9be3
6 changed files with 92 additions and 18 deletions

View File

@ -1115,6 +1115,7 @@ pub fn keyCallback(
self.renderer_state.mutex.lock();
const cursor_key_application = self.io.terminal.modes.cursor_keys;
const keypad_key_application = self.io.terminal.modes.keypad_keys;
const modify_other_keys = self.io.terminal.modes.modify_other_keys;
self.renderer_state.mutex.unlock();
// Check if we're processing a function key.
@ -1133,10 +1134,8 @@ pub fn keyCallback(
switch (entry.modify_other_keys) {
.any => {},
// TODO
.set => {},
.set_other => continue,
.set => if (modify_other_keys) continue,
.set_other => if (!modify_other_keys) continue,
}
const mods_int: u8 = @bitCast(binding_mods);

View File

@ -99,6 +99,10 @@ modes: packed struct {
bracketed_paste: bool = false, // 2004
// This is set via ESC[4;2m. Any other modify key mode just sets
// this to false.
modify_other_keys: bool = false,
// This isn't a mode, this is set by OSC 133 using the "A" event.
// If this is true, it tells us that the shell supports redrawing
// the prompt and that when we resize, if the cursor is at a prompt,

View File

@ -192,3 +192,12 @@ pub const StatusDisplay = enum(u16) {
// Non-exhaustive so that @intToEnum never fails for unsupported values.
_,
};
/// The possible modify key formats to ESC[>{a};{b}m
/// Note: this is not complete, we should add more as we support more
pub const ModifyKeyFormat = union(enum) {
legacy: void,
cursor_keys: void,
function_keys: void,
other_keys: enum { none, numeric_except, numeric },
};

View File

@ -21,6 +21,7 @@ pub const CursorStyle = ansi.CursorStyle;
pub const DeviceAttributeReq = ansi.DeviceAttributeReq;
pub const DeviceStatusReq = ansi.DeviceStatusReq;
pub const Mode = ansi.Mode;
pub const ModifyKeyFormat = ansi.ModifyKeyFormat;
pub const StatusLineType = ansi.StatusLineType;
pub const StatusDisplay = ansi.StatusDisplay;
pub const EraseDisplay = csi.EraseDisplay;

View File

@ -437,25 +437,75 @@ pub fn Stream(comptime Handler: type) type {
} else log.warn("unimplemented CSI callback: {}", .{action}),
// SGR - Select Graphic Rendition
'm' => if (action.intermediates.len == 0) {
if (@hasDecl(T, "setAttribute")) {
'm' => switch (action.intermediates.len) {
0 => if (@hasDecl(T, "setAttribute")) {
var p: sgr.Parser = .{ .params = action.params, .colon = action.sep == .colon };
while (p.next()) |attr| {
// log.info("SGR attribute: {}", .{attr});
try self.handler.setAttribute(attr);
}
} else log.warn("unimplemented CSI callback: {}", .{action});
} else {
// Nothing, but I wanted a place to put this comment:
// there are others forms of CSI m that have intermediates.
// `vim --clean` uses `CSI ? 4 m` and I don't know what
// that means. And there is also `CSI > m` which is used
// to control modifier key reporting formats that we don't
// support yet.
log.debug(
"ignoring unimplemented CSI m with intermediates: {s}",
.{action.intermediates},
);
} else log.warn("unimplemented CSI callback: {}", .{action}),
1 => switch (action.intermediates[0]) {
'>' => if (@hasDecl(T, "setModifyKeyFormat")) blk: {
if (action.params.len == 0) {
// Reset
try self.handler.setModifyKeyFormat(.{ .legacy = {} });
break :blk;
}
var format: ansi.ModifyKeyFormat = switch (action.params[0]) {
0 => .{ .legacy = {} },
1 => .{ .cursor_keys = {} },
2 => .{ .function_keys = {} },
4 => .{ .other_keys = .none },
else => {
log.warn("invalid setModifyKeyFormat: {}", .{action});
break :blk;
},
};
if (action.params.len > 2) {
log.warn("invalid setModifyKeyFormat: {}", .{action});
break :blk;
}
if (action.params.len == 2) {
switch (format) {
// We don't support any of the subparams yet for these.
.legacy => {},
.cursor_keys => {},
.function_keys => {},
// We only support the numeric form.
.other_keys => |*v| switch (action.params[1]) {
2 => v.* = .numeric,
else => v.* = .none,
},
}
}
try self.handler.setModifyKeyFormat(format);
} else log.warn("unimplemented setModifyKeyFormat: {}", .{action}),
else => log.warn(
"unknown CSI m with intermediate: {}",
.{action.intermediates[0]},
),
},
else => {
// Nothing, but I wanted a place to put this comment:
// there are others forms of CSI m that have intermediates.
// `vim --clean` uses `CSI ? 4 m` and I don't know what
// that means. And there is also `CSI > m` which is used
// to control modifier key reporting formats that we don't
// support yet.
log.warn(
"ignoring unimplemented CSI m with intermediates: {s}",
.{action.intermediates},
);
},
},
// CPR - Request Cursor Position Report

View File

@ -1183,6 +1183,17 @@ const StreamHandler = struct {
self.terminal.setScrollingRegion(top, bot);
}
pub fn setModifyKeyFormat(self: *StreamHandler, format: terminal.ModifyKeyFormat) !void {
self.terminal.modes.modify_other_keys = false;
switch (format) {
.other_keys => |v| switch (v) {
.numeric => self.terminal.modes.modify_other_keys = true,
else => {},
},
else => {},
}
}
pub fn setMode(self: *StreamHandler, mode: terminal.Mode, enabled: bool) !void {
// Note: this function doesn't need to grab the render state or
// terminal locks because it is only called from process() which