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
|
||||
const err = std.posix.execvpeZ(argv_buf.ptr[0].?, argv_buf.ptr, envp);
|
||||
_ = err catch {};
|
||||
@panic("a");
|
||||
// const EOT = "\x04";
|
||||
// _ = std.posix.write(self.pty.tty, EOT) catch {};
|
||||
}
|
||||
|
||||
var act = posix.Sigaction{
|
||||
|
|
|
@ -166,11 +166,11 @@ pub fn print(
|
|||
self: *Screen,
|
||||
grapheme: []const u8,
|
||||
width: u8,
|
||||
) void {
|
||||
// TODO: wrap mode handling
|
||||
if (self.cursor.col + width > self.width) {
|
||||
self.cursor.col = 0;
|
||||
self.cursor.row += 1;
|
||||
wrap: bool,
|
||||
) !void {
|
||||
if (self.cursor.pending_wrap) {
|
||||
try self.index();
|
||||
self.cursor.col = self.scrolling_region.left;
|
||||
}
|
||||
if (self.cursor.col >= self.width) return;
|
||||
if (self.cursor.row >= self.height) return;
|
||||
|
@ -195,6 +195,7 @@ pub fn print(
|
|||
self.buf[i].width = width;
|
||||
self.buf[i].dirty = true;
|
||||
|
||||
if (wrap and self.cursor.col >= self.width - 1) self.cursor.pending_wrap = true;
|
||||
self.cursor.col += width;
|
||||
}
|
||||
|
||||
|
@ -333,6 +334,7 @@ pub fn cursorLeft(self: *Screen, n: usize) void {
|
|||
}
|
||||
|
||||
pub fn cursorRight(self: *Screen, n: usize) void {
|
||||
self.cursor.pending_wrap = false;
|
||||
if (self.withinScrollingRegion())
|
||||
self.cursor.col = @min(
|
||||
self.cursor.col + n,
|
||||
|
@ -341,7 +343,7 @@ pub fn cursorRight(self: *Screen, n: usize) void {
|
|||
else
|
||||
self.cursor.col = @min(
|
||||
self.cursor.col + n,
|
||||
self.width,
|
||||
self.width - 1,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -418,24 +420,28 @@ pub fn deleteLine(self: *Screen, n: usize) !void {
|
|||
pub fn insertLine(self: *Screen, n: usize) !void {
|
||||
if (n == 0) return;
|
||||
|
||||
self.cursor.pending_wrap = false;
|
||||
// Don't insert if outside scroll region
|
||||
if (!self.withinScrollingRegion()) return;
|
||||
|
||||
self.cursor.pending_wrap = false;
|
||||
|
||||
// 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;
|
||||
const adjusted_n = @min(self.scrolling_region.bottom - self.cursor.row, n);
|
||||
const stride = (self.width) * adjusted_n;
|
||||
|
||||
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;
|
||||
while (col <= self.scrolling_region.right) : (col += 1) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -480,3 +486,26 @@ pub fn deleteCharacters(self: *Screen, n: usize) !void {
|
|||
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 {
|
||||
origin: bool = false,
|
||||
autowrap: bool = true,
|
||||
cursor: bool = true,
|
||||
sync: bool = false,
|
||||
};
|
||||
|
@ -203,10 +204,10 @@ pub fn draw(self: *Terminal, win: vaxis.Window) !void {
|
|||
}
|
||||
}
|
||||
|
||||
if (self.mode.cursor) {
|
||||
win.setCursorShape(self.front_screen.cursor.shape);
|
||||
win.showCursor(self.front_screen.cursor.col, self.front_screen.cursor.row);
|
||||
} else win.hideCursor();
|
||||
// if (self.mode.cursor) {
|
||||
win.setCursorShape(self.front_screen.cursor.shape);
|
||||
win.showCursor(self.front_screen.cursor.col, self.front_screen.cursor.row);
|
||||
// } else win.hideCursor();
|
||||
}
|
||||
|
||||
pub fn tryEvent(self: *Terminal) ?Event {
|
||||
|
@ -265,11 +266,35 @@ fn run(self: *Terminal) !void {
|
|||
const gr = g.bytes(str);
|
||||
// TODO: use actual instead of .unicode
|
||||
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),
|
||||
.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}),
|
||||
.ss3 => |ss3| std.log.err("unhandled ss3: {c}", .{ss3}),
|
||||
.csi => |seq| {
|
||||
|
@ -288,7 +313,6 @@ fn run(self: *Terminal) !void {
|
|||
},
|
||||
// Cursor Right
|
||||
'C' => {
|
||||
self.back_screen.cursor.pending_wrap = false;
|
||||
var iter = seq.iterator(u16);
|
||||
const delta = iter.next() orelse 1;
|
||||
self.back_screen.cursorRight(delta);
|
||||
|
@ -318,6 +342,11 @@ fn run(self: *Terminal) !void {
|
|||
var iter = seq.iterator(u16);
|
||||
const col = iter.next() orelse 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
|
||||
'H', 'f' => {
|
||||
|
@ -326,6 +355,7 @@ fn run(self: *Terminal) !void {
|
|||
const col = iter.next() orelse 1;
|
||||
self.back_screen.cursor.col = col -| 1;
|
||||
self.back_screen.cursor.row = row -| 1;
|
||||
self.back_screen.cursor.pending_wrap = false;
|
||||
},
|
||||
// Cursor Horizontal Tab
|
||||
'I' => {
|
||||
|
@ -396,17 +426,7 @@ fn run(self: *Terminal) !void {
|
|||
'T' => {
|
||||
var iter = seq.iterator(u16);
|
||||
const n = iter.next() orelse 1;
|
||||
const cur_row = self.back_screen.cursor.row;
|
||||
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);
|
||||
try self.back_screen.scrollDown(n);
|
||||
},
|
||||
// Tab Control
|
||||
'W' => {
|
||||
|
@ -464,7 +484,7 @@ fn run(self: *Terminal) !void {
|
|||
const w = try vaxis.gwidth.gwidth(self.last_printed, .unicode, &self.unicode.width_data);
|
||||
var i: usize = 0;
|
||||
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
|
||||
|
@ -483,20 +503,25 @@ fn run(self: *Terminal) !void {
|
|||
},
|
||||
// Cursor Vertical Position Absolute
|
||||
'd' => {
|
||||
self.back_screen.cursor.pending_wrap = false;
|
||||
var iter = seq.iterator(u16);
|
||||
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.row = @min(
|
||||
self.back_screen.height -| 1,
|
||||
max,
|
||||
n -| 1,
|
||||
);
|
||||
},
|
||||
// Cursor Horizontal Position Absolute
|
||||
// Cursor Vertical Position Absolute
|
||||
'e' => {
|
||||
var iter = seq.iterator(u16);
|
||||
const n = iter.next() orelse 1;
|
||||
self.back_screen.cursor.pending_wrap = false;
|
||||
self.back_screen.cursor.col = @min(
|
||||
self.back_screen.cursor.row = @min(
|
||||
self.back_screen.width -| 1,
|
||||
n -| 1,
|
||||
);
|
||||
|
@ -601,8 +626,16 @@ fn run(self: *Terminal) !void {
|
|||
var iter = seq.iterator(u16);
|
||||
const top = iter.next() orelse 1;
|
||||
const bottom = iter.next() orelse self.back_screen.height;
|
||||
self.back_screen.scrolling_region.top = top - 1;
|
||||
self.back_screen.scrolling_region.bottom = bottom - 1;
|
||||
self.back_screen.scrolling_region.top = top -| 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}),
|
||||
}
|
||||
|
@ -648,6 +681,7 @@ inline fn handleC0(self: *Terminal, b: ansi.C0) !void {
|
|||
|
||||
pub fn setMode(self: *Terminal, mode: u16, val: bool) void {
|
||||
switch (mode) {
|
||||
7 => self.mode.autowrap = val,
|
||||
25 => self.mode.cursor = val,
|
||||
1049 => {
|
||||
if (val)
|
||||
|
@ -703,7 +737,7 @@ pub fn horizontalTab(self: *Terminal, n: usize) void {
|
|||
} else self.back_screen.width - 1;
|
||||
|
||||
// Move right the delta
|
||||
self.back_screen.cursorRight(final - col);
|
||||
self.back_screen.cursorRight(final -| col);
|
||||
}
|
||||
|
||||
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