1 /* $OpenBSD: tty-features.c,v 1.16 2020/06/04 10:36:28 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 NULL 116 }; 117 static const struct tty_feature tty_feature_usstyle = { 118 "usstyle", 119 tty_feature_usstyle_capabilities, 120 0 121 }; 122 123 /* Terminal supports bracketed paste. */ 124 static const char *tty_feature_bpaste_capabilities[] = { 125 "Enbp=\\E[?2004h", 126 "Dsbp=\\E[?2004l", 127 NULL 128 }; 129 static const struct tty_feature tty_feature_bpaste = { 130 "bpaste", 131 tty_feature_bpaste_capabilities, 132 0 133 }; 134 135 /* Terminal supports focus reporting. */ 136 static const char *tty_feature_focus_capabilities[] = { 137 "Enfcs=\\E[?1004h", 138 "Dsfcs=\\E[?1004l", 139 NULL 140 }; 141 static const struct tty_feature tty_feature_focus = { 142 "focus", 143 tty_feature_focus_capabilities, 144 0 145 }; 146 147 /* Terminal supports cursor styles. */ 148 static const char *tty_feature_cstyle_capabilities[] = { 149 "Ss=\\E[%p1%d q", 150 "Se=\\E[2 q", 151 NULL 152 }; 153 static const struct tty_feature tty_feature_cstyle = { 154 "cstyle", 155 tty_feature_cstyle_capabilities, 156 0 157 }; 158 159 /* Terminal supports cursor colours. */ 160 static const char *tty_feature_ccolour_capabilities[] = { 161 "Cs=\\E]12;%p1%s\\a", 162 "Cr=\\E]112\\a", 163 NULL 164 }; 165 static const struct tty_feature tty_feature_ccolour = { 166 "ccolour", 167 tty_feature_ccolour_capabilities, 168 0 169 }; 170 171 /* Terminal supports strikethrough. */ 172 static const char *tty_feature_strikethrough_capabilities[] = { 173 "smxx=\\E[9m", 174 NULL 175 }; 176 static const struct tty_feature tty_feature_strikethrough = { 177 "strikethrough", 178 tty_feature_strikethrough_capabilities, 179 0 180 }; 181 182 /* Terminal supports synchronized updates. */ 183 static const char *tty_feature_sync_capabilities[] = { 184 "Sync=\\EP=%p1%ds\\E\\\\", 185 NULL 186 }; 187 static const struct tty_feature tty_feature_sync = { 188 "sync", 189 tty_feature_sync_capabilities, 190 0 191 }; 192 193 /* Terminal supports extended keys. */ 194 static const char *tty_feature_extkeys_capabilities[] = { 195 "Eneks=\\E[>4;1m", 196 "Dseks=\\E[>4m", 197 NULL 198 }; 199 static const struct tty_feature tty_feature_extkeys = { 200 "extkeys", 201 tty_feature_extkeys_capabilities, 202 0 203 }; 204 205 /* Terminal supports DECSLRM margins. */ 206 static const char *tty_feature_margins_capabilities[] = { 207 "Enmg=\\E[?69h", 208 "Dsmg=\\E[?69l", 209 "Clmg=\\E[s", 210 "Cmg=\\E[%i%p1%d;%p2%ds", 211 NULL 212 }; 213 static const struct tty_feature tty_feature_margins = { 214 "margins", 215 tty_feature_margins_capabilities, 216 TERM_DECSLRM 217 }; 218 219 /* Terminal supports DECFRA rectangle fill. */ 220 static const struct tty_feature tty_feature_rectfill = { 221 "rectfill", 222 NULL, 223 TERM_DECFRA 224 }; 225 226 /* Available terminal features. */ 227 static const struct tty_feature *tty_features[] = { 228 &tty_feature_256, 229 &tty_feature_bpaste, 230 &tty_feature_ccolour, 231 &tty_feature_clipboard, 232 &tty_feature_cstyle, 233 &tty_feature_extkeys, 234 &tty_feature_focus, 235 &tty_feature_margins, 236 &tty_feature_overline, 237 &tty_feature_rectfill, 238 &tty_feature_rgb, 239 &tty_feature_strikethrough, 240 &tty_feature_sync, 241 &tty_feature_title, 242 &tty_feature_usstyle 243 }; 244 245 void 246 tty_add_features(int *feat, const char *s, const char *separators) 247 { 248 const struct tty_feature *tf; 249 char *next, *loop, *copy; 250 u_int i; 251 252 log_debug("adding terminal features %s", s); 253 254 loop = copy = xstrdup(s); 255 while ((next = strsep(&loop, separators)) != NULL) { 256 for (i = 0; i < nitems(tty_features); i++) { 257 tf = tty_features[i]; 258 if (strcasecmp(tf->name, next) == 0) 259 break; 260 } 261 if (i == nitems(tty_features)) { 262 log_debug("unknown terminal feature: %s", next); 263 break; 264 } 265 if (~(*feat) & (1 << i)) { 266 log_debug("adding terminal feature: %s", tf->name); 267 (*feat) |= (1 << i); 268 } 269 } 270 free(copy); 271 } 272 273 const char * 274 tty_get_features(int feat) 275 { 276 const struct tty_feature *tf; 277 static char s[512]; 278 u_int i; 279 280 *s = '\0'; 281 for (i = 0; i < nitems(tty_features); i++) { 282 if (~feat & (1 << i)) 283 continue; 284 tf = tty_features[i]; 285 286 strlcat(s, tf->name, sizeof s); 287 strlcat(s, ",", sizeof s); 288 } 289 if (*s != '\0') 290 s[strlen(s) - 1] = '\0'; 291 return (s); 292 } 293 294 int 295 tty_apply_features(struct tty_term *term, int feat) 296 { 297 const struct tty_feature *tf; 298 const char **capability; 299 u_int i; 300 301 if (feat == 0) 302 return (0); 303 log_debug("applying terminal features: %s", tty_get_features(feat)); 304 305 for (i = 0; i < nitems(tty_features); i++) { 306 if ((term->features & (1 << i)) || (~feat & (1 << i))) 307 continue; 308 tf = tty_features[i]; 309 310 log_debug("applying terminal feature: %s", tf->name); 311 if (tf->capabilities != NULL) { 312 capability = tf->capabilities; 313 while (*capability != NULL) { 314 log_debug("adding capability: %s", *capability); 315 tty_term_apply(term, *capability, 1); 316 capability++; 317 } 318 } 319 term->flags |= tf->flags; 320 } 321 if ((term->features | feat) == term->features) 322 return (0); 323 term->features |= feat; 324 return (1); 325 } 326 327 void 328 tty_default_features(int *feat, const char *name, u_int version) 329 { 330 static struct { 331 const char *name; 332 u_int version; 333 const char *features; 334 } table[] = { 335 #define TTY_FEATURES_BASE_MODERN_XTERM \ 336 "256,RGB,bpaste,clipboard,strikethrough,title" 337 { .name = "mintty", 338 .features = TTY_FEATURES_BASE_MODERN_XTERM 339 ",ccolour,cstyle,extkeys,margins,overline" 340 }, 341 { .name = "tmux", 342 .features = TTY_FEATURES_BASE_MODERN_XTERM 343 ",ccolour,cstyle,focus,overline,usstyle" 344 }, 345 { .name = "rxvt-unicode", 346 .features = "256,bpaste,ccolour,cstyle,title" 347 }, 348 { .name = "iTerm2", 349 .features = TTY_FEATURES_BASE_MODERN_XTERM 350 ",cstyle,extkeys,margins,sync" 351 }, 352 { .name = "XTerm", 353 .features = TTY_FEATURES_BASE_MODERN_XTERM 354 ",ccolour,cstyle,extkeys,focus,margins,rectfill" 355 } 356 }; 357 u_int i; 358 359 for (i = 0; i < nitems(table); i++) { 360 if (strcmp(table[i].name, name) != 0) 361 continue; 362 if (version != 0 && version < table[i].version) 363 continue; 364 tty_add_features(feat, table[i].features, ","); 365 } 366 } 367