sfm

simple file manager
git clone https://git.afify.dev/sfm.git
Log | Files | Refs | README | LICENSE

termbox.c (33321B)


      1 #if defined(__linux__)
      2 #define _GNU_SOURCE
      3 #elif defined(__APPLE__)
      4 #define _DARWIN_C_SOURCE
      5 #elif defined(__FreeBSD__)
      6 #define __BSD_VISIBLE 1
      7 #endif
      8 #include "termbox.h"
      9 
     10 #include <sys/ioctl.h>
     11 #include <sys/select.h>
     12 #include <sys/stat.h>
     13 #include <sys/time.h>
     14 
     15 #include <assert.h>
     16 #include <errno.h>
     17 #include <fcntl.h>
     18 #include <signal.h>
     19 #include <stdbool.h>
     20 #include <stdio.h>
     21 #include <stdlib.h>
     22 #include <string.h>
     23 #include <termios.h>
     24 #include <unistd.h>
     25 #include <wchar.h>
     26 
     27 #define ENTER_MOUSE_SEQ "\x1b[?1000h\x1b[?1002h\x1b[?1015h\x1b[?1006h"
     28 #define EXIT_MOUSE_SEQ "\x1b[?1006l\x1b[?1015l\x1b[?1002l\x1b[?1000l"
     29 #define EUNSUPPORTED_TERM -1
     30 #define TI_MAGIC 0432
     31 #define TI_ALT_MAGIC 542
     32 #define TI_HEADER_LENGTH 12
     33 #define TB_KEYS_NUM 22
     34 #define CELL(buf, x, y) (buf)->cells[(y) * (buf)->width + (x)]
     35 #define IS_CURSOR_HIDDEN(cx, cy) (cx == -1 || cy == -1)
     36 #define LAST_COORD_INIT -1
     37 #define WRITE_LITERAL(X) bytebuffer_append(&output_buffer, (X), sizeof(X) - 1)
     38 #define WRITE_INT(X) \
     39 	bytebuffer_append(&output_buffer, buf, convertnum((X), buf))
     40 
     41 struct cellbuf {
     42 	int width;
     43 	int height;
     44 	struct tb_cell *cells;
     45 };
     46 
     47 struct bytebuffer {
     48 	char *buf;
     49 	int len;
     50 	int cap;
     51 };
     52 
     53 enum {
     54 	T_ENTER_CA,
     55 	T_EXIT_CA,
     56 	T_SHOW_CURSOR,
     57 	T_HIDE_CURSOR,
     58 	T_CLEAR_SCREEN,
     59 	T_SGR0,
     60 	T_UNDERLINE,
     61 	T_BOLD,
     62 	T_BLINK,
     63 	T_REVERSE,
     64 	T_ENTER_KEYPAD,
     65 	T_EXIT_KEYPAD,
     66 	T_ENTER_MOUSE,
     67 	T_EXIT_MOUSE,
     68 	T_FUNCS_NUM,
     69 };
     70 
     71 static int init_term_builtin(void);
     72 static char *read_file(const char *file);
     73 static char *terminfo_try_path(const char *path, const char *term);
     74 static char *load_terminfo(void);
     75 static const char *terminfo_copy_string(char *data, int str, int table);
     76 static int init_term(void);
     77 static void shutdown_term(void);
     78 static bool starts_with(const char *s1, int len, const char *s2);
     79 static int parse_mouse_event(struct tb_event *event, const char *buf, int len);
     80 static int parse_escape_seq(struct tb_event *event, const char *buf, int len);
     81 static int convertnum(uint32_t num, char *buf);
     82 static void write_cursor(int x, int y);
     83 static void write_sgr(uint16_t fg, uint16_t bg);
     84 static void cellbuf_init(struct cellbuf *buf, int width, int height);
     85 static void cellbuf_resize(struct cellbuf *buf, int width, int height);
     86 static void cellbuf_clear(struct cellbuf *buf);
     87 static void cellbuf_free(struct cellbuf *buf);
     88 static void get_term_size(int *w, int *h);
     89 static void update_term_size(void);
     90 static void send_attr(uint16_t fg, uint16_t bg);
     91 static void send_char(int x, int y, uint32_t c);
     92 static void send_clear(void);
     93 static void sigwinch_handler(int xxx);
     94 static void update_size(void);
     95 static int read_up_to(int n);
     96 static int wait_fill_event(struct tb_event *event, struct timeval *timeout);
     97 static void bytebuffer_reserve(struct bytebuffer *b, int cap);
     98 static void bytebuffer_init(struct bytebuffer *b, int cap);
     99 static void bytebuffer_free(struct bytebuffer *b);
    100 static void bytebuffer_clear(struct bytebuffer *b);
    101 static void bytebuffer_append(struct bytebuffer *b, const char *data, int len);
    102 static void bytebuffer_puts(struct bytebuffer *b, const char *str);
    103 static void bytebuffer_resize(struct bytebuffer *b, int len);
    104 static void bytebuffer_flush(struct bytebuffer *b, int fd);
    105 static void bytebuffer_truncate(struct bytebuffer *b, int n);
    106 
    107 static struct termios orig_tios;
    108 static struct cellbuf back_buffer;
    109 static struct cellbuf front_buffer;
    110 static struct bytebuffer output_buffer;
    111 static struct bytebuffer input_buffer;
    112 static int termw = -1;
    113 static int termh = -1;
    114 static int inputmode = TB_INPUT_ESC;
    115 static int outputmode = TB_OUTPUT_NORMAL;
    116 static int inout;
    117 static int winch_fds[2];
    118 static int lastx = LAST_COORD_INIT;
    119 static int lasty = LAST_COORD_INIT;
    120 static int cursor_x = -1;
    121 static int cursor_y = -1;
    122 static uint16_t background = TB_DEFAULT;
    123 static uint16_t foreground = TB_DEFAULT;
    124 /* may happen in a different thread */
    125 static volatile int buffer_size_change_request;
    126 // rxvt-256color
    127 static const char *rxvt_256color_keys[] = { "\033[11~", "\033[12~", "\033[13~",
    128 	"\033[14~", "\033[15~", "\033[17~", "\033[18~", "\033[19~", "\033[20~",
    129 	"\033[21~", "\033[23~", "\033[24~", "\033[2~", "\033[3~", "\033[7~",
    130 	"\033[8~", "\033[5~", "\033[6~", "\033[A", "\033[B", "\033[D", "\033[C",
    131 	0 };
    132 static const char *rxvt_256color_funcs[] = {
    133 	"\0337\033[?47h",
    134 	"\033[2J\033[?47l\0338",
    135 	"\033[?25h",
    136 	"\033[?25l",
    137 	"\033[H\033[2J",
    138 	"\033[m",
    139 	"\033[4m",
    140 	"\033[1m",
    141 	"\033[5m",
    142 	"\033[7m",
    143 	"\033=",
    144 	"\033>",
    145 	ENTER_MOUSE_SEQ,
    146 	EXIT_MOUSE_SEQ,
    147 };
    148 // Eterm
    149 static const char *eterm_keys[] = { "\033[11~", "\033[12~", "\033[13~",
    150 	"\033[14~", "\033[15~", "\033[17~", "\033[18~", "\033[19~", "\033[20~",
    151 	"\033[21~", "\033[23~", "\033[24~", "\033[2~", "\033[3~", "\033[7~",
    152 	"\033[8~", "\033[5~", "\033[6~", "\033[A", "\033[B", "\033[D", "\033[C",
    153 	0 };
    154 static const char *eterm_funcs[] = {
    155 	"\0337\033[?47h",
    156 	"\033[2J\033[?47l\0338",
    157 	"\033[?25h",
    158 	"\033[?25l",
    159 	"\033[H\033[2J",
    160 	"\033[m",
    161 	"\033[4m",
    162 	"\033[1m",
    163 	"\033[5m",
    164 	"\033[7m",
    165 	"",
    166 	"",
    167 	"",
    168 	"",
    169 };
    170 // screen
    171 static const char *screen_keys[] = { "\033OP", "\033OQ", "\033OR", "\033OS",
    172 	"\033[15~", "\033[17~", "\033[18~", "\033[19~", "\033[20~", "\033[21~",
    173 	"\033[23~", "\033[24~", "\033[2~", "\033[3~", "\033[1~", "\033[4~",
    174 	"\033[5~", "\033[6~", "\033OA", "\033OB", "\033OD", "\033OC", 0 };
    175 static const char *screen_funcs[] = {
    176 	"\033[?1049h",
    177 	"\033[?1049l",
    178 	"\033[34h\033[?25h",
    179 	"\033[?25l",
    180 	"\033[H\033[J",
    181 	"\033[m",
    182 	"\033[4m",
    183 	"\033[1m",
    184 	"\033[5m",
    185 	"\033[7m",
    186 	"\033[?1h\033=",
    187 	"\033[?1l\033>",
    188 	ENTER_MOUSE_SEQ,
    189 	EXIT_MOUSE_SEQ,
    190 };
    191 // rxvt-unicode
    192 static const char *rxvt_unicode_keys[] = { "\033[11~", "\033[12~", "\033[13~",
    193 	"\033[14~", "\033[15~", "\033[17~", "\033[18~", "\033[19~", "\033[20~",
    194 	"\033[21~", "\033[23~", "\033[24~", "\033[2~", "\033[3~", "\033[7~",
    195 	"\033[8~", "\033[5~", "\033[6~", "\033[A", "\033[B", "\033[D", "\033[C",
    196 	0 };
    197 static const char *rxvt_unicode_funcs[] = {
    198 	"\033[?1049h",
    199 	"\033[r\033[?1049l",
    200 	"\033[?25h",
    201 	"\033[?25l",
    202 	"\033[H\033[2J",
    203 	"\033[m\033(B",
    204 	"\033[4m",
    205 	"\033[1m",
    206 	"\033[5m",
    207 	"\033[7m",
    208 	"\033=",
    209 	"\033>",
    210 	ENTER_MOUSE_SEQ,
    211 	EXIT_MOUSE_SEQ,
    212 };
    213 // linux
    214 static const char *linux_keys[] = { "\033[[A", "\033[[B", "\033[[C", "\033[[D",
    215 	"\033[[E", "\033[17~", "\033[18~", "\033[19~", "\033[20~", "\033[21~",
    216 	"\033[23~", "\033[24~", "\033[2~", "\033[3~", "\033[1~", "\033[4~",
    217 	"\033[5~", "\033[6~", "\033[A", "\033[B", "\033[D", "\033[C", 0 };
    218 static const char *linux_funcs[] = {
    219 	"",
    220 	"",
    221 	"\033[?25h\033[?0c",
    222 	"\033[?25l\033[?1c",
    223 	"\033[H\033[J",
    224 	"\033[0;10m",
    225 	"\033[4m",
    226 	"\033[1m",
    227 	"\033[5m",
    228 	"\033[7m",
    229 	"",
    230 	"",
    231 	"",
    232 	"",
    233 };
    234 // xterm
    235 static const char *xterm_keys[] = { "\033OP", "\033OQ", "\033OR", "\033OS",
    236 	"\033[15~", "\033[17~", "\033[18~", "\033[19~", "\033[20~", "\033[21~",
    237 	"\033[23~", "\033[24~", "\033[2~", "\033[3~", "\033OH", "\033OF",
    238 	"\033[5~", "\033[6~", "\033OA", "\033OB", "\033OD", "\033OC", 0 };
    239 static const char *xterm_funcs[] = {
    240 	"\033[?1049h",
    241 	"\033[?1049l",
    242 	"\033[?12l\033[?25h",
    243 	"\033[?25l",
    244 	"\033[H\033[2J",
    245 	"\033(B\033[m",
    246 	"\033[4m",
    247 	"\033[1m",
    248 	"\033[5m",
    249 	"\033[7m",
    250 	"\033[?1h\033=",
    251 	"\033[?1l\033>",
    252 	ENTER_MOUSE_SEQ,
    253 	EXIT_MOUSE_SEQ,
    254 };
    255 static struct term {
    256 	const char *name;
    257 	const char **keys;
    258 	const char **funcs;
    259 } terms[] = {
    260 	{ "rxvt-256color", rxvt_256color_keys, rxvt_256color_funcs },
    261 	{ "Eterm", eterm_keys, eterm_funcs },
    262 	{ "screen", screen_keys, screen_funcs },
    263 	{ "rxvt-unicode", rxvt_unicode_keys, rxvt_unicode_funcs },
    264 	{ "linux", linux_keys, linux_funcs },
    265 	{ "xterm", xterm_keys, xterm_funcs },
    266 	{ 0, 0, 0 },
    267 };
    268 static bool init_from_terminfo = false;
    269 static const char **keys;
    270 static const char **funcs;
    271 
    272 static int
    273 try_compatible(const char *term, const char *name, const char **tkeys,
    274 	const char **tfuncs)
    275 {
    276 	if (strstr(term, name)) {
    277 		keys = tkeys;
    278 		funcs = tfuncs;
    279 		return 0;
    280 	}
    281 
    282 	return EUNSUPPORTED_TERM;
    283 }
    284 
    285 static int
    286 init_term_builtin(void)
    287 {
    288 	int i;
    289 	const char *term = getenv("TERM");
    290 
    291 	if (term) {
    292 		for (i = 0; terms[i].name; i++) {
    293 			if (!strcmp(terms[i].name, term)) {
    294 				keys = terms[i].keys;
    295 				funcs = terms[i].funcs;
    296 				return 0;
    297 			}
    298 		}
    299 
    300 		/* let's do some heuristic, maybe it's a compatible terminal */
    301 		if (try_compatible(term, "xterm", xterm_keys, xterm_funcs) == 0)
    302 			return 0;
    303 		if (try_compatible(term, "rxvt", rxvt_unicode_keys,
    304 			    rxvt_unicode_funcs) == 0)
    305 			return 0;
    306 		if (try_compatible(term, "linux", linux_keys, linux_funcs) == 0)
    307 			return 0;
    308 		if (try_compatible(term, "Eterm", eterm_keys, eterm_funcs) == 0)
    309 			return 0;
    310 		if (try_compatible(term, "screen", screen_keys, screen_funcs) ==
    311 			0)
    312 			return 0;
    313 		if (try_compatible(term, "tmux", screen_keys, screen_funcs) ==
    314 			0)
    315 			return 0;
    316 		/* let's assume that 'cygwin' is xterm compatible */
    317 		if (try_compatible(term, "cygwin", xterm_keys, xterm_funcs) ==
    318 			0)
    319 			return 0;
    320 	}
    321 
    322 	return EUNSUPPORTED_TERM;
    323 }
    324 
    325 //----------------------------------------------------------------------
    326 // terminfo
    327 //----------------------------------------------------------------------
    328 
    329 static char *
    330 read_file(const char *file)
    331 {
    332 	FILE *f = fopen(file, "rb");
    333 	if (!f)
    334 		return 0;
    335 
    336 	struct stat st;
    337 	if (fstat(fileno(f), &st) != 0) {
    338 		fclose(f);
    339 		return 0;
    340 	}
    341 
    342 	char *data = malloc(st.st_size);
    343 	if (!data) {
    344 		fclose(f);
    345 		return 0;
    346 	}
    347 
    348 	if (fread(data, 1, st.st_size, f) != (size_t)st.st_size) {
    349 		fclose(f);
    350 		free(data);
    351 		return 0;
    352 	}
    353 
    354 	fclose(f);
    355 	return data;
    356 }
    357 
    358 static char *
    359 terminfo_try_path(const char *path, const char *term)
    360 {
    361 	char tmp[4096];
    362 	snprintf(tmp, sizeof(tmp), "%s/%c/%s", path, term[0], term);
    363 	tmp[sizeof(tmp) - 1] = '\0';
    364 	char *data = read_file(tmp);
    365 	if (data) {
    366 		return data;
    367 	}
    368 
    369 	// fallback to darwin specific dirs structure
    370 	snprintf(tmp, sizeof(tmp), "%s/%x/%s", path, term[0], term);
    371 	tmp[sizeof(tmp) - 1] = '\0';
    372 	return read_file(tmp);
    373 }
    374 
    375 static char *
    376 load_terminfo(void)
    377 {
    378 	char tmp[4096];
    379 	const char *term = getenv("TERM");
    380 	if (!term) {
    381 		return 0;
    382 	}
    383 
    384 	// if TERMINFO is set, no other directory should be searched
    385 	const char *terminfo = getenv("TERMINFO");
    386 	if (terminfo) {
    387 		return terminfo_try_path(terminfo, term);
    388 	}
    389 
    390 	// next, consider ~/.terminfo
    391 	const char *home = getenv("HOME");
    392 	if (home) {
    393 		snprintf(tmp, sizeof(tmp), "%s/.terminfo", home);
    394 		tmp[sizeof(tmp) - 1] = '\0';
    395 		char *data = terminfo_try_path(tmp, term);
    396 		if (data)
    397 			return data;
    398 	}
    399 
    400 	// next, TERMINFO_DIRS
    401 	const char *dirs = getenv("TERMINFO_DIRS");
    402 	if (dirs) {
    403 		snprintf(tmp, sizeof(tmp), "%s", dirs);
    404 		tmp[sizeof(tmp) - 1] = '\0';
    405 		char *dir = strtok(tmp, ":");
    406 		while (dir) {
    407 			const char *cdir = dir;
    408 			if (strcmp(cdir, "") == 0) {
    409 				cdir = "/usr/share/terminfo";
    410 			}
    411 			char *data = terminfo_try_path(cdir, term);
    412 			if (data)
    413 				return data;
    414 			dir = strtok(0, ":");
    415 		}
    416 	}
    417 
    418 	// fallback to /usr/share/terminfo
    419 	return terminfo_try_path("/usr/share/terminfo", term);
    420 }
    421 
    422 static const char *
    423 terminfo_copy_string(char *data, int str, int table)
    424 {
    425 	const int16_t off = *(int16_t *)(data + str);
    426 	const char *src = data + table + off;
    427 	int len = strlen(src);
    428 	char *dst = malloc(len + 1);
    429 	strcpy(dst, src);
    430 	return dst;
    431 }
    432 
    433 static const int16_t ti_funcs[] = {
    434 	28,
    435 	40,
    436 	16,
    437 	13,
    438 	5,
    439 	39,
    440 	36,
    441 	27,
    442 	26,
    443 	34,
    444 	89,
    445 	88,
    446 };
    447 
    448 static const int16_t ti_keys[] = {
    449 	66,
    450 	68 /* apparently not a typo; 67 is F10 for whatever reason */,
    451 	69,
    452 	70,
    453 	71,
    454 	72,
    455 	73,
    456 	74,
    457 	75,
    458 	67,
    459 	216,
    460 	217,
    461 	77,
    462 	59,
    463 	76,
    464 	164,
    465 	82,
    466 	81,
    467 	87,
    468 	61,
    469 	79,
    470 	83,
    471 };
    472 
    473 static int
    474 init_term(void)
    475 {
    476 	int i;
    477 	char *data = load_terminfo();
    478 	if (!data) {
    479 		init_from_terminfo = false;
    480 		return init_term_builtin();
    481 	}
    482 
    483 	int16_t *header = (int16_t *)data;
    484 
    485 	const int number_sec_len = header[0] == TI_ALT_MAGIC ? 4 : 2;
    486 
    487 	if ((header[1] + header[2]) % 2) {
    488 		// old quirk to align everything on word boundaries
    489 		header[2] += 1;
    490 	}
    491 
    492 	const int str_offset = TI_HEADER_LENGTH + header[1] + header[2] +
    493 		number_sec_len * header[3];
    494 	const int table_offset = str_offset + 2 * header[4];
    495 
    496 	keys = malloc(sizeof(const char *) * (TB_KEYS_NUM + 1));
    497 	for (i = 0; i < TB_KEYS_NUM; i++) {
    498 		keys[i] = terminfo_copy_string(
    499 			data, str_offset + 2 * ti_keys[i], table_offset);
    500 	}
    501 	keys[TB_KEYS_NUM] = 0;
    502 
    503 	funcs = malloc(sizeof(const char *) * T_FUNCS_NUM);
    504 	// the last two entries are reserved for mouse. because the table offset is
    505 	// not there, the two entries have to fill in manually
    506 	for (i = 0; i < T_FUNCS_NUM - 2; i++) {
    507 		funcs[i] = terminfo_copy_string(
    508 			data, str_offset + 2 * ti_funcs[i], table_offset);
    509 	}
    510 
    511 	funcs[T_FUNCS_NUM - 2] = ENTER_MOUSE_SEQ;
    512 	funcs[T_FUNCS_NUM - 1] = EXIT_MOUSE_SEQ;
    513 
    514 	init_from_terminfo = true;
    515 	free(data);
    516 	return 0;
    517 }
    518 
    519 static void
    520 shutdown_term(void)
    521 {
    522 	if (init_from_terminfo) {
    523 		int i;
    524 		for (i = 0; i < TB_KEYS_NUM; i++) {
    525 			free((void *)keys[i]);
    526 		}
    527 		// the last two entries are reserved for mouse. because the table offset
    528 		// is not there, the two entries have to fill in manually and do not
    529 		// need to be freed.
    530 		for (i = 0; i < T_FUNCS_NUM - 2; i++) {
    531 			free((void *)funcs[i]);
    532 		}
    533 		free(keys);
    534 		free(funcs);
    535 	}
    536 }
    537 
    538 // if s1 starts with s2 returns true, else false
    539 // len is the length of s1
    540 // s2 should be null-terminated
    541 static bool
    542 starts_with(const char *s1, int len, const char *s2)
    543 {
    544 	int n = 0;
    545 	while (*s2 && n < len) {
    546 		if (*s1++ != *s2++)
    547 			return false;
    548 		n++;
    549 	}
    550 	return *s2 == 0;
    551 }
    552 
    553 static int
    554 parse_mouse_event(struct tb_event *event, const char *buf, int len)
    555 {
    556 	if (len >= 6 && starts_with(buf, len, "\033[M")) {
    557 		// X10 mouse encoding, the simplest one
    558 		// \033 [ M Cb Cx Cy
    559 		int b = buf[3] - 32;
    560 		switch (b & 3) {
    561 		case 0:
    562 			if ((b & 64) != 0)
    563 				event->key = TB_KEY_MOUSE_WHEEL_UP;
    564 			else
    565 				event->key = TB_KEY_MOUSE_LEFT;
    566 			break;
    567 		case 1:
    568 			if ((b & 64) != 0)
    569 				event->key = TB_KEY_MOUSE_WHEEL_DOWN;
    570 			else
    571 				event->key = TB_KEY_MOUSE_MIDDLE;
    572 			break;
    573 		case 2:
    574 			event->key = TB_KEY_MOUSE_RIGHT;
    575 			break;
    576 		case 3:
    577 			event->key = TB_KEY_MOUSE_RELEASE;
    578 			break;
    579 		default:
    580 			return -6;
    581 		}
    582 		event->type = TB_EVENT_MOUSE; // TB_EVENT_KEY by default
    583 		if ((b & 32) != 0)
    584 			event->mod |= TB_MOD_MOTION;
    585 
    586 		// the coord is 1,1 for upper left
    587 		event->x = (uint8_t)buf[4] - 1 - 32;
    588 		event->y = (uint8_t)buf[5] - 1 - 32;
    589 
    590 		return 6;
    591 	} else if (starts_with(buf, len, "\033[<") ||
    592 		starts_with(buf, len, "\033[")) {
    593 		// xterm 1006 extended mode or urxvt 1015 extended mode
    594 		// xterm: \033 [ < Cb ; Cx ; Cy (M or m)
    595 		// urxvt: \033 [ Cb ; Cx ; Cy M
    596 		int i, mi = -1, starti = -1;
    597 		int isM, isU, s1 = -1, s2 = -1;
    598 		int n1 = 0, n2 = 0, n3 = 0;
    599 
    600 		for (i = 0; i < len; i++) {
    601 			// We search the first (s1) and the last (s2) ';'
    602 			if (buf[i] == ';') {
    603 				if (s1 == -1)
    604 					s1 = i;
    605 				s2 = i;
    606 			}
    607 
    608 			// We search for the first 'm' or 'M'
    609 			if ((buf[i] == 'm' || buf[i] == 'M') && mi == -1) {
    610 				mi = i;
    611 				break;
    612 			}
    613 		}
    614 		if (mi == -1)
    615 			return 0;
    616 
    617 		// whether it's a capital M or not
    618 		isM = (buf[mi] == 'M');
    619 
    620 		if (buf[2] == '<') {
    621 			isU = 0;
    622 			starti = 3;
    623 		} else {
    624 			isU = 1;
    625 			starti = 2;
    626 		}
    627 
    628 		if (s1 == -1 || s2 == -1 || s1 == s2)
    629 			return 0;
    630 
    631 		n1 = strtoul(&buf[starti], NULL, 10);
    632 		n2 = strtoul(&buf[s1 + 1], NULL, 10);
    633 		n3 = strtoul(&buf[s2 + 1], NULL, 10);
    634 
    635 		if (isU)
    636 			n1 -= 32;
    637 
    638 		switch (n1 & 3) {
    639 		case 0:
    640 			if ((n1 & 64) != 0) {
    641 				event->key = TB_KEY_MOUSE_WHEEL_UP;
    642 			} else {
    643 				event->key = TB_KEY_MOUSE_LEFT;
    644 			}
    645 			break;
    646 		case 1:
    647 			if ((n1 & 64) != 0) {
    648 				event->key = TB_KEY_MOUSE_WHEEL_DOWN;
    649 			} else {
    650 				event->key = TB_KEY_MOUSE_MIDDLE;
    651 			}
    652 			break;
    653 		case 2:
    654 			event->key = TB_KEY_MOUSE_RIGHT;
    655 			break;
    656 		case 3:
    657 			event->key = TB_KEY_MOUSE_RELEASE;
    658 			break;
    659 		default:
    660 			return mi + 1;
    661 		}
    662 
    663 		if (!isM) {
    664 			// on xterm mouse release is signaled by lowercase m
    665 			event->key = TB_KEY_MOUSE_RELEASE;
    666 		}
    667 
    668 		event->type = TB_EVENT_MOUSE; // TB_EVENT_KEY by default
    669 		if ((n1 & 32) != 0)
    670 			event->mod |= TB_MOD_MOTION;
    671 
    672 		event->x = (uint8_t)n2 - 1;
    673 		event->y = (uint8_t)n3 - 1;
    674 
    675 		return mi + 1;
    676 	}
    677 
    678 	return 0;
    679 }
    680 
    681 // convert escape sequence to event, and return consumed bytes on success
    682 // (failure == 0)
    683 static int
    684 parse_escape_seq(struct tb_event *event, const char *buf, int len)
    685 {
    686 	int mouse_parsed = parse_mouse_event(event, buf, len);
    687 
    688 	if (mouse_parsed != 0)
    689 		return mouse_parsed;
    690 
    691 	// it's pretty simple here, find 'starts_with' match and return
    692 	// success, else return failure
    693 	int i;
    694 	for (i = 0; keys[i]; i++) {
    695 		if (starts_with(buf, len, keys[i])) {
    696 			event->ch = 0;
    697 			event->key = 0xFFFF - i;
    698 			return strlen(keys[i]);
    699 		}
    700 	}
    701 	return 0;
    702 }
    703 
    704 static bool
    705 extract_event(struct tb_event *event, struct bytebuffer *inbuf, int in)
    706 {
    707 	const char *buf = inbuf->buf;
    708 	const int len = inbuf->len;
    709 	if (len == 0)
    710 		return false;
    711 
    712 	if (buf[0] == '\033') {
    713 		int n = parse_escape_seq(event, buf, len);
    714 		if (n != 0) {
    715 			bool success = true;
    716 			if (n < 0) {
    717 				success = false;
    718 				n = -n;
    719 			}
    720 			bytebuffer_truncate(inbuf, n);
    721 			return success;
    722 		} else {
    723 			// it's not escape sequence, then it's ALT or ESC,
    724 			// check inputmode
    725 			if (in & TB_INPUT_ESC) {
    726 				// if we're in escape mode, fill ESC event, pop
    727 				// buffer, return success
    728 				event->ch = 0;
    729 				event->key = TB_KEY_ESC;
    730 				event->mod = 0;
    731 				bytebuffer_truncate(inbuf, 1);
    732 				return true;
    733 			} else if (in & TB_INPUT_ALT) {
    734 				// if we're in alt mode, set ALT modifier to
    735 				// event and redo parsing
    736 				event->mod = TB_MOD_ALT;
    737 				bytebuffer_truncate(inbuf, 1);
    738 				return extract_event(event, inbuf, in);
    739 			}
    740 			assert(!"never got here");
    741 		}
    742 	}
    743 
    744 	// if we're here, this is not an escape sequence and not an alt sequence
    745 	// so, it's a FUNCTIONAL KEY or a UNICODE character
    746 
    747 	// first of all check if it's a functional key
    748 	if ((unsigned char)buf[0] <= TB_KEY_SPACE ||
    749 		(unsigned char)buf[0] == TB_KEY_BACKSPACE2) {
    750 		// fill event, pop buffer, return success */
    751 		event->ch = 0;
    752 		event->key = (uint16_t)buf[0];
    753 		bytebuffer_truncate(inbuf, 1);
    754 		return true;
    755 	}
    756 
    757 	// feh... we got utf8 here
    758 
    759 	// check if there is all bytes
    760 	if (len >= tb_utf8_char_length(buf[0])) {
    761 		/* everything ok, fill event, pop buffer, return success */
    762 		tb_utf8_char_to_unicode(&event->ch, buf);
    763 		event->key = 0;
    764 		bytebuffer_truncate(inbuf, tb_utf8_char_length(buf[0]));
    765 		return true;
    766 	}
    767 
    768 	// event isn't recognized, perhaps there is not enough bytes in utf8
    769 	// sequence
    770 	return false;
    771 }
    772 
    773 /* -------------------------------------------------------- */
    774 
    775 int
    776 tb_init_fd(int inout_)
    777 {
    778 	inout = inout_;
    779 	if (inout == -1) {
    780 		return TB_EFAILED_TO_OPEN_TTY;
    781 	}
    782 
    783 	if (init_term() < 0) {
    784 		close(inout);
    785 		return TB_EUNSUPPORTED_TERMINAL;
    786 	}
    787 
    788 	if (pipe(winch_fds) < 0) {
    789 		close(inout);
    790 		return TB_EPIPE_TRAP_ERROR;
    791 	}
    792 
    793 	struct sigaction sa;
    794 	memset(&sa, 0, sizeof(sa));
    795 	sa.sa_handler = sigwinch_handler;
    796 	sa.sa_flags = 0;
    797 	sigaction(SIGWINCH, &sa, 0);
    798 
    799 	tcgetattr(inout, &orig_tios);
    800 
    801 	struct termios tios;
    802 	memcpy(&tios, &orig_tios, sizeof(tios));
    803 
    804 	tios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR |
    805 		ICRNL | IXON);
    806 	tios.c_oflag &= ~OPOST;
    807 	tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    808 	tios.c_cflag &= ~(CSIZE | PARENB);
    809 	tios.c_cflag |= CS8;
    810 	tios.c_cc[VMIN] = 0;
    811 	tios.c_cc[VTIME] = 0;
    812 	tcsetattr(inout, TCSAFLUSH, &tios);
    813 
    814 	bytebuffer_init(&input_buffer, 128);
    815 	bytebuffer_init(&output_buffer, 32 * 1024);
    816 
    817 	bytebuffer_puts(&output_buffer, funcs[T_ENTER_CA]);
    818 	bytebuffer_puts(&output_buffer, funcs[T_ENTER_KEYPAD]);
    819 	bytebuffer_puts(&output_buffer, funcs[T_HIDE_CURSOR]);
    820 	send_clear();
    821 
    822 	update_term_size();
    823 	cellbuf_init(&back_buffer, termw, termh);
    824 	cellbuf_init(&front_buffer, termw, termh);
    825 	cellbuf_clear(&back_buffer);
    826 	cellbuf_clear(&front_buffer);
    827 
    828 	return 0;
    829 }
    830 
    831 int
    832 tb_init_file(const char *name)
    833 {
    834 	return tb_init_fd(open(name, O_RDWR));
    835 }
    836 
    837 int
    838 tb_init(void)
    839 {
    840 	return tb_init_file("/dev/tty");
    841 }
    842 
    843 void
    844 tb_shutdown(void)
    845 {
    846 	if (termw == -1) {
    847 		fputs("tb_shutdown() should not be called twice.", stderr);
    848 		abort();
    849 	}
    850 
    851 	bytebuffer_puts(&output_buffer, funcs[T_SHOW_CURSOR]);
    852 	bytebuffer_puts(&output_buffer, funcs[T_SGR0]);
    853 	bytebuffer_puts(&output_buffer, funcs[T_CLEAR_SCREEN]);
    854 	bytebuffer_puts(&output_buffer, funcs[T_EXIT_CA]);
    855 	bytebuffer_puts(&output_buffer, funcs[T_EXIT_KEYPAD]);
    856 	bytebuffer_puts(&output_buffer, funcs[T_EXIT_MOUSE]);
    857 	bytebuffer_flush(&output_buffer, inout);
    858 	tcsetattr(inout, TCSAFLUSH, &orig_tios);
    859 
    860 	shutdown_term();
    861 	close(inout);
    862 	close(winch_fds[0]);
    863 	close(winch_fds[1]);
    864 
    865 	cellbuf_free(&back_buffer);
    866 	cellbuf_free(&front_buffer);
    867 	bytebuffer_free(&output_buffer);
    868 	bytebuffer_free(&input_buffer);
    869 	termw = termh = -1;
    870 }
    871 
    872 void
    873 tb_present(void)
    874 {
    875 	int x, y, w, i;
    876 	struct tb_cell *back, *front;
    877 
    878 	/* invalidate cursor position */
    879 	lastx = LAST_COORD_INIT;
    880 	lasty = LAST_COORD_INIT;
    881 
    882 	if (buffer_size_change_request) {
    883 		update_size();
    884 		buffer_size_change_request = 0;
    885 	}
    886 
    887 	for (y = 0; y < front_buffer.height; ++y) {
    888 		for (x = 0; x < front_buffer.width;) {
    889 			back = &CELL(&back_buffer, x, y);
    890 			front = &CELL(&front_buffer, x, y);
    891 			w = wcwidth(back->ch);
    892 			if (w < 1)
    893 				w = 1;
    894 			if (memcmp(back, front, sizeof(struct tb_cell)) == 0) {
    895 				x += w;
    896 				continue;
    897 			}
    898 			memcpy(front, back, sizeof(struct tb_cell));
    899 			send_attr(back->fg, back->bg);
    900 			if (w > 1 && x >= front_buffer.width - (w - 1)) {
    901 				// Not enough room for wide ch, so send spaces
    902 				for (i = x; i < front_buffer.width; ++i) {
    903 					send_char(i, y, ' ');
    904 				}
    905 			} else {
    906 				send_char(x, y, back->ch);
    907 				for (i = 1; i < w; ++i) {
    908 					front = &CELL(&front_buffer, x + i, y);
    909 					front->ch = 0;
    910 					front->fg = back->fg;
    911 					front->bg = back->bg;
    912 				}
    913 			}
    914 			x += w;
    915 		}
    916 	}
    917 	if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y))
    918 		write_cursor(cursor_x, cursor_y);
    919 	bytebuffer_flush(&output_buffer, inout);
    920 }
    921 
    922 void
    923 tb_set_cursor(int cx, int cy)
    924 {
    925 	if (IS_CURSOR_HIDDEN(cursor_x, cursor_y) && !IS_CURSOR_HIDDEN(cx, cy))
    926 		bytebuffer_puts(&output_buffer, funcs[T_SHOW_CURSOR]);
    927 
    928 	if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y) && IS_CURSOR_HIDDEN(cx, cy))
    929 		bytebuffer_puts(&output_buffer, funcs[T_HIDE_CURSOR]);
    930 
    931 	cursor_x = cx;
    932 	cursor_y = cy;
    933 	if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y))
    934 		write_cursor(cursor_x, cursor_y);
    935 }
    936 
    937 void
    938 tb_put_cell(int x, int y, const struct tb_cell *cell)
    939 {
    940 	if ((unsigned)x >= (unsigned)back_buffer.width)
    941 		return;
    942 	if ((unsigned)y >= (unsigned)back_buffer.height)
    943 		return;
    944 	CELL(&back_buffer, x, y) = *cell;
    945 }
    946 
    947 void
    948 tb_change_cell(int x, int y, uint32_t ch, uint16_t fg, uint16_t bg)
    949 {
    950 	struct tb_cell c = { ch, fg, bg };
    951 	tb_put_cell(x, y, &c);
    952 }
    953 
    954 void
    955 tb_blit(int x, int y, int w, int h, const struct tb_cell *cells)
    956 {
    957 	if (x + w < 0 || x >= back_buffer.width)
    958 		return;
    959 	if (y + h < 0 || y >= back_buffer.height)
    960 		return;
    961 	int xo = 0, yo = 0, ww = w, hh = h;
    962 	if (x < 0) {
    963 		xo = -x;
    964 		ww -= xo;
    965 		x = 0;
    966 	}
    967 	if (y < 0) {
    968 		yo = -y;
    969 		hh -= yo;
    970 		y = 0;
    971 	}
    972 	if (ww > back_buffer.width - x)
    973 		ww = back_buffer.width - x;
    974 	if (hh > back_buffer.height - y)
    975 		hh = back_buffer.height - y;
    976 
    977 	int sy;
    978 	struct tb_cell *dst = &CELL(&back_buffer, x, y);
    979 	const struct tb_cell *src = cells + yo * w + xo;
    980 	size_t size = sizeof(struct tb_cell) * ww;
    981 
    982 	for (sy = 0; sy < hh; ++sy) {
    983 		memcpy(dst, src, size);
    984 		dst += back_buffer.width;
    985 		src += w;
    986 	}
    987 }
    988 
    989 struct tb_cell *
    990 tb_cell_buffer(void)
    991 {
    992 	return back_buffer.cells;
    993 }
    994 
    995 int
    996 tb_poll_event(struct tb_event *event)
    997 {
    998 	return wait_fill_event(event, 0);
    999 }
   1000 
   1001 int
   1002 tb_peek_event(struct tb_event *event, int timeout)
   1003 {
   1004 	struct timeval tv;
   1005 	tv.tv_sec = timeout / 1000;
   1006 	tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000;
   1007 	return wait_fill_event(event, &tv);
   1008 }
   1009 
   1010 int
   1011 tb_width(void)
   1012 {
   1013 	return termw;
   1014 }
   1015 
   1016 int
   1017 tb_height(void)
   1018 {
   1019 	return termh;
   1020 }
   1021 
   1022 void
   1023 tb_clear(void)
   1024 {
   1025 	if (buffer_size_change_request) {
   1026 		update_size();
   1027 		buffer_size_change_request = 0;
   1028 	}
   1029 	cellbuf_clear(&back_buffer);
   1030 }
   1031 
   1032 int
   1033 tb_select_input_mode(int mode)
   1034 {
   1035 	if (mode) {
   1036 		if ((mode & (TB_INPUT_ESC | TB_INPUT_ALT)) == 0)
   1037 			mode |= TB_INPUT_ESC;
   1038 
   1039 		/* technically termbox can handle that, but let's be nice and show here
   1040            what mode is actually used */
   1041 		if ((mode & (TB_INPUT_ESC | TB_INPUT_ALT)) ==
   1042 			(TB_INPUT_ESC | TB_INPUT_ALT))
   1043 			mode &= ~TB_INPUT_ALT;
   1044 
   1045 		inputmode = mode;
   1046 		if (mode & TB_INPUT_MOUSE) {
   1047 			bytebuffer_puts(&output_buffer, funcs[T_ENTER_MOUSE]);
   1048 			bytebuffer_flush(&output_buffer, inout);
   1049 		} else {
   1050 			bytebuffer_puts(&output_buffer, funcs[T_EXIT_MOUSE]);
   1051 			bytebuffer_flush(&output_buffer, inout);
   1052 		}
   1053 	}
   1054 	return inputmode;
   1055 }
   1056 
   1057 int
   1058 tb_select_output_mode(int mode)
   1059 {
   1060 	if (mode)
   1061 		outputmode = mode;
   1062 	return outputmode;
   1063 }
   1064 
   1065 void
   1066 tb_set_clear_attributes(uint16_t fg, uint16_t bg)
   1067 {
   1068 	foreground = fg;
   1069 	background = bg;
   1070 }
   1071 
   1072 /* -------------------------------------------------------- */
   1073 
   1074 static int
   1075 convertnum(uint32_t num, char *buf)
   1076 {
   1077 	int i, l = 0;
   1078 	int ch;
   1079 	do {
   1080 		buf[l++] = '0' + (num % 10);
   1081 		num /= 10;
   1082 	} while (num);
   1083 	for (i = 0; i < l / 2; i++) {
   1084 		ch = buf[i];
   1085 		buf[i] = buf[l - 1 - i];
   1086 		buf[l - 1 - i] = ch;
   1087 	}
   1088 	return l;
   1089 }
   1090 
   1091 static void
   1092 write_cursor(int x, int y)
   1093 {
   1094 	char buf[32];
   1095 	WRITE_LITERAL("\033[");
   1096 	WRITE_INT(y + 1);
   1097 	WRITE_LITERAL(";");
   1098 	WRITE_INT(x + 1);
   1099 	WRITE_LITERAL("H");
   1100 }
   1101 
   1102 static void
   1103 write_sgr(uint16_t fg, uint16_t bg)
   1104 {
   1105 	char buf[32];
   1106 
   1107 	if (fg == TB_DEFAULT && bg == TB_DEFAULT)
   1108 		return;
   1109 
   1110 	switch (outputmode) {
   1111 	case TB_OUTPUT_256:
   1112 	case TB_OUTPUT_216:
   1113 	case TB_OUTPUT_GRAYSCALE:
   1114 		WRITE_LITERAL("\033[");
   1115 		if (fg != TB_DEFAULT) {
   1116 			WRITE_LITERAL("38;5;");
   1117 			WRITE_INT(fg);
   1118 			if (bg != TB_DEFAULT) {
   1119 				WRITE_LITERAL(";");
   1120 			}
   1121 		}
   1122 		if (bg != TB_DEFAULT) {
   1123 			WRITE_LITERAL("48;5;");
   1124 			WRITE_INT(bg);
   1125 		}
   1126 		WRITE_LITERAL("m");
   1127 		break;
   1128 	case TB_OUTPUT_NORMAL:
   1129 	default:
   1130 		WRITE_LITERAL("\033[");
   1131 		if (fg != TB_DEFAULT) {
   1132 			WRITE_LITERAL("3");
   1133 			WRITE_INT(fg - 1);
   1134 			if (bg != TB_DEFAULT) {
   1135 				WRITE_LITERAL(";");
   1136 			}
   1137 		}
   1138 		if (bg != TB_DEFAULT) {
   1139 			WRITE_LITERAL("4");
   1140 			WRITE_INT(bg - 1);
   1141 		}
   1142 		WRITE_LITERAL("m");
   1143 		break;
   1144 	}
   1145 }
   1146 
   1147 static void
   1148 cellbuf_init(struct cellbuf *buf, int width, int height)
   1149 {
   1150 	buf->cells = (struct tb_cell *)malloc(
   1151 		sizeof(struct tb_cell) * width * height);
   1152 	assert(buf->cells);
   1153 	buf->width = width;
   1154 	buf->height = height;
   1155 }
   1156 
   1157 static void
   1158 cellbuf_resize(struct cellbuf *buf, int width, int height)
   1159 {
   1160 	if (buf->width == width && buf->height == height)
   1161 		return;
   1162 
   1163 	int oldw = buf->width;
   1164 	int oldh = buf->height;
   1165 	struct tb_cell *oldcells = buf->cells;
   1166 
   1167 	cellbuf_init(buf, width, height);
   1168 	cellbuf_clear(buf);
   1169 
   1170 	int minw = (width < oldw) ? width : oldw;
   1171 	int minh = (height < oldh) ? height : oldh;
   1172 	int i;
   1173 
   1174 	for (i = 0; i < minh; ++i) {
   1175 		struct tb_cell *csrc = oldcells + (i * oldw);
   1176 		struct tb_cell *cdst = buf->cells + (i * width);
   1177 		memcpy(cdst, csrc, sizeof(struct tb_cell) * minw);
   1178 	}
   1179 
   1180 	free(oldcells);
   1181 }
   1182 
   1183 static void
   1184 cellbuf_clear(struct cellbuf *buf)
   1185 {
   1186 	int i;
   1187 	int ncells = buf->width * buf->height;
   1188 
   1189 	for (i = 0; i < ncells; ++i) {
   1190 		buf->cells[i].ch = ' ';
   1191 		buf->cells[i].fg = foreground;
   1192 		buf->cells[i].bg = background;
   1193 	}
   1194 }
   1195 
   1196 static void
   1197 cellbuf_free(struct cellbuf *buf)
   1198 {
   1199 	free(buf->cells);
   1200 }
   1201 
   1202 static void
   1203 get_term_size(int *w, int *h)
   1204 {
   1205 	struct winsize sz;
   1206 	memset(&sz, 0, sizeof(sz));
   1207 
   1208 	ioctl(inout, TIOCGWINSZ, &sz);
   1209 
   1210 	*w = sz.ws_col > 0 ? sz.ws_col : 80;
   1211 	*h = sz.ws_row > 0 ? sz.ws_row : 24;
   1212 }
   1213 
   1214 static void
   1215 update_term_size(void)
   1216 {
   1217 	struct winsize sz;
   1218 	memset(&sz, 0, sizeof(sz));
   1219 
   1220 	ioctl(inout, TIOCGWINSZ, &sz);
   1221 
   1222 	termw = sz.ws_col > 0 ? sz.ws_col : 80;
   1223 	termh = sz.ws_row > 0 ? sz.ws_row : 24;
   1224 }
   1225 
   1226 static void
   1227 send_attr(uint16_t fg, uint16_t bg)
   1228 {
   1229 #define LAST_ATTR_INIT 0xFFFF
   1230 	static uint16_t lastfg = LAST_ATTR_INIT, lastbg = LAST_ATTR_INIT;
   1231 	if (fg != lastfg || bg != lastbg) {
   1232 		bytebuffer_puts(&output_buffer, funcs[T_SGR0]);
   1233 
   1234 		uint16_t fgcol;
   1235 		uint16_t bgcol;
   1236 
   1237 		switch (outputmode) {
   1238 		case TB_OUTPUT_256:
   1239 			fgcol = fg & 0xFF;
   1240 			bgcol = bg & 0xFF;
   1241 			break;
   1242 
   1243 		case TB_OUTPUT_216:
   1244 			fgcol = fg & 0xFF;
   1245 			if (fgcol > 215)
   1246 				fgcol = 7;
   1247 			bgcol = bg & 0xFF;
   1248 			if (bgcol > 215)
   1249 				bgcol = 0;
   1250 			fgcol += 0x10;
   1251 			bgcol += 0x10;
   1252 			break;
   1253 
   1254 		case TB_OUTPUT_GRAYSCALE:
   1255 			fgcol = fg & 0xFF;
   1256 			if (fgcol > 23)
   1257 				fgcol = 23;
   1258 			bgcol = bg & 0xFF;
   1259 			if (bgcol > 23)
   1260 				bgcol = 0;
   1261 			fgcol += 0xe8;
   1262 			bgcol += 0xe8;
   1263 			break;
   1264 
   1265 		case TB_OUTPUT_NORMAL:
   1266 		default:
   1267 			fgcol = fg & 0x0F;
   1268 			bgcol = bg & 0x0F;
   1269 		}
   1270 
   1271 		if (fg & TB_BOLD)
   1272 			bytebuffer_puts(&output_buffer, funcs[T_BOLD]);
   1273 		if (bg & TB_BOLD)
   1274 			bytebuffer_puts(&output_buffer, funcs[T_BLINK]);
   1275 		if (fg & TB_UNDERLINE)
   1276 			bytebuffer_puts(&output_buffer, funcs[T_UNDERLINE]);
   1277 		if ((fg & TB_REVERSE) || (bg & TB_REVERSE))
   1278 			bytebuffer_puts(&output_buffer, funcs[T_REVERSE]);
   1279 
   1280 		write_sgr(fgcol, bgcol);
   1281 
   1282 		lastfg = fg;
   1283 		lastbg = bg;
   1284 	}
   1285 }
   1286 
   1287 static void
   1288 send_char(int x, int y, uint32_t c)
   1289 {
   1290 	char buf[7];
   1291 	int bw = tb_utf8_unicode_to_char(buf, c);
   1292 	if (x - 1 != lastx || y != lasty)
   1293 		write_cursor(x, y);
   1294 	lastx = x;
   1295 	lasty = y;
   1296 	if (!c)
   1297 		buf[0] = ' '; // replace 0 with whitespace
   1298 	bytebuffer_append(&output_buffer, buf, bw);
   1299 }
   1300 
   1301 static void
   1302 send_clear(void)
   1303 {
   1304 	send_attr(foreground, background);
   1305 	bytebuffer_puts(&output_buffer, funcs[T_CLEAR_SCREEN]);
   1306 	if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y))
   1307 		write_cursor(cursor_x, cursor_y);
   1308 	bytebuffer_flush(&output_buffer, inout);
   1309 
   1310 	/* we need to invalidate cursor position too and these two vars are
   1311      * used only for simple cursor positioning optimization, cursor
   1312      * actually may be in the correct place, but we simply discard
   1313      * optimization once and it gives us simple solution for the case when
   1314      * cursor moved */
   1315 	lastx = LAST_COORD_INIT;
   1316 	lasty = LAST_COORD_INIT;
   1317 }
   1318 
   1319 static void
   1320 sigwinch_handler(int xxx)
   1321 {
   1322 	(void)xxx;
   1323 	const int zzz = 1;
   1324 	write(winch_fds[1], &zzz, sizeof(int));
   1325 }
   1326 
   1327 static void
   1328 update_size(void)
   1329 {
   1330 	update_term_size();
   1331 	cellbuf_resize(&back_buffer, termw, termh);
   1332 	cellbuf_resize(&front_buffer, termw, termh);
   1333 	cellbuf_clear(&front_buffer);
   1334 	send_clear();
   1335 }
   1336 
   1337 static int
   1338 read_up_to(int n)
   1339 {
   1340 	assert(n > 0);
   1341 	const int prevlen = input_buffer.len;
   1342 	bytebuffer_resize(&input_buffer, prevlen + n);
   1343 
   1344 	int read_n = 0;
   1345 	while (read_n <= n) {
   1346 		ssize_t r = 0;
   1347 		if (read_n < n) {
   1348 			r = read(inout, input_buffer.buf + prevlen + read_n,
   1349 				n - read_n);
   1350 		}
   1351 #ifdef __CYGWIN__
   1352 		// While linux man for tty says when VMIN == 0 && VTIME == 0, read
   1353 		// should return 0 when there is nothing to read, cygwin's read returns
   1354 		// -1. Not sure why and if it's correct to ignore it, but let's pretend
   1355 		// it's zero.
   1356 		if (r < 0)
   1357 			r = 0;
   1358 #endif
   1359 		if (r < 0) {
   1360 			// EAGAIN / EWOULDBLOCK shouldn't occur here
   1361 			assert(errno != EAGAIN && errno != EWOULDBLOCK);
   1362 			return -1;
   1363 		} else if (r > 0) {
   1364 			read_n += r;
   1365 		} else {
   1366 			bytebuffer_resize(&input_buffer, prevlen + read_n);
   1367 			return read_n;
   1368 		}
   1369 	}
   1370 	assert(!"unreachable");
   1371 	return 0;
   1372 }
   1373 
   1374 static int
   1375 wait_fill_event(struct tb_event *event, struct timeval *timeout)
   1376 {
   1377 	// ;-)
   1378 #define ENOUGH_DATA_FOR_PARSING 64
   1379 	fd_set events;
   1380 	memset(event, 0, sizeof(struct tb_event));
   1381 
   1382 	// try to extract event from input buffer, return on success
   1383 	event->type = TB_EVENT_KEY;
   1384 	if (extract_event(event, &input_buffer, inputmode))
   1385 		return event->type;
   1386 
   1387 	// it looks like input buffer is incomplete, let's try the short path,
   1388 	// but first make sure there is enough space
   1389 	int n = read_up_to(ENOUGH_DATA_FOR_PARSING);
   1390 	if (n < 0)
   1391 		return -1;
   1392 	if (n > 0 && extract_event(event, &input_buffer, inputmode))
   1393 		return event->type;
   1394 
   1395 	// n == 0, or not enough data, let's go to select
   1396 	while (1) {
   1397 		FD_ZERO(&events);
   1398 		FD_SET(inout, &events);
   1399 		FD_SET(winch_fds[0], &events);
   1400 		int maxfd = (winch_fds[0] > inout) ? winch_fds[0] : inout;
   1401 		int result = select(maxfd + 1, &events, 0, 0, timeout);
   1402 		if (!result)
   1403 			return 0;
   1404 
   1405 		if (FD_ISSET(inout, &events)) {
   1406 			event->type = TB_EVENT_KEY;
   1407 			n = read_up_to(ENOUGH_DATA_FOR_PARSING);
   1408 			if (n < 0)
   1409 				return -1;
   1410 
   1411 			if (n == 0)
   1412 				continue;
   1413 
   1414 			if (extract_event(event, &input_buffer, inputmode))
   1415 				return event->type;
   1416 		}
   1417 		if (FD_ISSET(winch_fds[0], &events)) {
   1418 			event->type = TB_EVENT_RESIZE;
   1419 			int zzz = 0;
   1420 			read(winch_fds[0], &zzz, sizeof(int));
   1421 			buffer_size_change_request = 1;
   1422 			get_term_size(&event->w, &event->h);
   1423 			return TB_EVENT_RESIZE;
   1424 		}
   1425 	}
   1426 }
   1427 
   1428 static void
   1429 bytebuffer_reserve(struct bytebuffer *b, int cap)
   1430 {
   1431 	if (b->cap >= cap) {
   1432 		return;
   1433 	}
   1434 
   1435 	// prefer doubling capacity
   1436 	if (b->cap * 2 >= cap) {
   1437 		cap = b->cap * 2;
   1438 	}
   1439 
   1440 	char *newbuf = realloc(b->buf, cap);
   1441 	b->buf = newbuf;
   1442 	b->cap = cap;
   1443 }
   1444 
   1445 static void
   1446 bytebuffer_init(struct bytebuffer *b, int cap)
   1447 {
   1448 	b->cap = 0;
   1449 	b->len = 0;
   1450 	b->buf = 0;
   1451 
   1452 	if (cap > 0) {
   1453 		b->cap = cap;
   1454 		b->buf = malloc(cap); // just assume malloc works always
   1455 	}
   1456 }
   1457 
   1458 static void
   1459 bytebuffer_free(struct bytebuffer *b)
   1460 {
   1461 	if (b->buf)
   1462 		free(b->buf);
   1463 }
   1464 
   1465 static void
   1466 bytebuffer_clear(struct bytebuffer *b)
   1467 {
   1468 	b->len = 0;
   1469 }
   1470 
   1471 static void
   1472 bytebuffer_append(struct bytebuffer *b, const char *data, int len)
   1473 {
   1474 	bytebuffer_reserve(b, b->len + len);
   1475 	memcpy(b->buf + b->len, data, len);
   1476 	b->len += len;
   1477 }
   1478 
   1479 static void
   1480 bytebuffer_puts(struct bytebuffer *b, const char *str)
   1481 {
   1482 	bytebuffer_append(b, str, strlen(str));
   1483 }
   1484 
   1485 static void
   1486 bytebuffer_resize(struct bytebuffer *b, int len)
   1487 {
   1488 	bytebuffer_reserve(b, len);
   1489 	b->len = len;
   1490 }
   1491 
   1492 static void
   1493 bytebuffer_flush(struct bytebuffer *b, int fd)
   1494 {
   1495 	write(fd, b->buf, b->len);
   1496 	bytebuffer_clear(b);
   1497 }
   1498 
   1499 static void
   1500 bytebuffer_truncate(struct bytebuffer *b, int n)
   1501 {
   1502 	if (n <= 0)
   1503 		return;
   1504 	if (n > b->len)
   1505 		n = b->len;
   1506 	const int nmove = b->len - n;
   1507 	memmove(b->buf, b->buf + n, nmove);
   1508 	b->len -= n;
   1509 }