#include "koh_term.h" #include #include #include #include #include static struct termios koh_saved; static int koh_have_saved = 0; int koh_term_raw(int enable) { if (enable) { if (!isatty(STDIN_FILENO)) { return 0; } if (tcgetattr(STDIN_FILENO, &koh_saved) != 0) { return 0; } koh_have_saved = 1; struct termios raw = koh_saved; /* No line buffering, no echo, no flow control, no signal keys: the * view wants the bytes and has to be able to clean up after itself. */ raw.c_lflag &= (tcflag_t) ~(ICANON | ECHO | ISIG | IEXTEN); raw.c_iflag &= (tcflag_t) ~(IXON | ICRNL | BRKINT | INPCK | ISTRIP); raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; return tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == 0; } if (koh_have_saved) { tcsetattr(STDIN_FILENO, TCSAFLUSH, &koh_saved); koh_have_saved = 0; } return 1; } int koh_term_size(int *rows, int *cols) { struct winsize size; if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) == 0 && size.ws_row > 0 && size.ws_col > 0) { *rows = (int)size.ws_row; *cols = (int)size.ws_col; return 1; } *rows = 24; *cols = 80; return 0; } static int koh_poll_input(int milliseconds) { struct pollfd p; p.fd = STDIN_FILENO; p.events = POLLIN; p.revents = 0; return poll(&p, 1, milliseconds) > 0; } int koh_read_key(char *buf, int cap) { if (cap <= 0) { return 0; } ssize_t n = read(STDIN_FILENO, buf, 1); if (n <= 0) { return 0; } int total = 1; /* An arrow key arrives as ESC [ A, usually in one burst but not always, * so give the rest of the sequence a moment to show up. */ if (buf[0] == 0x1b) { if (koh_poll_input(25)) { ssize_t m = read(STDIN_FILENO, buf + 1, (size_t)(cap - 1)); if (m > 0) { total += (int)m; } } return total; } /* Otherwise deliver exactly one character: the continuation bytes of a * multi-byte character are gathered, but nothing else is. Two keys typed * quickly, or pasted, therefore arrive as two keystrokes rather than one * blob that no single key can be matched against. */ int remaining = 0; unsigned char first = (unsigned char)buf[0]; if ((first & 0xf8) == 0xf0) { remaining = 3; } else if ((first & 0xf0) == 0xe0) { remaining = 2; } else if ((first & 0xe0) == 0xc0) { remaining = 1; } while (remaining > 0 && total < cap) { if (!koh_poll_input(10)) { break; } ssize_t m = read(STDIN_FILENO, buf + total, 1); if (m <= 0) { break; } total += (int)m; remaining--; } return total; }