poe

Уређивач .po фајлова
Дневник | Датотеке | Референце | ПРОЧИТАЈМЕ | ЛИЦЕНЦА

termbox.c (15724B)


      1 #define _POSIX_C_SOURCE 200809L
      2 #define _XOPEN_SOURCE
      3 
      4 #include <assert.h>
      5 #include <stdlib.h>
      6 #include <string.h>
      7 #include <errno.h>
      8 #include <fcntl.h>
      9 #include <signal.h>
     10 #include <stdio.h>
     11 #include <stdbool.h>
     12 #include <sys/select.h>
     13 #include <sys/ioctl.h>
     14 #include <sys/time.h>
     15 #include <sys/stat.h>
     16 #include <termios.h>
     17 #include <unistd.h>
     18 #include <wchar.h>
     19 
     20 #include "termbox.h"
     21 
     22 #include "bytebuffer.inl"
     23 #include "term.inl"
     24 #include "input.inl"
     25 
     26 struct cellbuf {
     27 	int width;
     28 	int height;
     29 	struct tb_cell *cells;
     30 };
     31 
     32 #define CELL(buf, x, y) (buf)->cells[(y) * (buf)->width + (x)]
     33 #define IS_CURSOR_HIDDEN(cx, cy) (cx == -1 || cy == -1)
     34 #define LAST_COORD_INIT -1
     35 
     36 static struct termios orig_tios;
     37 
     38 static struct cellbuf back_buffer;
     39 static struct cellbuf front_buffer;
     40 static struct bytebuffer output_buffer;
     41 static struct bytebuffer input_buffer;
     42 
     43 static int termw = -1;
     44 static int termh = -1;
     45 
     46 static int inputmode = TB_INPUT_ESC;
     47 static int outputmode = TB_OUTPUT_NORMAL;
     48 
     49 static int inout;
     50 static int winch_fds[2];
     51 
     52 static int lastx = LAST_COORD_INIT;
     53 static int lasty = LAST_COORD_INIT;
     54 static int cursor_x = -1;
     55 static int cursor_y = -1;
     56 
     57 static uint16_t background = TB_DEFAULT;
     58 static uint16_t foreground = TB_DEFAULT;
     59 
     60 static void write_cursor(int x, int y);
     61 static void write_sgr(uint16_t fg, uint16_t bg);
     62 
     63 static void cellbuf_init(struct cellbuf *buf, int width, int height);
     64 static void cellbuf_resize(struct cellbuf *buf, int width, int height);
     65 static void cellbuf_clear(struct cellbuf *buf);
     66 static void cellbuf_free(struct cellbuf *buf);
     67 
     68 static void update_size(void);
     69 static void update_term_size(void);
     70 static void send_attr(uint16_t fg, uint16_t bg);
     71 static void send_char(int x, int y, uint32_t c);
     72 static void send_clear(void);
     73 static void sigwinch_handler(int xxx);
     74 static int wait_fill_event(struct tb_event *event, struct timeval *timeout);
     75 
     76 /* may happen in a different thread */
     77 static volatile int buffer_size_change_request;
     78 
     79 /* -------------------------------------------------------- */
     80 
     81 int tb_init_fd(int inout_)
     82 {
     83 	inout = inout_;
     84 	if (inout == -1) {
     85 		return TB_EFAILED_TO_OPEN_TTY;
     86 	}
     87 
     88 	if (init_term() < 0) {
     89 		close(inout);
     90 		return TB_EUNSUPPORTED_TERMINAL;
     91 	}
     92 
     93 	if (pipe(winch_fds) < 0) {
     94 		close(inout);
     95 		return TB_EPIPE_TRAP_ERROR;
     96 	}
     97 
     98 	struct sigaction sa;
     99 	memset(&sa, 0, sizeof(sa));
    100 	sa.sa_handler = sigwinch_handler;
    101 	sa.sa_flags = 0;
    102 	sigaction(SIGWINCH, &sa, 0);
    103 
    104 	tcgetattr(inout, &orig_tios);
    105 
    106 	struct termios tios;
    107 	memcpy(&tios, &orig_tios, sizeof(tios));
    108 
    109 	tios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
    110                            | INLCR | IGNCR | ICRNL | IXON);
    111 	tios.c_oflag &= ~OPOST;
    112 	tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    113 	tios.c_cflag &= ~(CSIZE | PARENB);
    114 	tios.c_cflag |= CS8;
    115 	tios.c_cc[VMIN] = 0;
    116 	tios.c_cc[VTIME] = 0;
    117 	tcsetattr(inout, TCSAFLUSH, &tios);
    118 
    119 	bytebuffer_init(&input_buffer, 128);
    120 	bytebuffer_init(&output_buffer, 32 * 1024);
    121 
    122 	bytebuffer_puts(&output_buffer, funcs[T_ENTER_CA]);
    123 	bytebuffer_puts(&output_buffer, funcs[T_ENTER_KEYPAD]);
    124 	bytebuffer_puts(&output_buffer, funcs[T_HIDE_CURSOR]);
    125 	send_clear();
    126 
    127 	update_term_size();
    128 	cellbuf_init(&back_buffer, termw, termh);
    129 	cellbuf_init(&front_buffer, termw, termh);
    130 	cellbuf_clear(&back_buffer);
    131 	cellbuf_clear(&front_buffer);
    132 
    133 	return 0;
    134 }
    135 
    136 int tb_init_file(const char* name){
    137 	return tb_init_fd(open(name, O_RDWR));
    138 }
    139 
    140 int tb_init(void)
    141 {
    142 	return tb_init_file("/dev/tty");
    143 }
    144 
    145 void tb_shutdown(void)
    146 {
    147 	if (termw == -1) {
    148 		fputs("tb_shutdown() should not be called twice.", stderr);
    149 		abort();
    150 	}
    151 
    152 	bytebuffer_puts(&output_buffer, funcs[T_SHOW_CURSOR]);
    153 	bytebuffer_puts(&output_buffer, funcs[T_SGR0]);
    154 	bytebuffer_puts(&output_buffer, funcs[T_CLEAR_SCREEN]);
    155 	bytebuffer_puts(&output_buffer, funcs[T_EXIT_CA]);
    156 	bytebuffer_puts(&output_buffer, funcs[T_EXIT_KEYPAD]);
    157 	bytebuffer_puts(&output_buffer, funcs[T_EXIT_MOUSE]);
    158 	bytebuffer_flush(&output_buffer, inout);
    159 	tcsetattr(inout, TCSAFLUSH, &orig_tios);
    160 
    161 	shutdown_term();
    162 	close(inout);
    163 	close(winch_fds[0]);
    164 	close(winch_fds[1]);
    165 
    166 	cellbuf_free(&back_buffer);
    167 	cellbuf_free(&front_buffer);
    168 	bytebuffer_free(&output_buffer);
    169 	bytebuffer_free(&input_buffer);
    170 	termw = termh = -1;
    171 }
    172 
    173 void tb_present(void)
    174 {
    175 	int x,y,w,i;
    176 	struct tb_cell *back, *front;
    177 
    178 	/* invalidate cursor position */
    179 	lastx = LAST_COORD_INIT;
    180 	lasty = LAST_COORD_INIT;
    181 
    182 	if (buffer_size_change_request) {
    183 		update_size();
    184 		buffer_size_change_request = 0;
    185 	}
    186 
    187 	for (y = 0; y < front_buffer.height; ++y) {
    188 		for (x = 0; x < front_buffer.width; ) {
    189 			back = &CELL(&back_buffer, x, y);
    190 			front = &CELL(&front_buffer, x, y);
    191 			w = wcwidth(back->ch);
    192 			if (w < 1) w = 1;
    193 			if (memcmp(back, front, sizeof(struct tb_cell)) == 0) {
    194 				x += w;
    195 				continue;
    196 			}
    197 			memcpy(front, back, sizeof(struct tb_cell));
    198 			send_attr(back->fg, back->bg);
    199 			if (w > 1 && x >= front_buffer.width - (w - 1)) {
    200 				// Not enough room for wide ch, so send spaces
    201 				for (i = x; i < front_buffer.width; ++i) {
    202 					send_char(i, y, ' ');
    203 				}
    204 			} else {
    205 				send_char(x, y, back->ch);
    206 				for (i = 1; i < w; ++i) {
    207 					front = &CELL(&front_buffer, x + i, y);
    208 					front->ch = 0;
    209 					front->fg = back->fg;
    210 					front->bg = back->bg;
    211 				}
    212 			}
    213 			x += w;
    214 		}
    215 	}
    216 	if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y))
    217 		write_cursor(cursor_x, cursor_y);
    218 	bytebuffer_flush(&output_buffer, inout);
    219 }
    220 
    221 void tb_set_cursor(int cx, int cy)
    222 {
    223 	if (IS_CURSOR_HIDDEN(cursor_x, cursor_y) && !IS_CURSOR_HIDDEN(cx, cy))
    224 		bytebuffer_puts(&output_buffer, funcs[T_SHOW_CURSOR]);
    225 
    226 	if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y) && IS_CURSOR_HIDDEN(cx, cy))
    227 		bytebuffer_puts(&output_buffer, funcs[T_HIDE_CURSOR]);
    228 
    229 	cursor_x = cx;
    230 	cursor_y = cy;
    231 	if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y))
    232 		write_cursor(cursor_x, cursor_y);
    233 }
    234 
    235 void tb_put_cell(int x, int y, const struct tb_cell *cell)
    236 {
    237 	if ((unsigned)x >= (unsigned)back_buffer.width)
    238 		return;
    239 	if ((unsigned)y >= (unsigned)back_buffer.height)
    240 		return;
    241 	CELL(&back_buffer, x, y) = *cell;
    242 }
    243 
    244 void tb_change_cell(int x, int y, uint32_t ch, uint16_t fg, uint16_t bg)
    245 {
    246 	struct tb_cell c = {ch, fg, bg};
    247 	tb_put_cell(x, y, &c);
    248 }
    249 
    250 void tb_blit(int x, int y, int w, int h, const struct tb_cell *cells)
    251 {
    252 	if (x + w < 0 || x >= back_buffer.width)
    253 		return;
    254 	if (y + h < 0 || y >= back_buffer.height)
    255 		return;
    256 	int xo = 0, yo = 0, ww = w, hh = h;
    257 	if (x < 0) {
    258 		xo = -x;
    259 		ww -= xo;
    260 		x = 0;
    261 	}
    262 	if (y < 0) {
    263 		yo = -y;
    264 		hh -= yo;
    265 		y = 0;
    266 	}
    267 	if (ww > back_buffer.width - x)
    268 		ww = back_buffer.width - x;
    269 	if (hh > back_buffer.height - y)
    270 		hh = back_buffer.height - y;
    271 
    272 	int sy;
    273 	struct tb_cell *dst = &CELL(&back_buffer, x, y);
    274 	const struct tb_cell *src = cells + yo * w + xo;
    275 	size_t size = sizeof(struct tb_cell) * ww;
    276 
    277 	for (sy = 0; sy < hh; ++sy) {
    278 		memcpy(dst, src, size);
    279 		dst += back_buffer.width;
    280 		src += w;
    281 	}
    282 }
    283 
    284 struct tb_cell *tb_cell_buffer(void)
    285 {
    286 	return back_buffer.cells;
    287 }
    288 
    289 int tb_poll_event(struct tb_event *event)
    290 {
    291 	return wait_fill_event(event, 0);
    292 }
    293 
    294 int tb_peek_event(struct tb_event *event, int timeout)
    295 {
    296 	struct timeval tv;
    297 	tv.tv_sec = timeout / 1000;
    298 	tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000;
    299 	return wait_fill_event(event, &tv);
    300 }
    301 
    302 int tb_width(void)
    303 {
    304 	return termw;
    305 }
    306 
    307 int tb_height(void)
    308 {
    309 	return termh;
    310 }
    311 
    312 void tb_clear(void)
    313 {
    314 	if (buffer_size_change_request) {
    315 		update_size();
    316 		buffer_size_change_request = 0;
    317 	}
    318 	cellbuf_clear(&back_buffer);
    319 }
    320 
    321 int tb_select_input_mode(int mode)
    322 {
    323 	if (mode) {
    324 		if ((mode & (TB_INPUT_ESC | TB_INPUT_ALT)) == 0)
    325 			mode |= TB_INPUT_ESC;
    326 
    327 		/* technically termbox can handle that, but let's be nice and show here
    328 		   what mode is actually used */
    329 		if ((mode & (TB_INPUT_ESC | TB_INPUT_ALT)) == (TB_INPUT_ESC | TB_INPUT_ALT))
    330 			mode &= ~TB_INPUT_ALT;
    331 
    332 		inputmode = mode;
    333 		if (mode&TB_INPUT_MOUSE) {
    334 			bytebuffer_puts(&output_buffer, funcs[T_ENTER_MOUSE]);
    335 			bytebuffer_flush(&output_buffer, inout);
    336 		} else {
    337 			bytebuffer_puts(&output_buffer, funcs[T_EXIT_MOUSE]);
    338 			bytebuffer_flush(&output_buffer, inout);
    339 		}
    340 	}
    341 	return inputmode;
    342 }
    343 
    344 int tb_select_output_mode(int mode)
    345 {
    346 	if (mode)
    347 		outputmode = mode;
    348 	return outputmode;
    349 }
    350 
    351 void tb_set_clear_attributes(uint16_t fg, uint16_t bg)
    352 {
    353 	foreground = fg;
    354 	background = bg;
    355 }
    356 
    357 /* -------------------------------------------------------- */
    358 
    359 static int convertnum(uint32_t num, char* buf) {
    360 	int i, l = 0;
    361 	int ch;
    362 	do {
    363 		buf[l++] = '0' + (num % 10);
    364 		num /= 10;
    365 	} while (num);
    366 	for(i = 0; i < l / 2; i++) {
    367 		ch = buf[i];
    368 		buf[i] = buf[l - 1 - i];
    369 		buf[l - 1 - i] = ch;
    370 	}
    371 	return l;
    372 }
    373 
    374 #define WRITE_LITERAL(X) bytebuffer_append(&output_buffer, (X), sizeof(X)-1)
    375 #define WRITE_INT(X) bytebuffer_append(&output_buffer, buf, convertnum((X), buf))
    376 
    377 static void write_cursor(int x, int y) {
    378 	char buf[32];
    379 	WRITE_LITERAL("\033[");
    380 	WRITE_INT(y+1);
    381 	WRITE_LITERAL(";");
    382 	WRITE_INT(x+1);
    383 	WRITE_LITERAL("H");
    384 }
    385 
    386 static void write_sgr(uint16_t fg, uint16_t bg) {
    387 	char buf[32];
    388 
    389 	if (fg == TB_DEFAULT && bg == TB_DEFAULT)
    390 		return;
    391 
    392 	switch (outputmode) {
    393 	case TB_OUTPUT_256:
    394 	case TB_OUTPUT_216:
    395 	case TB_OUTPUT_GRAYSCALE:
    396 		WRITE_LITERAL("\033[");
    397 		if (fg != TB_DEFAULT) {
    398 			WRITE_LITERAL("38;5;");
    399 			WRITE_INT(fg);
    400 			if (bg != TB_DEFAULT) {
    401 				WRITE_LITERAL(";");
    402 			}
    403 		}
    404 		if (bg != TB_DEFAULT) {
    405 			WRITE_LITERAL("48;5;");
    406 			WRITE_INT(bg);
    407 		}
    408 		WRITE_LITERAL("m");
    409 		break;
    410 	case TB_OUTPUT_NORMAL:
    411 	default:
    412 		WRITE_LITERAL("\033[");
    413 		if (fg != TB_DEFAULT) {
    414 			WRITE_LITERAL("3");
    415 			WRITE_INT(fg - 1);
    416 			if (bg != TB_DEFAULT) {
    417 				WRITE_LITERAL(";");
    418 			}
    419 		}
    420 		if (bg != TB_DEFAULT) {
    421 			WRITE_LITERAL("4");
    422 			WRITE_INT(bg - 1);
    423 		}
    424 		WRITE_LITERAL("m");
    425 		break;
    426 	}
    427 }
    428 
    429 static void cellbuf_init(struct cellbuf *buf, int width, int height)
    430 {
    431 	buf->cells = (struct tb_cell*)malloc(sizeof(struct tb_cell) * width * height);
    432 	assert(buf->cells);
    433 	buf->width = width;
    434 	buf->height = height;
    435 }
    436 
    437 static void cellbuf_resize(struct cellbuf *buf, int width, int height)
    438 {
    439 	if (buf->width == width && buf->height == height)
    440 		return;
    441 
    442 	int oldw = buf->width;
    443 	int oldh = buf->height;
    444 	struct tb_cell *oldcells = buf->cells;
    445 
    446 	cellbuf_init(buf, width, height);
    447 	cellbuf_clear(buf);
    448 
    449 	int minw = (width < oldw) ? width : oldw;
    450 	int minh = (height < oldh) ? height : oldh;
    451 	int i;
    452 
    453 	for (i = 0; i < minh; ++i) {
    454 		struct tb_cell *csrc = oldcells + (i * oldw);
    455 		struct tb_cell *cdst = buf->cells + (i * width);
    456 		memcpy(cdst, csrc, sizeof(struct tb_cell) * minw);
    457 	}
    458 
    459 	free(oldcells);
    460 }
    461 
    462 static void cellbuf_clear(struct cellbuf *buf)
    463 {
    464 	int i;
    465 	int ncells = buf->width * buf->height;
    466 
    467 	for (i = 0; i < ncells; ++i) {
    468 		buf->cells[i].ch = ' ';
    469 		buf->cells[i].fg = foreground;
    470 		buf->cells[i].bg = background;
    471 	}
    472 }
    473 
    474 static void cellbuf_free(struct cellbuf *buf)
    475 {
    476 	free(buf->cells);
    477 }
    478 
    479 static void get_term_size(int *w, int *h)
    480 {
    481 	struct winsize sz;
    482 	memset(&sz, 0, sizeof(sz));
    483 
    484 	ioctl(inout, TIOCGWINSZ, &sz);
    485 
    486 	if (w) *w = sz.ws_col;
    487 	if (h) *h = sz.ws_row;
    488 }
    489 
    490 static void update_term_size(void)
    491 {
    492 	struct winsize sz;
    493 	memset(&sz, 0, sizeof(sz));
    494 
    495 	ioctl(inout, TIOCGWINSZ, &sz);
    496 
    497 	termw = sz.ws_col;
    498 	termh = sz.ws_row;
    499 }
    500 
    501 static void send_attr(uint16_t fg, uint16_t bg)
    502 {
    503 #define LAST_ATTR_INIT 0xFFFF
    504 	static uint16_t lastfg = LAST_ATTR_INIT, lastbg = LAST_ATTR_INIT;
    505 	if (fg != lastfg || bg != lastbg) {
    506 		bytebuffer_puts(&output_buffer, funcs[T_SGR0]);
    507 
    508 		uint16_t fgcol;
    509 		uint16_t bgcol;
    510 
    511 		switch (outputmode) {
    512 		case TB_OUTPUT_256:
    513 			fgcol = fg & 0xFF;
    514 			bgcol = bg & 0xFF;
    515 			break;
    516 
    517 		case TB_OUTPUT_216:
    518 			fgcol = fg & 0xFF; if (fgcol > 215) fgcol = 7;
    519 			bgcol = bg & 0xFF; if (bgcol > 215) bgcol = 0;
    520 			fgcol += 0x10;
    521 			bgcol += 0x10;
    522 			break;
    523 
    524 		case TB_OUTPUT_GRAYSCALE:
    525 			fgcol = fg & 0xFF; if (fgcol > 23) fgcol = 23;
    526 			bgcol = bg & 0xFF; if (bgcol > 23) bgcol = 0;
    527 			fgcol += 0xe8;
    528 			bgcol += 0xe8;
    529 			break;
    530 
    531 		case TB_OUTPUT_NORMAL:
    532 		default:
    533 			fgcol = fg & 0x0F;
    534 			bgcol = bg & 0x0F;
    535 		}
    536 
    537 		if (fg & TB_BOLD)
    538 			bytebuffer_puts(&output_buffer, funcs[T_BOLD]);
    539 		if (bg & TB_BOLD)
    540 			bytebuffer_puts(&output_buffer, funcs[T_BLINK]);
    541 		if (fg & TB_UNDERLINE)
    542 			bytebuffer_puts(&output_buffer, funcs[T_UNDERLINE]);
    543 		if ((fg & TB_REVERSE) || (bg & TB_REVERSE))
    544 			bytebuffer_puts(&output_buffer, funcs[T_REVERSE]);
    545 
    546 		write_sgr(fgcol, bgcol);
    547 
    548 		lastfg = fg;
    549 		lastbg = bg;
    550 	}
    551 }
    552 
    553 static void send_char(int x, int y, uint32_t c)
    554 {
    555 	char buf[7];
    556 	int bw = tb_utf8_unicode_to_char(buf, c);
    557 	if (x-1 != lastx || y != lasty)
    558 		write_cursor(x, y);
    559 	lastx = x; lasty = y;
    560 	if(!c) buf[0] = ' '; // replace 0 with whitespace
    561 	bytebuffer_append(&output_buffer, buf, bw);
    562 }
    563 
    564 static void send_clear(void)
    565 {
    566 	send_attr(foreground, background);
    567 	bytebuffer_puts(&output_buffer, funcs[T_CLEAR_SCREEN]);
    568 	if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y))
    569 		write_cursor(cursor_x, cursor_y);
    570 	bytebuffer_flush(&output_buffer, inout);
    571 
    572 	/* we need to invalidate cursor position too and these two vars are
    573 	 * used only for simple cursor positioning optimization, cursor
    574 	 * actually may be in the correct place, but we simply discard
    575 	 * optimization once and it gives us simple solution for the case when
    576 	 * cursor moved */
    577 	lastx = LAST_COORD_INIT;
    578 	lasty = LAST_COORD_INIT;
    579 }
    580 
    581 static void sigwinch_handler(int xxx)
    582 {
    583 	(void) xxx;
    584 	const int zzz = 1;
    585 	write(winch_fds[1], &zzz, sizeof(int));
    586 }
    587 
    588 static void update_size(void)
    589 {
    590 	update_term_size();
    591 	cellbuf_resize(&back_buffer, termw, termh);
    592 	cellbuf_resize(&front_buffer, termw, termh);
    593 	cellbuf_clear(&front_buffer);
    594 	send_clear();
    595 }
    596 
    597 static int read_up_to(int n) {
    598 	assert(n > 0);
    599 	const int prevlen = input_buffer.len;
    600 	bytebuffer_resize(&input_buffer, prevlen + n);
    601 
    602 	int read_n = 0;
    603 	while (read_n <= n) {
    604 		ssize_t r = 0;
    605 		if (read_n < n) {
    606 			r = read(inout, input_buffer.buf + prevlen + read_n, n - read_n);
    607 		}
    608 #ifdef __CYGWIN__
    609 		// While linux man for tty says when VMIN == 0 && VTIME == 0, read
    610 		// should return 0 when there is nothing to read, cygwin's read returns
    611 		// -1. Not sure why and if it's correct to ignore it, but let's pretend
    612 		// it's zero.
    613 		if (r < 0) r = 0;
    614 #endif
    615 		if (r < 0) {
    616 			// EAGAIN / EWOULDBLOCK shouldn't occur here
    617 			assert(errno != EAGAIN && errno != EWOULDBLOCK);
    618 			return -1;
    619 		} else if (r > 0) {
    620 			read_n += r;
    621 		} else {
    622 			bytebuffer_resize(&input_buffer, prevlen + read_n);
    623 			return read_n;
    624 		}
    625 	}
    626 	assert(!"unreachable");
    627 	return 0;
    628 }
    629 
    630 static int wait_fill_event(struct tb_event *event, struct timeval *timeout)
    631 {
    632 	// ;-)
    633 #define ENOUGH_DATA_FOR_PARSING 64
    634 	fd_set events;
    635 	memset(event, 0, sizeof(struct tb_event));
    636 
    637 	// try to extract event from input buffer, return on success
    638 	event->type = TB_EVENT_KEY;
    639 	if (extract_event(event, &input_buffer, inputmode))
    640 		return event->type;
    641 
    642 	// it looks like input buffer is incomplete, let's try the short path,
    643 	// but first make sure there is enough space
    644 	int n = read_up_to(ENOUGH_DATA_FOR_PARSING);
    645 	if (n < 0)
    646 		return -1;
    647 	if (n > 0 && extract_event(event, &input_buffer, inputmode))
    648 		return event->type;
    649 
    650 	// n == 0, or not enough data, let's go to select
    651 	while (1) {
    652 		FD_ZERO(&events);
    653 		FD_SET(inout, &events);
    654 		FD_SET(winch_fds[0], &events);
    655 		int maxfd = (winch_fds[0] > inout) ? winch_fds[0] : inout;
    656 		int result = select(maxfd+1, &events, 0, 0, timeout);
    657 		if (!result)
    658 			return 0;
    659 
    660 		if (FD_ISSET(inout, &events)) {
    661 			event->type = TB_EVENT_KEY;
    662 			n = read_up_to(ENOUGH_DATA_FOR_PARSING);
    663 			if (n < 0)
    664 				return -1;
    665 
    666 			if (n == 0)
    667 				continue;
    668 
    669 			if (extract_event(event, &input_buffer, inputmode))
    670 				return event->type;
    671 		}
    672 		if (FD_ISSET(winch_fds[0], &events)) {
    673 			event->type = TB_EVENT_RESIZE;
    674 			int zzz = 0;
    675 			read(winch_fds[0], &zzz, sizeof(int));
    676 			buffer_size_change_request = 1;
    677 			get_term_size(&event->w, &event->h);
    678 			return TB_EVENT_RESIZE;
    679 		}
    680 	}
    681 }