| import core.stdc.stdio: fwrite; //fwrite
|
| import std.stdio;
|
| import core.sys.posix.termios;
|
| import core.sys.posix.unistd; // has isatty()
|
|
|
|
|
| // https://www.cs.uleth.ca/~holzmann/C/system/ttyraw.c
|
| // https://zig.news/lhp/want-to-create-a-tui-application-the-basics-of-uncooked-terminal-io-17gm
|
| // https://gitlab.com/dejan/iv.d/-/blob/master/rawtty.d?ref_type=heads
|
| // https://github.com/adamdruppe/arsd/blob/master/terminal.d
|
|
|
| struct TUI {
|
| File ttyFile;
|
| termios origTermios; // original termios saved so that we can restore it at the end.
|
| termios tuiTermios; // termios structure modified for our needs.
|
| }
|
|
|
| void enterCookMode(ref TUI tui) {
|
| // Right before we return to the OS we change terminal settings back to where they were before we
|
| // started the application.
|
| if (tcsetattr(tui.ttyFile.fileno, TCSAFLUSH, &tui.origTermios) < 0) {
|
| writeln("Error - can't restore TTY");
|
| }
|
|
|
| write("\033[?1049l"); // Disable alternative buffer.
|
| write("\033[?47l"); // Restore screen.
|
| writeln("\033[u"); // Restore cursor position.
|
| }
|
|
|
| void main() {
|
| writeln("hello");
|
|
|
| TUI tui;
|
| tui.ttyFile = File("/dev/tty", "rw"); // open for writing
|
| // File automatically calls close
|
| writeln("fileD:", tui.ttyFile.fileno());
|
| writeln();
|
|
|
| if (!isatty(tui.ttyFile.fileno())) {
|
| return;
|
| }
|
|
|
| if (!isatty(stdin.fileno)) {
|
| return;
|
| }
|
|
|
| if (!isatty(stdout.fileno)) {
|
| return;
|
| }
|
|
|
| /*
|
| tcflag_t = uint;
|
| struct termios
|
| {
|
| tcflag_t c_iflag;
|
| tcflag_t c_oflag;
|
| tcflag_t c_cflag;
|
| tcflag_t c_lflag;
|
| cc_t c_line;
|
| cc_t[NCCS] c_cc;
|
| speed_t c_ispeed;
|
| speed_t c_ospeed;
|
| }
|
|
|
| */
|
|
|
| auto origTermios = termios();
|
|
|
| int iret = tcgetattr(tui.ttyFile.fileno, &origTermios);
|
| tui.origTermios = origTermios;
|
| writeln(origTermios);
|
|
|
| // Copy the original termios structure into a new value that we modify later below.
|
| auto tuiTermios = origTermios;
|
|
|
| writeln(tuiTermios.c_lflag);
|
| // ECHO: Stop the terminal from displaying pressed keys.
|
| // ICANON: Disable canonical ("cooked") input mode. Allows us to read inputs
|
| // byte-wise instead of line-wise.
|
| // ISIG: Disable signals for Ctrl-C (SIGINT) and Ctrl-Z (SIGTSTP), so we
|
| // can handle them as "normal" escape sequences.
|
| // IEXTEN: Disable input preprocessing. This allows us to handle Ctrl-V,
|
| // which would otherwise be intercepted by some terminals.
|
| tuiTermios.c_lflag &= ~(ECHO | ICANON | ISIG | IEXTEN);
|
| writeln(tuiTermios.c_lflag);
|
|
|
| writeln(tuiTermios.c_iflag);
|
| // IXON: Disable software control flow. This allows us to handle Ctrl-S
|
| // and Ctrl-Q.
|
| // ICRNL: Disable converting carriage returns to newlines. Allows us to
|
| // handle Ctrl-J and Ctrl-M.
|
| // BRKINT: Disable converting sending SIGINT on break conditions. Likely has
|
| // no effect on anything remotely modern.
|
| // INPCK: Disable parity checking. Likely has no effect on anything
|
| // remotely modern.
|
| // ISTRIP: Disable stripping the 8th bit of characters. Likely has no effect
|
| // on anything remotely modern.
|
| tuiTermios.c_iflag &= ~(IXON | ICRNL | BRKINT | INPCK | ISTRIP);
|
| writeln(tuiTermios.c_iflag);
|
|
|
| writeln(tuiTermios.c_oflag);
|
| // Disable output processing. Common output processing includes prefixing
|
| // newline with a carriage return.
|
| tuiTermios.c_oflag &= ~(OPOST);
|
| writeln(tuiTermios.c_oflag);
|
|
|
| // Set the character size to 8 bits per byte. Likely has no efffect on
|
| // anything remotely modern.
|
| tuiTermios.c_cflag |= CS8;
|
|
|
| /*
|
| Two fields of the termios structs cc member deserve special attention, vtime and vmin. These are used
|
| to control the read syscall when getting input from the terminal interface. The first sets the timeout,
|
| after which the syscall will return, the second the minimum amount of bytes it reads before returning.
|
| Note that timeout takes precedence over the minimum byte count.
|
| */
|
| tuiTermios.c_cc[VTIME] = 0;
|
| tuiTermios.c_cc[VMIN] = 1;
|
| tui.tuiTermios = tuiTermios;
|
|
|
| // Finally set the terminal attributes:
|
| if (tcsetattr(tui.ttyFile.fileno, TCSAFLUSH, &tuiTermios) < 0) {
|
| writeln("Error - the terminal can't enter the RAW mode.");
|
| }
|
|
|
| auto readBuf = new char[1];
|
| while (true) {
|
| auto buffer = tui.ttyFile.rawRead(readBuf);
|
| if (buffer[0] == 'q') {
|
| enterCookMode(tui);
|
| // exit
|
| return;
|
| } else if (buffer[0] == '\x1B') {
|
| writeln("input: excape\n");
|
| } else if (buffer[0] == '\n' || buffer[0] == '\r') {
|
| writeln("input: return\n");
|
| } else {
|
| writeln("input:", buffer[0]);
|
| }
|
| }
|
|
|
| enterCookMode(tui);
|
| }
|