terminal: many more conversions
parent
2520e27aef
commit
f68ea7c907
|
|
@ -1,3 +1,7 @@
|
|||
const build_options = @import("terminal_options");
|
||||
const lib = @import("../lib/main.zig");
|
||||
const lib_target: lib.Target = if (build_options.c_abi) .c else .zig;
|
||||
|
||||
/// Modes for the ED CSI command.
|
||||
pub const EraseDisplay = enum(u8) {
|
||||
below = 0,
|
||||
|
|
@ -33,13 +37,16 @@ pub const TabClear = enum(u8) {
|
|||
};
|
||||
|
||||
/// Style formats for terminal size reports.
|
||||
pub const SizeReportStyle = enum {
|
||||
// XTWINOPS
|
||||
csi_14_t,
|
||||
csi_16_t,
|
||||
csi_18_t,
|
||||
csi_21_t,
|
||||
};
|
||||
pub const SizeReportStyle = lib.Enum(
|
||||
lib_target,
|
||||
&.{
|
||||
// XTWINOPS
|
||||
"csi_14_t",
|
||||
"csi_16_t",
|
||||
"csi_18_t",
|
||||
"csi_21_t",
|
||||
},
|
||||
);
|
||||
|
||||
/// XTWINOPS CSI 22/23
|
||||
pub const TitlePushPop = struct {
|
||||
|
|
|
|||
|
|
@ -81,9 +81,13 @@ pub const Action = union(Key) {
|
|||
save_cursor,
|
||||
restore_cursor,
|
||||
modify_key_format: ansi.ModifyKeyFormat,
|
||||
mouse_shift_capture: bool,
|
||||
protected_mode_off,
|
||||
protected_mode_iso,
|
||||
protected_mode_dec,
|
||||
size_report: csi.SizeReportStyle,
|
||||
title_push: u16,
|
||||
title_pop: u16,
|
||||
xtversion,
|
||||
kitty_keyboard_query,
|
||||
kitty_keyboard_push: KittyKeyboardFlags,
|
||||
|
|
@ -150,9 +154,13 @@ pub const Action = union(Key) {
|
|||
"save_cursor",
|
||||
"restore_cursor",
|
||||
"modify_key_format",
|
||||
"mouse_shift_capture",
|
||||
"protected_mode_off",
|
||||
"protected_mode_iso",
|
||||
"protected_mode_dec",
|
||||
"size_report",
|
||||
"title_push",
|
||||
"title_pop",
|
||||
"xtversion",
|
||||
"kitty_keyboard_query",
|
||||
"kitty_keyboard_push",
|
||||
|
|
@ -1443,7 +1451,7 @@ pub fn Stream(comptime Handler: type) type {
|
|||
},
|
||||
|
||||
// XTSHIFTESCAPE
|
||||
'>' => if (@hasDecl(T, "setMouseShiftCapture")) capture: {
|
||||
'>' => capture: {
|
||||
const capture = switch (input.params.len) {
|
||||
0 => false,
|
||||
1 => switch (input.params[0]) {
|
||||
|
|
@ -1460,11 +1468,8 @@ pub fn Stream(comptime Handler: type) type {
|
|||
},
|
||||
};
|
||||
|
||||
try self.handler.setMouseShiftCapture(capture);
|
||||
} else log.warn(
|
||||
"unimplemented CSI callback: {f}",
|
||||
.{input},
|
||||
),
|
||||
try self.handler.vt(.mouse_shift_capture, capture);
|
||||
},
|
||||
|
||||
else => log.warn(
|
||||
"unknown CSI s with intermediate: {f}",
|
||||
|
|
@ -1485,48 +1490,28 @@ pub fn Stream(comptime Handler: type) type {
|
|||
switch (input.params[0]) {
|
||||
14 => if (input.params.len == 1) {
|
||||
// report the text area size in pixels
|
||||
if (@hasDecl(T, "sendSizeReport")) {
|
||||
self.handler.sendSizeReport(.csi_14_t);
|
||||
} else log.warn(
|
||||
"ignoring unimplemented CSI 14 t",
|
||||
.{},
|
||||
);
|
||||
try self.handler.vt(.size_report, .csi_14_t);
|
||||
} else log.warn(
|
||||
"ignoring CSI 14 t with extra parameters: {f}",
|
||||
.{input},
|
||||
),
|
||||
16 => if (input.params.len == 1) {
|
||||
// report cell size in pixels
|
||||
if (@hasDecl(T, "sendSizeReport")) {
|
||||
self.handler.sendSizeReport(.csi_16_t);
|
||||
} else log.warn(
|
||||
"ignoring unimplemented CSI 16 t",
|
||||
.{},
|
||||
);
|
||||
try self.handler.vt(.size_report, .csi_16_t);
|
||||
} else log.warn(
|
||||
"ignoring CSI 16 t with extra parameters: {f}",
|
||||
.{input},
|
||||
),
|
||||
18 => if (input.params.len == 1) {
|
||||
// report screen size in characters
|
||||
if (@hasDecl(T, "sendSizeReport")) {
|
||||
self.handler.sendSizeReport(.csi_18_t);
|
||||
} else log.warn(
|
||||
"ignoring unimplemented CSI 18 t",
|
||||
.{},
|
||||
);
|
||||
try self.handler.vt(.size_report, .csi_18_t);
|
||||
} else log.warn(
|
||||
"ignoring CSI 18 t with extra parameters: {f}",
|
||||
.{input},
|
||||
),
|
||||
21 => if (input.params.len == 1) {
|
||||
// report window title
|
||||
if (@hasDecl(T, "sendSizeReport")) {
|
||||
self.handler.sendSizeReport(.csi_21_t);
|
||||
} else log.warn(
|
||||
"ignoring unimplemented CSI 21 t",
|
||||
.{},
|
||||
);
|
||||
try self.handler.vt(.size_report, .csi_21_t);
|
||||
} else log.warn(
|
||||
"ignoring CSI 21 t with extra parameters: {f}",
|
||||
.{input},
|
||||
|
|
@ -1538,22 +1523,15 @@ pub fn Stream(comptime Handler: type) type {
|
|||
input.params[1] == 2))
|
||||
{
|
||||
// push/pop title
|
||||
if (@hasDecl(T, "pushPopTitle")) {
|
||||
self.handler.pushPopTitle(.{
|
||||
.op = switch (number) {
|
||||
22 => .push,
|
||||
23 => .pop,
|
||||
else => @compileError("unreachable"),
|
||||
},
|
||||
.index = if (input.params.len == 3)
|
||||
input.params[2]
|
||||
else
|
||||
0,
|
||||
});
|
||||
} else log.warn(
|
||||
"ignoring unimplemented CSI 22/23 t",
|
||||
.{},
|
||||
);
|
||||
const index: u16 = if (input.params.len == 3)
|
||||
input.params[2]
|
||||
else
|
||||
0;
|
||||
switch (number) {
|
||||
22 => try self.handler.vt(.title_push, index),
|
||||
23 => try self.handler.vt(.title_pop, index),
|
||||
else => @compileError("unreachable"),
|
||||
}
|
||||
} else log.warn(
|
||||
"ignoring CSI 22/23 t with extra parameters: {f}",
|
||||
.{input},
|
||||
|
|
@ -1575,10 +1553,7 @@ pub fn Stream(comptime Handler: type) type {
|
|||
},
|
||||
|
||||
'u' => switch (input.intermediates.len) {
|
||||
0 => if (@hasDecl(T, "restoreCursor"))
|
||||
try self.handler.restoreCursor()
|
||||
else
|
||||
log.warn("unimplemented CSI callback: {f}", .{input}),
|
||||
0 => try self.handler.vt(.restore_cursor, {}),
|
||||
|
||||
// Kitty keyboard protocol
|
||||
1 => switch (input.intermediates[0]) {
|
||||
|
|
@ -2575,18 +2550,15 @@ test "stream: XTSHIFTESCAPE" {
|
|||
const H = struct {
|
||||
escape: ?bool = null,
|
||||
|
||||
pub fn setMouseShiftCapture(self: *@This(), v: bool) !void {
|
||||
self.escape = v;
|
||||
}
|
||||
|
||||
pub fn vt(
|
||||
self: *@This(),
|
||||
comptime action: anytype,
|
||||
value: anytype,
|
||||
comptime action: streampkg.Action.Tag,
|
||||
value: streampkg.Action.Value(action),
|
||||
) !void {
|
||||
_ = self;
|
||||
_ = action;
|
||||
_ = value;
|
||||
switch (action) {
|
||||
.mouse_shift_capture => self.escape = value,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -2698,18 +2670,16 @@ test "stream: SCORC" {
|
|||
const Self = @This();
|
||||
called: bool = false,
|
||||
|
||||
pub fn restoreCursor(self: *Self) !void {
|
||||
self.called = true;
|
||||
}
|
||||
|
||||
pub fn vt(
|
||||
self: *@This(),
|
||||
comptime action: anytype,
|
||||
value: anytype,
|
||||
self: *Self,
|
||||
comptime action: streampkg.Action.Tag,
|
||||
value: streampkg.Action.Value(action),
|
||||
) !void {
|
||||
_ = self;
|
||||
_ = action;
|
||||
_ = value;
|
||||
switch (action) {
|
||||
.restore_cursor => self.called = true,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -2759,18 +2729,15 @@ test "stream: send report with CSI t" {
|
|||
const H = struct {
|
||||
style: ?csi.SizeReportStyle = null,
|
||||
|
||||
pub fn sendSizeReport(self: *@This(), style: csi.SizeReportStyle) void {
|
||||
self.style = style;
|
||||
}
|
||||
|
||||
pub fn vt(
|
||||
self: *@This(),
|
||||
comptime action: anytype,
|
||||
value: anytype,
|
||||
comptime action: streampkg.Action.Tag,
|
||||
value: streampkg.Action.Value(action),
|
||||
) !void {
|
||||
_ = self;
|
||||
_ = action;
|
||||
_ = value;
|
||||
switch (action) {
|
||||
.size_report => self.style = value,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -2816,220 +2783,178 @@ test "stream: invalid CSI t" {
|
|||
|
||||
test "stream: CSI t push title" {
|
||||
const H = struct {
|
||||
op: ?csi.TitlePushPop = null,
|
||||
|
||||
pub fn pushPopTitle(self: *@This(), op: csi.TitlePushPop) void {
|
||||
self.op = op;
|
||||
}
|
||||
index: ?u16 = null,
|
||||
|
||||
pub fn vt(
|
||||
self: *@This(),
|
||||
comptime action: anytype,
|
||||
value: anytype,
|
||||
comptime action: streampkg.Action.Tag,
|
||||
value: streampkg.Action.Value(action),
|
||||
) !void {
|
||||
_ = self;
|
||||
_ = action;
|
||||
_ = value;
|
||||
switch (action) {
|
||||
.title_push => self.index = value,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var s: Stream(H) = .init(.{});
|
||||
|
||||
try s.nextSlice("\x1b[22;0t");
|
||||
try testing.expectEqual(csi.TitlePushPop{
|
||||
.op = .push,
|
||||
.index = 0,
|
||||
}, s.handler.op.?);
|
||||
try testing.expectEqual(@as(u16, 0), s.handler.index.?);
|
||||
}
|
||||
|
||||
test "stream: CSI t push title with explicit window" {
|
||||
const H = struct {
|
||||
op: ?csi.TitlePushPop = null,
|
||||
|
||||
pub fn pushPopTitle(self: *@This(), op: csi.TitlePushPop) void {
|
||||
self.op = op;
|
||||
}
|
||||
index: ?u16 = null,
|
||||
|
||||
pub fn vt(
|
||||
self: *@This(),
|
||||
comptime action: anytype,
|
||||
value: anytype,
|
||||
comptime action: streampkg.Action.Tag,
|
||||
value: streampkg.Action.Value(action),
|
||||
) !void {
|
||||
_ = self;
|
||||
_ = action;
|
||||
_ = value;
|
||||
switch (action) {
|
||||
.title_push => self.index = value,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var s: Stream(H) = .init(.{});
|
||||
|
||||
try s.nextSlice("\x1b[22;2t");
|
||||
try testing.expectEqual(csi.TitlePushPop{
|
||||
.op = .push,
|
||||
.index = 0,
|
||||
}, s.handler.op.?);
|
||||
try testing.expectEqual(@as(u16, 0), s.handler.index.?);
|
||||
}
|
||||
|
||||
test "stream: CSI t push title with explicit icon" {
|
||||
const H = struct {
|
||||
op: ?csi.TitlePushPop = null,
|
||||
|
||||
pub fn pushPopTitle(self: *@This(), op: csi.TitlePushPop) void {
|
||||
self.op = op;
|
||||
}
|
||||
index: ?u16 = null,
|
||||
|
||||
pub fn vt(
|
||||
self: *@This(),
|
||||
comptime action: anytype,
|
||||
value: anytype,
|
||||
comptime action: streampkg.Action.Tag,
|
||||
value: streampkg.Action.Value(action),
|
||||
) !void {
|
||||
_ = self;
|
||||
_ = action;
|
||||
_ = value;
|
||||
switch (action) {
|
||||
.title_push => self.index = value,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var s: Stream(H) = .init(.{});
|
||||
|
||||
try s.nextSlice("\x1b[22;1t");
|
||||
try testing.expectEqual(null, s.handler.op);
|
||||
try testing.expectEqual(null, s.handler.index);
|
||||
}
|
||||
|
||||
test "stream: CSI t push title with index" {
|
||||
const H = struct {
|
||||
op: ?csi.TitlePushPop = null,
|
||||
|
||||
pub fn pushPopTitle(self: *@This(), op: csi.TitlePushPop) void {
|
||||
self.op = op;
|
||||
}
|
||||
index: ?u16 = null,
|
||||
|
||||
pub fn vt(
|
||||
self: *@This(),
|
||||
comptime action: anytype,
|
||||
value: anytype,
|
||||
comptime action: streampkg.Action.Tag,
|
||||
value: streampkg.Action.Value(action),
|
||||
) !void {
|
||||
_ = self;
|
||||
_ = action;
|
||||
_ = value;
|
||||
switch (action) {
|
||||
.title_push => self.index = value,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var s: Stream(H) = .init(.{});
|
||||
|
||||
try s.nextSlice("\x1b[22;0;5t");
|
||||
try testing.expectEqual(csi.TitlePushPop{
|
||||
.op = .push,
|
||||
.index = 5,
|
||||
}, s.handler.op.?);
|
||||
try testing.expectEqual(@as(u16, 5), s.handler.index.?);
|
||||
}
|
||||
|
||||
test "stream: CSI t pop title" {
|
||||
const H = struct {
|
||||
op: ?csi.TitlePushPop = null,
|
||||
|
||||
pub fn pushPopTitle(self: *@This(), op: csi.TitlePushPop) void {
|
||||
self.op = op;
|
||||
}
|
||||
index: ?u16 = null,
|
||||
|
||||
pub fn vt(
|
||||
self: *@This(),
|
||||
comptime action: anytype,
|
||||
value: anytype,
|
||||
comptime action: streampkg.Action.Tag,
|
||||
value: streampkg.Action.Value(action),
|
||||
) !void {
|
||||
_ = self;
|
||||
_ = action;
|
||||
_ = value;
|
||||
switch (action) {
|
||||
.title_pop => self.index = value,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var s: Stream(H) = .init(.{});
|
||||
|
||||
try s.nextSlice("\x1b[23;0t");
|
||||
try testing.expectEqual(csi.TitlePushPop{
|
||||
.op = .pop,
|
||||
.index = 0,
|
||||
}, s.handler.op.?);
|
||||
try testing.expectEqual(@as(u16, 0), s.handler.index.?);
|
||||
}
|
||||
|
||||
test "stream: CSI t pop title with explicit window" {
|
||||
const H = struct {
|
||||
op: ?csi.TitlePushPop = null,
|
||||
|
||||
pub fn pushPopTitle(self: *@This(), op: csi.TitlePushPop) void {
|
||||
self.op = op;
|
||||
}
|
||||
index: ?u16 = null,
|
||||
|
||||
pub fn vt(
|
||||
self: *@This(),
|
||||
comptime action: anytype,
|
||||
value: anytype,
|
||||
comptime action: streampkg.Action.Tag,
|
||||
value: streampkg.Action.Value(action),
|
||||
) !void {
|
||||
_ = self;
|
||||
_ = action;
|
||||
_ = value;
|
||||
switch (action) {
|
||||
.title_pop => self.index = value,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var s: Stream(H) = .init(.{});
|
||||
|
||||
try s.nextSlice("\x1b[23;2t");
|
||||
try testing.expectEqual(csi.TitlePushPop{
|
||||
.op = .pop,
|
||||
.index = 0,
|
||||
}, s.handler.op.?);
|
||||
try testing.expectEqual(@as(u16, 0), s.handler.index.?);
|
||||
}
|
||||
|
||||
test "stream: CSI t pop title with explicit icon" {
|
||||
const H = struct {
|
||||
op: ?csi.TitlePushPop = null,
|
||||
|
||||
pub fn pushPopTitle(self: *@This(), op: csi.TitlePushPop) void {
|
||||
self.op = op;
|
||||
}
|
||||
index: ?u16 = null,
|
||||
|
||||
pub fn vt(
|
||||
self: *@This(),
|
||||
comptime action: anytype,
|
||||
value: anytype,
|
||||
comptime action: streampkg.Action.Tag,
|
||||
value: streampkg.Action.Value(action),
|
||||
) !void {
|
||||
_ = self;
|
||||
_ = action;
|
||||
_ = value;
|
||||
switch (action) {
|
||||
.title_pop => self.index = value,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var s: Stream(H) = .init(.{});
|
||||
|
||||
try s.nextSlice("\x1b[23;1t");
|
||||
try testing.expectEqual(null, s.handler.op);
|
||||
try testing.expectEqual(null, s.handler.index);
|
||||
}
|
||||
|
||||
test "stream: CSI t pop title with index" {
|
||||
const H = struct {
|
||||
op: ?csi.TitlePushPop = null,
|
||||
|
||||
pub fn pushPopTitle(self: *@This(), op: csi.TitlePushPop) void {
|
||||
self.op = op;
|
||||
}
|
||||
index: ?u16 = null,
|
||||
|
||||
pub fn vt(
|
||||
self: *@This(),
|
||||
comptime action: anytype,
|
||||
value: anytype,
|
||||
comptime action: streampkg.Action.Tag,
|
||||
value: streampkg.Action.Value(action),
|
||||
) !void {
|
||||
_ = self;
|
||||
_ = action;
|
||||
_ = value;
|
||||
switch (action) {
|
||||
.title_pop => self.index = value,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var s: Stream(H) = .init(.{});
|
||||
|
||||
try s.nextSlice("\x1b[23;0;5t");
|
||||
try testing.expectEqual(csi.TitlePushPop{
|
||||
.op = .pop,
|
||||
.index = 5,
|
||||
}, s.handler.op.?);
|
||||
try testing.expectEqual(@as(u16, 5), s.handler.index.?);
|
||||
}
|
||||
|
||||
test "stream CSI W clear tab stops" {
|
||||
|
|
|
|||
|
|
@ -270,6 +270,8 @@ pub const StreamHandler = struct {
|
|||
.protected_mode_off => self.terminal.setProtectedMode(.off),
|
||||
.protected_mode_iso => self.terminal.setProtectedMode(.iso),
|
||||
.protected_mode_dec => self.terminal.setProtectedMode(.dec),
|
||||
.mouse_shift_capture => self.terminal.flags.mouse_shift_capture = if (value) .true else .false,
|
||||
.size_report => self.sendSizeReport(value),
|
||||
.xtversion => try self.reportXtversion(),
|
||||
.kitty_keyboard_query => try self.queryKittyKeyboard(),
|
||||
.kitty_keyboard_push => {
|
||||
|
|
@ -296,6 +298,11 @@ pub const StreamHandler = struct {
|
|||
.end_of_input => try self.endOfInput(),
|
||||
.end_hyperlink => try self.endHyperlink(),
|
||||
.decaln => try self.decaln(),
|
||||
|
||||
// Unimplemented
|
||||
.title_push,
|
||||
.title_pop,
|
||||
=> {},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -692,10 +699,6 @@ pub const StreamHandler = struct {
|
|||
}
|
||||
}
|
||||
|
||||
pub inline fn setMouseShiftCapture(self: *StreamHandler, v: bool) !void {
|
||||
self.terminal.flags.mouse_shift_capture = if (v) .true else .false;
|
||||
}
|
||||
|
||||
pub inline fn setAttribute(self: *StreamHandler, attr: terminal.Attribute) !void {
|
||||
switch (attr) {
|
||||
.unknown => |unk| log.warn("unimplemented or unknown SGR attribute: {any}", .{unk}),
|
||||
|
|
|
|||
Loading…
Reference in New Issue