koh_term.c

2.5 KB · raw

1#include "koh_term.h"23#include <poll.h>4#include <stdio.h>5#include <sys/ioctl.h>6#include <termios.h>7#include <unistd.h>89static struct termios koh_saved;10static int koh_have_saved = 0;1112int koh_term_raw(int enable) {13	if (enable) {14		if (!isatty(STDIN_FILENO)) {15			return 0;16		}17		if (tcgetattr(STDIN_FILENO, &koh_saved) != 0) {18			return 0;19		}20		koh_have_saved = 1;21		struct termios raw = koh_saved;22		/* No line buffering, no echo, no flow control, no signal keys: the23		 * view wants the bytes and has to be able to clean up after itself. */24		raw.c_lflag &= (tcflag_t) ~(ICANON | ECHO | ISIG | IEXTEN);25		raw.c_iflag &= (tcflag_t) ~(IXON | ICRNL | BRKINT | INPCK | ISTRIP);26		raw.c_cc[VMIN] = 1;27		raw.c_cc[VTIME] = 0;28		return tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == 0;29	}30	if (koh_have_saved) {31		tcsetattr(STDIN_FILENO, TCSAFLUSH, &koh_saved);32		koh_have_saved = 0;33	}34	return 1;35}3637int koh_term_size(int *rows, int *cols) {38	struct winsize size;39	if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) == 0 && size.ws_row > 0 && size.ws_col > 0) {40		*rows = (int)size.ws_row;41		*cols = (int)size.ws_col;42		return 1;43	}44	*rows = 24;45	*cols = 80;46	return 0;47}4849static int koh_poll_input(int milliseconds) {50	struct pollfd p;51	p.fd = STDIN_FILENO;52	p.events = POLLIN;53	p.revents = 0;54	return poll(&p, 1, milliseconds) > 0;55}5657int koh_read_key(char *buf, int cap) {58	if (cap <= 0) {59		return 0;60	}61	ssize_t n = read(STDIN_FILENO, buf, 1);62	if (n <= 0) {63		return 0;64	}65	int total = 1;66	/* An arrow key arrives as ESC [ A, usually in one burst but not always,67	 * so give the rest of the sequence a moment to show up. */68	if (buf[0] == 0x1b) {69		if (koh_poll_input(25)) {70			ssize_t m = read(STDIN_FILENO, buf + 1, (size_t)(cap - 1));71			if (m > 0) {72				total += (int)m;73			}74		}75		return total;76	}77	/* Otherwise deliver exactly one character: the continuation bytes of a78	 * multi-byte character are gathered, but nothing else is. Two keys typed79	 * quickly, or pasted, therefore arrive as two keystrokes rather than one80	 * blob that no single key can be matched against. */81	int remaining = 0;82	unsigned char first = (unsigned char)buf[0];83	if ((first & 0xf8) == 0xf0) {84		remaining = 3;85	} else if ((first & 0xf0) == 0xe0) {86		remaining = 2;87	} else if ((first & 0xe0) == 0xc0) {88		remaining = 1;89	}90	while (remaining > 0 && total < cap) {91		if (!koh_poll_input(10)) {92			break;93		}94		ssize_t m = read(STDIN_FILENO, buf + total, 1);95		if (m <= 0) {96			break;97		}98		total += (int)m;99		remaining--;100	}101	return total;102}