1 /* $OpenBSD: tty-features.c,v 1.19 2021/06/10 07:28:45 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@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 char *tty_feature_rectfill_capabilities[] = { 222 "Rect", 223 NULL 224 }; 225 static const struct tty_feature tty_feature_rectfill = { 226 "rectfill", 227 tty_feature_rectfill_capabilities, 228 TERM_DECFRA 229 }; 230 231 /* Available terminal features. */ 232 static const struct tty_feature *tty_features[] = { 233 &tty_feature_256, 234 &tty_feature_bpaste, 235 &tty_feature_ccolour, 236 &tty_feature_clipboard, 237 &tty_feature_cstyle, 238 &tty_feature_extkeys, 239 &tty_feature_focus, 240 &tty_feature_margins, 241 &tty_feature_overline, 242 &tty_feature_rectfill, 243 &tty_feature_rgb, 244 &tty_feature_strikethrough, 245 &tty_feature_sync, 246 &tty_feature_title, 247 &tty_feature_usstyle 248 }; 249 250 void 251 tty_add_features(int *feat, const char *s, const char *separators) 252 { 253 const struct tty_feature *tf; 254 char *next, *loop, *copy; 255 u_int i; 256 257 log_debug("adding terminal features %s", s); 258 259 loop = copy = xstrdup(s); 260 while ((next = strsep(&loop, separators)) != NULL) { 261 for (i = 0; i < nitems(tty_features); i++) { 262 tf = tty_features[i]; 263 if (strcasecmp(tf->name, next) == 0) 264 break; 265 } 266 if (i == nitems(tty_features)) { 267 log_debug("unknown terminal feature: %s", next); 268 break; 269 } 270 if (~(*feat) & (1 << i)) { 271 log_debug("adding terminal feature: %s", tf->name); 272 (*feat) |= (1 << i); 273 } 274 } 275 free(copy); 276 } 277 278 const char * 279 tty_get_features(int feat) 280 { 281 const struct tty_feature *tf; 282 static char s[512]; 283 u_int i; 284 285 *s = '\0'; 286 for (i = 0; i < nitems(tty_features); i++) { 287 if (~feat & (1 << i)) 288 continue; 289 tf = tty_features[i]; 290 291 strlcat(s, tf->name, sizeof s); 292 strlcat(s, ",", sizeof s); 293 } 294 if (*s != '\0') 295 s[strlen(s) - 1] = '\0'; 296 return (s); 297 } 298 299 int 300 tty_apply_features(struct tty_term *term, int feat) 301 { 302 const struct tty_feature *tf; 303 const char **capability; 304 u_int i; 305 306 if (feat == 0) 307 return (0); 308 log_debug("applying terminal features: %s", tty_get_features(feat)); 309 310 for (i = 0; i < nitems(tty_features); i++) { 311 if ((term->features & (1 << i)) || (~feat & (1 << i))) 312 continue; 313 tf = tty_features[i]; 314 315 log_debug("applying terminal feature: %s", tf->name); 316 if (tf->capabilities != NULL) { 317 capability = tf->capabilities; 318 while (*capability != NULL) { 319 log_debug("adding capability: %s", *capability); 320 tty_term_apply(term, *capability, 1); 321 capability++; 322 } 323 } 324 term->flags |= tf->flags; 325 } 326 if ((term->features | feat) == term->features) 327 return (0); 328 term->features |= feat; 329 return (1); 330 } 331 332 void 333 tty_default_features(int *feat, const char *name, u_int version) 334 { 335 static struct { 336 const char *name; 337 u_int version; 338 const char *features; 339 } table[] = { 340 #define TTY_FEATURES_BASE_MODERN_XTERM \ 341 "256,RGB,bpaste,clipboard,strikethrough,title" 342 { .name = "mintty", 343 .features = TTY_FEATURES_BASE_MODERN_XTERM 344 ",ccolour,cstyle,extkeys,margins,overline,usstyle" 345 }, 346 { .name = "tmux", 347 .features = TTY_FEATURES_BASE_MODERN_XTERM 348 ",ccolour,cstyle,focus,overline,usstyle" 349 }, 350 { .name = "rxvt-unicode", 351 .features = "256,bpaste,ccolour,cstyle,title" 352 }, 353 { .name = "iTerm2", 354 .features = TTY_FEATURES_BASE_MODERN_XTERM 355 ",cstyle,extkeys,margins,sync" 356 }, 357 { .name = "XTerm", 358 /* 359 * xterm also supports DECSLRM and DECFRA, but they can be 360 * disabled so not set it here - they will be added if 361 * secondary DA shows VT420. 362 */ 363 .features = TTY_FEATURES_BASE_MODERN_XTERM 364 ",ccolour,cstyle,extkeys,focus" 365 } 366 }; 367 u_int i; 368 369 for (i = 0; i < nitems(table); i++) { 370 if (strcmp(table[i].name, name) != 0) 371 continue; 372 if (version != 0 && version < table[i].version) 373 continue; 374 tty_add_features(feat, table[i].features, ","); 375 } 376 } 377