Home | History | Annotate | Line # | Download | only in dist
tty-features.c revision 1.1
      1 /* $OpenBSD$ */
      2 
      3 /*
      4  * Copyright (c) 2020 Nicholas Marriott <nicholas.marriott (at) gmail.com>
      5  *
      6  * Permission to use, copy, modify, and distribute this software for any
      7  * purpose with or without fee is hereby granted, provided that the above
      8  * copyright notice and this permission notice appear in all copies.
      9  *
     10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
     15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
     16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17  */
     18 
     19 #include <sys/types.h>
     20 
     21 #include <stdlib.h>
     22 #include <string.h>
     23 
     24 #include "tmux.h"
     25 
     26 /*
     27  * Still hardcoded:
     28  * - mouse (under kmous capability);
     29  * - default colours (under AX or op capabilities);
     30  * - AIX colours (under colors >= 16);
     31  * - alternate escape (if terminal is VT100-like).
     32  *
     33  * Also:
     34  * - DECFRA uses a flag instead of capabilities;
     35  * - UTF-8 is a separate flag on the client; needed for unattached clients.
     36  */
     37 
     38 /* A named terminal feature. */
     39 struct tty_feature {
     40 	const char	 *name;
     41 	const char	**capabilities;
     42 	int		  flags;
     43 };
     44 
     45 /* Terminal has xterm(1) title setting. */
     46 static const char *tty_feature_title_capabilities[] = {
     47 	"tsl=\\E]0;", /* should be using TS really */
     48 	"fsl=\\a",
     49 	NULL
     50 };
     51 static const struct tty_feature tty_feature_title = {
     52 	"title",
     53 	tty_feature_title_capabilities,
     54 	0
     55 };
     56 
     57 /* Terminal can set the clipboard with OSC 52. */
     58 static const char *tty_feature_clipboard_capabilities[] = {
     59 	"Ms=\\E]52;%p1%s;%p2%s\\a",
     60 	NULL
     61 };
     62 static const struct tty_feature tty_feature_clipboard = {
     63 	"clipboard",
     64 	tty_feature_clipboard_capabilities,
     65 	0
     66 };
     67 
     68 /*
     69  * Terminal supports RGB colour. This replaces setab and setaf also since
     70  * terminals with RGB have versions that do not allow setting colours from the
     71  * 256 palette.
     72  */
     73 static const char *tty_feature_rgb_capabilities[] = {
     74 	"AX",
     75 	"setrgbf=\\E[38;2;%p1%d;%p2%d;%p3%dm",
     76 	"setrgbb=\\E[48;2;%p1%d;%p2%d;%p3%dm",
     77 	"setab=\\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m",
     78 	"setaf=\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m",
     79 	NULL
     80 };
     81 static const struct tty_feature tty_feature_rgb = {
     82 	"RGB",
     83 	tty_feature_rgb_capabilities,
     84 	TERM_256COLOURS|TERM_RGBCOLOURS
     85 };
     86 
     87 /* Terminal supports 256 colours. */
     88 static const char *tty_feature_256_capabilities[] = {
     89 	"AX",
     90 	"setab=\\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m",
     91 	"setaf=\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m",
     92 	NULL
     93 };
     94 static const struct tty_feature tty_feature_256 = {
     95 	"256",
     96 	tty_feature_256_capabilities,
     97 	TERM_256COLOURS
     98 };
     99 
    100 /* Terminal supports overline. */
    101 static const char *tty_feature_overline_capabilities[] = {
    102 	"Smol=\\E[53m",
    103 	NULL
    104 };
    105 static const struct tty_feature tty_feature_overline = {
    106 	"overline",
    107 	tty_feature_overline_capabilities,
    108 	0
    109 };
    110 
    111 /* Terminal supports underscore styles. */
    112 static const char *tty_feature_usstyle_capabilities[] = {
    113 	"Smulx=\\E[4::%p1%dm",
    114 	"Setulc=\\E[58::2::%p1%{65536}%/%d::%p1%{256}%/%{255}%&%d::%p1%{255}%&%d%;m",
    115 	"ol=\\E[59m",
    116 	NULL
    117 };
    118 static const struct tty_feature tty_feature_usstyle = {
    119 	"usstyle",
    120 	tty_feature_usstyle_capabilities,
    121 	0
    122 };
    123 
    124 /* Terminal supports bracketed paste. */
    125 static const char *tty_feature_bpaste_capabilities[] = {
    126 	"Enbp=\\E[?2004h",
    127 	"Dsbp=\\E[?2004l",
    128 	NULL
    129 };
    130 static const struct tty_feature tty_feature_bpaste = {
    131 	"bpaste",
    132 	tty_feature_bpaste_capabilities,
    133 	0
    134 };
    135 
    136 /* Terminal supports focus reporting. */
    137 static const char *tty_feature_focus_capabilities[] = {
    138 	"Enfcs=\\E[?1004h",
    139 	"Dsfcs=\\E[?1004l",
    140 	NULL
    141 };
    142 static const struct tty_feature tty_feature_focus = {
    143 	"focus",
    144 	tty_feature_focus_capabilities,
    145 	0
    146 };
    147 
    148 /* Terminal supports cursor styles. */
    149 static const char *tty_feature_cstyle_capabilities[] = {
    150 	"Ss=\\E[%p1%d q",
    151 	"Se=\\E[2 q",
    152 	NULL
    153 };
    154 static const struct tty_feature tty_feature_cstyle = {
    155 	"cstyle",
    156 	tty_feature_cstyle_capabilities,
    157 	0
    158 };
    159 
    160 /* Terminal supports cursor colours. */
    161 static const char *tty_feature_ccolour_capabilities[] = {
    162 	"Cs=\\E]12;%p1%s\\a",
    163 	"Cr=\\E]112\\a",
    164 	NULL
    165 };
    166 static const struct tty_feature tty_feature_ccolour = {
    167 	"ccolour",
    168 	tty_feature_ccolour_capabilities,
    169 	0
    170 };
    171 
    172 /* Terminal supports strikethrough. */
    173 static const char *tty_feature_strikethrough_capabilities[] = {
    174 	"smxx=\\E[9m",
    175 	NULL
    176 };
    177 static const struct tty_feature tty_feature_strikethrough = {
    178 	"strikethrough",
    179 	tty_feature_strikethrough_capabilities,
    180 	0
    181 };
    182 
    183 /* Terminal supports synchronized updates. */
    184 static const char *tty_feature_sync_capabilities[] = {
    185 	"Sync=\\EP=%p1%ds\\E\\\\",
    186 	NULL
    187 };
    188 static const struct tty_feature tty_feature_sync = {
    189 	"sync",
    190 	tty_feature_sync_capabilities,
    191 	0
    192 };
    193 
    194 /* Terminal supports extended keys. */
    195 static const char *tty_feature_extkeys_capabilities[] = {
    196 	"Eneks=\\E[>4;1m",
    197 	"Dseks=\\E[>4m",
    198 	NULL
    199 };
    200 static const struct tty_feature tty_feature_extkeys = {
    201 	"extkeys",
    202 	tty_feature_extkeys_capabilities,
    203 	0
    204 };
    205 
    206 /* Terminal supports DECSLRM margins. */
    207 static const char *tty_feature_margins_capabilities[] = {
    208 	"Enmg=\\E[?69h",
    209 	"Dsmg=\\E[?69l",
    210 	"Clmg=\\E[s",
    211 	"Cmg=\\E[%i%p1%d;%p2%ds",
    212 	NULL
    213 };
    214 static const struct tty_feature tty_feature_margins = {
    215 	"margins",
    216 	tty_feature_margins_capabilities,
    217 	TERM_DECSLRM
    218 };
    219 
    220 /* Terminal supports DECFRA rectangle fill. */
    221 static const struct tty_feature tty_feature_rectfill = {
    222 	"rectfill",
    223 	NULL,
    224 	TERM_DECFRA
    225 };
    226 
    227 /* Available terminal features. */
    228 static const struct tty_feature *tty_features[] = {
    229 	&tty_feature_256,
    230 	&tty_feature_bpaste,
    231 	&tty_feature_ccolour,
    232 	&tty_feature_clipboard,
    233 	&tty_feature_cstyle,
    234 	&tty_feature_extkeys,
    235 	&tty_feature_focus,
    236 	&tty_feature_margins,
    237 	&tty_feature_overline,
    238 	&tty_feature_rectfill,
    239 	&tty_feature_rgb,
    240 	&tty_feature_strikethrough,
    241 	&tty_feature_sync,
    242 	&tty_feature_title,
    243 	&tty_feature_usstyle
    244 };
    245 
    246 void
    247 tty_add_features(int *feat, const char *s, const char *separators)
    248 {
    249 	const struct tty_feature	 *tf;
    250 	char				 *next, *loop, *copy;
    251 	u_int				  i;
    252 
    253 	log_debug("adding terminal features %s", s);
    254 
    255 	loop = copy = xstrdup(s);
    256 	while ((next = strsep(&loop, separators)) != NULL) {
    257 		for (i = 0; i < nitems(tty_features); i++) {
    258 			tf = tty_features[i];
    259 			if (strcasecmp(tf->name, next) == 0)
    260 				break;
    261 		}
    262 		if (i == nitems(tty_features)) {
    263 			log_debug("unknown terminal feature: %s", next);
    264 			break;
    265 		}
    266 		if (~(*feat) & (1 << i)) {
    267 			log_debug("adding terminal feature: %s", tf->name);
    268 			(*feat) |= (1 << i);
    269 		}
    270 	}
    271 	free(copy);
    272 }
    273 
    274 const char *
    275 tty_get_features(int feat)
    276 {
    277 	const struct tty_feature	*tf;
    278 	static char			 s[512];
    279 	u_int				 i;
    280 
    281 	*s = '\0';
    282 	for (i = 0; i < nitems(tty_features); i++) {
    283 		if (~feat & (1 << i))
    284 			continue;
    285 		tf = tty_features[i];
    286 
    287 		strlcat(s, tf->name, sizeof s);
    288 		strlcat(s, ",", sizeof s);
    289 	}
    290 	if (*s != '\0')
    291 		s[strlen(s) - 1] = '\0';
    292 	return (s);
    293 }
    294 
    295 int
    296 tty_apply_features(struct tty_term *term, int feat)
    297 {
    298 	const struct tty_feature	 *tf;
    299 	const char			**capability;
    300 	u_int				  i;
    301 
    302 	if (feat == 0)
    303 		return (0);
    304 	log_debug("applying terminal features: %s", tty_get_features(feat));
    305 
    306 	for (i = 0; i < nitems(tty_features); i++) {
    307 		if ((term->features & (1 << i)) || (~feat & (1 << i)))
    308 			continue;
    309 		tf = tty_features[i];
    310 
    311 		log_debug("applying terminal feature: %s", tf->name);
    312 		if (tf->capabilities != NULL) {
    313 			capability = tf->capabilities;
    314 			while (*capability != NULL) {
    315 				log_debug("adding capability: %s", *capability);
    316 				tty_term_apply(term, *capability, 1);
    317 				capability++;
    318 			}
    319 		}
    320 		term->flags |= tf->flags;
    321 	}
    322 	if ((term->features | feat) == term->features)
    323 		return (0);
    324 	term->features |= feat;
    325 	return (1);
    326 }
    327 
    328 void
    329 tty_default_features(int *feat, const char *name, u_int version)
    330 {
    331 	static struct {
    332 		const char	*name;
    333 		u_int		 version;
    334 		const char	*features;
    335 	} table[] = {
    336 #define TTY_FEATURES_BASE_MODERN_XTERM \
    337 	"256,RGB,bpaste,clipboard,strikethrough,title"
    338 		{ .name = "mintty",
    339 		  .features = TTY_FEATURES_BASE_MODERN_XTERM
    340 			      ",ccolour,cstyle,extkeys,margins,overline,usstyle"
    341 		},
    342 		{ .name = "tmux",
    343 		  .features = TTY_FEATURES_BASE_MODERN_XTERM
    344 			      ",ccolour,cstyle,focus,overline,usstyle"
    345 		},
    346 		{ .name = "rxvt-unicode",
    347 		  .features = "256,bpaste,ccolour,cstyle,title"
    348 		},
    349 		{ .name = "iTerm2",
    350 		  .features = TTY_FEATURES_BASE_MODERN_XTERM
    351 			      ",cstyle,extkeys,margins,sync"
    352 		},
    353 		{ .name = "XTerm",
    354 		  .features = TTY_FEATURES_BASE_MODERN_XTERM
    355 			      ",ccolour,cstyle,extkeys,focus,margins,rectfill"
    356 		}
    357 	};
    358 	u_int	i;
    359 
    360 	for (i = 0; i < nitems(table); i++) {
    361 		if (strcmp(table[i].name, name) != 0)
    362 			continue;
    363 		if (version != 0 && version < table[i].version)
    364 			continue;
    365 		tty_add_features(feat, table[i].features, ",");
    366 	}
    367 }
    368