const std = @import("std");
const os = std.os;
var origTermios: std.posix.termios = undefined;
var origTermios: os.termios = undefined;
pub fn main() anyerror!void {
const stdin = std.io.getStdIn();
defer disableRawMode(stdin);
errdefer disableRawMode(stdin);
enableRawMode(stdin) catch |err| {
disableRawMode(stdin);
std.log.err("unable to set termios state: {}", .{err});
std.posix.exit(1);
};
defer disableRawMode(stdin);
var ch: [1]u8 = undefined;
while (!std.mem.eql(u8, &ch, &.{'q'})) : (_ = try stdin.read(&ch)) {
if (std.ascii.isControl(ch[0])) {
std.log.debug("{d}\r", .{ch});
} else {
std.log.debug("{d} {c}\r", .{ ch, ch });
}
}
}
fn enableRawMode(stdin: std.fs.File) anyerror!void {
origTermios = try os.tcgetattr(stdin.handle);
origTermios = try std.posix.tcgetattr(stdin.handle);
var termState = origTermios;
var termState = origTermios;
try os.tcsetattr(stdin.handle, linux.TCSA.FLUSH, termState);
// These are the flags for setting up raw mode (see man 3 termios)
// The explanation is for the switched off state.
// ~ECHO: not-repeating every input back to output
// ~ECHONL:
// ~ICANON: read input by bytes, no need to press <Enter> after every line
// ~ISIG: send raw bytes instead of signals for the program (C-c, C-z etc.)
// ~IEXTEN: disable (C-v) literal signal sending
// ~IXON: disable flow control: suspend (C-s), resume (C-q)
// ~ICRNL: disable C-m rewrite from \r to \n (from 13 to 10)
// ~INLCR:
// ~BRKINT, ~IGNBRK, ~PARMRK: BREAK reads as null byte ('\0')
// ~ISTRIP: do not strip off 8th bit
// ~IGNCR: do not ignore carrige return
// ~OPOST: output post-processing: \n -> \r\n
// ~CSIZE:
// ~PARENB:
// only in snapdragon's kilo:
// ~INPCK: disable input parity check
// termState.lflag &= ~@as(linux.tcflag_t, linux.ECHO | linux.ECHONL | linux.ICANON |
// linux.ISIG | linux.IEXTEN);
// termState.iflag &= ~@as(linux.tcflag_t, linux.IXON | linux.ICRNL | linux.INLCR |
// linux.BRKINT | linux.IGNBRK | linux.PARMRK | linux.ISTRIP | linux.IGNCR);
// termState.oflag &= ~@as(linux.tcflag_t, linux.OPOST);
// termState.cflag &= ~@as(linux.tcflag_t, linux.CSIZE | linux.PARENB);
termState.lflag = std.posix.tc_lflag_t{
.ECHO = false,
.ECHONL = false,
.ICANON = false,
.ISIG = false,
.IEXTEN = false,
};
termState.iflag = std.posix.tc_iflag_t{
.IXON = false,
.ICRNL = false,
.INLCR = false,
.BRKINT = false,
.IGNBRK = false,
.PARMRK = false,
.ISTRIP = false,
.IGNCR = false,
};
termState.oflag = std.posix.tc_oflag_t{
.OPOST = false,
};
termState.cflag = std.posix.tc_cflag_t{
.CSIZE = .CS8,
.PARENB = false,
};
// CS8 bit mask sets up character size to 8 bit/byte
// termState.cflag |= linux.CS8;
try std.posix.tcsetattr(stdin.handle, std.posix.TCSA.FLUSH, termState);
}
fn disableRawMode(stdin: std.fs.File) void {
std.posix.tcsetattr(stdin.handle, std.posix.TCSA.FLUSH, origTermios) catch {};
}
fn disableRawMode(stdin: std.fs.File) void {
os.tcsetattr(stdin.handle, linux.TCSA.FLUSH, origTermios) catch |err| {
std.debug.print("{s}", .{err});
return;
};
}