Allow features to be disabled using @ suffix in terminal-features, from

Michael Grant.
This commit is contained in:
nicm
2026-08-17 14:47:41 +00:00
committed by tmux update bot
parent 1729bb8e8a
commit a18d4e0067
7 changed files with 89 additions and 57 deletions

8
tmux.1
View File

@@ -1,4 +1,4 @@
.\" $OpenBSD: tmux.1,v 1.1155 2026/08/06 09:05:04 nicm Exp $ .\" $OpenBSD: tmux.1,v 1.1156 2026/08/17 14:47:41 nicm Exp $
.\" .\"
.\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> .\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
.\" .\"
@@ -14,7 +14,7 @@
.\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING .\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
.\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\" .\"
.Dd $Mdocdate: August 6 2026 $ .Dd $Mdocdate: August 17 2026 $
.Dt TMUX 1 .Dt TMUX 1
.Os .Os
.Sh NAME .Sh NAME
@@ -5045,6 +5045,10 @@ This is an array option where each entry is a colon-separated string made up
of a terminal type pattern (matched using of a terminal type pattern (matched using
.Xr glob 7 .Xr glob 7
patterns) followed by a list of terminal features. patterns) followed by a list of terminal features.
A feature may be suffixed with
.Ql @
to disable it; for example,
.Ql xterm*:sync@ .
The available features are: The available features are:
.Bl -tag -width Ds .Bl -tag -width Ds
.It 256 .It 256

6
tmux.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: tmux.c,v 1.222 2026/07/19 19:09:30 nicm Exp $ */ /* $OpenBSD: tmux.c,v 1.223 2026/08/17 14:47:41 nicm Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -418,7 +418,7 @@ main(int argc, char **argv)
while ((opt = getopt(argc, argv, "2c:CDdf:hlL:NqS:T:uUvV")) != -1) { while ((opt = getopt(argc, argv, "2c:CDdf:hlL:NqS:T:uUvV")) != -1) {
switch (opt) { switch (opt) {
case '2': case '2':
tty_add_features(&feat, "256", ":,"); tty_parse_features("256", ":,", &feat, NULL);
break; break;
case 'c': case 'c':
shell_command = optarg; shell_command = optarg;
@@ -466,7 +466,7 @@ main(int argc, char **argv)
path = xstrdup(optarg); path = xstrdup(optarg);
break; break;
case 'T': case 'T':
tty_add_features(&feat, optarg, ":,"); tty_parse_features(optarg, ":,", &feat, NULL);
break; break;
case 'u': case 'u':
flags |= CLIENT_UTF8; flags |= CLIENT_UTF8;

16
tmux.h
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: tmux.h,v 1.1423 2026/08/17 07:56:56 nicm Exp $ */ /* $OpenBSD: tmux.h,v 1.1424 2026/08/17 14:47:41 nicm Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1684,7 +1684,7 @@ struct key_event {
struct tty_term { struct tty_term {
char *name; char *name;
struct tty *tty; struct tty *tty;
int features; int applied_features;
char acs[UCHAR_MAX + 1][2]; char acs[UCHAR_MAX + 1][2];
@@ -2185,6 +2185,7 @@ struct client {
char *term_name; char *term_name;
int term_features; int term_features;
int term_nofeatures;
char *term_type; char *term_type;
char **term_caps; char **term_caps;
u_int term_ncaps; u_int term_ncaps;
@@ -2954,8 +2955,7 @@ extern struct tty_terms tty_terms;
u_int tty_term_ncodes(void); u_int tty_term_ncodes(void);
void tty_term_apply(struct tty_term *, const char *, int); void tty_term_apply(struct tty_term *, const char *, int);
void tty_term_apply_overrides(struct tty_term *); void tty_term_apply_overrides(struct tty_term *);
struct tty_term *tty_term_create(struct tty *, char *, char **, u_int, int *, struct tty_term *tty_term_create(struct tty *, char *, char **, u_int, char **);
char **);
void tty_term_free(struct tty_term *); void tty_term_free(struct tty_term *);
int tty_term_read_list(const char *, int, char ***, u_int *, int tty_term_read_list(const char *, int, char ***, u_int *,
char **); char **);
@@ -2977,11 +2977,13 @@ int tty_term_flag(struct tty_term *, enum tty_code_code);
const char *tty_term_describe(struct tty_term *, enum tty_code_code); const char *tty_term_describe(struct tty_term *, enum tty_code_code);
/* tty-features.c */ /* tty-features.c */
void tty_add_features(int *, const char *, const char *); void tty_parse_client_features(struct client *, const char *,
const char *);
void tty_parse_features(const char *, const char *, int *, int *);
const char *tty_get_features(int); const char *tty_get_features(int);
int tty_feature_present(struct tty_term *, const char *); int tty_feature_present(struct tty_term *, const char *);
int tty_apply_features(struct tty_term *, int); int tty_apply_features(struct tty_term *);
void tty_default_features(int *, const char *, u_int); void tty_default_features(struct client *, const char *, u_int);
/* tty-acs.c */ /* tty-acs.c */
int tty_acs_needed(struct tty *); int tty_acs_needed(struct tty *);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: tty-features.c,v 1.41 2026/08/17 07:52:16 nicm Exp $ */ /* $OpenBSD: tty-features.c,v 1.42 2026/08/17 14:47:41 nicm Exp $ */
/* /*
* Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -383,17 +383,29 @@ static const struct tty_feature *const tty_features[] = {
&tty_feature_usstyle &tty_feature_usstyle
}; };
/* Parse features for client. */
void void
tty_add_features(int *feat, const char *s, const char *separators) tty_parse_client_features(struct client *c, const char *s, const char *sep)
{
tty_parse_features(s, sep, &c->term_features, &c->term_nofeatures);
}
/* Parse features list. */
void
tty_parse_features(const char *s, const char *sep, int *enabled, int *disabled)
{ {
const struct tty_feature *tf; const struct tty_feature *tf;
char *next, *loop, *copy; char *next, *loop, *copy;
u_int i; u_int i;
int remove;
log_debug("adding terminal features %s", s); log_debug("adding terminal features %s", s);
loop = copy = xstrdup(s); loop = copy = xstrdup(s);
while ((next = strsep(&loop, separators)) != NULL) { while ((next = strsep(&loop, sep)) != NULL) {
remove = (*next != '\0' && next[strlen(next) - 1] == '@');
if (remove)
next[strlen(next) - 1] = '\0';
for (i = 0; i < nitems(tty_features); i++) { for (i = 0; i < nitems(tty_features); i++) {
tf = tty_features[i]; tf = tty_features[i];
if (strcasecmp(tf->name, next) == 0) if (strcasecmp(tf->name, next) == 0)
@@ -403,14 +415,24 @@ tty_add_features(int *feat, const char *s, const char *separators)
log_debug("unknown terminal feature: %s", next); log_debug("unknown terminal feature: %s", next);
break; break;
} }
if (~(*feat) & (1 << i)) { if (remove) {
log_debug("removing terminal feature: %s", tf->name);
*enabled &= ~(1 << i);
if (disabled != NULL)
*disabled |= 1 << i;
continue;
}
if (disabled != NULL && *disabled & (1 << i))
continue;
if (~(*enabled) & (1 << i)) {
log_debug("adding terminal feature: %s", tf->name); log_debug("adding terminal feature: %s", tf->name);
(*feat) |= (1 << i); (*enabled) |= (1 << i);
} }
} }
free(copy); free(copy);
} }
/* Get features as string. */
const char * const char *
tty_get_features(int feat) tty_get_features(int feat)
{ {
@@ -432,6 +454,7 @@ tty_get_features(int feat)
return (s); return (s);
} }
/* Check if feature is present. */
int int
tty_feature_present(struct tty_term *term, const char *name) tty_feature_present(struct tty_term *term, const char *name)
{ {
@@ -443,8 +466,8 @@ tty_feature_present(struct tty_term *term, const char *name)
for (i = 0; i < nitems(tty_features); i++) { for (i = 0; i < nitems(tty_features); i++) {
tf = tty_features[i]; tf = tty_features[i];
if (strcmp(tf->name, name) == 0) { if (strcmp(tf->name, name) == 0) {
if (term->features & (1 << i)) if (term->applied_features & (1 << i))
return (1); return (1);
break; break;
} }
} }
@@ -471,19 +494,23 @@ tty_feature_present(struct tty_term *term, const char *name)
return (1); return (1);
} }
/* Apply featurs to terminal. */
int int
tty_apply_features(struct tty_term *term, int feat) tty_apply_features(struct tty_term *term)
{ {
struct client *c = term->tty->client;
const struct tty_feature *tf; const struct tty_feature *tf;
const char *const *capability; const char *const *capability;
int feat;
u_int i; u_int i;
feat = (c->term_features & ~c->term_nofeatures);
if (feat == 0) if (feat == 0)
return (0); return (0);
log_debug("applying terminal features: %s", tty_get_features(feat)); log_debug("applying terminal features: %s", tty_get_features(feat));
for (i = 0; i < nitems(tty_features); i++) { for (i = 0; i < nitems(tty_features); i++) {
if ((term->features & (1 << i)) || (~feat & (1 << i))) if ((term->applied_features & (1 << i)) || (~feat & (1 << i)))
continue; continue;
tf = tty_features[i]; tf = tty_features[i];
@@ -498,14 +525,15 @@ tty_apply_features(struct tty_term *term, int feat)
} }
term->flags |= tf->flags; term->flags |= tf->flags;
} }
if ((term->features | feat) == term->features) if ((term->applied_features|feat) == term->applied_features)
return (0); return (0);
term->features |= feat; term->applied_features |= feat;
return (1); return (1);
} }
/* Add default features for a terminal identified by name and version. */
void void
tty_default_features(int *feat, const char *name, u_int version) tty_default_features(struct client *c, const char *name, u_int version)
{ {
static const struct { static const struct {
const char *name; const char *name;
@@ -618,6 +646,6 @@ tty_default_features(int *feat, const char *name, u_int version)
continue; continue;
if (version != 0 && version < table[i].version) if (version != 0 && version < table[i].version)
continue; continue;
tty_add_features(feat, table[i].features, ","); tty_parse_client_features(c, table[i].features, ",");
} }
} }

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: tty-keys.c,v 1.212 2026/08/17 07:52:16 nicm Exp $ */ /* $OpenBSD: tty-keys.c,v 1.213 2026/08/17 14:47:41 nicm Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1447,7 +1447,6 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len,
size_t *size) size_t *size)
{ {
struct client *c = tty->client; struct client *c = tty->client;
int *features = &c->term_features;
u_int i, n = 0; u_int i, n = 0;
char tmp[128], *endptr, p[32] = { 0 }, *cp, *next; char tmp[128], *endptr, p[32] = { 0 }, *cp, *next;
@@ -1504,13 +1503,13 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len,
for (i = 1; i < n; i++) { for (i = 1; i < n; i++) {
log_debug("%s: DA feature: %d", c->name, p[i]); log_debug("%s: DA feature: %d", c->name, p[i]);
if (p[i] == 4) if (p[i] == 4)
tty_add_features(features, "sixel", ","); tty_parse_client_features(c, "sixel", ",");
if (p[i] == 21) if (p[i] == 21)
tty_add_features(features, "margins", ","); tty_parse_client_features(c, "margins", ",");
if (p[i] == 28) if (p[i] == 28)
tty_add_features(features, "rectfill", ","); tty_parse_client_features(c, "rectfill", ",");
if (p[i] == 52) if (p[i] == 52)
tty_add_features(features, "clipboard", ","); tty_parse_client_features(c, "clipboard", ",");
} }
break; break;
} }
@@ -1531,7 +1530,6 @@ tty_keys_device_attributes2(struct tty *tty, const char *buf, size_t len,
size_t *size) size_t *size)
{ {
struct client *c = tty->client; struct client *c = tty->client;
int *features = &c->term_features;
u_int i, n = 0; u_int i, n = 0;
char tmp[128], *endptr, p[32] = { 0 }, *cp, *next; char tmp[128], *endptr, p[32] = { 0 }, *cp, *next;
@@ -1585,13 +1583,13 @@ tty_keys_device_attributes2(struct tty *tty, const char *buf, size_t len,
*/ */
switch (p[0]) { switch (p[0]) {
case 'M': /* mintty */ case 'M': /* mintty */
tty_default_features(features, "mintty", 0); tty_default_features(c, "mintty", 0);
break; break;
case 'T': /* tmux */ case 'T': /* tmux */
tty_default_features(features, "tmux", 0); tty_default_features(c, "tmux", 0);
break; break;
case 'U': /* rxvt-unicode */ case 'U': /* rxvt-unicode */
tty_default_features(features, "rxvt-unicode", 0); tty_default_features(c, "rxvt-unicode", 0);
break; break;
} }
log_debug("%s: received secondary DA %.*s", c->name, (int)*size, buf); log_debug("%s: received secondary DA %.*s", c->name, (int)*size, buf);
@@ -1611,7 +1609,6 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
size_t len, size_t *size) size_t len, size_t *size)
{ {
struct client *c = tty->client; struct client *c = tty->client;
int *features = &c->term_features;
u_int i; u_int i;
char tmp[128]; char tmp[128];
@@ -1654,21 +1651,21 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
/* Add terminal features. */ /* Add terminal features. */
if (strncmp(tmp, "iTerm2 ", 7) == 0) if (strncmp(tmp, "iTerm2 ", 7) == 0)
tty_default_features(features, "iTerm2", 0); tty_default_features(c, "iTerm2", 0);
else if (strncmp(tmp, "tmux ", 5) == 0) else if (strncmp(tmp, "tmux ", 5) == 0)
tty_default_features(features, "tmux", 0); tty_default_features(c, "tmux", 0);
else if (strncmp(tmp, "XTerm(", 6) == 0) else if (strncmp(tmp, "XTerm(", 6) == 0)
tty_default_features(features, "XTerm", 0); tty_default_features(c, "XTerm", 0);
else if (strncmp(tmp, "mintty ", 7) == 0) else if (strncmp(tmp, "mintty ", 7) == 0)
tty_default_features(features, "mintty", 0); tty_default_features(c, "mintty", 0);
else if (strncmp(tmp, "foot(", 5) == 0) else if (strncmp(tmp, "foot(", 5) == 0)
tty_default_features(features, "foot", 0); tty_default_features(c, "foot", 0);
else if (strncmp(tmp, "WezTerm ", 7) == 0) else if (strncmp(tmp, "WezTerm ", 7) == 0)
tty_default_features(features, "WezTerm", 0); tty_default_features(c, "WezTerm", 0);
else if (strncmp(tmp, "ghostty ", 8) == 0) else if (strncmp(tmp, "ghostty ", 8) == 0)
tty_default_features(features, "ghostty", 0); tty_default_features(c, "ghostty", 0);
else if (strncmp(tmp, "Rio ", 4) == 0) else if (strncmp(tmp, "Rio ", 4) == 0)
tty_default_features(features, "Rio", 0); tty_default_features(c, "Rio", 0);
log_debug("%s: received extended DA %.*s", c->name, (int)*size, buf); log_debug("%s: received extended DA %.*s", c->name, (int)*size, buf);
free(c->term_type); free(c->term_type);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: tty-term.c,v 1.107 2026/08/05 08:54:56 nicm Exp $ */ /* $OpenBSD: tty-term.c,v 1.108 2026/08/17 14:47:41 nicm Exp $ */
/* /*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -541,8 +541,9 @@ tty_term_validate(struct tty_term *term)
struct tty_term * struct tty_term *
tty_term_create(struct tty *tty, char *name, char **caps, u_int ncaps, tty_term_create(struct tty *tty, char *name, char **caps, u_int ncaps,
int *feat, char **cause) char **cause)
{ {
struct client *c = tty->client;
struct tty_term *term; struct tty_term *term;
const struct tty_term_code_entry *ent; const struct tty_term_code_entry *ent;
struct tty_code *code; struct tty_code *code;
@@ -614,19 +615,19 @@ tty_term_create(struct tty *tty, char *name, char **caps, u_int ncaps,
offset = 0; offset = 0;
first = tty_term_override_next(s, &offset); first = tty_term_override_next(s, &offset);
if (first != NULL && fnmatch(first, term->name, 0) == 0) if (first != NULL && fnmatch(first, term->name, 0) == 0)
tty_add_features(feat, s + offset, ":"); tty_parse_client_features(c, s + offset, ":");
a = options_array_next(a); a = options_array_next(a);
} }
/* Check for COLORTERM. */ /* Check for COLORTERM. */
envent = environ_find(tty->client->environ, "COLORTERM"); envent = environ_find(c->environ, "COLORTERM");
if (envent != NULL) { if (envent != NULL) {
log_debug("%s COLORTERM=%s", tty->client->name, envent->value); log_debug("%s COLORTERM=%s", c->name, envent->value);
if (strcasecmp(envent->value, "truecolor") == 0 || if (strcasecmp(envent->value, "truecolor") == 0 ||
strcasecmp(envent->value, "24bit") == 0) strcasecmp(envent->value, "24bit") == 0)
tty_add_features(feat, "RGB", ","); tty_parse_client_features(c, "RGB", ",");
else if (strstr(envent->value, "256") != NULL) else if (strstr(envent->value, "256") != NULL)
tty_add_features(feat, "256", ","); tty_parse_client_features(c, "256", ",");
} }
/* Apply overrides so any capabilities used for features are changed. */ /* Apply overrides so any capabilities used for features are changed. */
@@ -657,17 +658,17 @@ tty_term_create(struct tty *tty, char *name, char **caps, u_int ncaps,
s = tty_term_string(term, TTYC_CLEAR); s = tty_term_string(term, TTYC_CLEAR);
if (tty_term_flag(term, TTYC_XT) || strncmp(s, "\033[", 2) == 0) { if (tty_term_flag(term, TTYC_XT) || strncmp(s, "\033[", 2) == 0) {
term->flags |= TERM_VT100LIKE; term->flags |= TERM_VT100LIKE;
tty_add_features(feat, "bpaste,focus,title", ","); tty_parse_client_features(c, "bpaste,focus,title", ",");
} }
/* Add RGB feature if terminal has RGB colours. */ /* Add RGB feature if terminal has RGB colours. */
if ((tty_term_flag(term, TTYC_TC) || tty_term_has(term, TTYC_RGB)) && if ((tty_term_flag(term, TTYC_TC) || tty_term_has(term, TTYC_RGB)) &&
(!tty_term_has(term, TTYC_SETRGBF) || (!tty_term_has(term, TTYC_SETRGBF) ||
!tty_term_has(term, TTYC_SETRGBB))) !tty_term_has(term, TTYC_SETRGBB)))
tty_add_features(feat, "RGB", ","); tty_parse_client_features(c, "RGB", ",");
/* Apply the features and overrides again. */ /* Apply the features and overrides again. */
if (tty_apply_features(term, *feat)) if (tty_apply_features(term))
tty_term_apply_overrides(term); tty_term_apply_overrides(term);
/* Log the capabilities. */ /* Log the capabilities. */

6
tty.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: tty.c,v 1.477 2026/07/17 12:42:51 nicm Exp $ */ /* $OpenBSD: tty.c,v 1.478 2026/08/17 14:47:41 nicm Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -272,7 +272,7 @@ tty_open(struct tty *tty, char **cause)
struct client *c = tty->client; struct client *c = tty->client;
tty->term = tty_term_create(tty, c->term_name, c->term_caps, tty->term = tty_term_create(tty, c->term_name, c->term_caps,
c->term_ncaps, &c->term_features, cause); c->term_ncaps, cause);
if (tty->term == NULL) { if (tty->term == NULL) {
tty_close(tty); tty_close(tty);
return (-1); return (-1);
@@ -531,7 +531,7 @@ tty_update_features(struct tty *tty)
{ {
struct client *c = tty->client; struct client *c = tty->client;
if (tty_apply_features(tty->term, c->term_features)) if (tty_apply_features(tty->term))
tty_term_apply_overrides(tty->term); tty_term_apply_overrides(tty->term);
if (tty_use_margin(tty)) if (tty_use_margin(tty))