mod(tui): create own terminal interface framework

This commit is contained in:
2024-11-04 22:27:45 +01:00
parent 0330b3a2f5
commit 14aab9ef50
9 changed files with 559 additions and 96 deletions

View File

@@ -1,8 +1,5 @@
const std = @import("std");
const posix = std.posix;
const fmt = std.fmt;
const log = std.log.scoped(.terminal);
pub const Size = struct {
@@ -26,20 +23,44 @@ pub const ReportMode = enum {
/// Gets number of rows and columns in the terminal
pub fn getTerminalSize() Size {
var ws: posix.winsize = undefined;
_ = posix.system.ioctl(posix.STDERR_FILENO, posix.T.IOCGWINSZ, &ws);
var ws: std.posix.winsize = undefined;
_ = std.posix.system.ioctl(std.posix.STDERR_FILENO, std.posix.T.IOCGWINSZ, &ws);
return .{ .cols = ws.ws_col, .rows = ws.ws_row };
}
pub fn saveScreen() !void {
_ = try std.posix.write(std.posix.STDERR_FILENO, "\x1b[?47h");
}
pub fn restoreScreen() !void {
_ = try std.posix.write(std.posix.STDERR_FILENO, "\x1b[?47l");
}
pub fn enterAltScreen() !void {
_ = try std.posix.write(std.posix.STDERR_FILENO, "\x1b[?1049h");
}
pub fn existAltScreen() !void {
_ = try std.posix.write(std.posix.STDERR_FILENO, "\x1b[?1049l");
}
pub fn clearScreen() !void {
_ = try std.posix.write(std.posix.STDERR_FILENO, "\x1b[2J");
}
pub fn setCursorPositionHome() !void {
_ = try std.posix.write(std.posix.STDERR_FILENO, "\x1b[H");
}
pub fn getCursorPosition() !Position {
// Needs Raw mode (no wait for \n) to work properly cause
// control sequence will not be written without it.
_ = try posix.write(posix.STDERR_FILENO, "\x1b[6n");
_ = try std.posix.write(std.posix.STDERR_FILENO, "\x1b[6n");
var buf: [64]u8 = undefined;
// format: \x1b, "[", R1,..., Rn, ";", C1, ..., Cn, "R"
const len = try posix.read(posix.STDIN_FILENO, &buf);
const len = try std.posix.read(std.posix.STDIN_FILENO, &buf);
if (!isCursorPosition(buf[0..len])) {
return error.InvalidValueReturned;
@@ -73,8 +94,8 @@ pub fn getCursorPosition() !Position {
}
return .{
.row = try fmt.parseInt(u16, row[0..ridx], 10),
.col = try fmt.parseInt(u16, col[0..cidx], 10),
.row = try std.fmt.parseInt(u16, row[0..ridx], 10),
.col = try std.fmt.parseInt(u16, col[0..cidx], 10),
};
}
@@ -102,8 +123,8 @@ pub fn isCursorPosition(buf: []u8) bool {
///
/// `bak`: pointer to store termios struct backup before
/// altering, this is used to disable raw mode.
pub fn enableRawMode(bak: *posix.termios) !void {
var termios = try posix.tcgetattr(posix.STDIN_FILENO);
pub fn enableRawMode(bak: *std.posix.termios) !void {
var termios = try std.posix.tcgetattr(std.posix.STDIN_FILENO);
bak.* = termios;
termios.iflag.IXON = false;
@@ -114,18 +135,18 @@ pub fn enableRawMode(bak: *posix.termios) !void {
termios.lflag.IEXTEN = false;
termios.lflag.ISIG = false;
try posix.tcsetattr(
posix.STDIN_FILENO,
posix.TCSA.FLUSH,
try std.posix.tcsetattr(
std.posix.STDIN_FILENO,
.FLUSH,
termios,
);
}
/// Reverts `enableRawMode` to restore initial functionality.
pub fn disableRawMode(bak: *posix.termios) !void {
try posix.tcsetattr(
posix.STDIN_FILENO,
posix.TCSA.FLUSH,
pub fn disableRawMode(bak: *std.posix.termios) !void {
try std.posix.tcsetattr(
std.posix.STDIN_FILENO,
.FLUSH,
bak.*,
);
}
@@ -134,12 +155,12 @@ pub fn disableRawMode(bak: *posix.termios) !void {
pub fn canSynchornizeOutput() !bool {
// Needs Raw mode (no wait for \n) to work properly cause
// control sequence will not be written without it.
_ = try posix.write(posix.STDERR_FILENO, "\x1b[?2026$p");
_ = try std.posix.write(std.posix.STDERR_FILENO, "\x1b[?2026$p");
var buf: [64]u8 = undefined;
// format: \x1b, "[", "?", "2", "0", "2", "6", ";", n, "$", "y"
const len = try posix.read(posix.STDIN_FILENO, &buf);
const len = try std.posix.read(std.posix.STDIN_FILENO, &buf);
if (!std.mem.eql(u8, buf[0..len], "\x1b[?2026;") or len < 9) {
return false;
}