commit 3cca8cd87590b0bf9b15af1aba58a6b1fc1c5820
parent d8542d315fbe1910f0757f4358d9eee165104195
Author: sin <sin@2f30.org>
Date: Sun, 3 Mar 2013 03:42:18 +0200
Add termbox library
source: https://github.com/nsf/termbox/
Diffstat:
13 files changed, 1333 insertions(+), 9 deletions(-)
diff --git a/Makefile b/Makefile
@@ -2,6 +2,8 @@ BIN = memzap
VER = 0.1
SRC = memzap.c mem.c utils.c md5.c mdiff.c linux_ops.c
OBJ = ${SRC:.c=.o}
+TERMBOX_SRC = $(wildcard termbox/*.c)
+TERMBOX_OBJ = ${TERMBOX_SRC:.c=.o}
PREFIX = /usr
@@ -13,14 +15,17 @@ LIBS = -L/usr/local/lib
CFLAGS += -g -O3 -Wall -Wextra -Wunused -DVERSION=\"${VER}\" ${INCS}
LDFLAGS +=
-${BIN}: ${OBJ}
- ${CC} ${CFLAGS} ${LDFLAGS} -o $@ ${OBJ}
+${BIN}: ${OBJ} ${TERMBOX_OBJ}
+ ${CC} ${CFLAGS} ${LDFLAGS} -o $@ ${OBJ} ${TERMBOX_OBJ}
+
+termbox/%.o: termbox/%.c $(wildcard termbox/*.h)
+ ${CC} ${CFLAGS} -Itermbox -c -o $@ $<
%.o: %.c
${CC} ${CFLAGS} -c -o $@ $<
clean:
- rm -rf ${BIN} ${OBJ}
+ rm -rf ${BIN} ${OBJ} ${TERMBOX_OBJ}
all: memzap
diff --git a/Makefile.linux b/Makefile.linux
@@ -2,6 +2,8 @@ BIN = memzap
VER = 0.1
SRC = memzap.c mem.c utils.c md5.c mdiff.c linux_ops.c
OBJ = ${SRC:.c=.o}
+TERMBOX_SRC = $(wildcard termbox/*.c)
+TERMBOX_OBJ = ${TERMBOX_SRC:.c=.o}
PREFIX = /usr
@@ -13,14 +15,17 @@ LIBS = -L/usr/local/lib
CFLAGS += -g -O3 -Wall -Wextra -Wunused -DVERSION=\"${VER}\" ${INCS}
LDFLAGS +=
-${BIN}: ${OBJ}
- ${CC} ${CFLAGS} ${LDFLAGS} -o $@ ${OBJ}
+${BIN}: ${OBJ} ${TERMBOX_OBJ}
+ ${CC} ${CFLAGS} ${LDFLAGS} -o $@ ${OBJ} ${TERMBOX_OBJ}
+
+termbox/%.o: termbox/%.c $(wildcard termbox/*.h)
+ ${CC} ${CFLAGS} -Itermbox -c -o $@ $<
%.o: %.c
${CC} ${CFLAGS} -c -o $@ $<
clean:
- rm -rf ${BIN} ${OBJ}
+ rm -rf ${BIN} ${OBJ} ${TERMBOX_OBJ}
all: memzap
diff --git a/Makefile.openbsd b/Makefile.openbsd
@@ -2,6 +2,8 @@ BIN = memzap
VER = 0.1
SRC = memzap.c mem.c utils.c md5.c mdiff.c openbsd_ops.c
OBJ = ${SRC:.c=.o}
+TERMBOX_SRC = $(wildcard termbox/*.c)
+TERMBOX_OBJ = ${TERMBOX_SRC:.c=.o}
PREFIX = /usr
@@ -13,14 +15,17 @@ LIBS = -L/usr/local/lib
CFLAGS += -g -O3 -Wall -Wextra -Wunused -DVERSION=\"${VER}\" ${INCS}
LDFLAGS +=
-${BIN}: ${OBJ}
- ${CC} ${CFLAGS} ${LDFLAGS} -o $@ ${OBJ}
+${BIN}: ${OBJ} ${TERMBOX_OBJ}
+ ${CC} ${CFLAGS} ${LDFLAGS} -o $@ ${OBJ} ${TERMBOX_OBJ}
+
+termbox/%.o: termbox/%.c $(wildcard termbox/*.h)
+ ${CC} ${CFLAGS} -Itermbox -c -o $@ $<
%.o: %.c
${CC} ${CFLAGS} -c -o $@ $<
clean:
- rm -rf ${BIN} ${OBJ}
+ rm -rf ${BIN} ${OBJ} ${TERMBOX_OBJ}
all: memzap
diff --git a/termbox/input.c b/termbox/input.c
@@ -0,0 +1,100 @@
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "term.h"
+
+#define BUFFER_SIZE_MAX 16
+
+/* if s1 starts with s2 returns 1, else 0 */
+static int starts_with(const char *s1, const char *s2)
+{
+ /* nice huh? */
+ while (*s2) if (*s1++ != *s2++) return 0; return 1;
+}
+
+/* convert escape sequence to event, and return consumed bytes on success (failure == 0) */
+static int parse_escape_seq(struct tb_event *event, const char *buf)
+{
+ /* it's pretty simple here, find 'starts_with' match and return success, else return failure */
+ int i;
+ for (i = 0; keys[i]; i++) {
+ if (starts_with(buf, keys[i])) {
+ event->ch = 0;
+ event->key = 0xFFFF-i;
+ return strlen(keys[i]);
+ }
+ }
+ return 0;
+}
+
+bool extract_event(struct tb_event *event, struct ringbuffer *inbuf, int inputmode)
+{
+ char buf[BUFFER_SIZE_MAX+1];
+ int nbytes = ringbuffer_data_size(inbuf);
+
+ if (nbytes > BUFFER_SIZE_MAX)
+ nbytes = BUFFER_SIZE_MAX;
+
+ if (nbytes == 0)
+ return false;
+
+ ringbuffer_read(inbuf, buf, nbytes);
+ buf[nbytes] = '\0';
+
+ if (buf[0] == '\033') {
+ int n = parse_escape_seq(event, buf);
+ if (n) {
+ ringbuffer_pop(inbuf, 0, n);
+ return true;
+ } else {
+ /* it's not escape sequence, then it's ALT or ESC, check inputmode */
+ switch (inputmode) {
+ case TB_INPUT_ESC:
+ /* if we're in escape mode, fill ESC event, pop buffer, return success */
+ event->ch = 0;
+ event->key = TB_KEY_ESC;
+ event->mod = 0;
+ ringbuffer_pop(inbuf, 0, 1);
+ return true;
+ case TB_INPUT_ALT:
+ /* if we're in alt mode, set ALT modifier to event and redo parsing */
+ event->mod = TB_MOD_ALT;
+ ringbuffer_pop(inbuf, 0, 1);
+ return extract_event(event, inbuf, inputmode);
+ default:
+ assert(!"never got here");
+ break;
+ }
+ }
+ }
+
+ /* if we're here, this is not an escape sequence and not an alt sequence
+ * so, it's a FUNCTIONAL KEY or a UNICODE character
+ */
+
+ /* first of all check if it's a functional key */
+ if ((unsigned char)buf[0] <= TB_KEY_SPACE ||
+ (unsigned char)buf[0] == TB_KEY_BACKSPACE2)
+ {
+ /* fill event, pop buffer, return success */
+ event->ch = 0;
+ event->key = (uint16_t)buf[0];
+ ringbuffer_pop(inbuf, 0, 1);
+ return true;
+ }
+
+ /* feh... we got utf8 here */
+
+ /* check if there is all bytes */
+ if (nbytes >= utf8_char_length(buf[0])) {
+ /* everything ok, fill event, pop buffer, return success */
+ utf8_char_to_unicode(&event->ch, buf);
+ event->key = 0;
+ ringbuffer_pop(inbuf, 0, utf8_char_length(buf[0]));
+ return true;
+ }
+
+ /* fuck!!!!1111odin1odinodin */
+ return false;
+}
diff --git a/termbox/memstream.c b/termbox/memstream.c
@@ -0,0 +1,27 @@
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "memstream.h"
+
+void memstream_init(struct memstream *s, int fd, void* buffer, size_t len) {
+ s->file = fd,
+ s->data = buffer;
+ s->pos = 0;
+ s->capa = len;
+}
+
+void memstream_flush(struct memstream *s) {
+ write(s->file, s->data, s->pos);
+ s->pos = 0;
+}
+
+void memstream_write(struct memstream *s, void* source, size_t len) {
+ unsigned char* data = source;
+ if(s->pos + len > s->capa) memstream_flush(s);
+ memcpy(s->data + s->pos, data, len);
+ s->pos += len;
+}
+
+void memstream_puts(struct memstream *s, const char* str) {
+ memstream_write(s, (void*) str, strlen(str));
+}
diff --git a/termbox/memstream.h b/termbox/memstream.h
@@ -0,0 +1,19 @@
+#ifndef MEMSTREAM_H
+#define MEMSTREAM_H
+
+#include <stddef.h>
+#include <stdio.h>
+
+struct memstream {
+ size_t pos;
+ size_t capa;
+ int file;
+ unsigned char* data;
+};
+
+void memstream_init(struct memstream *s, int fd, void* buffer, size_t len);
+void memstream_flush(struct memstream *s);
+void memstream_write(struct memstream *s, void* source, size_t len);
+void memstream_puts(struct memstream *s, const char* str);
+
+#endif
diff --git a/termbox/ringbuffer.c b/termbox/ringbuffer.c
@@ -0,0 +1,135 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+/* for ptrdiff_t */
+#include <stddef.h>
+
+#include <string.h>
+
+#include "ringbuffer.h"
+
+int init_ringbuffer(struct ringbuffer *r, size_t size)
+{
+ r->buf = (char*)malloc(size);
+ if (!r->buf)
+ return ERINGBUFFER_ALLOC_FAIL;
+ r->size = size;
+ clear_ringbuffer(r);
+
+ return 0;
+}
+
+void free_ringbuffer(struct ringbuffer *r)
+{
+ free(r->buf);
+}
+
+void clear_ringbuffer(struct ringbuffer *r)
+{
+ r->begin = 0;
+ r->end = 0;
+}
+
+size_t ringbuffer_free_space(struct ringbuffer *r)
+{
+ if (r->begin == 0 && r->end == 0)
+ return r->size;
+
+ if (r->begin < r->end)
+ return r->size - (r->end - r->begin) - 1;
+ else
+ return r->begin - r->end - 1;
+}
+
+size_t ringbuffer_data_size(struct ringbuffer *r)
+{
+ if (r->begin == 0 && r->end == 0)
+ return 0;
+
+ if (r->begin <= r->end)
+ return r->end - r->begin + 1;
+ else
+ return r->size - (r->begin - r->end) + 1;
+}
+
+
+void ringbuffer_push(struct ringbuffer *r, const void *data, size_t size)
+{
+ if (ringbuffer_free_space(r) < size)
+ return;
+
+ if (r->begin == 0 && r->end == 0) {
+ memcpy(r->buf, data, size);
+ r->begin = r->buf;
+ r->end = r->buf + size - 1;
+ return;
+ }
+
+ r->end++;
+ if (r->begin < r->end) {
+ if ((size_t)(r->buf + (ptrdiff_t)r->size - r->begin) >= size) {
+ /* we can fit without cut */
+ memcpy(r->end, data, size);
+ r->end += size - 1;
+ } else {
+ /* make a cut */
+ size_t s = r->buf + r->size - r->end;
+ memcpy(r->end, data, s);
+ size -= s;
+ memcpy(r->buf, (char*)data+s, size);
+ r->end = r->buf + size - 1;
+ }
+ } else {
+ memcpy(r->end, data, size);
+ r->end += size - 1;
+ }
+}
+
+void ringbuffer_pop(struct ringbuffer *r, void *data, size_t size)
+{
+ if (ringbuffer_data_size(r) < size)
+ return;
+
+ int need_clear = 0;
+ if (ringbuffer_data_size(r) == size)
+ need_clear = 1;
+
+ if (r->begin < r->end) {
+ if (data) memcpy(data, r->begin, size);
+ r->begin += size;
+ } else {
+ if ((size_t)(r->buf + (ptrdiff_t)r->size - r->begin) >= size) {
+ if (data) memcpy(data, r->begin, size);
+ r->begin += size;
+ } else {
+ size_t s = r->buf + r->size - r->begin;
+ if (data) memcpy(data, r->begin, s);
+ size -= s;
+ if (data) memcpy((char*)data+s, r->buf, size);
+ r->begin = r->buf + size;
+ }
+ }
+
+ if (need_clear)
+ clear_ringbuffer(r);
+}
+
+void ringbuffer_read(struct ringbuffer *r, void *data, size_t size)
+{
+ if (ringbuffer_data_size(r) < size)
+ return;
+
+ if (r->begin < r->end)
+ memcpy(data, r->begin, size);
+ else {
+ if ((size_t)(r->buf + (ptrdiff_t)r->size - r->begin) >= size)
+ memcpy(data, r->begin, size);
+ else {
+ size_t s = r->buf + r->size - r->begin;
+ memcpy(data, r->begin, s);
+ size -= s;
+ memcpy((char*)data+s, r->buf, size);
+ }
+ }
+}
+
diff --git a/termbox/ringbuffer.h b/termbox/ringbuffer.h
@@ -0,0 +1,23 @@
+#ifndef TERMBOX_RINGBUFFER_H
+#define TERMBOX_RINGBUFFER_H
+
+#define ERINGBUFFER_ALLOC_FAIL -1
+
+struct ringbuffer {
+ char *buf;
+ size_t size;
+
+ char *begin;
+ char *end;
+};
+
+int init_ringbuffer(struct ringbuffer *r, size_t size);
+void free_ringbuffer(struct ringbuffer *r);
+void clear_ringbuffer(struct ringbuffer *r);
+size_t ringbuffer_free_space(struct ringbuffer *r);
+size_t ringbuffer_data_size(struct ringbuffer *r);
+void ringbuffer_push(struct ringbuffer *r, const void *data, size_t size);
+void ringbuffer_pop(struct ringbuffer *r, void *data, size_t size);
+void ringbuffer_read(struct ringbuffer *r, void *data, size_t size);
+
+#endif
diff --git a/termbox/term.c b/termbox/term.c
@@ -0,0 +1,174 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "term.h"
+
+/* Eterm */
+static const char *Eterm_keys[] = {
+ "\033[11~","\033[12~","\033[13~","\033[14~","\033[15~","\033[17~","\033[18~","\033[19~","\033[20~","\033[21~","\033[23~","\033[24~","\033[2~","\033[3~","\033[7~","\033[8~","\033[5~","\033[6~","\033[A","\033[B","\033[D","\033[C", 0
+};
+static const char *Eterm_funcs[] = {
+ [T_ENTER_CA] = "\0337\033[?47h",
+ [T_EXIT_CA] = "\033[2J\033[?47l\0338",
+ [T_SHOW_CURSOR] = "\033[?25h",
+ [T_HIDE_CURSOR] = "\033[?25l",
+ [T_CLEAR_SCREEN] = "\033[H\033[2J",
+ [T_SGR0] = "\033[m",
+ [T_UNDERLINE] = "\033[4m",
+ [T_BOLD] = "\033[1m",
+ [T_BLINK] = "\033[5m",
+ [T_ENTER_KEYPAD] = "",
+ [T_EXIT_KEYPAD] = "",
+};
+
+/* screen */
+static const char *screen_keys[] = {
+ "\033OP","\033OQ","\033OR","\033OS","\033[15~","\033[17~","\033[18~","\033[19~","\033[20~","\033[21~","\033[23~","\033[24~","\033[2~","\033[3~","\033[1~","\033[4~","\033[5~","\033[6~","\033OA","\033OB","\033OD","\033OC", 0
+};
+static const char *screen_funcs[] = {
+ [T_ENTER_CA] = "\033[?1049h",
+ [T_EXIT_CA] = "\033[?1049l",
+ [T_SHOW_CURSOR] = "\033[34h\033[?25h",
+ [T_HIDE_CURSOR] = "\033[?25l",
+ [T_CLEAR_SCREEN] = "\033[H\033[J",
+ [T_SGR0] = "\033[m",
+ [T_UNDERLINE] = "\033[4m",
+ [T_BOLD] = "\033[1m",
+ [T_BLINK] = "\033[5m",
+ [T_ENTER_KEYPAD] = "\033[?1h\033=",
+ [T_EXIT_KEYPAD] = "\033[?1l\033>",
+};
+
+/* xterm */
+static const char *xterm_keys[] = {
+ "\033OP","\033OQ","\033OR","\033OS","\033[15~","\033[17~","\033[18~","\033[19~","\033[20~","\033[21~","\033[23~","\033[24~","\033[2~","\033[3~","\033OH","\033OF","\033[5~","\033[6~","\033OA","\033OB","\033OD","\033OC", 0
+};
+static const char *xterm_funcs[] = {
+ [T_ENTER_CA] = "\033[?1049h",
+ [T_EXIT_CA] = "\033[?1049l",
+ [T_SHOW_CURSOR] = "\033[?12l\033[?25h",
+ [T_HIDE_CURSOR] = "\033[?25l",
+ [T_CLEAR_SCREEN] = "\033[H\033[2J",
+ [T_SGR0] = "\033(B\033[m",
+ [T_UNDERLINE] = "\033[4m",
+ [T_BOLD] = "\033[1m",
+ [T_BLINK] = "\033[5m",
+ [T_ENTER_KEYPAD] = "\033[?1h\033=",
+ [T_EXIT_KEYPAD] = "\033[?1l\033>",
+};
+
+/* rxvt-unicode */
+static const char *rxvt_unicode_keys[] = {
+ "\033[11~","\033[12~","\033[13~","\033[14~","\033[15~","\033[17~","\033[18~","\033[19~","\033[20~","\033[21~","\033[23~","\033[24~","\033[2~","\033[3~","\033[7~","\033[8~","\033[5~","\033[6~","\033[A","\033[B","\033[D","\033[C", 0
+};
+static const char *rxvt_unicode_funcs[] = {
+ [T_ENTER_CA] = "\033[?1049h",
+ [T_EXIT_CA] = "\033[r\033[?1049l",
+ [T_SHOW_CURSOR] = "\033[?25h",
+ [T_HIDE_CURSOR] = "\033[?25l",
+ [T_CLEAR_SCREEN] = "\033[H\033[2J",
+ [T_SGR0] = "\033[m\033(B",
+ [T_UNDERLINE] = "\033[4m",
+ [T_BOLD] = "\033[1m",
+ [T_BLINK] = "\033[5m",
+ [T_ENTER_KEYPAD] = "\033=",
+ [T_EXIT_KEYPAD] = "\033>",
+};
+
+/* linux */
+static const char *linux_keys[] = {
+ "\033[[A","\033[[B","\033[[C","\033[[D","\033[[E","\033[17~","\033[18~","\033[19~","\033[20~","\033[21~","\033[23~","\033[24~","\033[2~","\033[3~","\033[1~","\033[4~","\033[5~","\033[6~","\033[A","\033[B","\033[D","\033[C", 0
+};
+static const char *linux_funcs[] = {
+ [T_ENTER_CA] = "",
+ [T_EXIT_CA] = "",
+ [T_SHOW_CURSOR] = "\033[?25h\033[?0c",
+ [T_HIDE_CURSOR] = "\033[?25l\033[?1c",
+ [T_CLEAR_SCREEN] = "\033[H\033[J",
+ [T_SGR0] = "\033[0;10m",
+ [T_UNDERLINE] = "\033[4m",
+ [T_BOLD] = "\033[1m",
+ [T_BLINK] = "\033[5m",
+ [T_ENTER_KEYPAD] = "",
+ [T_EXIT_KEYPAD] = "",
+};
+
+/* rxvt-256color */
+static const char *rxvt_256color_keys[] = {
+ "\033[11~","\033[12~","\033[13~","\033[14~","\033[15~","\033[17~","\033[18~","\033[19~","\033[20~","\033[21~","\033[23~","\033[24~","\033[2~","\033[3~","\033[7~","\033[8~","\033[5~","\033[6~","\033[A","\033[B","\033[D","\033[C", 0
+};
+static const char *rxvt_256color_funcs[] = {
+ [T_ENTER_CA] = "\0337\033[?47h",
+ [T_EXIT_CA] = "\033[2J\033[?47l\0338",
+ [T_SHOW_CURSOR] = "\033[?25h",
+ [T_HIDE_CURSOR] = "\033[?25l",
+ [T_CLEAR_SCREEN] = "\033[H\033[2J",
+ [T_SGR0] = "\033[m",
+ [T_UNDERLINE] = "\033[4m",
+ [T_BOLD] = "\033[1m",
+ [T_BLINK] = "\033[5m",
+ [T_ENTER_KEYPAD] = "\033=",
+ [T_EXIT_KEYPAD] = "\033>",
+};
+
+static struct term {
+ const char *name;
+ const char **keys;
+ const char **funcs;
+} terms[] = {
+ {"Eterm", Eterm_keys, Eterm_funcs},
+ {"screen", screen_keys, screen_funcs},
+ {"xterm", xterm_keys, xterm_funcs},
+ {"rxvt-unicode", rxvt_unicode_keys, rxvt_unicode_funcs},
+ {"linux", linux_keys, linux_funcs},
+ {"rxvt-256color", rxvt_256color_keys, rxvt_256color_funcs},
+ {0,0,0}
+};
+
+const char **keys;
+const char **funcs;
+
+static int try_compatible(const char *term, const char *name,
+ const char **tkeys, const char **tfuncs)
+{
+ if (strstr(term, name)) {
+ keys = tkeys;
+ funcs = tfuncs;
+ return 0;
+ }
+
+ return EUNSUPPORTED_TERM;
+}
+
+int init_term(void)
+{
+ int i;
+ const char *term = getenv("TERM");
+
+ if (term) {
+ for (i = 0; terms[i].name; i++) {
+ if (!strcmp(terms[i].name, term)) {
+ keys = terms[i].keys;
+ funcs = terms[i].funcs;
+ return 0;
+ }
+ }
+
+ /* let's do some heuristic, maybe it's a compatible terminal */
+ if (try_compatible(term, "xterm", xterm_keys, xterm_funcs) == 0)
+ return 0;
+ if (try_compatible(term, "rxvt", rxvt_unicode_keys, rxvt_unicode_funcs) == 0)
+ return 0;
+ if (try_compatible(term, "linux", linux_keys, linux_funcs) == 0)
+ return 0;
+ if (try_compatible(term, "Eterm", Eterm_keys, Eterm_funcs) == 0)
+ return 0;
+ if (try_compatible(term, "screen", screen_keys, screen_funcs) == 0)
+ return 0;
+ /* let's assume that 'cygwin' is xterm compatible */
+ if (try_compatible(term, "cygwin", xterm_keys, xterm_funcs) == 0)
+ return 0;
+ }
+
+ return EUNSUPPORTED_TERM;
+}
diff --git a/termbox/term.h b/termbox/term.h
@@ -0,0 +1,31 @@
+#ifndef TERMBOX_TERM_H
+#define TERMBOX_TERM_H
+
+#include "termbox.h"
+#include "ringbuffer.h"
+
+enum {
+ T_ENTER_CA,
+ T_EXIT_CA,
+ T_SHOW_CURSOR,
+ T_HIDE_CURSOR,
+ T_CLEAR_SCREEN,
+ T_SGR0,
+ T_UNDERLINE,
+ T_BOLD,
+ T_BLINK,
+ T_ENTER_KEYPAD,
+ T_EXIT_KEYPAD
+};
+
+#define EUNSUPPORTED_TERM -1
+
+int init_term(void);
+
+/* true on success, false on failure */
+bool extract_event(struct tb_event *event, struct ringbuffer *inbuf, int inputmode);
+
+extern const char **keys;
+extern const char **funcs;
+
+#endif
diff --git a/termbox/termbox.c b/termbox/termbox.c
@@ -0,0 +1,536 @@
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "term.h"
+#include "termbox.h"
+#include "memstream.h"
+
+struct cellbuf {
+ unsigned int width;
+ unsigned int height;
+ struct tb_cell *cells;
+};
+
+#define CELL(buf, x, y) (buf)->cells[(y) * (buf)->width + (x)]
+#define IS_CURSOR_HIDDEN(cx, cy) (cx == -1 || cy == -1)
+
+#define LAST_COORD_INIT 0xFFFFFFFE
+
+static struct termios orig_tios;
+
+static struct cellbuf back_buffer;
+static struct cellbuf front_buffer;
+static unsigned char write_buffer_data[32 * 1024];
+static struct memstream write_buffer;
+
+static unsigned int termw;
+static unsigned int termh;
+
+static int inputmode = TB_INPUT_ESC;
+
+static struct ringbuffer inbuf;
+
+static int out;
+static FILE *in;
+
+static int out_fileno;
+static int in_fileno;
+
+static int winch_fds[2];
+
+static unsigned int lastx = LAST_COORD_INIT;
+static unsigned int lasty = LAST_COORD_INIT;
+static int cursor_x = -1;
+static int cursor_y = -1;
+
+static uint16_t background = TB_DEFAULT;
+static uint16_t foreground = TB_DEFAULT;
+
+static void write_cursor(unsigned x, unsigned y);
+static void write_sgr(uint32_t fg, uint32_t bg);
+
+static void cellbuf_init(struct cellbuf *buf, unsigned int width, unsigned int height);
+static void cellbuf_resize(struct cellbuf *buf, unsigned int width, unsigned int height);
+static void cellbuf_clear(struct cellbuf *buf);
+static void cellbuf_free(struct cellbuf *buf);
+
+static void update_size(void);
+static void update_term_size(void);
+static void send_attr(uint16_t fg, uint16_t bg);
+static void send_char(unsigned int x, unsigned int y, uint32_t c);
+static void send_clear(void);
+static void sigwinch_handler(int xxx);
+static int wait_fill_event(struct tb_event *event, struct timeval *timeout);
+
+/* may happen in a different thread */
+static volatile int buffer_size_change_request;
+
+/* -------------------------------------------------------- */
+
+int tb_init(void)
+{
+ out = open("/dev/tty", O_WRONLY);
+ in = fopen("/dev/tty", "r");
+
+ if (out == -1 || !in) {
+ if(out != -1)
+ close(out);
+
+ if(in)
+ fclose(in);
+
+ return TB_EFAILED_TO_OPEN_TTY;
+ }
+
+ out_fileno = out;
+ in_fileno = fileno(in);
+
+ if (init_term() < 0) {
+ close(out);
+ fclose(in);
+
+ return TB_EUNSUPPORTED_TERMINAL;
+ }
+
+ if (pipe(winch_fds) < 0) {
+ close(out);
+ fclose(in);
+
+ return TB_EPIPE_TRAP_ERROR;
+ }
+
+ struct sigaction sa;
+ sa.sa_handler = sigwinch_handler;
+ sa.sa_flags = 0;
+ sigaction(SIGWINCH, &sa, 0);
+
+ tcgetattr(out_fileno, &orig_tios);
+
+ struct termios tios;
+ memcpy(&tios, &orig_tios, sizeof(tios));
+
+ tios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
+ | INLCR | IGNCR | ICRNL | IXON);
+ tios.c_oflag &= ~OPOST;
+ tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
+ tios.c_cflag &= ~(CSIZE | PARENB);
+ tios.c_cflag |= CS8;
+ tios.c_cc[VMIN] = 0;
+ tios.c_cc[VTIME] = 0;
+ tcsetattr(out_fileno, TCSAFLUSH, &tios);
+
+ memstream_init(&write_buffer, out_fileno, write_buffer_data, sizeof(write_buffer_data));
+
+ memstream_puts(&write_buffer, funcs[T_ENTER_CA]);
+ memstream_puts(&write_buffer, funcs[T_ENTER_KEYPAD]);
+ memstream_puts(&write_buffer, funcs[T_HIDE_CURSOR]);
+ send_clear();
+
+ update_term_size();
+ cellbuf_init(&back_buffer, termw, termh);
+ cellbuf_init(&front_buffer, termw, termh);
+ cellbuf_clear(&back_buffer);
+ cellbuf_clear(&front_buffer);
+ init_ringbuffer(&inbuf, 4096);
+
+ return 0;
+}
+
+void tb_shutdown(void)
+{
+ memstream_puts(&write_buffer, funcs[T_SHOW_CURSOR]);
+ memstream_puts(&write_buffer, funcs[T_SGR0]);
+ memstream_puts(&write_buffer, funcs[T_CLEAR_SCREEN]);
+ memstream_puts(&write_buffer, funcs[T_EXIT_CA]);
+ memstream_puts(&write_buffer, funcs[T_EXIT_KEYPAD]);
+ memstream_flush(&write_buffer);
+ tcsetattr(out_fileno, TCSAFLUSH, &orig_tios);
+
+ close(out);
+ fclose(in);
+ close(winch_fds[0]);
+ close(winch_fds[1]);
+
+ cellbuf_free(&back_buffer);
+ cellbuf_free(&front_buffer);
+ free_ringbuffer(&inbuf);
+}
+
+void tb_present(void)
+{
+ unsigned int x,y;
+ struct tb_cell *back, *front;
+
+ /* invalidate cursor position */
+ lastx = LAST_COORD_INIT;
+ lasty = LAST_COORD_INIT;
+
+ if (buffer_size_change_request) {
+ update_size();
+ buffer_size_change_request = 0;
+ }
+
+ for (y = 0; y < front_buffer.height; ++y) {
+ for (x = 0; x < front_buffer.width; ++x) {
+ back = &CELL(&back_buffer, x, y);
+ front = &CELL(&front_buffer, x, y);
+ if (memcmp(back, front, sizeof(struct tb_cell)) == 0)
+ continue;
+ send_attr(back->fg, back->bg);
+ send_char(x, y, back->ch);
+ memcpy(front, back, sizeof(struct tb_cell));
+ }
+ }
+ if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y))
+ write_cursor(cursor_x, cursor_y);
+ memstream_flush(&write_buffer);
+}
+
+void tb_set_cursor(int cx, int cy)
+{
+ if (IS_CURSOR_HIDDEN(cursor_x, cursor_y) && !IS_CURSOR_HIDDEN(cx, cy))
+ memstream_puts(&write_buffer, funcs[T_SHOW_CURSOR]);
+
+ if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y) && IS_CURSOR_HIDDEN(cx, cy))
+ memstream_puts(&write_buffer, funcs[T_HIDE_CURSOR]);
+
+ cursor_x = cx;
+ cursor_y = cy;
+ if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y))
+ write_cursor(cursor_x, cursor_y);
+}
+
+void tb_put_cell(unsigned int x, unsigned int y, const struct tb_cell *cell)
+{
+ if (x >= back_buffer.width || y >= back_buffer.height)
+ return;
+ CELL(&back_buffer, x, y) = *cell;
+}
+
+void tb_change_cell(unsigned int x, unsigned int y, uint32_t ch, uint16_t fg, uint16_t bg)
+{
+ struct tb_cell c = {ch, fg, bg};
+ tb_put_cell(x, y, &c);
+}
+
+void tb_blit(unsigned int x, unsigned int y, unsigned int w, unsigned int h, const struct tb_cell *cells)
+{
+ if (x+w > back_buffer.width || y+h > back_buffer.height)
+ return;
+
+ unsigned int sy;
+ struct tb_cell *dst = &CELL(&back_buffer, x, y);
+ size_t size = sizeof(struct tb_cell) * w;
+
+ for (sy = 0; sy < h; ++sy) {
+ memcpy(dst, cells, size);
+ dst += back_buffer.width;
+ cells += w;
+ }
+}
+
+int tb_poll_event(struct tb_event *event)
+{
+ return wait_fill_event(event, 0);
+}
+
+int tb_peek_event(struct tb_event *event, unsigned int timeout)
+{
+ struct timeval tv;
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000;
+ return wait_fill_event(event, &tv);
+}
+
+unsigned int tb_width(void)
+{
+ return termw;
+}
+
+unsigned int tb_height(void)
+{
+ return termh;
+}
+
+void tb_clear(void)
+{
+ if (buffer_size_change_request) {
+ update_size();
+ buffer_size_change_request = 0;
+ }
+ cellbuf_clear(&back_buffer);
+}
+
+int tb_select_input_mode(int mode)
+{
+ if (mode)
+ inputmode = mode;
+ return inputmode;
+}
+
+void tb_set_clear_attributes(uint16_t fg, uint16_t bg)
+{
+ foreground = fg;
+ background = bg;
+}
+
+/* -------------------------------------------------------- */
+
+static unsigned convertnum(uint32_t num, char* buf) {
+ unsigned i, l = 0;
+ int ch;
+ do {
+ buf[l++] = '0' + (num % 10);
+ num /= 10;
+ } while (num);
+ for(i = 0; i < l / 2; i++) {
+ ch = buf[i];
+ buf[i] = buf[l - 1 - i];
+ buf[l - 1 - i] = ch;
+ }
+ return l;
+}
+
+#define WRITE_LITERAL(X) memstream_write(&write_buffer, (X), sizeof(X) -1)
+#define WRITE_INT(X) memstream_write(&write_buffer, buf, convertnum((X), buf))
+
+static void write_cursor(unsigned x, unsigned y) {
+ char buf[32];
+ WRITE_LITERAL("\033[");
+ WRITE_INT(y+1);
+ WRITE_LITERAL(";");
+ WRITE_INT(x+1);
+ WRITE_LITERAL("H");
+}
+
+static void write_sgr(uint32_t fg, uint32_t bg) {
+ char buf[32];
+ if (fg != TB_DEFAULT) {
+ WRITE_LITERAL("\033[3");
+ WRITE_INT(fg);
+ if (bg != TB_DEFAULT) {
+ WRITE_LITERAL(";4");
+ WRITE_INT(bg);
+ } else {
+ WRITE_LITERAL(";49");
+ }
+ WRITE_LITERAL("m");
+ } else {
+ WRITE_LITERAL("\033[39");
+ if (bg != TB_DEFAULT) {
+ WRITE_LITERAL(";4");
+ WRITE_INT(bg);
+ } else {
+ WRITE_LITERAL(";49");
+ }
+ WRITE_LITERAL("m");
+ }
+}
+
+static void cellbuf_init(struct cellbuf *buf, unsigned int width, unsigned int height)
+{
+ buf->cells = (struct tb_cell*)malloc(sizeof(struct tb_cell) * width * height);
+ assert(buf->cells);
+ buf->width = width;
+ buf->height = height;
+}
+
+static void cellbuf_resize(struct cellbuf *buf, unsigned int width, unsigned int height)
+{
+ if (buf->width == width && buf->height == height)
+ return;
+
+ unsigned int oldw = buf->width;
+ unsigned int oldh = buf->height;
+ struct tb_cell *oldcells = buf->cells;
+
+ cellbuf_init(buf, width, height);
+ cellbuf_clear(buf);
+
+ unsigned int minw = (width < oldw) ? width : oldw;
+ unsigned int minh = (height < oldh) ? height : oldh;
+ unsigned int i;
+
+ for (i = 0; i < minh; ++i) {
+ struct tb_cell *csrc = oldcells + (i * oldw);
+ struct tb_cell *cdst = buf->cells + (i * width);
+ memcpy(cdst, csrc, sizeof(struct tb_cell) * minw);
+ }
+
+ free(oldcells);
+}
+
+static void cellbuf_clear(struct cellbuf *buf)
+{
+ unsigned int i;
+ unsigned int ncells = buf->width * buf->height;
+
+ for (i = 0; i < ncells; ++i) {
+ buf->cells[i].ch = ' ';
+ buf->cells[i].fg = foreground;
+ buf->cells[i].bg = background;
+ }
+}
+
+static void cellbuf_free(struct cellbuf *buf)
+{
+ free(buf->cells);
+}
+
+static void get_term_size(int *w, int *h)
+{
+ struct winsize sz;
+ memset(&sz, 0, sizeof(sz));
+
+ ioctl(out_fileno, TIOCGWINSZ, &sz);
+
+ if (w) *w = sz.ws_col;
+ if (h) *h = sz.ws_row;
+}
+
+static void update_term_size(void)
+{
+ struct winsize sz;
+ memset(&sz, 0, sizeof(sz));
+
+ ioctl(out_fileno, TIOCGWINSZ, &sz);
+
+ termw = sz.ws_col;
+ termh = sz.ws_row;
+}
+
+static void send_attr(uint16_t fg, uint16_t bg)
+{
+#define LAST_ATTR_INIT 0xFFFF
+ static uint16_t lastfg = LAST_ATTR_INIT, lastbg = LAST_ATTR_INIT;
+ if (fg != lastfg || bg != lastbg) {
+ memstream_puts(&write_buffer, funcs[T_SGR0]);
+ write_sgr(fg & 0x0F, bg & 0x0F);
+ if (fg & TB_BOLD)
+ memstream_puts(&write_buffer, funcs[T_BOLD]);
+ if (bg & TB_BOLD)
+ memstream_puts(&write_buffer, funcs[T_BLINK]);
+ if (fg & TB_UNDERLINE)
+ memstream_puts(&write_buffer, funcs[T_UNDERLINE]);
+
+ lastfg = fg;
+ lastbg = bg;
+ }
+}
+
+static void send_char(unsigned int x, unsigned int y, uint32_t c)
+{
+ char buf[7];
+ int bw = utf8_unicode_to_char(buf, c);
+ buf[bw] = '\0';
+ if (x-1 != lastx || y != lasty)
+ write_cursor(x, y);
+ lastx = x; lasty = y;
+ if(!c) buf[0] = ' '; // replace 0 with whitespace
+ memstream_puts(&write_buffer, buf);
+}
+
+static void send_clear(void)
+{
+ send_attr(foreground, background);
+ memstream_puts(&write_buffer, funcs[T_CLEAR_SCREEN]);
+ if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y))
+ write_cursor(cursor_x, cursor_y);
+ memstream_flush(&write_buffer);
+
+ /* we need to invalidate cursor position too and these two vars are
+ * used only for simple cursor positioning optimization, cursor
+ * actually may be in the correct place, but we simply discard
+ * optimization once and it gives us simple solution for the case when
+ * cursor moved */
+ lastx = LAST_COORD_INIT;
+ lasty = LAST_COORD_INIT;
+}
+
+static void sigwinch_handler(int xxx)
+{
+ (void) xxx;
+ const int zzz = 1;
+ write(winch_fds[1], &zzz, sizeof(int));
+}
+
+static void update_size(void)
+{
+ update_term_size();
+ cellbuf_resize(&back_buffer, termw, termh);
+ cellbuf_resize(&front_buffer, termw, termh);
+ cellbuf_clear(&front_buffer);
+ send_clear();
+}
+
+static int wait_fill_event(struct tb_event *event, struct timeval *timeout)
+{
+ /* ;-) */
+#define ENOUGH_DATA_FOR_INPUT_PARSING 128
+ int result;
+ char buf[ENOUGH_DATA_FOR_INPUT_PARSING];
+ fd_set events;
+ memset(event, 0, sizeof(struct tb_event));
+
+ /* try to extract event from input buffer, return on success */
+ event->type = TB_EVENT_KEY;
+ if (extract_event(event, &inbuf, inputmode))
+ return TB_EVENT_KEY;
+
+ /* it looks like input buffer is incomplete, let's try the short path */
+ size_t r = fread(buf, 1, ENOUGH_DATA_FOR_INPUT_PARSING, in);
+ if (r < ENOUGH_DATA_FOR_INPUT_PARSING && feof(in))
+ clearerr(in);
+ if (r > 0) {
+ if (ringbuffer_free_space(&inbuf) < r)
+ return -1;
+ ringbuffer_push(&inbuf, buf, r);
+ if (extract_event(event, &inbuf, inputmode))
+ return TB_EVENT_KEY;
+ }
+
+ /* no stuff in FILE's internal buffer, block in select */
+ while (1) {
+ FD_ZERO(&events);
+ FD_SET(in_fileno, &events);
+ FD_SET(winch_fds[0], &events);
+ int maxfd = (winch_fds[0] > in_fileno) ? winch_fds[0] : in_fileno;
+ result = select(maxfd+1, &events, 0, 0, timeout);
+ if (!result)
+ return 0;
+
+ if (FD_ISSET(in_fileno, &events)) {
+ event->type = TB_EVENT_KEY;
+ size_t r = fread(buf, 1, ENOUGH_DATA_FOR_INPUT_PARSING, in);
+ if (r < ENOUGH_DATA_FOR_INPUT_PARSING && feof(in))
+ clearerr(in);
+ if (r == 0)
+ continue;
+ /* if there is no free space in input buffer, return error */
+ if (ringbuffer_free_space(&inbuf) < r)
+ return -1;
+ /* fill buffer */
+ ringbuffer_push(&inbuf, buf, r);
+ if (extract_event(event, &inbuf, inputmode))
+ return TB_EVENT_KEY;
+ }
+ if (FD_ISSET(winch_fds[0], &events)) {
+ event->type = TB_EVENT_RESIZE;
+ int zzz = 0;
+ read(winch_fds[0], &zzz, sizeof(int));
+ buffer_size_change_request = 1;
+ get_term_size(&event->w, &event->h);
+ return TB_EVENT_RESIZE;
+ }
+ }
+}
+
diff --git a/termbox/termbox.h b/termbox/termbox.h
@@ -0,0 +1,185 @@
+#ifndef TERMBOX_H
+#define TERMBOX_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+/* for shared objects */
+#if __GNUC__ >= 4
+ #define SO_IMPORT __attribute__((visibility("default")))
+#else
+ #define SO_IMPORT
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* --------------- keys ---------------- */
+
+/* These are safe subset of terminfo keys, which exists on all popular terminals.
+ I think it's important to use only these and not others.
+*/
+#define TB_KEY_F1 (0xFFFF-0)
+#define TB_KEY_F2 (0xFFFF-1)
+#define TB_KEY_F3 (0xFFFF-2)
+#define TB_KEY_F4 (0xFFFF-3)
+#define TB_KEY_F5 (0xFFFF-4)
+#define TB_KEY_F6 (0xFFFF-5)
+#define TB_KEY_F7 (0xFFFF-6)
+#define TB_KEY_F8 (0xFFFF-7)
+#define TB_KEY_F9 (0xFFFF-8)
+#define TB_KEY_F10 (0xFFFF-9)
+#define TB_KEY_F11 (0xFFFF-10)
+#define TB_KEY_F12 (0xFFFF-11)
+#define TB_KEY_INSERT (0xFFFF-12)
+#define TB_KEY_DELETE (0xFFFF-13)
+#define TB_KEY_HOME (0xFFFF-14)
+#define TB_KEY_END (0xFFFF-15)
+#define TB_KEY_PGUP (0xFFFF-16)
+#define TB_KEY_PGDN (0xFFFF-17)
+#define TB_KEY_ARROW_UP (0xFFFF-18)
+#define TB_KEY_ARROW_DOWN (0xFFFF-19)
+#define TB_KEY_ARROW_LEFT (0xFFFF-20)
+#define TB_KEY_ARROW_RIGHT (0xFFFF-21)
+
+/* These are all keys below SPACE character and BACKSPACE key */
+#define TB_KEY_CTRL_TILDE 0x00
+#define TB_KEY_CTRL_2 0x00 /* clash with 'CTRL_TILDE' */
+#define TB_KEY_CTRL_A 0x01
+#define TB_KEY_CTRL_B 0x02
+#define TB_KEY_CTRL_C 0x03
+#define TB_KEY_CTRL_D 0x04
+#define TB_KEY_CTRL_E 0x05
+#define TB_KEY_CTRL_F 0x06
+#define TB_KEY_CTRL_G 0x07
+#define TB_KEY_BACKSPACE 0x08
+#define TB_KEY_CTRL_H 0x08 /* clash with 'CTRL_BACKSPACE' */
+#define TB_KEY_TAB 0x09
+#define TB_KEY_CTRL_I 0x09 /* clash with 'TAB' */
+#define TB_KEY_CTRL_J 0x0A
+#define TB_KEY_CTRL_K 0x0B
+#define TB_KEY_CTRL_L 0x0C
+#define TB_KEY_ENTER 0x0D
+#define TB_KEY_CTRL_M 0x0D /* clash with 'ENTER' */
+#define TB_KEY_CTRL_N 0x0E
+#define TB_KEY_CTRL_O 0x0F
+#define TB_KEY_CTRL_P 0x10
+#define TB_KEY_CTRL_Q 0x11
+#define TB_KEY_CTRL_R 0x12
+#define TB_KEY_CTRL_S 0x13
+#define TB_KEY_CTRL_T 0x14
+#define TB_KEY_CTRL_U 0x15
+#define TB_KEY_CTRL_V 0x16
+#define TB_KEY_CTRL_W 0x17
+#define TB_KEY_CTRL_X 0x18
+#define TB_KEY_CTRL_Y 0x19
+#define TB_KEY_CTRL_Z 0x1A
+#define TB_KEY_ESC 0x1B
+#define TB_KEY_CTRL_LSQ_BRACKET 0x1B /* clash with 'ESC' */
+#define TB_KEY_CTRL_3 0x1B /* clash with 'ESC' */
+#define TB_KEY_CTRL_4 0x1C
+#define TB_KEY_CTRL_BACKSLASH 0x1C /* clash with 'CTRL_4' */
+#define TB_KEY_CTRL_5 0x1D
+#define TB_KEY_CTRL_RSQ_BRACKET 0x1D /* clash with 'CTRL_5' */
+#define TB_KEY_CTRL_6 0x1E
+#define TB_KEY_CTRL_7 0x1F
+#define TB_KEY_CTRL_SLASH 0x1F /* clash with 'CTRL_7' */
+#define TB_KEY_CTRL_UNDERSCORE 0x1F /* clash with 'CTRL_7' */
+#define TB_KEY_SPACE 0x20
+#define TB_KEY_BACKSPACE2 0x7F
+#define TB_KEY_CTRL_8 0x7F /* clash with 'DELETE' */
+
+/* These are fail ones (do not exist) */
+/* #define TB_KEY_CTRL_1 clash with '1' */
+/* #define TB_KEY_CTRL_9 clash with '9' */
+/* #define TB_KEY_CTRL_0 clash with '0' */
+
+/* Currently there is only one modificator */
+#define TB_MOD_ALT 0x01
+
+/* colors */
+#define TB_BLACK 0x00
+#define TB_RED 0x01
+#define TB_GREEN 0x02
+#define TB_YELLOW 0x03
+#define TB_BLUE 0x04
+#define TB_MAGENTA 0x05
+#define TB_CYAN 0x06
+#define TB_WHITE 0x07
+#define TB_DEFAULT 0x0F
+
+/* attributes */
+#define TB_BOLD 0x10
+#define TB_UNDERLINE 0x20
+
+struct tb_cell {
+ uint32_t ch;
+ uint16_t fg;
+ uint16_t bg;
+};
+
+struct tb_event {
+ uint8_t type;
+ uint8_t mod;
+ uint16_t key;
+ uint32_t ch;
+ int32_t w;
+ int32_t h;
+};
+
+#define TB_EUNSUPPORTED_TERMINAL -1
+#define TB_EFAILED_TO_OPEN_TTY -2
+#define TB_EPIPE_TRAP_ERROR -3
+
+SO_IMPORT int tb_init(void);
+SO_IMPORT void tb_shutdown(void);
+
+SO_IMPORT unsigned int tb_width(void);
+SO_IMPORT unsigned int tb_height(void);
+
+SO_IMPORT void tb_clear(void);
+SO_IMPORT void tb_present(void);
+
+#define TB_HIDE_CURSOR -1
+SO_IMPORT void tb_set_cursor(int cx, int cy);
+
+SO_IMPORT void tb_put_cell(unsigned int x, unsigned int y, const struct tb_cell *cell);
+SO_IMPORT void tb_change_cell(unsigned int x, unsigned int y, uint32_t ch, uint16_t fg, uint16_t bg);
+SO_IMPORT void tb_blit(unsigned int x, unsigned int y, unsigned int w, unsigned int h, const struct tb_cell *cells);
+
+#define TB_INPUT_ESC 1
+#define TB_INPUT_ALT 2
+/* with 0 returns current input mode */
+SO_IMPORT int tb_select_input_mode(int mode);
+SO_IMPORT void tb_set_clear_attributes(uint16_t fg, uint16_t bg);
+
+#define TB_EVENT_KEY 1
+#define TB_EVENT_RESIZE 2
+
+SO_IMPORT int tb_peek_event(struct tb_event *event, unsigned int timeout);
+SO_IMPORT int tb_poll_event(struct tb_event *event);
+/* these return:
+ 0 - no events, no errors,
+ 1 - key event
+ 2 - resize event
+ -1 - error (input buffer overflow, discarded input)
+
+ timeout in milliseconds
+*/
+
+
+/* glib based interface */
+/* TODO */
+
+/* utility utf8 functions */
+#define TB_EOF -1
+SO_IMPORT int utf8_char_length(char c);
+SO_IMPORT int utf8_char_to_unicode(uint32_t *out, const char *c);
+SO_IMPORT int utf8_unicode_to_char(char *out, uint32_t c);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ifndef TERMBOX_H */
diff --git a/termbox/utf8.c b/termbox/utf8.c
@@ -0,0 +1,79 @@
+#include "termbox.h"
+
+static const unsigned char utf8_length[256] = {
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1
+};
+
+static const unsigned char utf8_mask[6] = {
+ 0x7F,
+ 0x1F,
+ 0x0F,
+ 0x07,
+ 0x03,
+ 0x01
+};
+
+int utf8_char_length(char c)
+{
+ return utf8_length[(unsigned char)c];
+}
+
+int utf8_char_to_unicode(uint32_t *out, const char *c)
+{
+ if (*c == 0)
+ return TB_EOF;
+
+ int i;
+ unsigned char len = utf8_char_length(*c);
+ unsigned char mask = utf8_mask[len-1];
+ uint32_t result = c[0] & mask;
+ for (i = 1; i < len; ++i) {
+ result <<= 6;
+ result |= c[i] & 0x3f;
+ }
+
+ *out = result;
+ return (int)len;
+}
+
+int utf8_unicode_to_char(char *out, uint32_t c)
+{
+ int len = 0;
+ int first;
+ int i;
+
+ if (c < 0x80) {
+ first = 0;
+ len = 1;
+ } else if (c < 0x800) {
+ first = 0xc0;
+ len = 2;
+ } else if (c < 0x10000) {
+ first = 0xe0;
+ len = 3;
+ } else if (c < 0x200000) {
+ first = 0xf0;
+ len = 4;
+ } else if (c < 0x4000000) {
+ first = 0xf8;
+ len = 5;
+ } else {
+ first = 0xfc;
+ len = 6;
+ }
+
+ for (i = len - 1; i > 0; --i) {
+ out[i] = (c & 0x3f) | 0x80;
+ c >>= 6;
+ }
+ out[0] = c | first;
+
+ return len;
+}