// 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) {

// 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, int inputmode) {

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) {
                bytebuffer_truncate(inbuf, 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;
                        bytebuffer_truncate(inbuf, 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;
                        bytebuffer_truncate(inbuf, 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];
        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;

}