| /* $Id: tty-keys.c,v 1.5 2008-06-25 07:30:08 nicm Exp $ */ |
| |
| /* |
| * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> |
| * |
| * Permission to use, copy, modify, and distribute this software for any |
| * purpose with or without fee is hereby granted, provided that the above |
| * copyright notice and this permission notice appear in all copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER |
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
| * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| #include <sys/types.h> |
| |
| #include <string.h> |
| #include <time.h> |
| |
| #include "tmux.h" |
| |
| struct { |
| const char *name; |
| int code; |
| } tty_keys[] = { |
| /* { "kb", KEYC_BACKSPACE }, */ |
| { "kBEG", KEYC_SBEG }, |
| { "kCAN", KEYC_SCANCEL }, |
| { "kCMD", KEYC_SCOMMAND }, |
| { "kCPY", KEYC_SCOPY }, |
| { "kCRT", KEYC_SCREATE }, |
| { "kDC", KEYC_SDC }, |
| { "kDL", KEYC_SDL }, |
| { "kEND", KEYC_SEND }, |
| { "kEOL", KEYC_SEOL }, |
| { "kEXT", KEYC_SEXIT }, |
| { "kFND", KEYC_SFIND }, |
| { "kHLP", KEYC_SHELP }, |
| { "kHOM", KEYC_SHOME }, |
| { "kIC", KEYC_SIC }, |
| { "kLFT", KEYC_SLEFT }, |
| { "kMOV", KEYC_SMOVE }, |
| { "kMSG", KEYC_SMESSAGE }, |
| { "kNXT", KEYC_SNEXT }, |
| { "kOPT", KEYC_SOPTIONS }, |
| { "kPRT", KEYC_SPRINT }, |
| { "kPRV", KEYC_SPREVIOUS }, |
| { "kRDO", KEYC_SREDO }, |
| { "kRES", KEYC_SRSUME }, |
| { "kRIT", KEYC_SRIGHT }, |
| { "kRPL", KEYC_SREPLACE }, |
| { "kSAV", KEYC_SSAVE }, |
| { "kSPD", KEYC_SSUSPEND }, |
| { "kUND", KEYC_SUNDO }, |
| { "ka1", KEYC_A1 }, |
| { "ka3", KEYC_A3 }, |
| { "kb2", KEYC_B2 }, |
| { "kbeg", KEYC_BEG }, |
| { "kc1", KEYC_C1 }, |
| { "kc3", KEYC_C3 }, |
| { "kcan", KEYC_CANCEL }, |
| { "kcbt", KEYC_BTAB }, |
| { "kclo", KEYC_CLOSE }, |
| { "kclr", KEYC_CLEAR }, |
| { "kcmd", KEYC_COMMAND }, |
| { "kcpy", KEYC_COPY }, |
| { "kcrt", KEYC_CREATE }, |
| { "kctab", KEYC_CTAB }, |
| { "kcub1", KEYC_LEFT }, |
| { "kcud1", KEYC_DOWN }, |
| { "kcuf1", KEYC_RIGHT }, |
| { "kcuu1", KEYC_UP }, |
| { "kdch1", KEYC_DC }, |
| { "kdl1", KEYC_DL }, |
| { "ked", KEYC_EOS }, |
| { "kel", KEYC_EOL }, |
| { "kend", KEYC_END }, |
| { "kent", KEYC_ENTER }, |
| { "kext", KEYC_EXIT }, |
| { "kf0", KEYC_F0 }, |
| { "kf1", KEYC_F1 }, |
| { "kf10", KEYC_F10 }, |
| { "kf11", KEYC_F11 }, |
| { "kf12", KEYC_F12 }, |
| { "kf13", KEYC_F13 }, |
| { "kf14", KEYC_F14 }, |
| { "kf15", KEYC_F15 }, |
| { "kf16", KEYC_F16 }, |
| { "kf17", KEYC_F17 }, |
| { "kf18", KEYC_F18 }, |
| { "kf19", KEYC_F19 }, |
| { "kf2", KEYC_F2 }, |
| { "kf20", KEYC_F20 }, |
| { "kf21", KEYC_F21 }, |
| { "kf22", KEYC_F22 }, |
| { "kf23", KEYC_F23 }, |
| { "kf24", KEYC_F24 }, |
| { "kf25", KEYC_F25 }, |
| { "kf26", KEYC_F26 }, |
| { "kf27", KEYC_F27 }, |
| { "kf28", KEYC_F28 }, |
| { "kf29", KEYC_F29 }, |
| { "kf3", KEYC_F3 }, |
| { "kf30", KEYC_F30 }, |
| { "kf31", KEYC_F31 }, |
| { "kf32", KEYC_F32 }, |
| { "kf33", KEYC_F33 }, |
| { "kf34", KEYC_F34 }, |
| { "kf35", KEYC_F35 }, |
| { "kf36", KEYC_F36 }, |
| { "kf37", KEYC_F37 }, |
| { "kf38", KEYC_F38 }, |
| { "kf39", KEYC_F39 }, |
| { "kf4", KEYC_F4 }, |
| { "kf40", KEYC_F40 }, |
| { "kf41", KEYC_F41 }, |
| { "kf42", KEYC_F42 }, |
| { "kf43", KEYC_F43 }, |
| { "kf44", KEYC_F44 }, |
| { "kf45", KEYC_F45 }, |
| { "kf46", KEYC_F46 }, |
| { "kf47", KEYC_F47 }, |
| { "kf48", KEYC_F48 }, |
| { "kf49", KEYC_F49 }, |
| { "kf5", KEYC_F5 }, |
| { "kf50", KEYC_F50 }, |
| { "kf51", KEYC_F51 }, |
| { "kf52", KEYC_F52 }, |
| { "kf53", KEYC_F53 }, |
| { "kf54", KEYC_F54 }, |
| { "kf55", KEYC_F55 }, |
| { "kf56", KEYC_F56 }, |
| { "kf57", KEYC_F57 }, |
| { "kf58", KEYC_F58 }, |
| { "kf59", KEYC_F59 }, |
| { "kf6", KEYC_F6 }, |
| { "kf60", KEYC_F60 }, |
| { "kf61", KEYC_F61 }, |
| { "kf62", KEYC_F62 }, |
| { "kf63", KEYC_F63 }, |
| { "kf7", KEYC_F7 }, |
| { "kf8", KEYC_F8 }, |
| { "kf9", KEYC_F9 }, |
| { "kfnd", KEYC_FIND }, |
| { "khlp", KEYC_HELP }, |
| { "khome", KEYC_HOME }, |
| { "khts", KEYC_STAB }, |
| { "kich1", KEYC_IC }, |
| { "kil1", KEYC_IL }, |
| { "kind", KEYC_SF }, |
| { "kll", KEYC_LL }, |
| { "kmov", KEYC_MOVE }, |
| { "kmrk", KEYC_MARK }, |
| { "kmsg", KEYC_MESSAGE }, |
| { "knp", KEYC_NPAGE }, |
| { "knxt", KEYC_NEXT }, |
| { "kopn", KEYC_OPEN }, |
| { "kopt", KEYC_OPTIONS }, |
| { "kpp", KEYC_PPAGE }, |
| { "kprt", KEYC_PRINT }, |
| { "kprv", KEYC_PREVIOUS }, |
| { "krdo", KEYC_REDO }, |
| { "kref", KEYC_REFERENCE }, |
| { "kres", KEYC_RESUME }, |
| { "krfr", KEYC_REFRESH }, |
| { "kri", KEYC_SR }, |
| { "krmir", KEYC_EIC }, |
| { "krpl", KEYC_REPLACE }, |
| { "krst", KEYC_RESTART }, |
| { "ksav", KEYC_SAVE }, |
| { "kslt", KEYC_SELECT }, |
| { "kspd", KEYC_SUSPEND }, |
| { "ktbc", KEYC_CATAB }, |
| { "kund", KEYC_UNDO }, |
| { "pmous", KEYC_MOUSE }, |
| }; |
| #define NTTYKEYS (sizeof tty_keys / sizeof tty_keys[0]) |
| |
| RB_GENERATE(tty_keys, tty_key, entry, tty_keys_cmp); |
| |
| struct tty_key *tty_keys_find(struct tty *, char *, size_t, size_t *); |
| |
| int |
| tty_keys_cmp(struct tty_key *k1, struct tty_key *k2) |
| { |
| return (strcmp(k1->string, k2->string)); |
| } |
| |
| void |
| tty_keys_init(struct tty *tty) |
| { |
| struct tty_key *tk; |
| u_int i; |
| char *s; |
| |
| RB_INIT(&tty->ktree); |
| |
| tty->ksize = 0; |
| for (i = 0; i < NTTYKEYS; i++) { |
| s = tigetstr(tty_keys[i].name); |
| if (s == (char *) -1 || s == (char *) 0) |
| continue; |
| if (s[0] != '\033' || s[1] == '\0') |
| continue; |
| |
| tk = xmalloc(sizeof *tk); |
| tk->string = xstrdup(s + 1); |
| tk->code = tty_keys[i].code; |
| |
| if (strlen(tk->string) > tty->ksize) |
| tty->ksize = strlen(tk->string); |
| RB_INSERT(tty_keys, &tty->ktree, tk); |
| |
| log_debug("found key %x: size now %zu (%s)", |
| tk->code, tty->ksize, tk->string); |
| } |
| } |
| |
| void |
| tty_keys_free(struct tty *tty) |
| { |
| struct tty_key *tk, *tl; |
| |
| for (tk = RB_MIN(tty_keys, &tty->ktree); tk != NULL; tk = tl) { |
| tl = RB_NEXT(tty_keys, &tty->ktree, tk); |
| RB_REMOVE(tty_keys, &tty->ktree, tk); |
| xfree(tk->string); |
| xfree(tk); |
| } |
| } |
| |
| struct tty_key * |
| tty_keys_find(struct tty *tty, char *buf, size_t len, size_t *size) |
| { |
| struct tty_key *tk, tl; |
| char *s; |
| |
| if (len == 0) |
| return (NULL); |
| |
| s = xmalloc(tty->ksize + 1); |
| for (*size = tty->ksize; (*size) > 0; (*size)--) { |
| if ((*size) > len) |
| continue; |
| memcpy(s, buf, *size); |
| s[*size] = '\0'; |
| |
| log_debug2("looking for key: %s", s); |
| |
| tl.string = s; |
| tk = RB_FIND(tty_keys, &tty->ktree, &tl); |
| if (tk != NULL) |
| return (tk); |
| } |
| xfree(s); |
| |
| return (NULL); |
| } |
| |
| int |
| tty_keys_next(struct tty *tty, int *code) |
| { |
| struct tty_key *tk; |
| size_t size; |
| struct timespec ts; |
| |
| size = BUFFER_USED(tty->in); |
| if (size == 0) |
| return (1); |
| log_debug("keys are %zu (%.*s)", size, (int) size, BUFFER_OUT(tty->in)); |
| |
| /* If a normal key, return it. */ |
| if (*BUFFER_OUT(tty->in) != '\033') { |
| *code = buffer_read8(tty->in); |
| return (0); |
| } |
| |
| /* Look for matching key string and return if found. */ |
| tk = tty_keys_find(tty, BUFFER_OUT(tty->in) + 1, size - 1, &size); |
| if (tk != NULL) { |
| *code = tk->code; |
| buffer_remove(tty->in, size + 1); |
| |
| tty->flags &= ~TTY_ESCAPE; |
| return (0); |
| } |
| |
| /* Escape but no key string. If the timer isn't started, start it. */ |
| if (!(tty->flags & TTY_ESCAPE)) { |
| ts.tv_sec = 0; |
| ts.tv_nsec = 500 * 1000000L; |
| if (clock_gettime(CLOCK_REALTIME, &tty->key_timer) != 0) |
| fatal("clock_gettime"); |
| timespecadd(&tty->key_timer, &ts, &tty->key_timer); |
| |
| tty->flags |= TTY_ESCAPE; |
| return (1); |
| } |
| |
| /* Otherwise, if the timer hasn't expired, wait. */ |
| if (clock_gettime(CLOCK_REALTIME, &ts) != 0) |
| fatal("clock_gettime"); |
| if (!timespeccmp(&tty->key_timer, &ts, >)) |
| return (1); |
| tty->flags &= ~TTY_ESCAPE; |
| |
| /* Remove the leading escape. */ |
| buffer_remove(tty->in, 1); |
| size = BUFFER_USED(tty->in); |
| |
| /* If we have no following data, return escape. */ |
| if (size == 0) { |
| *code = '\033'; |
| return (0); |
| } |
| |
| /* If a normal key follows, return it. */ |
| if (*BUFFER_OUT(tty->in) != '\033') { |
| *code = KEYC_ADDESCAPE(buffer_read8(tty->in)); |
| return (0); |
| } |
| |
| /* Try to look up the key. */ |
| tk = tty_keys_find(tty, BUFFER_OUT(tty->in) + 1, size - 1, &size); |
| if (tk != NULL) { |
| *code = KEYC_ADDESCAPE(tk->code); |
| buffer_remove(tty->in, size + 1); |
| return (0); |
| } |
| |
| /* If not found, return escape-escape. */ |
| *code = KEYC_ADDESCAPE('\033'); |
| buffer_remove(tty->in, 1); |
| return (0); |
| } |