1 /* $OpenBSD: tty-features.c,v 1.13 2020/05/16 14:46:14 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 DECSLRM margins. */ 194 static const char *tty_feature_margins_capabilities[] = { 195 "Enmg=\\E[?69h", 196 "Dsmg=\\E[?69l", 197 "Clmg=\\E[s", 198 "Cmg=\\E[%i%p1%d;%p2%ds", 199 NULL 200 }; 201 static const struct tty_feature tty_feature_margins = { 202 "margins", 203 tty_feature_margins_capabilities, 204 TERM_DECSLRM 205 }; 206 207 /* Terminal supports DECFRA rectangle fill. */ 208 static const struct tty_feature tty_feature_rectfill = { 209 "rectfill", 210 NULL, 211 TERM_DECFRA 212 }; 213 214 /* Available terminal features. */ 215 static const struct tty_feature *tty_features[] = { 216 &tty_feature_256, 217 &tty_feature_bpaste, 218 &tty_feature_ccolour, 219 &tty_feature_clipboard, 220 &tty_feature_cstyle, 221 &tty_feature_focus, 222 &tty_feature_margins, 223 &tty_feature_overline, 224 &tty_feature_rectfill, 225 &tty_feature_rgb, 226 &tty_feature_strikethrough, 227 &tty_feature_sync, 228 &tty_feature_title, 229 &tty_feature_usstyle 230 }; 231 232 void 233 tty_add_features(int *feat, const char *s, const char *separators) 234 { 235 const struct tty_feature *tf; 236 char *next, *loop, *copy; 237 u_int i; 238 239 log_debug("adding terminal features %s", s); 240 241 loop = copy = xstrdup(s); 242 while ((next = strsep(&loop, separators)) != NULL) { 243 for (i = 0; i < nitems(tty_features); i++) { 244 tf = tty_features[i]; 245 if (strcasecmp(tf->name, next) == 0) 246 break; 247 } 248 if (i == nitems(tty_features)) { 249 log_debug("unknown terminal feature: %s", next); 250 break; 251 } 252 if (~(*feat) & (1 << i)) { 253 log_debug("adding terminal feature: %s", tf->name); 254 (*feat) |= (1 << i); 255 } 256 } 257 free(copy); 258 } 259 260 const char * 261 tty_get_features(int feat) 262 { 263 const struct tty_feature *tf; 264 static char s[512]; 265 u_int i; 266 267 *s = '\0'; 268 for (i = 0; i < nitems(tty_features); i++) { 269 if (~feat & (1 << i)) 270 continue; 271 tf = tty_features[i]; 272 273 strlcat(s, tf->name, sizeof s); 274 strlcat(s, ",", sizeof s); 275 } 276 if (*s != '\0') 277 s[strlen(s) - 1] = '\0'; 278 return (s); 279 } 280 281 int 282 tty_apply_features(struct tty_term *term, int feat) 283 { 284 const struct tty_feature *tf; 285 const char **capability; 286 u_int i; 287 288 if (feat == 0) 289 return (0); 290 log_debug("applying terminal features: %s", tty_get_features(feat)); 291 292 for (i = 0; i < nitems(tty_features); i++) { 293 if ((term->features & (1 << i)) || (~feat & (1 << i))) 294 continue; 295 tf = tty_features[i]; 296 297 log_debug("applying terminal feature: %s", tf->name); 298 if (tf->capabilities != NULL) { 299 capability = tf->capabilities; 300 while (*capability != NULL) { 301 log_debug("adding capability: %s", *capability); 302 tty_term_apply(term, *capability, 1); 303 capability++; 304 } 305 } 306 term->flags |= tf->flags; 307 } 308 if ((term->features | feat) == term->features) 309 return (0); 310 term->features |= feat; 311 return (1); 312 } 313 314 void 315 tty_default_features(int *feat, const char *name, u_int version) 316 { 317 static struct { 318 const char *name; 319 u_int version; 320 const char *features; 321 } table[] = { 322 #define TTY_FEATURES_BASE_MODERN_XTERM "256,RGB,bpaste,clipboard,strikethrough,title" 323 { .name = "mintty", 324 .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,margins,overline" 325 }, 326 { .name = "tmux", 327 .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,focus,overline,usstyle" 328 }, 329 { .name = "rxvt-unicode", 330 .features = "256,bpaste,ccolour,cstyle,title" 331 }, 332 { .name = "iTerm2", 333 .features = TTY_FEATURES_BASE_MODERN_XTERM ",cstyle,margins,sync" 334 }, 335 { .name = "XTerm", 336 .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,focus,margins,rectfill" 337 } 338 }; 339 u_int i; 340 341 for (i = 0; i < nitems(table); i++) { 342 if (strcmp(table[i].name, name) != 0) 343 continue; 344 if (version != 0 && version < table[i].version) 345 continue; 346 tty_add_features(feat, table[i].features, ","); 347 } 348 } 349