widgets(terminal): implement escapes, fix scrollDown

This commit is contained in:
Tim Culverhouse 2024-06-11 10:27:18 -05:00
parent b688732f56
commit 05f52ed4ee
5 changed files with 128 additions and 44 deletions

View file

@ -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{

View file

@ -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);
}

View file

@ -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
View 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
View file