Example #1
0
bool getch2(struct input_ctx *input_ctx)
{
    int retval = read(0, &getch2_buf[getch2_pos], BUF_LEN - getch2_len - getch2_pos);
    /* Return false on EOF to stop running select() on the FD, as it'd
     * trigger all the time. Note that it's possible to get temporary
     * EOF on terminal if the user presses ctrl-d, but that shouldn't
     * happen if the terminal state change done in getch2_enable()
     * works.
     */
    if (retval == 0)
        return false;
    if (retval == -1)
        return errno != EBADF && errno != EINVAL;
    getch2_len += retval;

    while (getch2_pos < getch2_len) {
        unsigned char c = getch2_buf[getch2_pos++];

        switch (state) {
            case STATE_INITIAL: {
                int match_count = keys_count_matches(&getch2_buf[0], getch2_len);
                if (match_count == 1) {
                    keycode_st *st = keys_search(&getch2_buf[0], getch2_len);

                    if (st) {
                        mp_input_put_key(input_ctx, st->code);
                        walk_buf(st->len);
                    } /* else this is still a partial (but unique) match */

                    continue;
                } else if (match_count > 1) {
                    continue; /* need more bytes to disambiguate */
                } else {
                    /* backtrack, send as UTF-8 */
                    getch2_pos = 1;
                    c = getch2_buf[0];
                }
                utf8_len = bstr_parse_utf8_code_length(c);

                if (utf8_len > 1) {
                    state = STATE_UTF8;
                } else if (utf8_len == 1) {
                    switch (c) {
                    case 0x1b: /* ESC that's not part of escape sequence */
                        /* only if ESC was typed twice, otherwise ignore it */
                        if (getch2_len > 1 && getch2_buf[1] == 0x1b) {
                            walk_buf(1); /* eat the second ESC */
                            mp_input_put_key(input_ctx, MP_KEY_ESC);
                        }
                        break;
                    default:
                        mp_input_put_key(input_ctx, c);
                    }
                    walk_buf(1);
                } else
                    walk_buf(getch2_pos);

                break;
            }
            case STATE_UTF8: {
                if (getch2_pos < utf8_len) /* need more bytes */
                    continue;

                struct bstr s = {getch2_buf, utf8_len};
                int unicode = bstr_decode_utf8(s, NULL);

                if (unicode > 0) {
                    mp_input_put_key(input_ctx, unicode);
                }
                walk_buf(utf8_len);
                state = STATE_INITIAL;
                continue;
            }
        }
    }

    return true;
}
Example #2
0
File: getch2.c Project: maletor/mpv
bool getch2(struct mp_fifo *fifo)
{
    int retval = read(0, &getch2_buf[getch2_len], BUF_LEN-getch2_len);
    /* Return false on EOF to stop running select() on the FD, as it'd
     * trigger all the time. Note that it's possible to get temporary
     * EOF on terminal if the user presses ctrl-d, but that shouldn't
     * happen if the terminal state change done in getch2_enable()
     * works.
     */
    if (retval < 1)
        return retval;
    getch2_len += retval;

    while (getch2_len > 0 && (getch2_len > 1 || getch2_buf[0] != 27)) {
        int i, len, code;

        /* First find in the TERMCAP database: */
        for (i = 0; i < getch2_key_db; i++) {
            if ((len = getch2_keys[i].len) <= getch2_len)
                if(memcmp(getch2_keys[i].chars, getch2_buf, len) == 0) {
                    code = getch2_keys[i].code;
                    goto found;
                }
        }
        /* We always match some keypress here, with length 1 if nothing else.
         * Since some of the cases explicitly test remaining buffer length
         * having a keycode only partially read in the buffer could incorrectly
         * use the first byte as an independent character.
         * However the buffer is big enough that this shouldn't happen too
         * easily, and it's been this way for years without many complaints.
         * I see no simple fix as there's no easy test which would tell
         * whether a string must be part of a longer keycode. */
        len = 1;
        code = getch2_buf[0];
        /* Check the well-known codes... */
        if (code != 27) {
            if (code == 'A'-64) code = MP_KEY_HOME;
            else if (code == 'E'-64) code = MP_KEY_END;
            else if (code == 'D'-64) code = MP_KEY_DEL;
            else if (code == 'H'-64) code = MP_KEY_BS;
            else if (code == 'U'-64) code = MP_KEY_PGUP;
            else if (code == 'V'-64) code = MP_KEY_PGDWN;
            else if (code == 8 || code==127) code = MP_KEY_BS;
            else if (code == 10 || code==13) {
                if (getch2_len > 1) {
                    int c = getch2_buf[1];
                    if ((c == 10 || c == 13) && (c != code))
                        len = 2;
                }
                code = MP_KEY_ENTER;
            } else {
                int utf8len = bstr_parse_utf8_code_length(code);
                if (utf8len > 0 && utf8len <= getch2_len) {
                    struct bstr s = { getch2_buf, utf8len };
                    int unicode = bstr_decode_utf8(s, NULL);
                    if (unicode > 0) {
                        len = utf8len;
                        code = unicode;
                    }
                }
            }
        }
        else if (getch2_len > 1) {
            int c = getch2_buf[1];
            if (c == 27) {
                code = MP_KEY_ESC;
                len = 2;
                goto found;
            }
            if (c >= '0' && c <= '9') {
                code = c-'0'+MP_KEY_F;
                len = 2;
                goto found;
            }
            if (getch2_len >= 4 && c == '[' && getch2_buf[2] == '[') {
                int c = getch2_buf[3];
                if (c >= 'A' && c < 'A'+12) {
                    code = MP_KEY_F+1 + c-'A';
                    len = 4;
                    goto found;
                }
            }
            if ((c == '[' || c == 'O') && getch2_len >= 3) {
                int c = getch2_buf[2];
                const int ctable[] = {
                    MP_KEY_UP, MP_KEY_DOWN, MP_KEY_RIGHT, MP_KEY_LEFT, 0,
                    MP_KEY_END, MP_KEY_PGDWN, MP_KEY_HOME, MP_KEY_PGUP, 0, 0, MP_KEY_INS, 0, 0, 0,
                    MP_KEY_F+1, MP_KEY_F+2, MP_KEY_F+3, MP_KEY_F+4};
                if (c >= 'A' && c <= 'S')
                    if (ctable[c - 'A']) {
                        code = ctable[c - 'A'];
                        len = 3;
                        goto found;
                    }
            }
            if (getch2_len >= 4 && c == '[' && getch2_buf[3] == '~') {
                int c = getch2_buf[2];
                const int ctable[8] = {MP_KEY_HOME, MP_KEY_INS, MP_KEY_DEL, MP_KEY_END, MP_KEY_PGUP, MP_KEY_PGDWN, MP_KEY_HOME, MP_KEY_END};
                if (c >= '1' && c <= '8') {
                    code = ctable[c - '1'];
                    len = 4;
                    goto found;
                }
            }
            if (getch2_len >= 5 && c == '[' && getch2_buf[4] == '~') {
                int i = getch2_buf[2] - '0';
                int j = getch2_buf[3] - '0';
                if (i >= 0 && i <= 9 && j >= 0 && j <= 9) {
                    const short ftable[20] = {
                        11,12,13,14,15, 17,18,19,20,21,
                        23,24,25,26,28, 29,31,32,33,34 };
                    int a = i*10 + j;
                    for (i = 0; i < 20; i++)
                        if (ftable[i] == a) {
                            code = MP_KEY_F+1 + i;
                            len = 5;
                            goto found;
                        }
                }
            }
        }
    found:
        getch2_len -= len;
        for (i = 0; i < getch2_len; i++)
            getch2_buf[i] = getch2_buf[len+i];
        mplayer_put_key(fifo, code);
    }
    return true;
}