1 /* $OpenBSD: tty-features.c,v 1.18 2020/10/05 09:53:01 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 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