diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 3278ea7f..4604ee27 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -43,7 +43,7 @@ cmd_attach_session_exec(struct cmd *self, struct cmd_ctx *ctx) struct cmd_target_data *data = self->data; struct session *s; struct client *c; - char *cause; + char *overrides, *cause; u_int i; if (ARRAY_LENGTH(&sessions) == 0) { @@ -80,7 +80,9 @@ cmd_attach_session_exec(struct cmd *self, struct cmd_ctx *ctx) return (-1); } - if (tty_open(&ctx->cmdclient->tty, &cause) != 0) { + overrides = + options_get_string(&s->options, "terminal-overrides"); + if (tty_open(&ctx->cmdclient->tty, overrides, &cause) != 0) { ctx->error(ctx, "terminal open failed: %s", cause); xfree(cause); return (-1); diff --git a/cmd-new-session.c b/cmd-new-session.c index ea2e5b12..c1ba6e30 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -108,7 +108,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx) { struct cmd_new_session_data *data = self->data; struct session *s; - char *cmd, *cwd, *cause; + char *overrides, *cmd, *cwd, *cause; int detached; u_int sx, sy; @@ -147,7 +147,9 @@ cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx) return (-1); } - if (tty_open(&ctx->cmdclient->tty, &cause) != 0) { + overrides = + options_get_string(&global_s_options, "terminal-overrides"); + if (tty_open(&ctx->cmdclient->tty, overrides, &cause) != 0) { ctx->error(ctx, "open terminal failed: %s", cause); xfree(cause); return (-1); diff --git a/cmd-set-option.c b/cmd-set-option.c index 2e3ca734..5c2d5870 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -77,7 +77,8 @@ const struct set_option_entry set_option_table[] = { { "status-left-length", SET_OPTION_NUMBER, 0, SHRT_MAX, NULL }, { "status-right", SET_OPTION_STRING, 0, 0, NULL }, { "status-right-length", SET_OPTION_NUMBER, 0, SHRT_MAX, NULL }, - { "status-utf8", SET_OPTION_FLAG, 0, 0, NULL }, + { "status-utf8", SET_OPTION_FLAG, 0, 0, NULL }, + { "terminal-overrides", SET_OPTION_STRING, 0, 0, NULL }, { "visual-activity", SET_OPTION_FLAG, 0, 0, NULL }, { "visual-bell", SET_OPTION_FLAG, 0, 0, NULL }, { "visual-content", SET_OPTION_FLAG, 0, 0, NULL }, diff --git a/cmd-string.c b/cmd-string.c index 9ba399b2..c12ac1d7 100644 --- a/cmd-string.c +++ b/cmd-string.c @@ -215,6 +215,9 @@ cmd_string_string(const char *s, size_t *p, char endch, int esc) switch (ch = cmd_string_getc(s, p)) { case EOF: goto error; + case 'e': + ch = '\033'; + break; case 'r': ch = '\r'; break; diff --git a/tmux.1 b/tmux.1 index 1f361d17..e60424d3 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1418,6 +1418,44 @@ and .Ic status-right strings as UTF-8; notably, this is important for wide characters. This option defaults to off. +.It Xo Ic terminal-overrides +.Ar string +.Xc +Contains a list of entries which override terminal descriptions read using +.Xr terminfo 5 . +.Ar string +is a comma-separated list of items each a colon-separated string made up of a +terminal type pattern (matched using +.Xr fnmatch 3 ) +and a set of +.Em name=value +entries. +.Pp +For example, to set the +.Ql clear +.Xr terminfo 5 +entry to +.Ql \ee[H\ee[2J +for all terminal types and the +.Ql dch1 +entry to +.Ql \ee[P +for the +.Ql rxvt +terminal type, the option could be set to the string: +.Bd -literal -offset indent +"*:clear=\ee[H\ee[2J,rxvt:dch1=\ee[P" +.Ed +.Pp +The terminal entry value is passed through +.Xr strunvis 3 +before interpretation. +The default value forcibly corrects the +.Ql colors +entry for terminals which support 88 or 256 colours: +.Bd -literal -offset indent +"*88col*:colors=88,*256col*:colors=256" +.Ed .It Xo Ic visual-activity .Op Ic on | Ic off .Xc diff --git a/tmux.c b/tmux.c index dd7b83f5..ff1bd4c4 100644 --- a/tmux.c +++ b/tmux.c @@ -367,6 +367,8 @@ main(int argc, char **argv) options_set_number(&global_s_options, "status-utf8", 1); else options_set_number(&global_s_options, "status-utf8", 0); + options_set_string(&global_s_options, + "terminal-overrides", "*88col*:colors=88,*256col*:colors=256"); options_set_number(&global_s_options, "visual-activity", 0); options_set_number(&global_s_options, "visual-bell", 0); options_set_number(&global_s_options, "visual-content", 0); diff --git a/tmux.h b/tmux.h index 62a2dac0..37686e13 100644 --- a/tmux.h +++ b/tmux.h @@ -1130,7 +1130,7 @@ void tty_detect_utf8(struct tty *); void tty_set_title(struct tty *, const char *); void tty_update_mode(struct tty *, int); void tty_draw_line(struct tty *, struct screen *, u_int, u_int, u_int); -int tty_open(struct tty *, char **); +int tty_open(struct tty *, const char *, char **); void tty_close(struct tty *, int); void tty_free(struct tty *, int); void tty_write(void (*)(struct tty *, struct tty_ctx *), struct tty_ctx *); @@ -1153,7 +1153,7 @@ void tty_cmd_reverseindex(struct tty *, struct tty_ctx *); /* tty-term.c */ extern struct tty_terms tty_terms; extern struct tty_term_code_entry tty_term_codes[NTTYCODE]; -struct tty_term *tty_term_find(char *, int, char **); +struct tty_term *tty_term_find(char *, int, const char *, char **); void tty_term_free(struct tty_term *); int tty_term_has(struct tty_term *, enum tty_code_code); const char *tty_term_string(struct tty_term *, enum tty_code_code); diff --git a/tty-term.c b/tty-term.c index c8b47783..2813be61 100644 --- a/tty-term.c +++ b/tty-term.c @@ -19,12 +19,15 @@ #include #include +#include +#include #include #include +#include #include "tmux.h" -void tty_term_quirks(struct tty_term *); +void tty_term_override(struct tty_term *, const char *); char *tty_term_strip(const char *); struct tty_terms tty_terms = SLIST_HEAD_INITIALIZER(tty_terms); @@ -140,27 +143,87 @@ tty_term_strip(const char *s) } void -tty_term_quirks(struct tty_term *term) +tty_term_override(struct tty_term *term, const char *overrides) { - if (strncmp(term->name, "rxvt", 4) == 0) { - /* rxvt supports dch1 but some termcap files do not have it. */ - if (!tty_term_has(term, TTYC_DCH1)) { - term->codes[TTYC_DCH1].type = TTYCODE_STRING; - term->codes[TTYC_DCH1].value.string = xstrdup("\033[P"); + struct tty_term_code_entry *ent; + struct tty_code *code; + char *termnext, *termstr, *entnext, *entstr; + char *s, *ptr, *val; + const char *errstr; + u_int i; + int n, removeflag; + + s = xstrdup(overrides); + + termnext = s; + while ((termstr = strsep(&termnext, ",")) != NULL) { + entnext = termstr; + + entstr = strsep(&entnext, ":"); + if (entstr == NULL || entnext == NULL) + continue; + if (fnmatch(entstr, term->name, 0) != 0) + continue; + while ((entstr = strsep(&entnext, ":")) != NULL) { + if (*entstr == '\0') + continue; + + val = NULL; + removeflag = 0; + if ((ptr = strchr(entstr, '=')) != NULL) { + *ptr++ = '\0'; + val = xstrdup(ptr); + if (strunvis(val, ptr) == NULL) { + xfree(val); + val = xstrdup(ptr); + } + } else if (entstr[strlen(entstr) - 1] == '@') { + entstr[strlen(entstr) - 1] = '\0'; + removeflag = 1; + } + + for (i = 0; i < NTTYCODE; i++) { + ent = &tty_term_codes[i]; + if (strcmp(entstr, ent->name) != 0) + continue; + code = &term->codes[ent->code]; + + if (removeflag) { + code->type = TTYCODE_NONE; + continue; + } + switch (ent->type) { + case TTYCODE_NONE: + break; + case TTYCODE_STRING: + xfree(code->value.string); + code->value.string = xstrdup(val); + code->type = ent->type; + break; + case TTYCODE_NUMBER: + n = strtonum(val, 0, INT_MAX, &errstr); + if (errstr != NULL) + break; + code->value.number = n; + code->type = ent->type; + break; + case TTYCODE_FLAG: + code->value.flag = 1; + code->type = ent->type; + break; + } + } + + if (val != NULL) + xfree(val); } } - if (strncmp(term->name, "xterm", 5) == 0) { - /* xterm supports ich1 but some termcaps omit it. */ - if (!tty_term_has(term, TTYC_ICH1)) { - term->codes[TTYC_ICH1].type = TTYCODE_STRING; - term->codes[TTYC_ICH1].value.string = xstrdup("\033[@"); - } - } + xfree(s); } struct tty_term * -tty_term_find(char *name, int fd, char **cause) +tty_term_find(char *name, int fd, const char *overrides, char **cause) { struct tty_term *term; struct tty_term_code_entry *ent; @@ -235,7 +298,7 @@ tty_term_find(char *name, int fd, char **cause) break; } } - tty_term_quirks(term); + tty_term_override(term, overrides); /* Delete curses data. */ del_curterm(cur_term); @@ -297,12 +360,8 @@ tty_term_find(char *name, int fd, char **cause) */ if (tty_term_number(term, TTYC_COLORS) == 256) term->flags |= TERM_256COLOURS; - if (strstr(name, "256col") != NULL) /* XXX HACK */ - term->flags |= TERM_256COLOURS; if (tty_term_number(term, TTYC_COLORS) == 88) term->flags |= TERM_88COLOURS; - if (strstr(name, "88col") != NULL) /* XXX HACK */ - term->flags |= TERM_88COLOURS; /* * Terminals without xenl (eat newline glitch) wrap at at $COLUMNS - 1 diff --git a/tty.c b/tty.c index 48b10f7d..d3a4ba1d 100644 --- a/tty.c +++ b/tty.c @@ -57,7 +57,7 @@ tty_init(struct tty *tty, char *path, char *term) } int -tty_open(struct tty *tty, char **cause) +tty_open(struct tty *tty, const char *overrides, char **cause) { int mode; @@ -79,7 +79,8 @@ tty_open(struct tty *tty, char **cause) else tty->log_fd = -1; - if ((tty->term = tty_term_find(tty->termname, tty->fd, cause)) == NULL) + tty->term = tty_term_find(tty->termname, tty->fd, overrides, cause); + if (tty->term == NULL) goto error; tty->in = buffer_create(BUFSIZ);