summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--client.c9
-rw-r--r--cmd-show-messages.c14
-rw-r--r--format.c7
-rw-r--r--options-table.c13
-rw-r--r--server-client.c24
-rw-r--r--tmux.192
-rw-r--r--tmux.c20
-rw-r--r--tmux.h30
-rw-r--r--tty-acs.c2
-rw-r--r--tty-features.c264
-rw-r--r--tty-keys.c62
-rw-r--r--tty-term.c104
-rw-r--r--tty.c121
14 files changed, 540 insertions, 223 deletions
diff --git a/Makefile b/Makefile
index 24117bf1..0f1c94c4 100644
--- a/Makefile
+++ b/Makefile
@@ -111,6 +111,7 @@ SRCS= alerts.c \
style.c \
tmux.c \
tty-acs.c \
+ tty-features.c \
tty-keys.c \
tty-term.c \
tty.c \
diff --git a/client.c b/client.c
index c1d6e1b5..98565c5d 100644
--- a/client.c
+++ b/client.c
@@ -57,7 +57,7 @@ static struct client_files client_files = RB_INITIALIZER(&client_files);
static __dead void client_exec(const char *,const char *);
static int client_get_lock(char *);
static int client_connect(struct event_base *, const char *, int);
-static void client_send_identify(const char *, const char *);
+static void client_send_identify(const char *, const char *, int);
static void client_signal(int);
static void client_dispatch(struct imsg *, void *);
static void client_dispatch_attached(struct imsg *);
@@ -233,7 +233,7 @@ client_exit(void)
/* Client main loop. */
int
-client_main(struct event_base *base, int argc, char **argv, int flags)
+client_main(struct event_base *base, int argc, char **argv, int flags, int feat)
{
struct cmd_parse_result *pr;
struct msg_command *data;
@@ -340,7 +340,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags)
}
/* Send identify messages. */
- client_send_identify(ttynam, cwd);
+ client_send_identify(ttynam, cwd, feat);
/* Send first command. */
if (msg == MSG_COMMAND) {
@@ -406,7 +406,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags)
/* Send identify messages to server. */
static void
-client_send_identify(const char *ttynam, const char *cwd)
+client_send_identify(const char *ttynam, const char *cwd, int feat)
{
const char *s;
char **ss;
@@ -419,6 +419,7 @@ client_send_identify(const char *ttynam, const char *cwd)
if ((s = getenv("TERM")) == NULL)
s = "";
proc_send(client_peer, MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1);
+ proc_send(client_peer, MSG_IDENTIFY_FEATURES, -1, &feat, sizeof feat);
proc_send(client_peer, MSG_IDENTIFY_TTYNAME, -1, ttynam,
strlen(ttynam) + 1);
diff --git a/cmd-show-messages.c b/cmd-show-messages.c
index 78e5859c..d734ca65 100644
--- a/cmd-show-messages.c
+++ b/cmd-show-messages.c
@@ -43,22 +43,24 @@ const struct cmd_entry cmd_show_messages_entry = {
.exec = cmd_show_messages_exec
};
-static int cmd_show_messages_terminals(struct cmdq_item *, int);
-
static int
-cmd_show_messages_terminals(struct cmdq_item *item, int blank)
+cmd_show_messages_terminals(struct cmd *self, struct cmdq_item *item, int blank)
{
+ struct args *args = cmd_get_args(self);
+ struct client *tc = cmdq_get_target_client(item);
struct tty_term *term;
u_int i, n;
n = 0;
LIST_FOREACH(term, &tty_terms, entry) {
+ if (args_has(args, 't') && term != tc->tty.term)
+ continue;
if (blank) {
cmdq_print(item, "%s", "");
blank = 0;
}
- cmdq_print(item, "Terminal %u: %s [references=%u, flags=0x%x]:",
- n, term->name, term->references, term->flags);
+ cmdq_print(item, "Terminal %u: %s for %s, flags=0x%x:", n,
+ term->name, term->tty->client->name, term->flags);
n++;
for (i = 0; i < tty_term_ncodes(); i++)
cmdq_print(item, "%s", tty_term_describe(term, i));
@@ -77,7 +79,7 @@ cmd_show_messages_exec(struct cmd *self, struct cmdq_item *item)
done = blank = 0;
if (args_has(args, 'T')) {
- blank = cmd_show_messages_terminals(item, blank);
+ blank = cmd_show_messages_terminals(self, item, blank);
done = 1;
}
if (args_has(args, 'J')) {
diff --git a/format.c b/format.c
index df610a50..f1f52895 100644
--- a/format.c
+++ b/format.c
@@ -2553,8 +2553,9 @@ format_defaults_client(struct format_tree *ft, struct client *c)
format_add(ft, "client_control_mode", "%d",
!!(c->flags & CLIENT_CONTROL));
- if (tty->term_name != NULL)
- format_add(ft, "client_termname", "%s", tty->term_name);
+ format_add(ft, "client_termname", "%s", c->term_name);
+ format_add(ft, "client_termfeatures", "%s",
+ tty_get_features(c->term_features));
format_add_tv(ft, "client_created", &c->creation_time);
format_add_tv(ft, "client_activity", &c->activity_time);
@@ -2569,7 +2570,7 @@ format_defaults_client(struct format_tree *ft, struct client *c)
format_add(ft, "client_prefix", "%d", 1);
format_add(ft, "client_key_table", "%s", c->keytable->name);
- if (tty_get_flags(tty) & TERM_UTF8)
+ if (c->flags & CLIENT_UTF8)
format_add(ft, "client_utf8", "%d", 1);
else
format_add(ft, "client_utf8", "%d", 0);
diff --git a/options-table.c b/options-table.c
index 1401f05d..722429f7 100644
--- a/options-table.c
+++ b/options-table.c
@@ -261,9 +261,16 @@ const struct options_table_entry options_table[] = {
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_ARRAY,
- .default_str = "xterm*:XT:Ms=\\E]52;%p1%s;%p2%s\\007"
- ":Cs=\\E]12;%p1%s\\007:Cr=\\E]112\\007"
- ":Ss=\\E[%p1%d q:Se=\\E[2 q,screen*:XT",
+ .default_str = "tmux*:XT,screen*:XT",
+ .separator = ","
+ },
+
+ { .name = "terminal-features",
+ .type = OPTIONS_TABLE_STRING,
+ .scope = OPTIONS_TABLE_SERVER,
+ .flags = OPTIONS_TABLE_IS_ARRAY,
+ .default_str = "xterm*:clipboard:ccolour:cstyle:title,"
+ "screen*:title",
.separator = ","
},
diff --git a/server-client.c b/server-client.c
index 1df1a77f..cd850593 100644
--- a/server-client.c
+++ b/server-client.c
@@ -296,7 +296,7 @@ server_client_lost(struct client *c)
if (c->flags & CLIENT_TERMINAL)
tty_free(&c->tty);
free(c->ttyname);
- free(c->term);
+ free(c->term_name);
status_free(c);
@@ -1845,6 +1845,7 @@ server_client_dispatch(struct imsg *imsg, void *arg)
datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
switch (imsg->hdr.type) {
+ case MSG_IDENTIFY_FEATURES:
case MSG_IDENTIFY_FLAGS:
case MSG_IDENTIFY_TERM:
case MSG_IDENTIFY_TTYNAME:
@@ -2003,7 +2004,7 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg)
{
const char *data, *home;
size_t datalen;
- int flags;
+ int flags, feat;
char *name;
if (c->flags & CLIENT_IDENTIFIED)
@@ -2013,6 +2014,14 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg)
datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
switch (imsg->hdr.type) {
+ case MSG_IDENTIFY_FEATURES:
+ if (datalen != sizeof feat)
+ fatalx("bad MSG_IDENTIFY_FEATURES size");
+ memcpy(&feat, data, sizeof feat);
+ c->term_features |= feat;
+ log_debug("client %p IDENTIFY_FEATURES %s", c,
+ tty_get_features(feat));
+ break;
case MSG_IDENTIFY_FLAGS:
if (datalen != sizeof flags)
fatalx("bad MSG_IDENTIFY_FLAGS size");
@@ -2023,7 +2032,10 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg)
case MSG_IDENTIFY_TERM:
if (datalen == 0 || data[datalen - 1] != '\0')
fatalx("bad MSG_IDENTIFY_TERM string");
- c->term = xstrdup(data);
+ if (*data == '\0')
+ c->term_name = xstrdup("unknown");
+ else
+ c->term_name = xstrdup(data);
log_debug("client %p IDENTIFY_TERM %s", c, data);
break;
case MSG_IDENTIFY_TTYNAME:
@@ -2084,14 +2096,10 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg)
control_start(c);
c->tty.fd = -1;
} else if (c->fd != -1) {
- if (tty_init(&c->tty, c, c->fd, c->term) != 0) {
+ if (tty_init(&c->tty, c, c->fd) != 0) {
close(c->fd);
c->fd = -1;
} else {
- if (c->flags & CLIENT_UTF8)
- c->tty.term_flags |= TERM_UTF8;
- if (c->flags & CLIENT_256COLOURS)
- c->tty.term_flags |= TERM_256COLOURS;
tty_resize(&c->tty);
c->flags |= CLIENT_TERMINAL;
}
diff --git a/tmux.1 b/tmux.1
index 325ccd98..ca4e30e0 100644
--- a/tmux.1
+++ b/tmux.1
@@ -28,6 +28,7 @@
.Op Fl f Ar file
.Op Fl L Ar socket-name
.Op Fl S Ar socket-path
+.Op Fl T Ar features
.Op Ar command Op Ar flags
.Ek
.Sh DESCRIPTION
@@ -98,6 +99,8 @@ The options are as follows:
Force
.Nm
to assume the terminal supports 256 colours.
+This is equivalent to
+.Fl T Ar 256 .
.It Fl C
Start in control mode (see the
.Sx CONTROL MODE
@@ -186,6 +189,14 @@ that is set does not contain
.Qq UTF-8
or
.Qq UTF8 .
+This is equivalent to
+.Fl T Ar UTF-8 .
+.It Fl T Ar features
+Set terminal features for the client.
+This is a comma-separated list of features.
+See the
+.Ic terminal-features
+option.
.It Fl v
Request verbose logging.
Log messages will be saved into
@@ -3166,6 +3177,63 @@ disallowedWindowOps: 20,21,SetXprop
Or changing this property from the
.Xr xterm 1
interactive menu when required.
+.It Ic terminal-features[] Ar string
+Set terminal features for terminal types read from
+.Xr terminfo 5 .
+.Nm
+has a set of named terminal features.
+Each will apply appropriate changes to the
+.Xr terminfo 5
+entry in use.
+.Pp
+.Nm
+can detect features for a few common terminals; this option can be used to
+easily tell tmux about features supported by terminals it cannot detect.
+The
+.Ic terminal-overrides
+option allows individual
+.Xr terminfo 5
+capabilities to be set instead,
+.Ic terminal-features
+is intended for classes of functionality supported in a standard way but not
+reported by
+.Xr terminfo 5 .
+Care must be taken only to configure this with features the terminal actually
+support.
+.Pp
+This is an array option where each entry is a colon-separated string made up
+of a terminal type pattern (matched using
+.Xr fnmatch 3 )
+followed by a list of terminal features.
+The available features are:
+.Bl -tag -width Ds
+.It 256
+Supports 256 colours with the SGR escape sequences.
+.It clipboard
+Allows setting the system clipboard.
+.It ccolour
+Allows setting the cursor colour.
+.It cstyle
+Allows setting the cursor style.
+.It margins
+Supports DECSLRM margins.
+.It overline
+Supports the overline SGR attribute.
+.It rectfill
+Supports the DECFRA rectangle fill escape sequence.
+.It RGB
+Supports RGB colour with the SGR escape sequences.
+.It sync
+Supports synchronized updates.
+.It title
+Supports
+.Xr xterm 1
+title setting.
+.It usstyle
+Allows underscore style and colour to be set.
+.It UTF-8
+Is able to handle UTF-8 output.
+.El
.It Ic terminal-overrides[] Ar string
Allow terminal descriptions read using
.Xr terminfo 5
@@ -4383,6 +4451,7 @@ The following variables are available, where appropriate:
.It Li "client_readonly" Ta "" Ta "1 if client is readonly"
.It Li "client_session" Ta "" Ta "Name of the client's session"
.It Li "client_termname" Ta "" Ta "Terminal name of client"
+.It Li "client_termfeatures" Ta "" Ta "Terminal features of client"
.It Li "client_tty" Ta "" Ta "Pseudo terminal of client"
.It Li "client_utf8" Ta "" Ta "1 if client supports UTF-8"
.It Li "client_width" Ta "" Ta "Width of client"
@@ -5465,7 +5534,10 @@ The server crashed or otherwise exited without telling the client the reason.
.Sh TERMINFO EXTENSIONS
.Nm
understands some unofficial extensions to
-.Xr terminfo 5 :
+.Xr terminfo 5.
+It is not normally necessary to set these manually, instead the
+.Ic terminal-features
+option should be used.
.Bl -tag -width Ds
.It Em \&Cs , Cr
Set the cursor colour.
@@ -5479,33 +5551,15 @@ $ printf '\e033]12;red\e033\e\e'
.Ed
.It Em \&Smol
Enable the overline attribute.
-The capability is usually SGR 53 and can be added to
-.Ic terminal-overrides
-as:
-.Bd -literal -offset indent
-Smol=\eE[53m
-.Ed
.It Em \&Smulx
Set a styled underscore.
The single parameter is one of: 0 for no underscore, 1 for normal
underscore, 2 for double underscore, 3 for curly underscore, 4 for dotted
underscore and 5 for dashed underscore.
-The capability can typically be added to
-.Ic terminal-overrides
-as:
-.Bd -literal -offset indent
-Smulx=\eE[4::%p1%dm
-.Ed
.It Em \&Setulc
Set the underscore colour.
The argument is (red * 65536) + (green * 256) + blue where each is between 0
and 255.
-The capability can typically be added to
-.Ic terminal-overrides
-as:
-.Bd -literal -offset indent
-Setulc=\eE[58::2::%p1%{65536}%/%d::%p1%{256}%/%{255}%&%d::%p1%{255}%&%d%;m
-.Ed
.It Em \&Ss , Se
Set or reset the cursor style.
If set, a sequence such as this may be used
diff --git a/tmux.c b/tmux.c
index c9678d9e..515f1543 100644
--- a/tmux.c
+++ b/tmux.c
@@ -58,7 +58,7 @@ usage(void)
{
fprintf(stderr,
"usage: %s [-2CluvV] [-c shell-command] [-f file] [-L socket-name]\n"
- " [-S socket-path] [command [flags]]\n",
+ " [-S socket-path] [-T features] [command [flags]]\n",
getprogname());
exit(1);
}
@@ -242,9 +242,11 @@ getversion(void)
int
main(int argc, char **argv)
{
- char *path, *label, *cause, **var;
+ char *path = NULL, *label = NULL;
+ char *cause, **var;
const char *s, *shell, *cwd;
- int opt, flags, keys;
+ int opt, flags = 0, keys;
+ int feat = 0;
const struct options_table_entry *oe;
if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL &&
@@ -261,14 +263,11 @@ main(int argc, char **argv)
if (**argv == '-')
flags = CLIENT_LOGIN;
- else
- flags = 0;
- label = path = NULL;
- while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUvV")) != -1) {
+ while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:T:uUvV")) != -1) {
switch (opt) {
case '2':
- flags |= CLIENT_256COLOURS;
+ tty_add_features(&feat, "256", ":,");
break;
case 'c':
shell_command = optarg;
@@ -298,6 +297,9 @@ main(int argc, char **argv)
free(path);
path = xstrdup(optarg);
break;
+ case 'T':
+ tty_add_features(&feat, optarg, ":,");
+ break;
case 'u':
flags |= CLIENT_UTF8;
break;
@@ -405,5 +407,5 @@ main(int argc, char **argv)
free(label);
/* Pass control to the client. */
- exit(client_main(event_init(), argc, argv, flags));
+ exit(client_main(event_init(), argc, argv, flags, feat));
}
diff --git a/tmux.h b/tmux.h
index bbac756b..3853b09f 100644
--- a/tmux.h
+++ b/tmux.h
@@ -474,6 +474,7 @@ enum msgtype {
MSG_IDENTIFY_DONE,
MSG_IDENTIFY_CLIENTPID,
MSG_IDENTIFY_CWD,
+ MSG_IDENTIFY_FEATURES,
MSG_COMMAND = 200,
MSG_DETACH,
@@ -1176,7 +1177,8 @@ struct tty_key {
struct tty_code;
struct tty_term {
char *name;
- u_int references;
+ struct tty *tty;
+ int features;
char acs[UCHAR_MAX + 1][2];
@@ -1187,8 +1189,6 @@ struct tty_term {
#define TERM_DECSLRM 0x4
#define TERM_DECFRA 0x8
#define TERM_RGBCOLOURS 0x10
-#define TERM_SYNC 0x20
-#define TERM_UTF8 0x40
int flags;
LIST_ENTRY(tty_term) entry;
@@ -1252,8 +1252,6 @@ struct tty {
int flags;
struct tty_term *term;
- char *term_name;
- int term_flags;
u_int mouse_last_x;
u_int mouse_last_y;
@@ -1267,7 +1265,6 @@ struct tty {
struct event key_timer;
struct tty_key *key_tree;
};
-#define tty_term_flags(tty) (tty->term->flags|tty->term_flags)
/* TTY command context. */
struct tty_ctx {
@@ -1498,7 +1495,9 @@ struct client {
char *title;
const char *cwd;
- char *term;
+ char *term_name;
+ int term_features;
+
char *ttyname;
struct tty tty;
@@ -1531,7 +1530,7 @@ struct client {
#define CLIENT_CONTROLCONTROL 0x4000
#define CLIENT_FOCUSED 0x8000
#define CLIENT_UTF8 0x10000
-#define CLIENT_256COLOURS 0x20000
+/* 0x20000 unused */
#define CLIENT_IDENTIFIED 0x40000
#define CLIENT_STATUSFORCE 0x80000
#define CLIENT_DOUBLECLICK 0x100000
@@ -1953,7 +1952,7 @@ void tty_putcode_ptr2(struct tty *, enum tty_code_code, const void *,
void tty_puts(struct tty *, const char *);
void tty_putc(struct tty *, u_char);
void tty_putn(struct tty *, const void *, size_t, u_int);
-int tty_init(struct tty *, struct client *, int, char *);
+int tty_init(struct tty *, struct client *, int);
void tty_resize(struct tty *);
void tty_set_size(struct tty *, u_int, u_int, u_int, u_int);
void tty_start_tty(struct tty *);
@@ -1968,8 +1967,7 @@ void tty_sync_end(struct tty *);
int tty_open(struct tty *, char **);
void tty_close(struct tty *);
void tty_free(struct tty *);
-void tty_set_flags(struct tty *, int);
-int tty_get_flags(struct tty *);
+void tty_update_features(struct tty *);
void tty_write(void (*)(struct tty *, const struct tty_ctx *),
struct tty_ctx *);
void tty_cmd_alignmenttest(struct tty *, const struct tty_ctx *);
@@ -1999,7 +1997,8 @@ void tty_cmd_syncend(struct tty *, const struct tty_ctx *);
/* tty-term.c */
extern struct tty_terms tty_terms;
u_int tty_term_ncodes(void);
-struct tty_term *tty_term_find(char *, int, char **);
+void tty_term_apply(struct tty_term *, const char *, int);
+struct tty_term *tty_term_create(struct tty *, char *, int *, int, 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);
@@ -2016,6 +2015,11 @@ int tty_term_number(struct tty_term *, enum tty_code_code);
int tty_term_flag(struct tty_term *, enum tty_code_code);
const char *tty_term_describe(struct tty_term *, enum tty_code_code);
+/* tty-features.c */
+void tty_add_features(int *, const char *, const char *);
+const char *tty_get_features(int);
+void tty_apply_features(struct tty_term *, int);
+
/* tty-acs.c */
int tty_acs_needed(struct tty *);
const char *tty_acs_get(struct tty *, u_char);
@@ -2161,7 +2165,7 @@ void printflike(2, 3) cmdq_error(struct cmdq_item *, const char *, ...);
void cmd_wait_for_flush(void);
/* client.c */
-int client_main(struct event_base *, int, char **, int);
+int client_main(struct event_base *, int, char **, int, int);
/* key-bindings.c */
struct key_table *key_bindings_get_table(const char *, int);
diff --git a/tty-acs.c b/tty-acs.c
index f5352d3e..3e811103 100644
--- a/tty-acs.c
+++ b/tty-acs.c
@@ -99,7 +99,7 @@ tty_acs_needed(struct tty *tty)
tty_term_number(tty->term, TTYC_U8) == 0)
return (1);
- if (tty_get_flags(tty) & TERM_UTF8)
+ if (tty->client->flags & CLIENT_UTF8)
return (0);
return (1);
}
diff --git a/tty-features.c b/tty-features.c
new file mode 100644
index 00000000..58cbae7b
--- /dev/null
+++ b/tty-features.c
@@ -0,0 +1,264 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+/*
+ * Still hardcoded:
+ * - bracket paste (sent if application asks for it);
+ * - mouse (under kmous capability);
+ * - focus events (under focus-events option);
+ * - default colours (under AX or op capabilities);
+ * - AIX colours (under colors >= 16);
+ * - alternate escape (under XT).
+ *
+ * Also:
+ * - XT is used to decide whether to send DA and DSR,
+ * - DECSLRM and DECFRA use a flag instead of capabilities.
+ * - Sync is a flag rather than a string capability.
+ * - UTF-8 is a separate flag on the client; needed for unattached clients.
+ */
+
+/* A named terminal feature. */
+struct tty_feature {
+ const char *name;
+ const char **capabilities;
+ int flags;
+};
+
+/* Terminal has xterm(1) title setting. */
+static const char *tty_feature_title_capabilities[] = {
+ "tsl=\\E]0;", /* should be using TS really */
+ "fsl=\\a",
+ NULL
+};
+static struct tty_feature tty_feature_title = {
+ "title",
+ tty_feature_title_capabilities,
+ 0
+};
+
+/* Terminal can set the clipboard with OSC 52. */
+static const char *tty_feature_clipboard_capabilities[] = {
+ "Ms=\\E]52;%p1%s;%p2%s\\a",
+ NULL
+};
+static struct tty_feature tty_feature_clipboard = {
+ "clipboard",
+ tty_feature_clipboard_capabilities,
+ 0
+};
+
+/*
+ * Terminal supports RGB colour. This replaces setab and setaf also since
+ * terminals with RGB have versions that do not allow setting colours from the
+ * 256 palette.
+ */
+static const char *tty_feature_rgb_capabilities[] = {
+ "setrgbf=\\E[38;2;%p1%d;%p2%d;%p3%dm",
+ "setrgbb=\\E[48;2;%p1%d;%p2%d;%p3%dm",
+ "setab=\\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m",
+ "setaf=\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m",
+ NULL
+};
+static struct tty_feature tty_feature_rgb = {
+ "RGB",
+ tty_feature_rgb_capabilities,
+ (TERM_256COLOURS|TERM_RGBCOLOURS)
+};
+
+/* Terminal supports 256 colours. */
+static const char *tty_feature_256_capabilities[] = {
+ "setab=\\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m",
+ "setaf=\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m",
+ NULL
+};
+static struct tty_feature tty_feature_256 = {
+ "256",
+ tty_feature_256_capabilities,
+ TERM_256COLOURS
+};
+
+/* Terminal supports overline. */
+static const char *tty_feature_overline_capabilities[] = {
+ "Smol=\\E[53m",
+ NULL
+};
+static struct tty_feature tty_feature_overline = {
+ "overline",
+ tty_feature_overline_capabilities,
+ 0
+};
+
+/* Terminal supports underscore styles. */
+static const char *tty_feature_usstyle_capabilities[] = {
+ "Smulx=\E[4::%p1%dm",
+ "Setulc=\E[58::2::%p1%{65536}%/%d::%p1%{256}%/%{255}%&%d::%p1%{255}%&%d%;m",
+ NULL
+};
+static struct tty_feature tty_feature_usstyle = {
+ "usstyle",
+ tty_feature_usstyle_capabilities,
+ 0
+};
+
+/* Terminal supports cursor styles. */
+static const char *tty_feature_cstyle_capabilities[] = {
+ "Ss=\\E[%p1%d q",
+ "Se=\\E[2 q",
+ NULL
+};
+static struct tty_feature tty_feature_cstyle = {
+ "cstyle",
+ tty_feature_cstyle_capabilities,
+ 0
+};
+
+/* Terminal supports cursor colours. */
+static const char *tty_feature_ccolour_capabilities[] = {
+ "Cs=\\E]12;%p1%s\\a",
+ "Cr=\\E]112\\a",
+ NULL
+};
+static struct tty_feature tty_feature_ccolour = {
+ "ccolour",
+ tty_feature_ccolour_capabilities,
+ 0
+};
+
+/* Terminal supports synchronized updates. */
+static const char *tty_feature_sync_capabilities[] = {
+ "Sync",
+ NULL
+};
+static struct tty_feature tty_feature_sync = {
+ "sync",
+ tty_feature_sync_capabilities,
+ 0
+};
+
+/* Terminal supports DECSLRM margins. */
+static struct tty_feature tty_feature_margins = {
+ "margins",
+ NULL,
+ TERM_DECSLRM
+};
+
+/* Terminal supports DECFRA rectangle fill. */
+static struct tty_feature tty_feature_rectfill = {
+ "rectfill",
+ NULL,
+ TERM_DECFRA
+};
+
+/* Available terminal features. */
+static const struct tty_feature *tty_features[] = {
+ &tty_feature_256,
+ &tty_feature_clipboard,
+ &tty_feature_ccolour,
+ &tty_feature_cstyle,
+ &tty_feature_margins,
+ &tty_feature_overline,
+ &tty_feature_rectfill,
+ &tty_feature_rgb,
+ &tty_feature_sync,
+ &tty_feature_title,
+ &tty_feature_usstyle
+};
+
+void
+tty_add_features(int *feat, const char *s, const char *separators)
+{
+ const struct tty_feature *tf;
+ char *next, *loop, *copy;
+ u_int i;
+
+ loop = copy = xstrdup(s);
+ while ((next = strsep(&loop, separators)) != NULL) {
+ for (i = 0; i < nitems(tty_features); i++) {
+ tf = tty_features[i];
+ if (strcasecmp(tf->name, next) == 0)
+ break;
+ }
+ if (i == nitems(tty_features)) {
+ log_debug("unknown terminal feature: %s", next);
+ break;
+ }
+ if (~(*feat) & (1 << i)) {
+ log_debug("adding terminal feature: %s", tf->name);
+ (*feat) |= (1 << i);
+ }
+ }
+ free(copy);
+}
+
+const char *
+tty_get_features(int feat)
+{
+ const struct tty_feature *tf;
+ static char s[512];
+ u_int i;
+
+ *s = '\0';
+ for (i = 0; i < nitems(tty_features); i++) {
+ if (~feat & (1 << i))
+ continue;
+ tf = tty_features[i];
+
+ strlcat(s, tf->name, sizeof s);
+ strlcat(s, ",", sizeof s);
+ }
+ if (*s != '\0')
+ s[strlen(s) - 1] = '\0';
+ return (s);
+}
+
+void
+tty_apply_features(struct tty_term *term, int feat)
+{
+ const struct tty_feature *tf;
+ const char **capability;
+ u_int i;
+
+ if (feat == 0)
+ return;
+ log_debug("applying terminal features: %s", tty_get_features(feat));
+
+ for (i = 0; i < nitems(tty_features); i++) {
+ if ((term->features & (1 << i)) || (~feat & (1 << i)))
+ continue;
+ tf = tty_features[i];
+
+ log_debug("applying terminal feature: %s", tf->name);
+ if (tf->capabilities != NULL) {
+ capability = tf->capabilities;
+ while (*capability != NULL) {
+ log_debug("adding capability: %s", *capability);
+ tty_term_apply(term, *capability, 1);
+ capability++;
+ }
+ }
+ term->flags |= tf->flags;
+ }
+ term->features |= feat;
+}
diff --git a/tty-keys.c b/tty-keys.c
index 10c9c670..14952c05 100644
--- a/tty-keys.c
+++ b/tty-keys.c
@@ -1020,7 +1020,6 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len,
struct client *c = tty->client;
u_int i, n = 0;
char tmp[64], *endptr, p[32] = { 0 }, *cp, *next;
- int flags = 0;
*size = 0;
if (tty->flags & TTY_HAVEDA)
@@ -1060,24 +1059,42 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len,
n++;
}
- /* Set terminal flags. */
+ /* Add terminal features. */
switch (p[0]) {
case 41: /* VT420 */
- flags |= (TERM_DECFRA|TERM_DECSLRM);
+ tty_add_features(&c->term_features,
+ "margins,"
+ "rectfill",
+ ",");
break;
case 'M': /* mintty */
- flags |= (TERM_256COLOURS|TERM_RGBCOLOURS);
+ tty_add_features(&c->term_features,
+ "256,"
+ "RGB,"
+ "title",
+ ",");
break;
- case 'T': /* tmux - new versons reply to DSR which will set RGB */
- flags |= (TERM_UTF8|TERM_256COLOURS);
+ case 'T': /* tmux */
+ tty_add_features(&c->term_features,
+ "256,"
+ "RGB,"
+ "ccolour,"
+ "cstyle,"
+ "overline,"
+ "title,"
+ "usstyle",
+ ",");
break;
case 'U': /* rxvt-unicode */
- flags |= (TERM_UTF8);
+ tty_add_features(&c->term_features,
+ "256,"
+ "title",
+ ",");
break;
}
log_debug("%s: received secondary DA %.*s", c->name, (int)*size, buf);
- tty_set_flags(tty, flags);
+ tty_update_features(tty);
tty->flags |= TTY_HAVEDA;
return (0);
@@ -1094,7 +1111,6 @@ tty_keys_device_status_report(struct tty *tty, const char *buf, size_t len,
struct client *c = tty->client;
u_int i;
char tmp[64];
- int flags = 0;
*size = 0;
if (tty->flags & TTY_HAVEDSR)
@@ -1125,15 +1141,31 @@ tty_keys_device_status_report(struct tty *tty, const char *buf, size_t len,
tmp[i] = '\0';
*size = 3 + i;
- /* Set terminal flags. */
+ /* Add terminal features. */
if (strncmp(tmp, "ITERM2 ", 7) == 0) {
- flags |= (TERM_UTF8|TERM_DECSLRM|TERM_SYNC|TERM_256COLOURS|
- TERM_RGBCOLOURS);
- } else if (strncmp(tmp, "TMUX ", 5) == 0)
- flags |= (TERM_UTF8|TERM_256COLOURS|TERM_RGBCOLOURS);
+ tty_add_features(&c->term_features,
+ "256,"
+ "RGB,"
+ "clipboard,"
+ "cstyle,"
+ "margins,"
+ "sync,"
+ "title,",
+ ",");
+ } else if (strncmp(tmp, "TMUX ", 5) == 0) {
+ tty_add_features(&c->term_features,
+ "256,"
+ "RGB,"
+ "ccolour,"
+ "cstyle,"
+ "overline,"
+ "title,"
+ "usstyle",
+ ",");
+ }
log_debug("%s: received DSR %.*s", c->name, (int)*size, buf);
- tty_set_flags(tty, flags);
+ tty_update_features(tty);
tty->flags |= TTY_HAVEDSR;
return (0);
diff --git a/tty-term.c b/tty-term.c
index d6faa6ff..1c87ac15 100644
--- a/tty-term.c
+++ b/tty-term.c
@@ -27,7 +27,6 @@
#include "tmux.h"
-static void tty_term_override(struct tty_term *, const char *);
static char *tty_term_strip(const char *);
struct tty_terms tty_terms = LIST_HEAD_INITIALIZER(tty_terms);
@@ -335,22 +334,18 @@ tty_term_override_next(const char *s, size_t *offset)
return (value);
}
-static void
-tty_term_override(struct tty_term *term, const char *override)
+void
+tty_term_apply(struct tty_term *term, const char *capabilities, int quiet)
{
const struct tty_term_code_entry *ent;
struct tty_code *code;
size_t offset = 0;
char *cp, *value, *s;
- const char *errstr;
+ const char *errstr, *name = term->name;
u_int i;
int n, remove;
- s = tty_term_override_next(override, &offset);
- if (s == NULL || fnmatch(s, term->name, 0) != 0)
- return;
-
- while ((s = tty_term_override_next(override, &offset)) != NULL) {
+ while ((s = tty_term_override_next(capabilities, &offset)) != NULL) {
if (*s == '\0')
continue;
value = NULL;
@@ -369,12 +364,14 @@ tty_term_override(struct tty_term *term, const char *override)
} else
value = xstrdup("");
- if (remove)
- log_debug("%s override: %s@", term->name, s);
- else if (*value == '\0')
- log_debug("%s override: %s", term->name, s);
- else
- log_debug("%s override: %s=%s", term->name, s, value);
+ if (!quiet) {
+ if (remove)
+ log_debug("%s override: %s@", name, s);
+ else if (*value == '\0')
+ log_debug("%s override: %s", name, s);
+ else
+ log_debug("%s override: %s=%s", name, s, value);
+ }
for (i = 0; i < tty_term_ncodes(); i++) {
ent = &tty_term_codes[i];
@@ -414,7 +411,7 @@ tty_term_override(struct tty_term *term, const char *override)
}
struct tty_term *
-tty_term_find(char *name, int fd, char **cause)
+tty_term_create(struct tty *tty, char *name, int *feat, int fd, char **cause)
{
struct tty_term *term;
const struct tty_term_code_entry *ent;
@@ -425,19 +422,14 @@ tty_term_find(char *name, int fd, char **cause)
u_int i;
int n, error;
const char *s, *acs;
+ size_t offset;
+ char *first;
- LIST_FOREACH(term, &tty_terms, entry) {
- if (strcmp(term->name, name) == 0) {
- term->references++;
- return (term);
- }
- }
- log_debug("new term: %s", name);
+ log_debug("adding term %s", name);
- term = xmalloc(sizeof *term);
+ term = xcalloc(1, sizeof *term);
+ term->tty = tty;
term->name = xstrdup(name);
- term->references = 1;
- term->flags = 0;
term->codes = xcalloc(tty_term_ncodes(), sizeof *term->codes);
LIST_INSERT_HEAD(&tty_terms, term, entry);
@@ -495,12 +487,31 @@ tty_term_find(char *name, int fd, char **cause)
}
}
+ /* Apply terminal features. */
+ o = options_get_only(global_options, "terminal-features");
+ a = options_array_first(o);
+ while (a != NULL) {
+ ov = options_array_item_value(a);
+ s = ov->string;
+
+ offset = 0;
+ first = tty_term_override_next(s, &offset);
+ if (first != NULL && fnmatch(first, term->name, 0) == 0)
+ tty_add_features(feat, s + offset, ":");
+ a = options_array_next(a);
+ }
+