diff options
Diffstat (limited to 'cpp/termbox')
-rw-r--r-- | cpp/termbox/COPYING | 19 | ||||
-rw-r--r-- | cpp/termbox/README | 2 | ||||
-rw-r--r-- | cpp/termbox/bytebuffer.inl | 79 | ||||
-rw-r--r-- | cpp/termbox/input.inl | 118 | ||||
-rw-r--r-- | cpp/termbox/makefile | 9 | ||||
-rw-r--r-- | cpp/termbox/output.inl | 306 | ||||
-rw-r--r-- | cpp/termbox/termbox.c | 562 | ||||
-rw-r--r-- | cpp/termbox/termbox.h | 210 | ||||
-rw-r--r-- | cpp/termbox/utf8.c | 79 |
9 files changed, 0 insertions, 1384 deletions
diff --git a/cpp/termbox/COPYING b/cpp/termbox/COPYING deleted file mode 100644 index e9bb4eac..00000000 --- a/cpp/termbox/COPYING +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (C) 2010-2013 nsf <no.smile.face@gmail.com> - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/cpp/termbox/README b/cpp/termbox/README deleted file mode 100644 index eccbee72..00000000 --- a/cpp/termbox/README +++ /dev/null @@ -1,2 +0,0 @@ -Fork of https://github.com/nsf/termbox as of 2015-04-22 -git hash 7c154d98a7d9207d768ee0a8e519ede74c0105cf diff --git a/cpp/termbox/bytebuffer.inl b/cpp/termbox/bytebuffer.inl deleted file mode 100644 index aae8f073..00000000 --- a/cpp/termbox/bytebuffer.inl +++ /dev/null @@ -1,79 +0,0 @@ -struct bytebuffer { - char *buf; - int len; - int cap; -}; - -static void bytebuffer_reserve(struct bytebuffer *b, int cap) { - if (b->cap >= cap) { - return; - } - - // prefer doubling capacity - if (b->cap * 2 >= cap) { - cap = b->cap * 2; - } - - char *newbuf = malloc(cap); - if (b->len > 0) { - // copy what was there, b->len > 0 assumes b->buf != null - memcpy(newbuf, b->buf, b->len); - } - if (b->buf) { - // in case there was an allocated buffer, free it - free(b->buf); - } - b->buf = newbuf; - b->cap = cap; -} - -static void bytebuffer_init(struct bytebuffer *b, int cap) { - b->cap = 0; - b->len = 0; - b->buf = 0; - - if (cap > 0) { - b->cap = cap; - b->buf = malloc(cap); // just assume malloc works always - } -} - -static void bytebuffer_free(struct bytebuffer *b) { - if (b->buf) - free(b->buf); -} - -static void bytebuffer_clear(struct bytebuffer *b) { - b->len = 0; -} - -static void bytebuffer_append(struct bytebuffer *b, const char *data, int len) { - bytebuffer_reserve(b, b->len + len); - memcpy(b->buf + b->len, data, len); - b->len += len; -} - -static void bytebuffer_puts(struct bytebuffer *b, const char *str) { - bytebuffer_append(b, str, strlen(str)); -} - -static void bytebuffer_resize(struct bytebuffer *b, int len) { - bytebuffer_reserve(b, len); - b->len = len; -} - -static void bytebuffer_flush(struct bytebuffer *b, int fd) { - int yyy = write(fd, b->buf, b->len); - (void) yyy; - bytebuffer_clear(b); -} - -static void bytebuffer_truncate(struct bytebuffer *b, int n) { - if (n <= 0) - return; - if (n > b->len) - n = b->len; - const int nmove = b->len - n; - memmove(b->buf, b->buf+n, nmove); - b->len -= n; -} diff --git a/cpp/termbox/input.inl b/cpp/termbox/input.inl deleted file mode 100644 index 6493273c..00000000 --- a/cpp/termbox/input.inl +++ /dev/null @@ -1,118 +0,0 @@ -// if s1 starts with s2 returns true, else false -// len is the length of s1 -// s2 should be null-terminated -static bool starts_with(const char *s1, int len, const char *s2) -{ - int n = 0; - while (*s2 && n < len) { - if (*s1++ != *s2++) - return false; - n++; - } - return *s2 == 0; -} - -// 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, int len) -{ - if (len >= 6 && starts_with(buf, len, "\033[M")) { - - switch (buf[3] & 3) { - case 0: - if (buf[3] == 0x60) - event->key = TB_KEY_MOUSE_WHEEL_UP; - else - event->key = TB_KEY_MOUSE_LEFT; - break; - case 1: - if (buf[3] == 0x61) - event->key = TB_KEY_MOUSE_WHEEL_DOWN; - else - event->key = TB_KEY_MOUSE_MIDDLE; - break; - case 2: - event->key = TB_KEY_MOUSE_RIGHT; - break; - case 3: - event->key = TB_KEY_MOUSE_RELEASE; - break; - default: - return -6; - } - event->type = TB_EVENT_MOUSE; // TB_EVENT_KEY by default - - // the coord is 1,1 for upper left - event->x = buf[4] - 1 - 32; - event->y = buf[5] - 1 - 32; - - return 6; - } - - // 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, len, keys[i])) { - event->ch = 0; - event->key = 0xFFFF-i; - return strlen(keys[i]); - } - } - return 0; -} - -static bool extract_event(struct tb_event *event, struct bytebuffer *inbuf) -{ - const char *buf = inbuf->buf; - const int len = inbuf->len; - if (len == 0) - return false; - - if (buf[0] == '\033') { - int n = parse_escape_seq(event, buf, len); - if (n != 0) { - bool success = true; - if (n < 0) { - success = false; - n = -n; - } - bytebuffer_truncate(inbuf, n); - return success; - } else { - // it's not escape sequence; assume it's esc - event->ch = 0; - event->key = TB_KEY_ESC; - bytebuffer_truncate(inbuf, 1); - return true; - } - } - - // 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]; - bytebuffer_truncate(inbuf, 1); - return true; - } - - // feh... we got utf8 here - - // check if there is all bytes - if (len >= tb_utf8_char_length(buf[0])) { - /* everything ok, fill event, pop buffer, return success */ - tb_utf8_char_to_unicode(&event->ch, buf); - event->key = 0; - bytebuffer_truncate(inbuf, tb_utf8_char_length(buf[0])); - return true; - } - - // event isn't recognized, perhaps there is not enough bytes in utf8 - // sequence - return false; -} diff --git a/cpp/termbox/makefile b/cpp/termbox/makefile deleted file mode 100644 index 8bdd2704..00000000 --- a/cpp/termbox/makefile +++ /dev/null @@ -1,9 +0,0 @@ -CFLAGS=-O3 -Wall -Wextra -D_XOPEN_SOURCE - -libtermbox.a: utf8.o termbox.o - ar rcs libtermbox.a *.o - -termbox.o: termbox.c input.inl output.inl bytebuffer.inl - -clean: - -rm *.o libtermbox.a diff --git a/cpp/termbox/output.inl b/cpp/termbox/output.inl deleted file mode 100644 index 7c66ce2d..00000000 --- a/cpp/termbox/output.inl +++ /dev/null @@ -1,306 +0,0 @@ -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_REVERSE, - T_ENTER_KEYPAD, - T_EXIT_KEYPAD, - T_ENTER_MOUSE, - T_EXIT_MOUSE, - T_FUNCS_NUM, -}; - -#define EUNSUPPORTED_TERM -1 - -// 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[] = { - "\0337\033[?47h", "\033[2J\033[?47l\0338", "\033[?25h", "\033[?25l", "\033[H\033[2J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "\033=", "\033>", "\033[?1000h", "\033[?1000l", -}; - -// 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[] = { - "\0337\033[?47h", "\033[2J\033[?47l\0338", "\033[?25h", "\033[?25l", "\033[H\033[2J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "", "", "", "", -}; - -// 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[] = { - "\033[?1049h", "\033[?1049l", "\033[34h\033[?25h", "\033[?25l", "\033[H\033[J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "\033[?1h\033=", "\033[?1l\033>", "\033[?1000h", "\033[?1000l", -}; - -// 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[] = { - "\033[?1049h", "\033[r\033[?1049l", "\033[?25h", "\033[?25l", "\033[H\033[2J", "\033[m\033(B", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "\033=", "\033>", "\033[?1000h", "\033[?1000l", -}; - -// 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[] = { - "", "", "\033[?25h\033[?0c", "\033[?25l\033[?1c", "\033[H\033[J", "\033[0;10m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "", "", "", "", -}; - -// 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[] = { - "\033[?1049h", "\033[?1049l", "\033[?12l\033[?25h", "\033[?25l", "\033[H\033[2J", "\033(B\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "\033[?1h\033=", "\033[?1l\033>", "\033[?1000h", "\033[?1000l", -}; - -static struct term { - const char *name; - const char **keys; - const char **funcs; -} terms[] = { - {"rxvt-256color", rxvt_256color_keys, rxvt_256color_funcs}, - {"Eterm", eterm_keys, eterm_funcs}, - {"screen", screen_keys, screen_funcs}, - {"rxvt-unicode", rxvt_unicode_keys, rxvt_unicode_funcs}, - {"linux", linux_keys, linux_funcs}, - {"xterm", xterm_keys, xterm_funcs}, - {0, 0, 0}, -}; - -static bool init_from_terminfo = false; -static const char **keys; -static 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; -} - -static int init_term_builtin(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; -} - -//---------------------------------------------------------------------- -// terminfo -//---------------------------------------------------------------------- - -static char *read_file(const char *file) { - FILE *f = fopen(file, "rb"); - if (!f) - return 0; - - struct stat st; - if (fstat(fileno(f), &st) != 0) { - fclose(f); - return 0; - } - - char *data = malloc(st.st_size); - if (!data) { - fclose(f); - return 0; - } - - if (fread(data, 1, st.st_size, f) != (size_t)st.st_size) { - fclose(f); - free(data); - return 0; - } - - fclose(f); - return data; -} - -static char *terminfo_try_path(const char *path, const char *term) { - char tmp[4096]; - // snprintf guarantee for older compilers - assert(sizeof(tmp) > sizeof(path)+sizeof("/x/")+sizeof(term)+1); - sprintf(tmp, "%s/%c/%s", path, term[0], term); - char *data = read_file(tmp); - if (data) { - return data; - } - - // fallback to darwin specific dirs structure - // snprintf guarantee above still applies - sprintf(tmp, "%s/%x/%s", path, term[0], term); - return read_file(tmp); -} - -static char *load_terminfo(void) { - char tmp[4096]; - const char *term = getenv("TERM"); - if (!term) { - return 0; - } - - // if TERMINFO is set, no other directory should be searched - const char *terminfo = getenv("TERMINFO"); - if (terminfo) { - return terminfo_try_path(terminfo, term); - } - - // next, consider ~/.terminfo - const char *home = getenv("HOME"); - if (home) { - // snprintf guarantee for older compilers - assert(sizeof(tmp) > sizeof(home)+sizeof("/.terminfo")+1); - strncpy(tmp, home, sizeof(tmp)); - strcat(tmp, "/.terminfo"); - char *data = terminfo_try_path(tmp, term); - if (data) - return data; - } - - // next, TERMINFO_DIRS - const char *dirs = getenv("TERMINFO_DIRS"); - if (dirs) { - // snprintf guarantee for older compilers - assert(sizeof(tmp) > sizeof(dirs)); - strncpy(tmp, dirs, sizeof(tmp)); - char *dir = strtok(tmp, ":"); - while (dir) { - const char *cdir = dir; - if (strcmp(cdir, "") == 0) { - cdir = "/usr/share/terminfo"; - } - char *data = terminfo_try_path(cdir, term); - if (data) - return data; - dir = strtok(0, ":"); - } - } - - // fallback to /usr/share/terminfo - return terminfo_try_path("/usr/share/terminfo", term); -} - -#define TI_MAGIC 0432 -#define TI_HEADER_LENGTH 12 -#define TB_KEYS_NUM 22 - -static const char *terminfo_copy_string(char *data, int str, int table) { - const int16_t off = *(int16_t*)(data + str); - const char *src = data + table + off; - int len = strlen(src); - char *dst = malloc(len+1); - strcpy(dst, src); - return dst; -} - -static const int16_t ti_funcs[] = { - 28, 40, 16, 13, 5, 39, 36, 27, 26, 34, 89, 88, -}; - -static const int16_t ti_keys[] = { - 66, 68 /* apparently not a typo; 67 is F10 for whatever reason */, 69, - 70, 71, 72, 73, 74, 75, 67, 216, 217, 77, 59, 76, 164, 82, 81, 87, 61, - 79, 83, -}; - -static int init_term(void) { - int i; - char *data = load_terminfo(); - if (!data) { - init_from_terminfo = false; - return init_term_builtin(); - } - - int16_t *header = (int16_t*)data; - if ((header[1] + header[2]) % 2) { - // old quirk to align everything on word boundaries - header[2] += 1; - } - - const int str_offset = TI_HEADER_LENGTH + - header[1] + header[2] + 2 * header[3]; - const int table_offset = str_offset + 2 * header[4]; - - keys = malloc(sizeof(const char*) * (TB_KEYS_NUM+1)); - for (i = 0; i < TB_KEYS_NUM; i++) { - keys[i] = terminfo_copy_string(data, - str_offset + 2 * ti_keys[i], table_offset); - } - keys[TB_KEYS_NUM] = 0; - - funcs = malloc(sizeof(const char*) * T_FUNCS_NUM); - // the last two entries are reserved for mouse. because the table offset is - // not there, the two entries have to fill in manually - for (i = 0; i < T_FUNCS_NUM-2; i++) { - funcs[i] = terminfo_copy_string(data, - str_offset + 2 * ti_funcs[i], table_offset); - } - - funcs[T_FUNCS_NUM-2] = "\033[?1000h"; - funcs[T_FUNCS_NUM-1] = "\033[?1000l"; - - init_from_terminfo = true; - free(data); - return 0; -} - -static void shutdown_term(void) { - if (init_from_terminfo) { - int i; - for (i = 0; i < TB_KEYS_NUM; i++) { - free((void*)keys[i]); - } - // the last two entries are reserved for mouse. because the table offset - // is not there, the two entries have to fill in manually and do not - // need to be freed. - for (i = 0; i < T_FUNCS_NUM-2; i++) { - free((void*)funcs[i]); - } - free(keys); - free(funcs); - } -} diff --git a/cpp/termbox/termbox.c b/cpp/termbox/termbox.c deleted file mode 100644 index 13ef1359..00000000 --- a/cpp/termbox/termbox.c +++ /dev/null @@ -1,562 +0,0 @@ -#include <assert.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <fcntl.h> -#include <signal.h> -#include <stdio.h> -#include <stdbool.h> -#include <sys/ioctl.h> -#include <sys/time.h> -#include <sys/stat.h> -#include <termios.h> -#include <unistd.h> -#include <wchar.h> - -#include "termbox.h" - -#include "bytebuffer.inl" -#include "output.inl" -#include "input.inl" - -struct cellbuf { - int width; - 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 -1 - -static struct termios orig_tios; - -static struct cellbuf back_buffer; -static struct cellbuf front_buffer; -static struct bytebuffer output_buffer; -static struct bytebuffer input_buffer; - -static int termw = -1; -static int termh = -1; - -static int inout; -static int winch_fds[2]; - -static int lastx = LAST_COORD_INIT; -static 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(int x, int y); -static void write_sgr_fg(uint16_t fg); -static void write_sgr_bg(uint16_t bg); -static void write_sgr(uint16_t fg, uint16_t bg); - -static void cellbuf_init(struct cellbuf *buf, int width, int height); -static void cellbuf_resize(struct cellbuf *buf, int width, 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(int x, 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) -{ - inout = open("/dev/tty", O_RDWR); - if (inout == -1) { - return TB_EFAILED_TO_OPEN_TTY; - } - - if (init_term() < 0) { - close(inout); - return TB_EUNSUPPORTED_TERMINAL; - } - - if (pipe(winch_fds) < 0) { - close(inout); - return TB_EPIPE_TRAP_ERROR; - } - - struct sigaction sa; - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = sigwinch_handler; - sa.sa_flags = 0; - sigaction(SIGWINCH, &sa, 0); - - tcgetattr(inout, &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(inout, TCSAFLUSH, &tios); - - bytebuffer_init(&input_buffer, 128); - bytebuffer_init(&output_buffer, 32 * 1024); - - bytebuffer_puts(&output_buffer, funcs[T_ENTER_CA]); - bytebuffer_puts(&output_buffer, funcs[T_ENTER_KEYPAD]); - bytebuffer_puts(&output_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); - - return 0; -} - -void tb_shutdown(void) -{ - if (termw == -1) { - fputs("tb_shutdown() should not be called twice.", stderr); - abort(); - } - - bytebuffer_puts(&output_buffer, funcs[T_SHOW_CURSOR]); - bytebuffer_puts(&output_buffer, funcs[T_SGR0]); - bytebuffer_puts(&output_buffer, funcs[T_CLEAR_SCREEN]); - bytebuffer_puts(&output_buffer, funcs[T_EXIT_CA]); - bytebuffer_puts(&output_buffer, funcs[T_EXIT_KEYPAD]); - bytebuffer_puts(&output_buffer, funcs[T_EXIT_MOUSE]); - bytebuffer_flush(&output_buffer, inout); - tcsetattr(inout, TCSAFLUSH, &orig_tios); - - shutdown_term(); - close(inout); - close(winch_fds[0]); - close(winch_fds[1]); - - cellbuf_free(&back_buffer); - cellbuf_free(&front_buffer); - bytebuffer_free(&output_buffer); - bytebuffer_free(&input_buffer); - termw = termh = -1; -} - -void tb_present(void) -{ - int x,y,w,i; - 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; ) { - back = &CELL(&back_buffer, x, y); - front = &CELL(&front_buffer, x, y); - w = wcwidth(back->ch); - if (w < 1) w = 1; - if (memcmp(back, front, sizeof(struct tb_cell)) == 0) { - x += w; - continue; - } - memcpy(front, back, sizeof(struct tb_cell)); - send_attr(back->fg, back->bg); - if (w > 1 && x >= front_buffer.width - (w - 1)) { - // Not enough room for wide ch, so send spaces - for (i = x; i < front_buffer.width; ++i) { - send_char(i, y, ' '); - } - } else { - send_char(x, y, back->ch); - for (i = 1; i < w; ++i) { - front = &CELL(&front_buffer, x + i, y); - front->ch = 0; - front->fg = back->fg; - front->bg = back->bg; - } - } - x += w; - } - } - if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y)) - write_cursor(cursor_x, cursor_y); - bytebuffer_flush(&output_buffer, inout); -} - -void tb_set_cursor(int cx, int cy) -{ - if (IS_CURSOR_HIDDEN(cursor_x, cursor_y) && !IS_CURSOR_HIDDEN(cx, cy)) - bytebuffer_puts(&output_buffer, funcs[T_SHOW_CURSOR]); - - if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y) && IS_CURSOR_HIDDEN(cx, cy)) - bytebuffer_puts(&output_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_change_cell(int x, int y, uint32_t ch, uint16_t fg, uint16_t bg) -{ - if ((unsigned)x >= (unsigned)back_buffer.width) - return; - if ((unsigned)y >= (unsigned)back_buffer.height) - return; - struct tb_cell c = {ch, fg, bg}; - CELL(&back_buffer, x, y) = c; -} - -struct tb_cell *tb_cell_buffer() -{ - return back_buffer.cells; -} - -int tb_poll_event(struct tb_event *event) -{ - return wait_fill_event(event, 0); -} - -int tb_peek_event(struct tb_event *event, 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); -} - -int tb_width(void) -{ - return termw; -} - -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); -} - -void tb_set_clear_attributes(uint16_t fg, uint16_t bg) -{ - foreground = fg; - background = bg; -} - -/* -------------------------------------------------------- */ - -static int convertnum(uint32_t num, char* buf) { - int 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) bytebuffer_append(&output_buffer, (X), sizeof(X)-1) -#define WRITE_INT(X) bytebuffer_append(&output_buffer, buf, convertnum((X), buf)) - -static void write_cursor(int x, int y) { - char buf[32]; - WRITE_LITERAL("\033["); - WRITE_INT(y+1); - WRITE_LITERAL(";"); - WRITE_INT(x+1); - WRITE_LITERAL("H"); -} - -static void write_sgr_fg(uint16_t fg) { - char buf[32]; - WRITE_LITERAL("\033[3"); - WRITE_INT(fg-1); - WRITE_LITERAL("m"); -} - -static void write_sgr_bg(uint16_t bg) { - char buf[32]; - WRITE_LITERAL("\033[4"); - WRITE_INT(bg-1); - WRITE_LITERAL("m"); -} - -static void write_sgr(uint16_t fg, uint16_t bg) { - char buf[32]; - WRITE_LITERAL("\033[3"); - WRITE_INT(fg-1); - WRITE_LITERAL(";4"); - WRITE_INT(bg-1); - WRITE_LITERAL("m"); -} - -static void cellbuf_init(struct cellbuf *buf, int width, 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, int width, int height) -{ - if (buf->width == width && buf->height == height) - return; - - int oldw = buf->width; - int oldh = buf->height; - struct tb_cell *oldcells = buf->cells; - - cellbuf_init(buf, width, height); - cellbuf_clear(buf); - - int minw = (width < oldw) ? width : oldw; - int minh = (height < oldh) ? height : oldh; - 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) -{ - int i; - 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(inout, 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(inout, 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) { - bytebuffer_puts(&output_buffer, funcs[T_SGR0]); - - uint16_t fgcol = fg & 0x0F; - uint16_t bgcol = bg & 0x0F; - - if (fg & TB_BOLD) - bytebuffer_puts(&output_buffer, funcs[T_BOLD]); - if (bg & TB_BOLD) - bytebuffer_puts(&output_buffer, funcs[T_BLINK]); - if (fg & TB_UNDERLINE) - bytebuffer_puts(&output_buffer, funcs[T_UNDERLINE]); - if ((fg & TB_REVERSE) || (bg & TB_REVERSE)) - bytebuffer_puts(&output_buffer, funcs[T_REVERSE]); - - if (fgcol != TB_DEFAULT) { - if (bgcol != TB_DEFAULT) - write_sgr(fgcol, bgcol); - else - write_sgr_fg(fgcol); - } else if (bgcol != TB_DEFAULT) { - write_sgr_bg(bgcol); - } - - lastfg = fg; - lastbg = bg; - } -} - -static void send_char(int x, int y, uint32_t c) -{ - char buf[7]; - int bw = tb_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 - bytebuffer_puts(&output_buffer, buf); -} - -static void send_clear(void) -{ - send_attr(foreground, background); - bytebuffer_puts(&output_buffer, funcs[T_CLEAR_SCREEN]); - if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y)) - write_cursor(cursor_x, cursor_y); - bytebuffer_flush(&output_buffer, inout); - - /* 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; - int yyy = write(winch_fds[1], &zzz, sizeof(int)); - (void) yyy; -} - -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 read_up_to(int n) { - assert(n > 0); - const int prevlen = input_buffer.len; - bytebuffer_resize(&input_buffer, prevlen + n); - - int read_n = 0; - while (read_n <= n) { - ssize_t r = 0; - if (read_n < n) { - r = read(inout, input_buffer.buf + prevlen + read_n, n - read_n); - } -#ifdef __CYGWIN__ - // While linux man for tty says when VMIN == 0 && VTIME == 0, read - // should return 0 when there is nothing to read, cygwin's read returns - // -1. Not sure why and if it's correct to ignore it, but let's pretend - // it's zero. - if (r < 0) r = 0; -#endif - if (r < 0) { - // EAGAIN / EWOULDBLOCK shouldn't occur here - assert(errno != EAGAIN && errno != EWOULDBLOCK); - return -1; - } else if (r > 0) { - read_n += r; - } else { - bytebuffer_resize(&input_buffer, prevlen + read_n); - return read_n; - } - } - assert(!"unreachable"); - return 0; -} - -static int wait_fill_event(struct tb_event *event, struct timeval *timeout) -{ - // ;-) -#define ENOUGH_DATA_FOR_PARSING 64 - 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, &input_buffer)) - return event->type; - - // it looks like input buffer is incomplete, let's try the short path, - // but first make sure there is enough space - int n = read_up_to(ENOUGH_DATA_FOR_PARSING); - if (n < 0) - return -1; - if (n > 0 && extract_event(event, &input_buffer)) - return event->type; - - // n == 0, or not enough data, let's go to select - while (1) { - FD_ZERO(&events); - FD_SET(inout, &events); - FD_SET(winch_fds[0], &events); - int maxfd = (winch_fds[0] > inout) ? winch_fds[0] : inout; - int result = select(maxfd+1, &events, 0, 0, timeout); - if (!result) - return 0; - - if (FD_ISSET(inout, &events)) { - event->type = TB_EVENT_KEY; - n = read_up_to(ENOUGH_DATA_FOR_PARSING); - if (n < 0) - return -1; - - if (n == 0) - continue; - - if (extract_event(event, &input_buffer)) - return event->type; - } - if (FD_ISSET(winch_fds[0], &events)) { - event->type = TB_EVENT_RESIZE; - int zzz = 0; - int yyy = read(winch_fds[0], &zzz, sizeof(int)); - (void) yyy; - buffer_size_change_request = 1; - get_term_size(&event->w, &event->h); - return TB_EVENT_RESIZE; - } - } -} diff --git a/cpp/termbox/termbox.h b/cpp/termbox/termbox.h deleted file mode 100644 index 3e2228de..00000000 --- a/cpp/termbox/termbox.h +++ /dev/null @@ -1,210 +0,0 @@ -#pragma once - -#include <stdint.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/*** 1. Controlling the screen. */ - -/* The screen is a 2D array of cells. */ -struct tb_cell { - uint32_t ch; /* unicode character */ - uint16_t fg; /* foreground color and attributes */ - uint16_t bg; /* background color and attributes */ -}; - -/* Possible colors in tb_cell.fg and tb_cell.bg. */ -#define TB_DEFAULT 0x00 -#define TB_BLACK 0x01 -#define TB_RED 0x02 -#define TB_GREEN 0x03 -#define TB_YELLOW 0x04 -#define TB_BLUE 0x05 -#define TB_MAGENTA 0x06 -#define TB_CYAN 0x07 -#define TB_WHITE 0x08 - -/* Colors in tb_cell can be combined using bitwise-OR with multiple - * of the following attributes. */ -#define TB_BOLD 0x0100 -#define TB_UNDERLINE 0x0200 -#define TB_REVERSE 0x0400 - -/* Initialize screen and keyboard. */ -int tb_init(void); -/* Possible error codes returned by tb_init() */ -#define TB_EUNSUPPORTED_TERMINAL -1 -#define TB_EFAILED_TO_OPEN_TTY -2 -/* Termbox uses unix pipes in order to deliver a message from a signal handler - * (SIGWINCH) to the main event reading loop. */ -#define TB_EPIPE_TRAP_ERROR -3 - -/* Restore terminal mode. */ -void tb_shutdown(void); - -/* Size of the screen. Return negative values before tb_init() or after - * tb_shutdown() */ -int tb_width(void); -int tb_height(void); - -/* Update the screen with internal state. Most methods below modify just the - * internal state of the screen. Changes won't be visible until you call - * tb_present(). */ -void tb_present(void); - -/* Returns a pointer to the internal screen state: a 1D array of cells in - * raster order. You'll need to call tb_width() and tb_height() for the - * array's dimensions. The array stays valid until tb_clear() or tb_present() - * are called. */ -struct tb_cell *tb_cell_buffer(); - -/* Clear the internal screen state using either TB_DEFAULT or the - * color/attributes set by tb_set_clear_attributes(). */ -void tb_clear(void); -void tb_set_clear_attributes(uint16_t fg, uint16_t bg); - -/* Move the cursor. Upper-left character is (0, 0). - */ -void tb_set_cursor(int cx, int cy); -/* To hide the cursor, call tb_set_cursor(TB_HIDE_CURSOR, TB_HIDE_CURSOR). - * Cursor starts out hidden. */ -#define TB_HIDE_CURSOR -1 - -/* Modify a specific cell of the screen. Don't forget to call tb_present() to - * commit your changes. */ -void tb_change_cell(int x, int y, uint32_t ch, uint16_t fg, uint16_t bg); - - - -/*** 2. Controlling keyboard events. */ - -struct tb_event { - uint8_t type; - /* fields for type TB_EVENT_KEY */ - uint16_t key; - uint32_t ch; - /* fields for type TB_EVENT_RESIZE */ - int32_t w; - int32_t h; - /* fields for type TB_EVENT_MOUSE */ - int32_t x; - int32_t y; -}; - -/* Possible values for tb_event.type. */ -#define TB_EVENT_KEY 1 -#define TB_EVENT_RESIZE 2 -#define TB_EVENT_MOUSE 3 - -/* Possible values for tb_event.key. - * - * These are a safe subset of terminfo keys, which exist on all popular - * terminals. Termbox uses only them to stay truly portable. - */ -#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) -#define TB_KEY_MOUSE_LEFT (0xFFFF-22) -#define TB_KEY_MOUSE_RIGHT (0xFFFF-23) -#define TB_KEY_MOUSE_MIDDLE (0xFFFF-24) -#define TB_KEY_MOUSE_RELEASE (0xFFFF-25) -#define TB_KEY_MOUSE_WHEEL_UP (0xFFFF-26) -#define TB_KEY_MOUSE_WHEEL_DOWN (0xFFFF-27) -/* These are all ASCII code points below SPACE character and a 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 non-existing ones. - * - * #define TB_KEY_CTRL_1 clash with '1' - * #define TB_KEY_CTRL_9 clash with '9' - * #define TB_KEY_CTRL_0 clash with '0' - */ - -/* Wait for an event up to 'timeout' milliseconds and fill the 'event' - * structure with it, when the event is available. Returns the type of the - * event (one of TB_EVENT_* constants) or -1 if there was an error or 0 in case - * there were no event during 'timeout' period. - */ -int tb_peek_event(struct tb_event *event, int timeout); - -/* Wait for an event forever and fill the 'event' structure with it, when the - * event is available. Returns the type of the event (one of TB_EVENT_* - * constants) or -1 if there was an error. - */ -int tb_poll_event(struct tb_event *event); - - - -/*** 3. Utility utf8 functions. */ -#define TB_EOF -1 -int tb_utf8_char_length(char c); -int tb_utf8_char_to_unicode(uint32_t *out, const char *c); -int tb_utf8_unicode_to_char(char *out, uint32_t c); - -#ifdef __cplusplus -} -#endif diff --git a/cpp/termbox/utf8.c b/cpp/termbox/utf8.c deleted file mode 100644 index 26c0c27b..00000000 --- a/cpp/termbox/utf8.c +++ /dev/null @@ -1,79 +0,0 @@ -#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 tb_utf8_char_length(char c) -{ - return utf8_length[(unsigned char)c]; -} - -int tb_utf8_char_to_unicode(uint32_t *out, const char *c) -{ - if (*c == 0) - return TB_EOF; - - int i; - unsigned char len = tb_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 tb_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; -} |