diff --git a/ke/Makefile b/ke/Makefile index c5cceb4..87d40de 100644 --- a/ke/Makefile +++ b/ke/Makefile @@ -1,5 +1,5 @@ BIN := ke -OBJS := main.o +OBJS := main.o terminal.o util.o LDFLAGS := -static CFLAGS := -pedantic -Wall -Werror -Wextra -O2 -std=c99 @@ -16,6 +16,7 @@ clean: .PHONY: run run: $(BIN) + reset ./$(BIN) %.o: %.c diff --git a/ke/main.c b/ke/main.c index 0f80869..2baaffc 100644 --- a/ke/main.c +++ b/ke/main.c @@ -1,68 +1,62 @@ #include +#include #include #include #include #include - -/* entry_term contains a snapshot of the termios settings at startup. */ -struct termios entry_term; +#include "terminal.h" +#include "util.h" -/* - * A text editor needs the terminal to be in raw mode; but the default - * is to be in canonical (cooked) mode, which is a buffered input mode. - */ - -static void -enable_termraw() +void +loop() { - struct termios raw; + char c; + int command = 0; - /* Read the current terminal parameters for standard input. */ - tcgetattr(STDIN_FILENO, &raw); + while (1) { + c = '\0'; + /* + * Note that Cygwin, apparently, will treat a read time- + * out as an error with errno == EAGAIN. I don't really + * have any interest in supporting code for Cygwin. + */ + if ((read(STDIN_FILENO, &c, 1) == -1)) { + die("failed to read from the terminal"); + } - /* - * Turn off the local ECHO mode, which we need to do in raw mode - * because what gets displayed is going to need extra control. - * - * NOTE(kyle): look into cfmakeraw, which will require - * snapshotting. - */ - raw.c_lflag &= ~(ECHO|ICANON); + if (c == 0x00) { + continue; + } - /* - * Now write the terminal parameters to the current terminal, - * after flushing any waiting input out. - */ - tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw); -} + if (command && ((c == 'q') || (c == 0x11))) { + break; + } + if (c == 0x0b) { + if (!command) { + command = 1; + continue; + } + } else { + command = 0; + } -static void -disable_termraw() -{ - tcsetattr(STDIN_FILENO, TCSAFLUSH, &entry_term); + if (iscntrl(c)) { + printf("%02x\r\n", c); + } else { + printf("%02x ('%c')\r\n", c, c); + } + } } int main() { - char c; - - /* prepare the terminal */ - tcgetattr(STDIN_FILENO, &entry_term); - atexit(disable_termraw); - enable_termraw(); - - while (read(STDIN_FILENO, &c, 1) == 1 && c != 'q') { - if (iscntrl(c)) { - printf("%02x\n", c); - } else { - printf("%02x ('%c')\n", c, c); - } - } + setup_terminal(); + loop(); return 0; } diff --git a/ke/terminal.c b/ke/terminal.c new file mode 100644 index 0000000..0adf61c --- /dev/null +++ b/ke/terminal.c @@ -0,0 +1,70 @@ +#include +#include +#include + +#include "util.h" + + +static struct termios entry_term; + + +/* + * A text editor needs the terminal to be in raw mode; but the default + * is to be in canonical (cooked) mode, which is a buffered input mode. + */ + +static void +enable_termraw() +{ + struct termios raw; + + /* Read the current terminal parameters for standard input. */ + if (tcgetattr(STDIN_FILENO, &raw) == -1) { + die("tcgetattr while enabling raw mode"); + } + + /* + * Put the terminal into raw mode. + */ + cfmakeraw(&raw); + + /* + * Set timeout for read(2). + * + * VMIN: what is the minimum number of bytes required for read + * to return? + * + * VTIME: max time before read(2) returns in hundreds of milli- + * seconds. + */ + raw.c_cc[VMIN] = 0; + raw.c_cc[VTIME] = 1; + + /* + * Now write the terminal parameters to the current terminal, + * after flushing any waiting input out. + */ + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) { + die("tcsetattr while enabling raw mode"); + } +} + + +static void +disable_termraw() +{ + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &entry_term) == -1) { + die("couldn't disable terminal raw mode"); + } +} + + +void +setup_terminal() +{ + if (tcgetattr(STDIN_FILENO, &entry_term) == -1) { + die("can't snapshot terminal settings"); + } + atexit(disable_termraw); + enable_termraw(); +} diff --git a/ke/terminal.h b/ke/terminal.h new file mode 100644 index 0000000..8d4b472 --- /dev/null +++ b/ke/terminal.h @@ -0,0 +1,8 @@ +#ifndef KE_TERMINAL_H +#define KE_TERMINAL_H + + +void setup_terminal(); + + +#endif /* KE_TERMINAL_H */ diff --git a/ke/util.c b/ke/util.c new file mode 100644 index 0000000..446297e --- /dev/null +++ b/ke/util.c @@ -0,0 +1,10 @@ +#include +#include + + +void +die(const char *s) +{ + perror(s); + exit(1); +} diff --git a/ke/util.h b/ke/util.h new file mode 100644 index 0000000..f27925b --- /dev/null +++ b/ke/util.h @@ -0,0 +1,8 @@ +#ifndef KE_UTIL_H +#define KE_UTIL_H + + +void die(const char *s); + + +#endif /* KE_UTIL_H */