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