1 /* $OpenBSD$ */ 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 #if defined(HAVE_CURSES_H) 25 #include <curses.h> 26 #elif defined(HAVE_NCURSES_H) 27 #include <ncurses.h> 28 #endif 29 30 #include "tmux.h" 31 32 /* 33 * Still hardcoded: 34 * - default colours (under AX or op capabilities); 35 * - AIX colours (under colors >= 16); 36 * - alternate escape (if terminal is VT100-like). 37 * 38 * Also: 39 * - DECFRA uses a flag instead of capabilities; 40 * - UTF-8 is a separate flag on the client; needed for unattached clients. 41 */ 42 43 /* A named terminal feature. */ 44 struct tty_feature { 45 const char *name; 46 const char *const *capabilities; 47 int flags; 48 }; 49 50 /* Terminal has xterm(1) title setting. */ 51 static const char *const tty_feature_title_capabilities[] = { 52 "tsl=\\E]0;", /* should be using TS really */ 53 "fsl=\\a", 54 NULL 55 }; 56 static const struct tty_feature tty_feature_title = { 57 "title", 58 tty_feature_title_capabilities, 59 0 60 }; 61 62 /* Terminal has OSC 7 working directory. */ 63 static const char *const tty_feature_osc7_capabilities[] = { 64 "Swd=\\E]7;", 65 "fsl=\\a", 66 NULL 67 }; 68 static const struct tty_feature tty_feature_osc7 = { 69 "osc7", 70 tty_feature_osc7_capabilities, 71 0 72 }; 73 74 /* Terminal has mouse support. */ 75 static const char *const tty_feature_mouse_capabilities[] = { 76 "kmous=\\E[M", 77 NULL 78 }; 79 static const struct tty_feature tty_feature_mouse = { 80 "mouse", 81 tty_feature_mouse_capabilities, 82 0 83 }; 84 85 /* Terminal can set the clipboard with OSC 52. */ 86 static const char *const tty_feature_clipboard_capabilities[] = { 87 "Ms=\\E]52;%p1%s;%p2%s\\a", 88 NULL 89 }; 90 static const struct tty_feature tty_feature_clipboard = { 91 "clipboard", 92 tty_feature_clipboard_capabilities, 93 0 94 }; 95 96 /* Terminal supports OSC 8 hyperlinks. */ 97 static const char *tty_feature_hyperlinks_capabilities[] = { 98 #if defined (__OpenBSD__) || (defined(NCURSES_VERSION_MAJOR) && \ 99 (NCURSES_VERSION_MAJOR > 5 || \ 100 (NCURSES_VERSION_MAJOR == 5 && NCURSES_VERSION_MINOR > 8))) 101 "*:Hls=\\E]8;%?%p1%l%tid=%p1%s%;;%p2%s\\E\\\\", 102 #endif 103 NULL 104 }; 105 static const struct tty_feature tty_feature_hyperlinks = { 106 "hyperlinks", 107 tty_feature_hyperlinks_capabilities, 108 0 109 }; 110 111 /* 112 * Terminal supports RGB colour. This replaces setab and setaf also since 113 * terminals with RGB have versions that do not allow setting colours from the 114 * 256 palette. 115 */ 116 static const char *const tty_feature_rgb_capabilities[] = { 117 "AX", 118 "setrgbf=\\E[38;2;%p1%d;%p2%d;%p3%dm", 119 "setrgbb=\\E[48;2;%p1%d;%p2%d;%p3%dm", 120 "setab=\\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", 121 "setaf=\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", 122 NULL 123 }; 124 static const struct tty_feature tty_feature_rgb = { 125 "RGB", 126 tty_feature_rgb_capabilities, 127 TERM_256COLOURS|TERM_RGBCOLOURS 128 }; 129 130 /* Terminal supports 256 colours. */ 131 static const char *const tty_feature_256_capabilities[] = { 132 "AX", 133 "setab=\\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", 134 "setaf=\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", 135 NULL 136 }; 137 static const struct tty_feature tty_feature_256 = { 138 "256", 139 tty_feature_256_capabilities, 140 TERM_256COLOURS 141 }; 142 143 /* Terminal supports overline. */ 144 static const char *const tty_feature_overline_capabilities[] = { 145 "Smol=\\E[53m", 146 NULL 147 }; 148 static const struct tty_feature tty_feature_overline = { 149 "overline", 150 tty_feature_overline_capabilities, 151 0 152 }; 153 154 /* Terminal supports underscore styles. */ 155 static const char *const tty_feature_usstyle_capabilities[] = { 156 "Smulx=\\E[4::%p1%dm", 157 "Setulc=\\E[58::2::%p1%{65536}%/%d::%p1%{256}%/%{255}%&%d::%p1%{255}%&%d%;m", 158 "Setulc1=\\E[58::5::%p1%dm", 159 "ol=\\E[59m", 160 NULL 161 }; 162 static const struct tty_feature tty_feature_usstyle = { 163 "usstyle", 164 tty_feature_usstyle_capabilities, 165 0 166 }; 167 168 /* Terminal supports bracketed paste. */ 169 static const char *const tty_feature_bpaste_capabilities[] = { 170 "Enbp=\\E[?2004h", 171 "Dsbp=\\E[?2004l", 172 NULL 173 }; 174 static const struct tty_feature tty_feature_bpaste = { 175 "bpaste", 176 tty_feature_bpaste_capabilities, 177 0 178 }; 179 180 /* Terminal supports focus reporting. */ 181 static const char *const tty_feature_focus_capabilities[] = { 182 "Enfcs=\\E[?1004h", 183 "Dsfcs=\\E[?1004l", 184 NULL 185 }; 186 static const struct tty_feature tty_feature_focus = { 187 "focus", 188 tty_feature_focus_capabilities, 189 0 190 }; 191 192 /* Terminal supports cursor styles. */ 193 static const char *const tty_feature_cstyle_capabilities[] = { 194 "Ss=\\E[%p1%d q", 195 "Se=\\E[2 q", 196 NULL 197 }; 198 static const struct tty_feature tty_feature_cstyle = { 199 "cstyle", 200 tty_feature_cstyle_capabilities, 201 0 202 }; 203 204 /* Terminal supports cursor colours. */ 205 static const char *const tty_feature_ccolour_capabilities[] = { 206 "Cs=\\E]12;%p1%s\\a", 207 "Cr=\\E]112\\a", 208 NULL 209 }; 210 static const struct tty_feature tty_feature_ccolour = { 211 "ccolour", 212 tty_feature_ccolour_capabilities, 213 0 214 }; 215 216 /* Terminal supports strikethrough. */ 217 static const char *const tty_feature_strikethrough_capabilities[] = { 218 "smxx=\\E[9m", 219 NULL 220 }; 221 static const struct tty_feature tty_feature_strikethrough = { 222 "strikethrough", 223 tty_feature_strikethrough_capabilities, 224 0 225 }; 226 227 /* Terminal supports synchronized updates. */ 228 static const char *const tty_feature_sync_capabilities[] = { 229 "Sync=\\E[?2026%?%p1%{1}%-%tl%eh%;", 230 NULL 231 }; 232 static const struct tty_feature tty_feature_sync = { 233 "sync", 234 tty_feature_sync_capabilities, 235 0 236 }; 237 238 /* Terminal supports extended keys. */ 239 static const char *const tty_feature_extkeys_capabilities[] = { 240 "Eneks=\\E[>4;2m", 241 "Dseks=\\E[>4m", 242 NULL 243 }; 244 static const struct tty_feature tty_feature_extkeys = { 245 "extkeys", 246 tty_feature_extkeys_capabilities, 247 0 248 }; 249 250 /* Terminal supports DECSLRM margins. */ 251 static const char *const tty_feature_margins_capabilities[] = { 252 "Enmg=\\E[?69h", 253 "Dsmg=\\E[?69l", 254 "Clmg=\\E[s", 255 "Cmg=\\E[%i%p1%d;%p2%ds", 256 NULL 257 }; 258 static const struct tty_feature tty_feature_margins = { 259 "margins", 260 tty_feature_margins_capabilities, 261 TERM_DECSLRM 262 }; 263 264 /* Terminal supports DECFRA rectangle fill. */ 265 static const char *const tty_feature_rectfill_capabilities[] = { 266 "Rect", 267 NULL 268 }; 269 static const struct tty_feature tty_feature_rectfill = { 270 "rectfill", 271 tty_feature_rectfill_capabilities, 272 TERM_DECFRA 273 }; 274 275 /* Use builtin function keys only. */ 276 static const char *const tty_feature_ignorefkeys_capabilities[] = { 277 "kf0@", 278 "kf1@", 279 "kf2@", 280 "kf3@", 281 "kf4@", 282 "kf5@", 283 "kf6@", 284 "kf7@", 285 "kf8@", 286 "kf9@", 287 "kf10@", 288 "kf11@", 289 "kf12@", 290 "kf13@", 291 "kf14@", 292 "kf15@", 293 "kf16@", 294 "kf17@", 295 "kf18@", 296 "kf19@", 297 "kf20@", 298 "kf21@", 299 "kf22@", 300 "kf23@", 301 "kf24@", 302 "kf25@", 303 "kf26@", 304 "kf27@", 305 "kf28@", 306 "kf29@", 307 "kf30@", 308 "kf31@", 309 "kf32@", 310 "kf33@", 311 "kf34@", 312 "kf35@", 313 "kf36@", 314 "kf37@", 315 "kf38@", 316 "kf39@", 317 "kf40@", 318 "kf41@", 319 "kf42@", 320 "kf43@", 321 "kf44@", 322 "kf45@", 323 "kf46@", 324 "kf47@", 325 "kf48@", 326 "kf49@", 327 "kf50@", 328 "kf51@", 329 "kf52@", 330 "kf53@", 331 "kf54@", 332 "kf55@", 333 "kf56@", 334 "kf57@", 335 "kf58@", 336 "kf59@", 337 "kf60@", 338 "kf61@", 339 "kf62@", 340 "kf63@", 341 NULL 342 }; 343 static const struct tty_feature tty_feature_ignorefkeys = { 344 "ignorefkeys", 345 tty_feature_ignorefkeys_capabilities, 346 0 347 }; 348 349 /* Terminal has sixel capability. */ 350 static const char *const tty_feature_sixel_capabilities[] = { 351 "Sxl", 352 NULL 353 }; 354 static const struct tty_feature tty_feature_sixel = { 355 "sixel", 356 tty_feature_sixel_capabilities, 357 TERM_SIXEL 358 }; 359 360 /* Available terminal features. */ 361 static const struct tty_feature *const tty_features[] = { 362 &tty_feature_256, 363 &tty_feature_bpaste, 364 &tty_feature_ccolour, 365 &tty_feature_clipboard, 366 &tty_feature_hyperlinks, 367 &tty_feature_cstyle, 368 &tty_feature_extkeys, 369 &tty_feature_focus, 370 &tty_feature_ignorefkeys, 371 &tty_feature_margins, 372 &tty_feature_mouse, 373 &tty_feature_osc7, 374 &tty_feature_overline, 375 &tty_feature_rectfill, 376 &tty_feature_rgb, 377 &tty_feature_sixel, 378 &tty_feature_strikethrough, 379 &tty_feature_sync, 380 &tty_feature_title, 381 &tty_feature_usstyle 382 }; 383 384 void 385 tty_add_features(int *feat, const char *s, const char *separators) 386 { 387 const struct tty_feature *tf; 388 char *next, *loop, *copy; 389 u_int i; 390 391 log_debug("adding terminal features %s", s); 392 393 loop = copy = xstrdup(s); 394 while ((next = strsep(&loop, separators)) != NULL) { 395 for (i = 0; i < nitems(tty_features); i++) { 396 tf = tty_features[i]; 397 if (strcasecmp(tf->name, next) == 0) 398 break; 399 } 400 if (i == nitems(tty_features)) { 401 log_debug("unknown terminal feature: %s", next); 402 break; 403 } 404 if (~(*feat) & (1 << i)) { 405 log_debug("adding terminal feature: %s", tf->name); 406 (*feat) |= (1 << i); 407 } 408 } 409 free(copy); 410 } 411 412 const char * 413 tty_get_features(int feat) 414 { 415 const struct tty_feature *tf; 416 static char s[512]; 417 u_int i; 418 419 *s = '\0'; 420 for (i = 0; i < nitems(tty_features); i++) { 421 if (~feat & (1 << i)) 422 continue; 423 tf = tty_features[i]; 424 425 strlcat(s, tf->name, sizeof s); 426 strlcat(s, ",", sizeof s); 427 } 428 if (*s != '\0') 429 s[strlen(s) - 1] = '\0'; 430 return (s); 431 } 432 433 int 434 tty_apply_features(struct tty_term *term, int feat) 435 { 436 const struct tty_feature *tf; 437 const char *const *capability; 438 u_int i; 439 440 if (feat == 0) 441 return (0); 442 log_debug("applying terminal features: %s", tty_get_features(feat)); 443 444 for (i = 0; i < nitems(tty_features); i++) { 445 if ((term->features & (1 << i)) || (~feat & (1 << i))) 446 continue; 447 tf = tty_features[i]; 448 449 log_debug("applying terminal feature: %s", tf->name); 450 if (tf->capabilities != NULL) { 451 capability = tf->capabilities; 452 while (*capability != NULL) { 453 log_debug("adding capability: %s", *capability); 454 tty_term_apply(term, *capability, 1); 455 capability++; 456 } 457 } 458 term->flags |= tf->flags; 459 } 460 if ((term->features | feat) == term->features) 461 return (0); 462 term->features |= feat; 463 return (1); 464 } 465 466 void 467 tty_default_features(int *feat, const char *name, u_int version) 468 { 469 static const struct { 470 const char *name; 471 u_int version; 472 const char *features; 473 } table[] = { 474 #define TTY_FEATURES_BASE_MODERN_XTERM \ 475 "256,RGB,bpaste,clipboard,mouse,strikethrough,title" 476 { .name = "mintty", 477 .features = TTY_FEATURES_BASE_MODERN_XTERM 478 ",ccolour,cstyle,extkeys,margins,overline,usstyle" 479 }, 480 { .name = "tmux", 481 .features = TTY_FEATURES_BASE_MODERN_XTERM 482 ",ccolour,cstyle,focus,overline,usstyle,hyperlinks" 483 }, 484 { .name = "rxvt-unicode", 485 .features = "256,bpaste,ccolour,cstyle,mouse,title,ignorefkeys" 486 }, 487 { .name = "iTerm2", 488 .features = TTY_FEATURES_BASE_MODERN_XTERM 489 ",cstyle,extkeys,margins,usstyle,sync,osc7,hyperlinks" 490 }, 491 { .name = "XTerm", 492 /* 493 * xterm also supports DECSLRM and DECFRA, but they can be 494 * disabled so not set it here - they will be added if 495 * secondary DA shows VT420. 496 */ 497 .features = TTY_FEATURES_BASE_MODERN_XTERM 498 ",ccolour,cstyle,extkeys,focus" 499 } 500 }; 501 u_int i; 502 503 for (i = 0; i < nitems(table); i++) { 504 if (strcmp(table[i].name, name) != 0) 505 continue; 506 if (version != 0 && version < table[i].version) 507 continue; 508 tty_add_features(feat, table[i].features, ","); 509 } 510 } 511