diff options
Diffstat (limited to 'local.c')
-rw-r--r-- | local.c | 690 |
1 files changed, 690 insertions, 0 deletions
diff --git a/local.c b/local.c new file mode 100644 index 00000000..eeadb659 --- /dev/null +++ b/local.c @@ -0,0 +1,690 @@ +/* $Id: local.c,v 1.1.1.1 2007-07-09 19:03:30 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 <curses.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <term.h> +#include <unistd.h> + +#include "tmux.h" + +/* + * Functions to translate input and write output to the local client terminal. + * This file should probably be called tty or terminal.c. + */ + +int local_cmp(const void *, const void *); +int local_putc(int); +void local_putp(const char *); + +/* Local key types and key codes. */ +struct local_key { + const char *name; + char *string; + size_t size; + int code; +}; +struct local_key local_keys[] = { + { "ka1", NULL, 0, KEYC_A1 }, + { "ka3", NULL, 0, KEYC_A3 }, + { "kb2", NULL, 0, KEYC_B2 }, + { "kbs", NULL, 0, KEYC_BACKSPACE }, + { "kbeg", NULL, 0, KEYC_BEG }, + { "kcbt", NULL, 0, KEYC_BTAB }, + { "kc1", NULL, 0, KEYC_C1 }, + { "kc3", NULL, 0, KEYC_C3 }, + { "kcan", NULL, 0, KEYC_CANCEL }, + { "ktbc", NULL, 0, KEYC_CATAB }, + { "kclr", NULL, 0, KEYC_CLEAR }, + { "kclo", NULL, 0, KEYC_CLOSE }, + { "kcmd", NULL, 0, KEYC_COMMAND }, + { "kcpy", NULL, 0, KEYC_COPY }, + { "kcrt", NULL, 0, KEYC_CREATE }, + { "kctab", NULL, 0, KEYC_CTAB }, + { "kdch1", NULL, 0, KEYC_DC }, + { "kdl1", NULL, 0, KEYC_DL }, + { "kcud1", NULL, 0, KEYC_DOWN }, + { "krmir", NULL, 0, KEYC_EIC }, + { "kend", NULL, 0, KEYC_END }, + { "kent", NULL, 0, KEYC_ENTER }, + { "kel", NULL, 0, KEYC_EOL }, + { "ked", NULL, 0, KEYC_EOS }, + { "kext", NULL, 0, KEYC_EXIT }, + { "kf0", NULL, 0, KEYC_F0 }, + { "kf1", NULL, 0, KEYC_F1 }, + { "kf10", NULL, 0, KEYC_F10 }, + { "kf11", NULL, 0, KEYC_F11 }, + { "kf12", NULL, 0, KEYC_F12 }, + { "kf13", NULL, 0, KEYC_F13 }, + { "kf14", NULL, 0, KEYC_F14 }, + { "kf15", NULL, 0, KEYC_F15 }, + { "kf16", NULL, 0, KEYC_F16 }, + { "kf17", NULL, 0, KEYC_F17 }, + { "kf18", NULL, 0, KEYC_F18 }, + { "kf19", NULL, 0, KEYC_F19 }, + { "kf2", NULL, 0, KEYC_F2 }, + { "kf20", NULL, 0, KEYC_F20 }, + { "kf21", NULL, 0, KEYC_F21 }, + { "kf22", NULL, 0, KEYC_F22 }, + { "kf23", NULL, 0, KEYC_F23 }, + { "kf24", NULL, 0, KEYC_F24 }, + { "kf25", NULL, 0, KEYC_F25 }, + { "kf26", NULL, 0, KEYC_F26 }, + { "kf27", NULL, 0, KEYC_F27 }, + { "kf28", NULL, 0, KEYC_F28 }, + { "kf29", NULL, 0, KEYC_F29 }, + { "kf3", NULL, 0, KEYC_F3 }, + { "kf30", NULL, 0, KEYC_F30 }, + { "kf31", NULL, 0, KEYC_F31 }, + { "kf32", NULL, 0, KEYC_F32 }, + { "kf33", NULL, 0, KEYC_F33 }, + { "kf34", NULL, 0, KEYC_F34 }, + { "kf35", NULL, 0, KEYC_F35 }, + { "kf36", NULL, 0, KEYC_F36 }, + { "kf37", NULL, 0, KEYC_F37 }, + { "kf38", NULL, 0, KEYC_F38 }, + { "kf39", NULL, 0, KEYC_F39 }, + { "kf4", NULL, 0, KEYC_F4 }, + { "kf40", NULL, 0, KEYC_F40 }, + { "kf41", NULL, 0, KEYC_F41 }, + { "kf42", NULL, 0, KEYC_F42 }, + { "kf43", NULL, 0, KEYC_F43 }, + { "kf44", NULL, 0, KEYC_F44 }, + { "kf45", NULL, 0, KEYC_F45 }, + { "kf46", NULL, 0, KEYC_F46 }, + { "kf47", NULL, 0, KEYC_F47 }, + { "kf48", NULL, 0, KEYC_F48 }, + { "kf49", NULL, 0, KEYC_F49 }, + { "kf5", NULL, 0, KEYC_F5 }, + { "kf50", NULL, 0, KEYC_F50 }, + { "kf51", NULL, 0, KEYC_F51 }, + { "kf52", NULL, 0, KEYC_F52 }, + { "kf53", NULL, 0, KEYC_F53 }, + { "kf54", NULL, 0, KEYC_F54 }, + { "kf55", NULL, 0, KEYC_F55 }, + { "kf56", NULL, 0, KEYC_F56 }, + { "kf57", NULL, 0, KEYC_F57 }, + { "kf58", NULL, 0, KEYC_F58 }, + { "kf59", NULL, 0, KEYC_F59 }, + { "kf6", NULL, 0, KEYC_F6 }, + { "kf60", NULL, 0, KEYC_F60 }, + { "kf61", NULL, 0, KEYC_F61 }, + { "kf62", NULL, 0, KEYC_F62 }, + { "kf63", NULL, 0, KEYC_F63 }, + { "kf7", NULL, 0, KEYC_F7 }, + { "kf8", NULL, 0, KEYC_F8 }, + { "kf9", NULL, 0, KEYC_F9 }, + { "kfnd", NULL, 0, KEYC_FIND }, + { "khlp", NULL, 0, KEYC_HELP }, + { "khome", NULL, 0, KEYC_HOME }, + { "kich1", NULL, 0, KEYC_IC }, + { "kil1", NULL, 0, KEYC_IL }, + { "kcub1", NULL, 0, KEYC_LEFT }, + { "kll", NULL, 0, KEYC_LL }, + { "kmrk", NULL, 0, KEYC_MARK }, + { "kmsg", NULL, 0, KEYC_MESSAGE }, + { "kmov", NULL, 0, KEYC_MOVE }, + { "knxt", NULL, 0, KEYC_NEXT }, + { "knp", NULL, 0, KEYC_NPAGE }, + { "kopn", NULL, 0, KEYC_OPEN }, + { "kopt", NULL, 0, KEYC_OPTIONS }, + { "kpp", NULL, 0, KEYC_PPAGE }, + { "kprv", NULL, 0, KEYC_PREVIOUS }, + { "kprt", NULL, 0, KEYC_PRINT }, + { "krdo", NULL, 0, KEYC_REDO }, + { "kref", NULL, 0, KEYC_REFERENCE }, + { "krfr", NULL, 0, KEYC_REFRESH }, + { "krpl", NULL, 0, KEYC_REPLACE }, + { "krst", NULL, 0, KEYC_RESTART }, + { "kres", NULL, 0, KEYC_RESUME }, + { "kcuf1", NULL, 0, KEYC_RIGHT }, + { "ksav", NULL, 0, KEYC_SAVE }, + { "kBEG", NULL, 0, KEYC_SBEG }, + { "kCAN", NULL, 0, KEYC_SCANCEL }, + { "kCMD", NULL, 0, KEYC_SCOMMAND }, + { "kCPY", NULL, 0, KEYC_SCOPY }, + { "kCRT", NULL, 0, KEYC_SCREATE }, + { "kDC", NULL, 0, KEYC_SDC }, + { "kDL", NULL, 0, KEYC_SDL }, + { "kslt", NULL, 0, KEYC_SELECT }, + { "kEND", NULL, 0, KEYC_SEND }, + { "kEOL", NULL, 0, KEYC_SEOL }, + { "kEXT", NULL, 0, KEYC_SEXIT }, + { "kind", NULL, 0, KEYC_SF }, + { "kFND", NULL, 0, KEYC_SFIND }, + { "kHLP", NULL, 0, KEYC_SHELP }, + { "kHOM", NULL, 0, KEYC_SHOME }, + { "kIC", NULL, 0, KEYC_SIC }, + { "kLFT", NULL, 0, KEYC_SLEFT }, + { "kMSG", NULL, 0, KEYC_SMESSAGE }, + { "kMOV", NULL, 0, KEYC_SMOVE }, + { "kNXT", NULL, 0, KEYC_SNEXT }, + { "kOPT", NULL, 0, KEYC_SOPTIONS }, + { "kPRV", NULL, 0, KEYC_SPREVIOUS }, + { "kPRT", NULL, 0, KEYC_SPRINT }, + { "kri", NULL, 0, KEYC_SR }, + { "kRDO", NULL, 0, KEYC_SREDO }, + { "kRPL", NULL, 0, KEYC_SREPLACE }, + { "kRIT", NULL, 0, KEYC_SRIGHT }, + { "kRES", NULL, 0, KEYC_SRSUME }, + { "kSAV", NULL, 0, KEYC_SSAVE }, + { "kSPD", NULL, 0, KEYC_SSUSPEND }, + { "khts", NULL, 0, KEYC_STAB }, + { "kUND", NULL, 0, KEYC_SUNDO }, + { "kspd", NULL, 0, KEYC_SUSPEND }, + { "kund", NULL, 0, KEYC_UNDO }, + { "kcuu1", NULL, 0, KEYC_UP }, + { "pmous", NULL, 0, KEYC_MOUSE }, + { NULL, NULL, 0, KEYC_NONE } +}; + +/* tty file descriptor and local terminal buffers. */ +int local_fd; +struct buffer *local_in; +struct buffer *local_out; +struct termios local_tio; + +/* Initialise local terminal. */ +int +local_init(struct buffer **in, struct buffer **out) +{ + char *tty; + int mode; + struct termios tio; + struct local_key *lk; + + if ((tty = ttyname(STDOUT_FILENO)) == NULL) + log_fatal("ttyname"); + if ((local_fd = open(tty, O_RDWR)) == -1) + log_fatal("open(\"%s\")", tty); + if ((mode = fcntl(local_fd, F_GETFL)) == -1) + log_fatal("fcntl"); + if (fcntl(local_fd, F_SETFL, mode|O_NONBLOCK) == -1) + log_fatal("fcntl"); + + *in = local_in = buffer_create(BUFSIZ); + *out = local_out = buffer_create(BUFSIZ); + + setupterm(NULL, STDOUT_FILENO, NULL); + + if (tcgetattr(local_fd, &local_tio) != 0) + log_fatal("tcgetattr"); + memcpy(&tio, &local_tio, sizeof tio); + tio.c_iflag &= ~(IXON|IXOFF|ICRNL|INLCR); + tio.c_oflag &= ~(OPOST|ONLCR|OCRNL|ONLRET); + tio.c_lflag &= ~(IEXTEN|ICANON|ECHO|ECHOE|ECHOKE|ECHOCTL|ISIG); + if (tcsetattr(local_fd, TCSANOW, &tio) != 0) + log_fatal("tcsetattr"); + + if (enter_ca_mode) + local_putp(enter_ca_mode); + local_putp(keypad_xmit); + local_putp(clear_screen); + + for (lk = local_keys; lk->name != NULL; lk++) { + lk->string = tigetstr(lk->name); + if (lk->string == (char *) -1 || lk->string == (char *) 0) + lk->string = NULL; + else { + lk->size = strlen(lk->string); + log_debug("string for %s (%d): \"%s\", length %zu", + lk->name, lk->code, lk->string, lk->size); + } + } + qsort(local_keys, sizeof local_keys / + sizeof local_keys[0], sizeof local_keys[0], local_cmp); + + return (local_fd); +} + +/* Compare keys. */ +int +local_cmp(const void *ptr1, const void *ptr2) +{ + const struct local_key *lk1 = ptr1, *lk2 = ptr2; + + if (lk1->string == NULL && lk2->string == NULL) + return (0); + if (lk1->string == NULL) + return (1); + if (lk2->string == NULL) + return (-1); + return (lk2->size - lk1->size); +} + +/* Tidy up and reset local terminal. */ +void +local_done(void) +{ + xfree(local_in); + xfree(local_out); + + if (tcsetattr(local_fd, TCSANOW, &local_tio) != 0) + log_fatal("tcsetattr"); + close(local_fd); + + putp(keypad_local); /* not local_putp */ + if (exit_ca_mode) + putp(exit_ca_mode); + putp(clear_screen); + putp(cursor_normal); + putp(exit_attribute_mode); +} + +/* Put a character. Used as parameter to tputs. */ +int +local_putc(int c) +{ + FILE *f; + u_char ch = c; + + if (c < 0 || c > (int) UCHAR_MAX) + log_fatalx("local_putc: invalid character"); + + if (debug_level > 1) { + f = fopen("tmux-out.log", "a+"); + fprintf(f, "%c", ch); + fclose(f); + } + + buffer_write(local_out, &ch, 1); + return (c); +} + +/* Put terminfo string. */ +void +local_putp(const char *s) +{ + if (s == NULL) + log_fatalx("local_putp: null pointer"); + + tputs(s, 1, local_putc); +} + +/* Return waiting keys if any. */ +int +local_key(size_t *used) +{ + struct local_key *lk; + u_int i; + size_t size; + + size = BUFFER_USED(local_in); + if (size == 0) + return (KEYC_NONE); + + i = 0; + lk = local_keys; + while (lk->string != NULL) { + if (strncmp(BUFFER_OUT(local_in), lk->string, size) == 0) { + if (size < lk->size) + return (KEYC_NONE); + log_debug("got key: %s %d", lk->name, lk->code); + buffer_remove(local_in, lk->size); + if (used != NULL) + *used = lk->size; + return (lk->code); + } + + i++; + lk = local_keys + i; + } + + if (used != NULL) + *used = 1; + return (input_extract8(local_in)); +} + +/* Display output data. */ +void +local_output(struct buffer *b, size_t size) +{ + u_char ch; + uint16_t ua, ub; + + while (size != 0) { + if (size < 1) + break; + size--; + ch = input_extract8(b); + if (ch != '\e') { + switch (ch) { + case '\n': /* LF */ + if (cursor_down) { + local_putp(cursor_down); + break; + } + log_fatalx("local_output: cursor_down"); + case '\r': /* CR */ + if (carriage_return) { + local_putp(carriage_return); + break; + } + log_fatalx("local_output: carriage_return"); + case '\010': /* BS */ + if (cursor_left) { + local_putp(cursor_left); + break; + } + log_fatalx("local_output: cursor_left"); + break; + default: + local_putc(ch); + break; + } + continue; + } + + if (size < 1) + log_fatalx("local_output: underflow"); + size--; + ch = input_extract8(b); + + log_debug("received code %hhu", ch); + switch (ch) { + case CODE_CURSORUP: + if (size < 2) + log_fatalx("local_output: underflow"); + size -= 2; + ua = input_extract16(b); + if (!parm_up_cursor) + log_fatalx("local_output: parm_up_cursor"); + local_putp(tparm(parm_up_cursor, ua)); + break; + case CODE_CURSORDOWN: + if (size < 2) + log_fatalx("local_output: underflow"); + size -= 2; + ua = input_extract16(b); + if (!parm_down_cursor) + log_fatalx("local_output: parm_down_cursor"); + local_putp(tparm(parm_down_cursor, ua)); + break; + case CODE_CURSORRIGHT: + if (size < 2) + log_fatalx("local_output: underflow"); + size -= 2; + ua = input_extract16(b); + if (!parm_right_cursor) + log_fatalx("local_output: parm_right_cursor"); + local_putp(tparm(parm_right_cursor, ua)); + break; + case CODE_CURSORLEFT: + if (size < 2) + log_fatalx("local_output: underflow"); + size -= 2; + ua = input_extract16(b); + if (!parm_left_cursor) + log_fatalx("local_output: parm_left_cursor"); + local_putp(tparm(parm_left_cursor, ua)); + break; + case CODE_CURSORMOVE: + if (size < 4) + log_fatalx("local_output: underflow"); + size -= 4; + ua = input_extract16(b); + ub = input_extract16(b); + if (!cursor_address) + log_fatalx("local_output: cursor_address"); + local_putp(tparm(cursor_address, ua - 1, ub - 1)); + break; + case CODE_CLEARENDOFSCREEN: + if (!clr_eos) + log_fatalx("local_output: clr_eos"); + local_putp(clr_eos); + break; + case CODE_CLEARSCREEN: + if (!clear_screen) + log_fatalx("local_output: clear_screen"); + local_putp(clear_screen); + break; + case CODE_CLEARENDOFLINE: + if (!clr_eol) + log_fatalx("local_output: clr_eol"); + local_putp(clr_eol); + break; + case CODE_CLEARSTARTOFLINE: + if (!clr_bol) + log_fatalx("local_output: clr_bol"); + local_putp(clr_bol); + break; + case CODE_CLEARLINE: + if (!clr_eol) + log_fatalx("local_output: clr_eol"); + local_putp(clr_eol); /* XXX */ + break; + case CODE_INSERTLINE: + if (size < 2) + log_fatalx("local_output: underflow"); + size -= 2; + ua = input_extract16(b); + if (!parm_insert_line) + log_fatalx("local_output: parm_insert_line"); + local_putp(tparm(parm_insert_line, ua)); + break; + case CODE_DELETELINE: + if (size < 2) + log_fatalx("local_output: underflow"); + size -= 2; + ua = input_extract16(b); + if (!parm_delete_line) + log_fatalx("local_output: parm_delete"); + local_putp(tparm(parm_delete_line, ua)); + break; + case CODE_INSERTCHARACTER: + if (size < 2) + log_fatalx("local_output: underflow"); + size -= 2; + ua = input_extract16(b); + if (!parm_ich) + log_fatalx("local_output: parm_ich"); + local_putp(tparm(parm_ich, ua)); + break; + case CODE_DELETECHARACTER: + if (size < 2) + log_fatalx("local_output: underflow"); + size -= 2; + ua = input_extract16(b); + if (!parm_dch) + log_fatalx("local_output: parm_dch"); + local_putp(tparm(parm_dch, ua)); + break; + case CODE_CURSORON: + if (!cursor_normal) + log_fatalx("local_output: cursor_normal"); + local_putp(cursor_normal); + break; + case CODE_CURSOROFF: + if (!cursor_invisible) + log_fatalx("local_output: cursor_invisible"); + local_putp(cursor_invisible); + break; + case CODE_CURSORUPSCROLL: + if (!scroll_reverse) + log_fatalx("local_output: scroll_reverse"); + local_putp(scroll_reverse); + break; + case CODE_CURSORDOWNSCROLL: + if (!scroll_forward) + log_fatalx("local_output: scroll_forward"); + local_putp(scroll_forward); + break; + case CODE_SCROLLREGION: + if (size < 4) + log_fatalx("local_output: underflow"); + size -= 4; + ua = input_extract16(b); + ub = input_extract16(b); + if (!change_scroll_region) { + log_fatalx( + "local_output: change_scroll_region"); + } + local_putp(tparm(change_scroll_region, ua - 1, ub - 1)); + break; + case CODE_INSERTON: + if (!enter_insert_mode) + log_fatalx("local_output: enter_insert_mode"); + local_putp(enter_insert_mode); + break; + case CODE_INSERTOFF: + if (!exit_insert_mode) + log_fatalx("local_output: exit_insert_mode"); + local_putp(exit_insert_mode); + break; + case CODE_KCURSOROFF: + /*t = tigetstr("CE"); + if (t != (char *) 0 && t != (char *) -1) + local_putp(t);*/ + break; + case CODE_KCURSORON: + /*t = tigetstr("CS"); + if (t != (char *) 0 && t != (char *) -1) + local_putp(t);*/ + break; + case CODE_KKEYPADOFF:/* + if (!keypad_local) + log_fatalx("local_output: keypad_local"); + local_putp(keypad_local);*/ + break; + case CODE_KKEYPADON:/* + if (!keypad_xmit) + log_fatalx("local_output: keypad_xmit"); + local_putp(keypad_xmit);*/ + break; + case CODE_TITLE: + if (size < 2) + log_fatalx("local_output: underflow"); + size -= 2; + ua = input_extract16(b); + + if (size < ua) + log_fatalx("local_output: underflow"); + size -= ua; + buffer_remove(b, ua); + break; + case CODE_ATTRIBUTES: + if (size < 2) + log_fatalx("local_output: underflow"); + size -= 2; + ua = input_extract16(b); + + if (!exit_attribute_mode) + log_fatalx("local_output: exit_attribute_mode"); + if (ua == 0) { + if (exit_attribute_mode) + local_putp(exit_attribute_mode); + break; + } + + while (ua-- != 0) { + if (size < 2) + log_fatalx("local_output: underflow"); + size -= 2; + ub = input_extract16(b); + + switch (ub) { + case 0: + case 10: + if (exit_attribute_mode) + local_putp(exit_attribute_mode); + break; + case 1: + if (enter_bold_mode) + local_putp(enter_bold_mode); + break; + case 2: + if (enter_dim_mode) + local_putp(enter_dim_mode); + break; + case 3: + if (enter_standout_mode) + local_putp(enter_standout_mode); + break; + case 4: + if (enter_underline_mode) + local_putp(enter_underline_mode); + break; + case 5: + if (enter_blink_mode) + local_putp(enter_blink_mode); + break; + case 7: + if (enter_reverse_mode) + local_putp(enter_reverse_mode); + break; + case 8: + if (enter_secure_mode) + local_putp(enter_secure_mode); + break; + case 23: + if (exit_standout_mode) + local_putp(exit_standout_mode); + break; + case 24: + if (exit_underline_mode) + local_putp(exit_underline_mode); + break; + case 30: + case 31: + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + local_putp( + tparm(set_a_foreground, ub - 30)); + break; + case 39: + if (tigetflag("AX") == TRUE) + local_putp("\e[39m"); + else { + local_putp( + tparm(set_a_background, 0)); + } + break; + case 40: + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + case 47: + local_putp( + tparm(set_a_background, ub - 40)); + break; + case 49: + if (tigetflag("AX") == TRUE) + local_putp("\e[49m"); + else { + local_putp( + tparm(set_a_background, 0)); + } + break; + } + } + break; + } + } +} |