[BACK]Return to tty-keys.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / tmux

Annotation of src/usr.bin/tmux/tty-keys.c, Revision 1.3

1.3     ! nicm        1: /* $OpenBSD: tty-keys.c,v 1.2 2009/07/21 17:57:29 nicm Exp $ */
1.1       nicm        2:
                      3: /*
                      4:  * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
                      5:  *
                      6:  * Permission to use, copy, modify, and distribute this software for any
                      7:  * purpose with or without fee is hereby granted, provided that the above
                      8:  * copyright notice and this permission notice appear in all copies.
                      9:  *
                     10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     11:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     12:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     13:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     14:  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
                     15:  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
                     16:  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     17:  */
                     18:
                     19: #include <sys/types.h>
                     20: #include <sys/time.h>
                     21:
                     22: #include <string.h>
1.3     ! nicm       23: #include <termios.h>
        !            24: #include <unistd.h>
1.1       nicm       25:
                     26: #include "tmux.h"
                     27:
                     28: void   tty_keys_add(struct tty *, const char *, int, int);
                     29: int    tty_keys_parse_xterm(struct tty *, char *, size_t, size_t *);
                     30: int    tty_keys_parse_mouse(struct tty *, char *, size_t, size_t *, u_char *);
                     31:
                     32: struct tty_key_ent {
                     33:        enum tty_code_code      code;
                     34:        const char             *string;
                     35:
                     36:        int                     key;
                     37:        int                     flags;
                     38: };
                     39:
                     40: struct tty_key_ent tty_keys[] = {
                     41:        /* Function keys. */
                     42:        { TTYC_KF1,   NULL,     KEYC_F1,    TTYKEY_CTRL },
                     43:        { TTYC_KF2,   NULL,     KEYC_F2,    TTYKEY_CTRL },
                     44:        { TTYC_KF3,   NULL,     KEYC_F3,    TTYKEY_CTRL },
                     45:        { TTYC_KF4,   NULL,     KEYC_F4,    TTYKEY_CTRL },
                     46:        { TTYC_KF5,   NULL,     KEYC_F5,    TTYKEY_CTRL },
                     47:        { TTYC_KF6,   NULL,     KEYC_F6,    TTYKEY_CTRL },
                     48:        { TTYC_KF7,   NULL,     KEYC_F7,    TTYKEY_CTRL },
                     49:        { TTYC_KF8,   NULL,     KEYC_F8,    TTYKEY_CTRL },
                     50:        { TTYC_KF9,   NULL,     KEYC_F9,    TTYKEY_CTRL },
                     51:        { TTYC_KF10,  NULL,     KEYC_F10,   TTYKEY_CTRL },
                     52:        { TTYC_KF11,  NULL,     KEYC_F11,   TTYKEY_CTRL },
                     53:        { TTYC_KF12,  NULL,     KEYC_F12,   TTYKEY_CTRL },
                     54:        { TTYC_KF13,  NULL,     KEYC_F13,   TTYKEY_CTRL },
                     55:        { TTYC_KF14,  NULL,     KEYC_F14,   TTYKEY_CTRL },
                     56:        { TTYC_KF15,  NULL,     KEYC_F15,   TTYKEY_CTRL },
                     57:        { TTYC_KF16,  NULL,     KEYC_F16,   TTYKEY_CTRL },
                     58:        { TTYC_KF17,  NULL,     KEYC_F17,   TTYKEY_CTRL },
                     59:        { TTYC_KF18,  NULL,     KEYC_F18,   TTYKEY_CTRL },
                     60:        { TTYC_KF19,  NULL,     KEYC_F19,   TTYKEY_CTRL },
                     61:        { TTYC_KF20,  NULL,     KEYC_F20,   TTYKEY_CTRL },
                     62:        { TTYC_KICH1, NULL,     KEYC_IC,    TTYKEY_CTRL },
                     63:        { TTYC_KDCH1, NULL,     KEYC_DC,    TTYKEY_CTRL },
                     64:        { TTYC_KHOME, NULL,     KEYC_HOME,  TTYKEY_CTRL },
                     65:        { TTYC_KEND,  NULL,     KEYC_END,   TTYKEY_CTRL },
                     66:        { TTYC_KNP,   NULL,     KEYC_NPAGE, TTYKEY_CTRL },
                     67:        { TTYC_KPP,   NULL,     KEYC_PPAGE, TTYKEY_CTRL },
                     68:        { TTYC_KCBT,  NULL,     KEYC_BTAB,  TTYKEY_CTRL },
                     69:
                     70:        /* Arrow keys. */
                     71:        { 0,          "\033OA", KEYC_UP,    TTYKEY_RAW },
                     72:        { 0,          "\033OB", KEYC_DOWN,  TTYKEY_RAW },
                     73:        { 0,          "\033OC", KEYC_RIGHT, TTYKEY_RAW },
                     74:        { 0,          "\033OD", KEYC_LEFT,  TTYKEY_RAW },
                     75:
                     76:        { 0,          "\033[A", KEYC_UP,    TTYKEY_RAW },
                     77:        { 0,          "\033[B", KEYC_DOWN,  TTYKEY_RAW },
                     78:        { 0,          "\033[C", KEYC_RIGHT, TTYKEY_RAW },
                     79:        { 0,          "\033[D", KEYC_LEFT,  TTYKEY_RAW },
                     80:
1.2       nicm       81:        { 0,          "\033Oa", KEYC_UP | KEYC_CTRL,    TTYKEY_RAW },
                     82:        { 0,          "\033Ob", KEYC_DOWN | KEYC_CTRL,  TTYKEY_RAW },
                     83:        { 0,          "\033Oc", KEYC_RIGHT | KEYC_CTRL, TTYKEY_RAW },
                     84:        { 0,          "\033Od", KEYC_LEFT | KEYC_CTRL,  TTYKEY_RAW },
                     85:        { 0,          "\033[a", KEYC_UP | KEYC_SHIFT,    TTYKEY_RAW },
                     86:        { 0,          "\033[b", KEYC_DOWN | KEYC_SHIFT,  TTYKEY_RAW },
                     87:        { 0,          "\033[c", KEYC_RIGHT | KEYC_SHIFT, TTYKEY_RAW },
                     88:        { 0,          "\033[d", KEYC_LEFT | KEYC_SHIFT,  TTYKEY_RAW },
1.1       nicm       89:
                     90:        { TTYC_KCUU1, NULL,     KEYC_UP,    TTYKEY_CTRL },
                     91:        { TTYC_KCUD1, NULL,     KEYC_DOWN,  TTYKEY_CTRL },
                     92:        { TTYC_KCUB1, NULL,     KEYC_LEFT,  TTYKEY_CTRL },
                     93:        { TTYC_KCUF1, NULL,     KEYC_RIGHT, TTYKEY_CTRL },
                     94:
                     95:        /*
                     96:         * Numeric keypad. termcap and terminfo are totally confusing for this.
                     97:         * There are definitions for some keypad keys and for function keys,
                     98:         * but these seem to now be used for the real function keys rather than
                     99:         * for the keypad keys in application mode (which is different from
                    100:         * what it says in the termcap file). So, we just hardcode the vt100
                    101:         * escape sequences here and always put the terminal into keypad_xmit
                    102:         * mode. Translation of numbers mode/applications mode is done in
                    103:         * input-keys.c.
                    104:         */
                    105:        { 0,          "\033Oo", KEYC_KP0_1, TTYKEY_RAW },
                    106:        { 0,          "\033Oj", KEYC_KP0_2, TTYKEY_RAW },
                    107:        { 0,          "\033Om", KEYC_KP0_3, TTYKEY_RAW },
                    108:        { 0,          "\033Ow", KEYC_KP1_0, TTYKEY_RAW },
                    109:        { 0,          "\033Ox", KEYC_KP1_1, TTYKEY_RAW },
                    110:        { 0,          "\033Oy", KEYC_KP1_2, TTYKEY_RAW },
                    111:        { 0,          "\033Ok", KEYC_KP1_3, TTYKEY_RAW },
                    112:        { 0,          "\033Ot", KEYC_KP2_0, TTYKEY_RAW },
                    113:        { 0,          "\033Ou", KEYC_KP2_1, TTYKEY_RAW },
                    114:        { 0,          "\033Ov", KEYC_KP2_2, TTYKEY_RAW },
                    115:        { 0,          "\033Oq", KEYC_KP3_0, TTYKEY_RAW },
                    116:        { 0,          "\033Or", KEYC_KP3_1, TTYKEY_RAW },
                    117:        { 0,          "\033Os", KEYC_KP3_2, TTYKEY_RAW },
                    118:        { 0,          "\033OM", KEYC_KP3_3, TTYKEY_RAW },
                    119:        { 0,          "\033Op", KEYC_KP4_0, TTYKEY_RAW },
                    120:        { 0,          "\033On", KEYC_KP4_2, TTYKEY_RAW },
                    121: };
                    122:
                    123: RB_GENERATE(tty_keys, tty_key, entry, tty_keys_cmp);
                    124:
                    125: struct tty_key *tty_keys_find(struct tty *, char *, size_t, size_t *);
                    126:
                    127: int
                    128: tty_keys_cmp(struct tty_key *k1, struct tty_key *k2)
                    129: {
                    130:        return (strcmp(k1->string, k2->string));
                    131: }
                    132:
                    133: void
                    134: tty_keys_add(struct tty *tty, const char *s, int key, int flags)
                    135: {
                    136:        struct tty_key  *tk, *tl;
                    137:
                    138:        tk = xmalloc(sizeof *tk);
                    139:        tk->string = xstrdup(s);
                    140:        tk->key = key;
                    141:        tk->flags = flags;
                    142:
                    143:        if ((tl = RB_INSERT(tty_keys, &tty->ktree, tk)) != NULL) {
                    144:                xfree(tk->string);
                    145:                xfree(tk);
                    146:                log_debug("key exists: %s (old %x, new %x)", s, tl->key, key);
                    147:                return;
                    148:        }
                    149:
                    150:        if (strlen(tk->string) > tty->ksize)
                    151:                tty->ksize = strlen(tk->string);
                    152:        log_debug("new key %x: size now %zu (%s)", key, tty->ksize, tk->string);
                    153: }
                    154:
                    155: void
                    156: tty_keys_init(struct tty *tty)
                    157: {
                    158:        struct tty_key_ent      *tke;
                    159:        u_int                    i;
                    160:        const char              *s;
                    161:        char                     tmp[64];
                    162:
                    163:        RB_INIT(&tty->ktree);
                    164:
                    165:        tty->ksize = 0;
                    166:        for (i = 0; i < nitems(tty_keys); i++) {
                    167:                tke = &tty_keys[i];
                    168:
                    169:                if (tke->flags & TTYKEY_RAW)
                    170:                        s = tke->string;
                    171:                else {
                    172:                        if (!tty_term_has(tty->term, tke->code))
                    173:                                continue;
                    174:                        s = tty_term_string(tty->term, tke->code);
                    175:                        if (s[0] != '\033' || s[1] == '\0')
                    176:                                continue;
                    177:                }
                    178:
                    179:                tty_keys_add(tty, s + 1, tke->key, tke->flags);
                    180:                if (tke->flags & TTYKEY_CTRL) {
                    181:                        if (strlcpy(tmp, s, sizeof tmp) >= sizeof tmp)
                    182:                                continue;
                    183:                        tmp[strlen(tmp) - 1] ^= 0x20;
1.2       nicm      184:                        tty_keys_add(tty, tmp + 1, tke->key | KEYC_CTRL, 0);
1.1       nicm      185:                }
                    186:        }
                    187: }
                    188:
                    189: void
                    190: tty_keys_free(struct tty *tty)
                    191: {
                    192:        struct tty_key  *tk;
                    193:
                    194:        while (!RB_EMPTY(&tty->ktree)) {
                    195:                tk = RB_ROOT(&tty->ktree);
                    196:                RB_REMOVE(tty_keys, &tty->ktree, tk);
                    197:                xfree(tk->string);
                    198:                xfree(tk);
                    199:        }
                    200: }
                    201:
                    202: struct tty_key *
                    203: tty_keys_find(struct tty *tty, char *buf, size_t len, size_t *size)
                    204: {
                    205:        struct tty_key  *tk, tl;
                    206:        char            *s;
                    207:
                    208:        if (len == 0)
                    209:                return (NULL);
                    210:
                    211:        s = xmalloc(tty->ksize + 1);
                    212:        for (*size = tty->ksize; (*size) > 0; (*size)--) {
                    213:                if ((*size) > len)
                    214:                        continue;
                    215:                memcpy(s, buf, *size);
                    216:                s[*size] = '\0';
                    217:
                    218:                log_debug2("looking for key: %s", s);
                    219:
                    220:                tl.string = s;
                    221:                tk = RB_FIND(tty_keys, &tty->ktree, &tl);
                    222:                if (tk != NULL) {
                    223:                        log_debug2("got key: 0x%x", tk->key);
                    224:                        xfree(s);
                    225:                        return (tk);
                    226:                }
                    227:        }
                    228:        xfree(s);
                    229:
                    230:        return (NULL);
                    231: }
                    232:
                    233: int
                    234: tty_keys_next(struct tty *tty, int *key, u_char *mouse)
                    235: {
                    236:        struct tty_key  *tk;
                    237:        struct timeval   tv;
                    238:        char            *buf;
                    239:        size_t           len, size;
1.3     ! nicm      240:        cc_t             bspace;
1.1       nicm      241:
                    242:        buf = BUFFER_OUT(tty->in);
                    243:        len = BUFFER_USED(tty->in);
                    244:        if (len == 0)
                    245:                return (1);
                    246:        log_debug("keys are %zu (%.*s)", len, (int) len, buf);
                    247:
                    248:        /* If a normal key, return it. */
                    249:        if (*buf != '\033') {
                    250:                *key = buffer_read8(tty->in);
1.3     ! nicm      251:
        !           252:                /*
        !           253:                 * Check for backspace key using termios VERASE - the terminfo
        !           254:                 * kbs entry is extremely unreliable, so cannot be safely
        !           255:                 * used. termios should have a better idea.
        !           256:                 */
        !           257:                bspace = tty->tio.c_cc[VERASE];
        !           258:                if (bspace != _POSIX_VDISABLE && *key == bspace)
        !           259:                        *key = KEYC_BSPACE;
1.1       nicm      260:                goto found;
                    261:        }
                    262:
                    263:        /* Look for matching key string and return if found. */
                    264:        tk = tty_keys_find(tty, buf + 1, len - 1, &size);
                    265:        if (tk != NULL) {
                    266:                buffer_remove(tty->in, size + 1);
                    267:                *key = tk->key;
                    268:                goto found;
                    269:        }
                    270:
                    271:        /* Not found. Is this a mouse key press? */
                    272:        *key = tty_keys_parse_mouse(tty, buf, len, &size, mouse);
                    273:        if (*key != KEYC_NONE) {
                    274:                buffer_remove(tty->in, size);
                    275:                goto found;
                    276:        }
                    277:
                    278:        /* Not found. Try to parse xterm-type arguments. */
                    279:        *key = tty_keys_parse_xterm(tty, buf, len, &size);
                    280:        if (*key != KEYC_NONE) {
                    281:                buffer_remove(tty->in, size);
                    282:                goto found;
                    283:        }
                    284:
                    285:        /* Escape but no key string. If the timer isn't started, start it. */
                    286:        if (!(tty->flags & TTY_ESCAPE)) {
                    287:                tv.tv_sec = 0;
                    288:                tv.tv_usec = ESCAPE_PERIOD * 1000L;
                    289:                if (gettimeofday(&tty->key_timer, NULL) != 0)
                    290:                        fatal("gettimeofday");
                    291:                timeradd(&tty->key_timer, &tv, &tty->key_timer);
                    292:
                    293:                tty->flags |= TTY_ESCAPE;
                    294:                return (1);
                    295:        }
                    296:
                    297:        /* Skip the escape. */
                    298:        buf++;
                    299:        len--;
                    300:
                    301:        /* Is there a normal key following? */
                    302:        if (len != 0 && *buf != '\033') {
                    303:                buffer_remove(tty->in, 1);
1.2       nicm      304:                *key = buffer_read8(tty->in) | KEYC_ESCAPE;
1.1       nicm      305:                goto found;
                    306:        }
                    307:
                    308:        /* Or a key string? */
                    309:        if (len > 1) {
                    310:                tk = tty_keys_find(tty, buf + 1, len - 1, &size);
                    311:                if (tk != NULL) {
                    312:                        buffer_remove(tty->in, size + 2);
1.2       nicm      313:                        *key = tk->key | KEYC_ESCAPE;
1.1       nicm      314:                        goto found;
                    315:                }
                    316:        }
                    317:
                    318:        /* If the timer hasn't expired, keep waiting. */
                    319:        if (gettimeofday(&tv, NULL) != 0)
                    320:                fatal("gettimeofday");
                    321:        if (timercmp(&tty->key_timer, &tv, >))
                    322:                return (1);
                    323:
                    324:        /* Give up and return the escape. */
                    325:        buffer_remove(tty->in, 1);
                    326:        *key = '\033';
                    327:
                    328: found:
                    329:        tty->flags &= ~TTY_ESCAPE;
                    330:        return (0);
                    331: }
                    332:
                    333: int
                    334: tty_keys_parse_mouse(
                    335:     unused struct tty *tty, char *buf, size_t len, size_t *size, u_char *mouse)
                    336: {
                    337:        /*
                    338:         * Mouse sequences are \033[M followed by three characters indicating
                    339:         * buttons, X and Y, all based at 32 with 1,1 top-left.
                    340:         */
                    341:
                    342:        log_debug("mouse input is: %.*s", (int) len, buf);
                    343:        if (len != 6 || memcmp(buf, "\033[M", 3) != 0)
                    344:                return (KEYC_NONE);
                    345:        *size = 6;
                    346:
                    347:        if (buf[3] < 32 || buf[4] < 33 || buf[5] < 33)
                    348:                return (KEYC_NONE);
                    349:
                    350:        mouse[0] = buf[3] - 32;
                    351:        mouse[1] = buf[4] - 33;
                    352:        mouse[2] = buf[5] - 33;
                    353:        return (KEYC_MOUSE);
                    354: }
                    355:
                    356: int
                    357: tty_keys_parse_xterm(struct tty *tty, char *buf, size_t len, size_t *size)
                    358: {
                    359:        struct tty_key  *tk;
                    360:        char             tmp[5];
                    361:        size_t           tmplen;
                    362:        int              key;
                    363:
                    364:        /*
                    365:         * xterm sequences with modifier keys are of the form:
                    366:         *
                    367:         * ^[[1;xD becomes ^[[D
                    368:         * ^[[5;x~ becomes ^[[5~
                    369:         *
                    370:         * This function is a bit of a hack. Need to figure out what exact
                    371:         * format and meaning xterm outputs and fix it. XXX
                    372:         */
                    373:
                    374:        log_debug("xterm input is: %.*s", (int) len, buf);
                    375:        if (len != 6 || memcmp(buf, "\033[1;", 4) != 0)
                    376:                return (KEYC_NONE);
                    377:        *size = 6;
                    378:
                    379:        tmplen = 0;
                    380:        tmp[tmplen++] = '[';
                    381:        if (buf[5] == '~') {
                    382:                tmp[tmplen++] = buf[2];
                    383:                tmp[tmplen++] = '~';
                    384:        } else
                    385:                tmp[tmplen++] = buf[5];
                    386:        log_debug("xterm output is: %.*s", (int) tmplen, tmp);
                    387:
                    388:        tk = tty_keys_find(tty, tmp, tmplen, size);
                    389:        if (tk == NULL)
                    390:                return (KEYC_NONE);
                    391:        key = tk->key;
                    392:
                    393:        switch (buf[4]) {
                    394:        case '8':
1.2       nicm      395:                key |= KEYC_SHIFT|KEYC_ESCAPE|KEYC_CTRL;
1.1       nicm      396:                break;
                    397:        case '7':
1.2       nicm      398:                key |= KEYC_ESCAPE|KEYC_CTRL;
1.1       nicm      399:                break;
                    400:        case '6':
1.2       nicm      401:                key |= KEYC_SHIFT|KEYC_CTRL;
1.1       nicm      402:                break;
                    403:        case '5':
1.2       nicm      404:                key |= KEYC_CTRL;
1.1       nicm      405:                break;
                    406:        case '4':
1.2       nicm      407:                key |= KEYC_SHIFT|KEYC_ESCAPE;
1.1       nicm      408:                break;
                    409:        case '3':
1.2       nicm      410:                key |= KEYC_ESCAPE;
1.1       nicm      411:                break;
                    412:        case '2':
1.2       nicm      413:                key |= KEYC_SHIFT;
1.1       nicm      414:                break;
                    415:        }
                    416:
                    417:        *size = 6;
                    418:        return (key);
                    419: }