widgets(terminal): implement escapes, fix scrollDown
This commit is contained in:
parent
b688732f56
commit
05f52ed4ee
5 changed files with 128 additions and 44 deletions
|
@ -47,9 +47,6 @@ pub fn spawn(self: *Command, allocator: std.mem.Allocator) !void {
|
||||||
// exec
|
// exec
|
||||||
const err = std.posix.execvpeZ(argv_buf.ptr[0].?, argv_buf.ptr, envp);
|
const err = std.posix.execvpeZ(argv_buf.ptr[0].?, argv_buf.ptr, envp);
|
||||||
_ = err catch {};
|
_ = err catch {};
|
||||||
@panic("a");
|
|
||||||
// const EOT = "\x04";
|
|
||||||
// _ = std.posix.write(self.pty.tty, EOT) catch {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var act = posix.Sigaction{
|
var act = posix.Sigaction{
|
||||||
|
|
|
@ -166,11 +166,11 @@ pub fn print(
|
||||||
self: *Screen,
|
self: *Screen,
|
||||||
grapheme: []const u8,
|
grapheme: []const u8,
|
||||||
width: u8,
|
width: u8,
|
||||||
) void {
|
wrap: bool,
|
||||||
// TODO: wrap mode handling
|
) !void {
|
||||||
if (self.cursor.col + width > self.width) {
|
if (self.cursor.pending_wrap) {
|
||||||
self.cursor.col = 0;
|
try self.index();
|
||||||
self.cursor.row += 1;
|
self.cursor.col = self.scrolling_region.left;
|
||||||
}
|
}
|
||||||
if (self.cursor.col >= self.width) return;
|
if (self.cursor.col >= self.width) return;
|
||||||
if (self.cursor.row >= self.height) return;
|
if (self.cursor.row >= self.height) return;
|
||||||
|
@ -195,6 +195,7 @@ pub fn print(
|
||||||
self.buf[i].width = width;
|
self.buf[i].width = width;
|
||||||
self.buf[i].dirty = true;
|
self.buf[i].dirty = true;
|
||||||
|
|
||||||
|
if (wrap and self.cursor.col >= self.width - 1) self.cursor.pending_wrap = true;
|
||||||
self.cursor.col += width;
|
self.cursor.col += width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,6 +334,7 @@ pub fn cursorLeft(self: *Screen, n: usize) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cursorRight(self: *Screen, n: usize) void {
|
pub fn cursorRight(self: *Screen, n: usize) void {
|
||||||
|
self.cursor.pending_wrap = false;
|
||||||
if (self.withinScrollingRegion())
|
if (self.withinScrollingRegion())
|
||||||
self.cursor.col = @min(
|
self.cursor.col = @min(
|
||||||
self.cursor.col + n,
|
self.cursor.col + n,
|
||||||
|
@ -341,7 +343,7 @@ pub fn cursorRight(self: *Screen, n: usize) void {
|
||||||
else
|
else
|
||||||
self.cursor.col = @min(
|
self.cursor.col = @min(
|
||||||
self.cursor.col + n,
|
self.cursor.col + n,
|
||||||
self.width,
|
self.width - 1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,26 +420,30 @@ pub fn deleteLine(self: *Screen, n: usize) !void {
|
||||||
pub fn insertLine(self: *Screen, n: usize) !void {
|
pub fn insertLine(self: *Screen, n: usize) !void {
|
||||||
if (n == 0) return;
|
if (n == 0) return;
|
||||||
|
|
||||||
|
self.cursor.pending_wrap = false;
|
||||||
// Don't insert if outside scroll region
|
// Don't insert if outside scroll region
|
||||||
if (!self.withinScrollingRegion()) return;
|
if (!self.withinScrollingRegion()) return;
|
||||||
|
|
||||||
self.cursor.pending_wrap = false;
|
const adjusted_n = @min(self.scrolling_region.bottom - self.cursor.row, n);
|
||||||
|
const stride = (self.width) * adjusted_n;
|
||||||
// Number of rows from here to top of scroll region or n
|
|
||||||
const cnt = @min(self.cursor.row - self.scrolling_region.top + 1, n);
|
|
||||||
const stride = (self.width) * cnt;
|
|
||||||
|
|
||||||
var row: usize = self.scrolling_region.bottom;
|
var row: usize = self.scrolling_region.bottom;
|
||||||
while (row > self.scrolling_region.top) : (row -= 1) {
|
while (row >= self.scrolling_region.top + adjusted_n) : (row -|= 1) {
|
||||||
var col: usize = self.scrolling_region.left;
|
var col: usize = self.scrolling_region.left;
|
||||||
while (col <= self.scrolling_region.right) : (col += 1) {
|
while (col <= self.scrolling_region.right) : (col += 1) {
|
||||||
const i = (row * self.width) + col;
|
const i = (row * self.width) + col;
|
||||||
if (row - cnt < self.scrolling_region.top)
|
|
||||||
self.buf[i].erase(self.cursor.style.bg)
|
|
||||||
else
|
|
||||||
try self.buf[i].copyFrom(self.buf[i - stride]);
|
try self.buf[i].copyFrom(self.buf[i - stride]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
row = self.scrolling_region.top;
|
||||||
|
while (row < self.scrolling_region.top + adjusted_n) : (row += 1) {
|
||||||
|
var col: usize = self.scrolling_region.left;
|
||||||
|
while (col <= self.scrolling_region.right) : (col += 1) {
|
||||||
|
const i = (row * self.width) + col;
|
||||||
|
self.buf[i].erase(self.cursor.style.bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eraseBelow(self: *Screen) void {
|
pub fn eraseBelow(self: *Screen) void {
|
||||||
|
@ -480,3 +486,26 @@ pub fn deleteCharacters(self: *Screen, n: usize) !void {
|
||||||
self.buf[col].erase(self.cursor.style.bg);
|
self.buf[col].erase(self.cursor.style.bg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn reverseIndex(self: *Screen) !void {
|
||||||
|
if (self.cursor.row != self.scrolling_region.top or
|
||||||
|
self.cursor.col < self.scrolling_region.left or
|
||||||
|
self.cursor.col > self.scrolling_region.right)
|
||||||
|
self.cursorUp(1)
|
||||||
|
else
|
||||||
|
try self.scrollDown(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scrollDown(self: *Screen, n: usize) !void {
|
||||||
|
const cur_row = self.cursor.row;
|
||||||
|
const cur_col = self.cursor.col;
|
||||||
|
const wrap = self.cursor.pending_wrap;
|
||||||
|
defer {
|
||||||
|
self.cursor.row = cur_row;
|
||||||
|
self.cursor.col = cur_col;
|
||||||
|
self.cursor.pending_wrap = wrap;
|
||||||
|
}
|
||||||
|
self.cursor.col = self.scrolling_region.left;
|
||||||
|
self.cursor.row = self.scrolling_region.top;
|
||||||
|
try self.insertLine(n);
|
||||||
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ pub const Options = struct {
|
||||||
|
|
||||||
pub const Mode = struct {
|
pub const Mode = struct {
|
||||||
origin: bool = false,
|
origin: bool = false,
|
||||||
|
autowrap: bool = true,
|
||||||
cursor: bool = true,
|
cursor: bool = true,
|
||||||
sync: bool = false,
|
sync: bool = false,
|
||||||
};
|
};
|
||||||
|
@ -203,10 +204,10 @@ pub fn draw(self: *Terminal, win: vaxis.Window) !void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.mode.cursor) {
|
// if (self.mode.cursor) {
|
||||||
win.setCursorShape(self.front_screen.cursor.shape);
|
win.setCursorShape(self.front_screen.cursor.shape);
|
||||||
win.showCursor(self.front_screen.cursor.col, self.front_screen.cursor.row);
|
win.showCursor(self.front_screen.cursor.col, self.front_screen.cursor.row);
|
||||||
} else win.hideCursor();
|
// } else win.hideCursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tryEvent(self: *Terminal) ?Event {
|
pub fn tryEvent(self: *Terminal) ?Event {
|
||||||
|
@ -265,11 +266,35 @@ fn run(self: *Terminal) !void {
|
||||||
const gr = g.bytes(str);
|
const gr = g.bytes(str);
|
||||||
// TODO: use actual instead of .unicode
|
// TODO: use actual instead of .unicode
|
||||||
const w = try vaxis.gwidth.gwidth(gr, .unicode, &self.unicode.width_data);
|
const w = try vaxis.gwidth.gwidth(gr, .unicode, &self.unicode.width_data);
|
||||||
self.back_screen.print(gr, @truncate(w));
|
try self.back_screen.print(gr, @truncate(w), self.mode.autowrap);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.c0 => |b| try self.handleC0(b),
|
.c0 => |b| try self.handleC0(b),
|
||||||
.escape => |_| {}, // std.log.err("unhandled escape: {s}", .{str}),
|
.escape => |esc| {
|
||||||
|
const final = esc[esc.len - 1];
|
||||||
|
switch (final) {
|
||||||
|
'B' => {}, // TODO: handle charsets
|
||||||
|
// Index
|
||||||
|
'D' => try self.back_screen.index(),
|
||||||
|
// Next Line
|
||||||
|
'E' => {
|
||||||
|
try self.back_screen.index();
|
||||||
|
self.carriageReturn();
|
||||||
|
},
|
||||||
|
// Horizontal Tab Set
|
||||||
|
'H' => {
|
||||||
|
const already_set: bool = for (self.tab_stops.items) |ts| {
|
||||||
|
if (ts == self.back_screen.cursor.col) break true;
|
||||||
|
} else false;
|
||||||
|
if (already_set) continue;
|
||||||
|
try self.tab_stops.append(@truncate(self.back_screen.cursor.col));
|
||||||
|
std.mem.sort(u16, self.tab_stops.items, {}, std.sort.asc(u16));
|
||||||
|
},
|
||||||
|
// Reverse Index
|
||||||
|
'M' => try self.back_screen.reverseIndex(),
|
||||||
|
else => std.log.err("unhandled escape: {s}", .{esc}),
|
||||||
|
}
|
||||||
|
},
|
||||||
.ss2 => |ss2| std.log.err("unhandled ss2: {c}", .{ss2}),
|
.ss2 => |ss2| std.log.err("unhandled ss2: {c}", .{ss2}),
|
||||||
.ss3 => |ss3| std.log.err("unhandled ss3: {c}", .{ss3}),
|
.ss3 => |ss3| std.log.err("unhandled ss3: {c}", .{ss3}),
|
||||||
.csi => |seq| {
|
.csi => |seq| {
|
||||||
|
@ -288,7 +313,6 @@ fn run(self: *Terminal) !void {
|
||||||
},
|
},
|
||||||
// Cursor Right
|
// Cursor Right
|
||||||
'C' => {
|
'C' => {
|
||||||
self.back_screen.cursor.pending_wrap = false;
|
|
||||||
var iter = seq.iterator(u16);
|
var iter = seq.iterator(u16);
|
||||||
const delta = iter.next() orelse 1;
|
const delta = iter.next() orelse 1;
|
||||||
self.back_screen.cursorRight(delta);
|
self.back_screen.cursorRight(delta);
|
||||||
|
@ -318,6 +342,11 @@ fn run(self: *Terminal) !void {
|
||||||
var iter = seq.iterator(u16);
|
var iter = seq.iterator(u16);
|
||||||
const col = iter.next() orelse 1;
|
const col = iter.next() orelse 1;
|
||||||
self.back_screen.cursor.col = col -| 1;
|
self.back_screen.cursor.col = col -| 1;
|
||||||
|
if (self.back_screen.cursor.col < self.back_screen.scrolling_region.left)
|
||||||
|
self.back_screen.cursor.col = self.back_screen.scrolling_region.left;
|
||||||
|
if (self.back_screen.cursor.col > self.back_screen.scrolling_region.right)
|
||||||
|
self.back_screen.cursor.col = self.back_screen.scrolling_region.right;
|
||||||
|
self.back_screen.cursor.pending_wrap = false;
|
||||||
},
|
},
|
||||||
// Cursor Absolute Position
|
// Cursor Absolute Position
|
||||||
'H', 'f' => {
|
'H', 'f' => {
|
||||||
|
@ -326,6 +355,7 @@ fn run(self: *Terminal) !void {
|
||||||
const col = iter.next() orelse 1;
|
const col = iter.next() orelse 1;
|
||||||
self.back_screen.cursor.col = col -| 1;
|
self.back_screen.cursor.col = col -| 1;
|
||||||
self.back_screen.cursor.row = row -| 1;
|
self.back_screen.cursor.row = row -| 1;
|
||||||
|
self.back_screen.cursor.pending_wrap = false;
|
||||||
},
|
},
|
||||||
// Cursor Horizontal Tab
|
// Cursor Horizontal Tab
|
||||||
'I' => {
|
'I' => {
|
||||||
|
@ -396,17 +426,7 @@ fn run(self: *Terminal) !void {
|
||||||
'T' => {
|
'T' => {
|
||||||
var iter = seq.iterator(u16);
|
var iter = seq.iterator(u16);
|
||||||
const n = iter.next() orelse 1;
|
const n = iter.next() orelse 1;
|
||||||
const cur_row = self.back_screen.cursor.row;
|
try self.back_screen.scrollDown(n);
|
||||||
const cur_col = self.back_screen.cursor.col;
|
|
||||||
const wrap = self.back_screen.cursor.pending_wrap;
|
|
||||||
defer {
|
|
||||||
self.back_screen.cursor.row = cur_row;
|
|
||||||
self.back_screen.cursor.col = cur_col;
|
|
||||||
self.back_screen.cursor.pending_wrap = wrap;
|
|
||||||
}
|
|
||||||
self.back_screen.cursor.col = self.back_screen.scrolling_region.left;
|
|
||||||
self.back_screen.cursor.row = self.back_screen.scrolling_region.top;
|
|
||||||
try self.back_screen.insertLine(n);
|
|
||||||
},
|
},
|
||||||
// Tab Control
|
// Tab Control
|
||||||
'W' => {
|
'W' => {
|
||||||
|
@ -464,7 +484,7 @@ fn run(self: *Terminal) !void {
|
||||||
const w = try vaxis.gwidth.gwidth(self.last_printed, .unicode, &self.unicode.width_data);
|
const w = try vaxis.gwidth.gwidth(self.last_printed, .unicode, &self.unicode.width_data);
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while (i < n) : (i += 1) {
|
while (i < n) : (i += 1) {
|
||||||
self.back_screen.print(self.last_printed, @truncate(w));
|
try self.back_screen.print(self.last_printed, @truncate(w), self.mode.autowrap);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Device Attributes
|
// Device Attributes
|
||||||
|
@ -483,20 +503,25 @@ fn run(self: *Terminal) !void {
|
||||||
},
|
},
|
||||||
// Cursor Vertical Position Absolute
|
// Cursor Vertical Position Absolute
|
||||||
'd' => {
|
'd' => {
|
||||||
|
self.back_screen.cursor.pending_wrap = false;
|
||||||
var iter = seq.iterator(u16);
|
var iter = seq.iterator(u16);
|
||||||
const n = iter.next() orelse 1;
|
const n = iter.next() orelse 1;
|
||||||
|
const max = if (self.mode.origin)
|
||||||
|
self.back_screen.scrolling_region.bottom
|
||||||
|
else
|
||||||
|
self.back_screen.height -| 1;
|
||||||
self.back_screen.cursor.pending_wrap = false;
|
self.back_screen.cursor.pending_wrap = false;
|
||||||
self.back_screen.cursor.row = @min(
|
self.back_screen.cursor.row = @min(
|
||||||
self.back_screen.height -| 1,
|
max,
|
||||||
n -| 1,
|
n -| 1,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
// Cursor Horizontal Position Absolute
|
// Cursor Vertical Position Absolute
|
||||||
'e' => {
|
'e' => {
|
||||||
var iter = seq.iterator(u16);
|
var iter = seq.iterator(u16);
|
||||||
const n = iter.next() orelse 1;
|
const n = iter.next() orelse 1;
|
||||||
self.back_screen.cursor.pending_wrap = false;
|
self.back_screen.cursor.pending_wrap = false;
|
||||||
self.back_screen.cursor.col = @min(
|
self.back_screen.cursor.row = @min(
|
||||||
self.back_screen.width -| 1,
|
self.back_screen.width -| 1,
|
||||||
n -| 1,
|
n -| 1,
|
||||||
);
|
);
|
||||||
|
@ -601,8 +626,16 @@ fn run(self: *Terminal) !void {
|
||||||
var iter = seq.iterator(u16);
|
var iter = seq.iterator(u16);
|
||||||
const top = iter.next() orelse 1;
|
const top = iter.next() orelse 1;
|
||||||
const bottom = iter.next() orelse self.back_screen.height;
|
const bottom = iter.next() orelse self.back_screen.height;
|
||||||
self.back_screen.scrolling_region.top = top - 1;
|
self.back_screen.scrolling_region.top = top -| 1;
|
||||||
self.back_screen.scrolling_region.bottom = bottom - 1;
|
self.back_screen.scrolling_region.bottom = bottom -| 1;
|
||||||
|
self.back_screen.cursor.pending_wrap = false;
|
||||||
|
if (self.mode.origin) {
|
||||||
|
self.back_screen.cursor.col = self.back_screen.scrolling_region.left;
|
||||||
|
self.back_screen.cursor.row = self.back_screen.scrolling_region.top;
|
||||||
|
} else {
|
||||||
|
self.back_screen.cursor.col = 0;
|
||||||
|
self.back_screen.cursor.row = 0;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
else => std.log.err("unhandled CSI: {}", .{seq}),
|
else => std.log.err("unhandled CSI: {}", .{seq}),
|
||||||
}
|
}
|
||||||
|
@ -648,6 +681,7 @@ inline fn handleC0(self: *Terminal, b: ansi.C0) !void {
|
||||||
|
|
||||||
pub fn setMode(self: *Terminal, mode: u16, val: bool) void {
|
pub fn setMode(self: *Terminal, mode: u16, val: bool) void {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
|
7 => self.mode.autowrap = val,
|
||||||
25 => self.mode.cursor = val,
|
25 => self.mode.cursor = val,
|
||||||
1049 => {
|
1049 => {
|
||||||
if (val)
|
if (val)
|
||||||
|
@ -703,7 +737,7 @@ pub fn horizontalTab(self: *Terminal, n: usize) void {
|
||||||
} else self.back_screen.width - 1;
|
} else self.back_screen.width - 1;
|
||||||
|
|
||||||
// Move right the delta
|
// Move right the delta
|
||||||
self.back_screen.cursorRight(final - col);
|
self.back_screen.cursorRight(final -| col);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn horizontalBackTab(self: *Terminal, n: usize) void {
|
pub fn horizontalBackTab(self: *Terminal, n: usize) void {
|
||||||
|
|
24
su.sh
Executable file
24
su.sh
Executable file
|
@ -0,0 +1,24 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
printf "\x1b[H"
|
||||||
|
printf "\x1b[2J"
|
||||||
|
|
||||||
|
printf "AAAA\n"
|
||||||
|
printf "BBBB\n"
|
||||||
|
printf "CCCC\n"
|
||||||
|
printf "DDDD\n"
|
||||||
|
printf "EEEE\n"
|
||||||
|
printf "FFFF\n"
|
||||||
|
printf "GGGG\n"
|
||||||
|
printf "HHHH\n"
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
printf "\x1b[T"
|
||||||
|
sleep 3
|
||||||
|
printf "\x1b[S"
|
||||||
|
sleep 3
|
||||||
|
printf "\x1b[2;4r"
|
||||||
|
printf "\x1b[T"
|
||||||
|
sleep 3
|
||||||
|
printf "\x1b[S"
|
||||||
|
sleep 3
|
0
vaxis.log2
Normal file
0
vaxis.log2
Normal file
Loading…
Reference in a new issue