| /* $OpenBSD$ */ |
| |
| /* |
| * Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com> |
| * |
| * Permission to use, copy, modify, and distribute this software for any |
| * purpose with or without fee is hereby granted, provided that the above |
| * copyright notice and this permission notice appear in all copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER |
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
| * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| #include <sys/types.h> |
| |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "tmux.h" |
| |
| /* |
| * Still hardcoded: |
| * - default colours (under AX or op capabilities); |
| * - AIX colours (under colors >= 16); |
| * - alternate escape (if terminal is VT100-like). |
| * |
| * Also: |
| * - DECFRA uses a flag instead of capabilities; |
| * - UTF-8 is a separate flag on the client; needed for unattached clients. |
| */ |
| |
| /* A named terminal feature. */ |
| struct tty_feature { |
| const char *name; |
| const char *const *capabilities; |
| int flags; |
| }; |
| |
| /* Terminal has xterm(1) title setting. */ |
| static const char *const tty_feature_title_capabilities[] = { |
| "tsl=\\E]0;", /* should be using TS really */ |
| "fsl=\\a", |
| NULL |
| }; |
| static const struct tty_feature tty_feature_title = { |
| "title", |
| tty_feature_title_capabilities, |
| 0 |
| }; |
| |
| /* Terminal has OSC 7 working directory. */ |
| static const char *const tty_feature_osc7_capabilities[] = { |
| "Swd=\\E]7;", |
| "fsl=\\a", |
| NULL |
| }; |
| static const struct tty_feature tty_feature_osc7 = { |
| "osc7", |
| tty_feature_osc7_capabilities, |
| 0 |
| }; |
| |
| /* Terminal has mouse support. */ |
| static const char *const tty_feature_mouse_capabilities[] = { |
| "kmous=\\E[M", |
| NULL |
| }; |
| static const struct tty_feature tty_feature_mouse = { |
| "mouse", |
| tty_feature_mouse_capabilities, |
| 0 |
| }; |
| |
| /* Terminal can set the clipboard with OSC 52. */ |
| static const char *const tty_feature_clipboard_capabilities[] = { |
| "Ms=\\E]52;%p1%s;%p2%s\\a", |
| NULL |
| }; |
| static const struct tty_feature tty_feature_clipboard = { |
| "clipboard", |
| tty_feature_clipboard_capabilities, |
| 0 |
| }; |
| |
| /* Terminal supports OSC 8 hyperlinks. */ |
| static const char *const tty_feature_hyperlinks_capabilities[] = { |
| "*:Hls=\\E]8;%?%p1%l%tid=%p1%s%;;%p2%s\\E\\\\", |
| NULL |
| }; |
| static const struct tty_feature tty_feature_hyperlinks = { |
| "hyperlinks", |
| tty_feature_hyperlinks_capabilities, |
| 0 |
| }; |
| |
| /* |
| * Terminal supports RGB colour. This replaces setab and setaf also since |
| * terminals with RGB have versions that do not allow setting colours from the |
| * 256 palette. |
| */ |
| static const char *const tty_feature_rgb_capabilities[] = { |
| "AX", |
| "setrgbf=\\E[38;2;%p1%d;%p2%d;%p3%dm", |
| "setrgbb=\\E[48;2;%p1%d;%p2%d;%p3%dm", |
| "setab=\\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", |
| "setaf=\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", |
| NULL |
| }; |
| static const struct tty_feature tty_feature_rgb = { |
| "RGB", |
| tty_feature_rgb_capabilities, |
| TERM_256COLOURS|TERM_RGBCOLOURS |
| }; |
| |
| /* Terminal supports 256 colours. */ |
| static const char *const tty_feature_256_capabilities[] = { |
| "AX", |
| "setab=\\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", |
| "setaf=\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", |
| NULL |
| }; |
| static const struct tty_feature tty_feature_256 = { |
| "256", |
| tty_feature_256_capabilities, |
| TERM_256COLOURS |
| }; |
| |
| /* Terminal supports overline. */ |
| static const char *const tty_feature_overline_capabilities[] = { |
| "Smol=\\E[53m", |
| NULL |
| }; |
| static const struct tty_feature tty_feature_overline = { |
| "overline", |
| tty_feature_overline_capabilities, |
| 0 |
| }; |
| |
| /* Terminal supports underscore styles. */ |
| static const char *const tty_feature_usstyle_capabilities[] = { |
| "Smulx=\\E[4::%p1%dm", |
| "Setulc=\\E[58::2::%p1%{65536}%/%d::%p1%{256}%/%{255}%&%d::%p1%{255}%&%d%;m", |
| "ol=\\E[59m", |
| NULL |
| }; |
| static const struct tty_feature tty_feature_usstyle = { |
| "usstyle", |
| tty_feature_usstyle_capabilities, |
| 0 |
| }; |
| |
| /* Terminal supports bracketed paste. */ |
| static const char *const tty_feature_bpaste_capabilities[] = { |
| "Enbp=\\E[?2004h", |
| "Dsbp=\\E[?2004l", |
| NULL |
| }; |
| static const struct tty_feature tty_feature_bpaste = { |
| "bpaste", |
| tty_feature_bpaste_capabilities, |
| 0 |
| }; |
| |
| /* Terminal supports focus reporting. */ |
| static const char *const tty_feature_focus_capabilities[] = { |
| "Enfcs=\\E[?1004h", |
| "Dsfcs=\\E[?1004l", |
| NULL |
| }; |
| static const struct tty_feature tty_feature_focus = { |
| "focus", |
| tty_feature_focus_capabilities, |
| 0 |
| }; |
| |
| /* Terminal supports cursor styles. */ |
| static const char *const tty_feature_cstyle_capabilities[] = { |
| "Ss=\\E[%p1%d q", |
| "Se=\\E[2 q", |
| NULL |
| }; |
| static const struct tty_feature tty_feature_cstyle = { |
| "cstyle", |
| tty_feature_cstyle_capabilities, |
| 0 |
| }; |
| |
| /* Terminal supports cursor colours. */ |
| static const char *const tty_feature_ccolour_capabilities[] = { |
| "Cs=\\E]12;%p1%s\\a", |
| "Cr=\\E]112\\a", |
| NULL |
| }; |
| static const struct tty_feature tty_feature_ccolour = { |
| "ccolour", |
| tty_feature_ccolour_capabilities, |
| 0 |
| }; |
| |
| /* Terminal supports strikethrough. */ |
| static const char *const tty_feature_strikethrough_capabilities[] = { |
| "smxx=\\E[9m", |
| NULL |
| }; |
| static const struct tty_feature tty_feature_strikethrough = { |
| "strikethrough", |
| tty_feature_strikethrough_capabilities, |
| 0 |
| }; |
| |
| /* Terminal supports synchronized updates. */ |
| static const char *const tty_feature_sync_capabilities[] = { |
| "Sync=\\EP=%p1%ds\\E\\\\", |
| NULL |
| }; |
| static const struct tty_feature tty_feature_sync = { |
| "sync", |
| tty_feature_sync_capabilities, |
| 0 |
| }; |
| |
| /* Terminal supports extended keys. */ |
| static const char *const tty_feature_extkeys_capabilities[] = { |
| "Eneks=\\E[>4;1m", |
| "Dseks=\\E[>4m", |
| NULL |
| }; |
| static const struct tty_feature tty_feature_extkeys = { |
| "extkeys", |
| tty_feature_extkeys_capabilities, |
| 0 |
| }; |
| |
| /* Terminal supports DECSLRM margins. */ |
| static const char *const tty_feature_margins_capabilities[] = { |
| "Enmg=\\E[?69h", |
| "Dsmg=\\E[?69l", |
| "Clmg=\\E[s", |
| "Cmg=\\E[%i%p1%d;%p2%ds", |
| NULL |
| }; |
| static const struct tty_feature tty_feature_margins = { |
| "margins", |
| tty_feature_margins_capabilities, |
| TERM_DECSLRM |
| }; |
| |
| /* Terminal supports DECFRA rectangle fill. */ |
| static const char *const tty_feature_rectfill_capabilities[] = { |
| "Rect", |
| NULL |
| }; |
| static const struct tty_feature tty_feature_rectfill = { |
| "rectfill", |
| tty_feature_rectfill_capabilities, |
| TERM_DECFRA |
| }; |
| |
| /* Use builtin function keys only. */ |
| static const char *const tty_feature_ignorefkeys_capabilities[] = { |
| "kf0@", |
| "kf1@", |
| "kf2@", |
| "kf3@", |
| "kf4@", |
| "kf5@", |
| "kf6@", |
| "kf7@", |
| "kf8@", |
| "kf9@", |
| "kf10@", |
| "kf11@", |
| "kf12@", |
| "kf13@", |
| "kf14@", |
| "kf15@", |
| "kf16@", |
| "kf17@", |
| "kf18@", |
| "kf19@", |
| "kf20@", |
| "kf21@", |
| "kf22@", |
| "kf23@", |
| "kf24@", |
| "kf25@", |
| "kf26@", |
| "kf27@", |
| "kf28@", |
| "kf29@", |
| "kf30@", |
| "kf31@", |
| "kf32@", |
| "kf33@", |
| "kf34@", |
| "kf35@", |
| "kf36@", |
| "kf37@", |
| "kf38@", |
| "kf39@", |
| "kf40@", |
| "kf41@", |
| "kf42@", |
| "kf43@", |
| "kf44@", |
| "kf45@", |
| "kf46@", |
| "kf47@", |
| "kf48@", |
| "kf49@", |
| "kf50@", |
| "kf51@", |
| "kf52@", |
| "kf53@", |
| "kf54@", |
| "kf55@", |
| "kf56@", |
| "kf57@", |
| "kf58@", |
| "kf59@", |
| "kf60@", |
| "kf61@", |
| "kf62@", |
| "kf63@", |
| NULL |
| }; |
| static const struct tty_feature tty_feature_ignorefkeys = { |
| "ignorefkeys", |
| tty_feature_ignorefkeys_capabilities, |
| 0 |
| }; |
| |
| /* Available terminal features. */ |
| static const struct tty_feature *const tty_features[] = { |
| &tty_feature_256, |
| &tty_feature_bpaste, |
| &tty_feature_ccolour, |
| &tty_feature_clipboard, |
| &tty_feature_hyperlinks, |
| &tty_feature_cstyle, |
| &tty_feature_extkeys, |
| &tty_feature_focus, |
| &tty_feature_ignorefkeys, |
| &tty_feature_margins, |
| &tty_feature_mouse, |
| &tty_feature_osc7, |
| &tty_feature_overline, |
| &tty_feature_rectfill, |
| &tty_feature_rgb, |
| &tty_feature_strikethrough, |
| &tty_feature_sync, |
| &tty_feature_title, |
| &tty_feature_usstyle |
| }; |
| |
| void |
| tty_add_features(int *feat, const char *s, const char *separators) |
| { |
| const struct tty_feature *tf; |
| char *next, *loop, *copy; |
| u_int i; |
| |
| log_debug("adding terminal features %s", s); |
| |
| loop = copy = xstrdup(s); |
| while ((next = strsep(&loop, separators)) != NULL) { |
| for (i = 0; i < nitems(tty_features); i++) { |
| tf = tty_features[i]; |
| if (strcasecmp(tf->name, next) == 0) |
| break; |
| } |
| if (i == nitems(tty_features)) { |
| log_debug("unknown terminal feature: %s", next); |
| break; |
| } |
| if (~(*feat) & (1 << i)) { |
| log_debug("adding terminal feature: %s", tf->name); |
| (*feat) |= (1 << i); |
| } |
| } |
| free(copy); |
| } |
| |
| const char * |
| tty_get_features(int feat) |
| { |
| const struct tty_feature *tf; |
| static char s[512]; |
| u_int i; |
| |
| *s = '\0'; |
| for (i = 0; i < nitems(tty_features); i++) { |
| if (~feat & (1 << i)) |
| continue; |
| tf = tty_features[i]; |
| |
| strlcat(s, tf->name, sizeof s); |
| strlcat(s, ",", sizeof s); |
| } |
| if (*s != '\0') |
| s[strlen(s) - 1] = '\0'; |
| return (s); |
| } |
| |
| int |
| tty_apply_features(struct tty_term *term, int feat) |
| { |
| const struct tty_feature *tf; |
| const char *const *capability; |
| u_int i; |
| |
| if (feat == 0) |
| return (0); |
| log_debug("applying terminal features: %s", tty_get_features(feat)); |
| |
| for (i = 0; i < nitems(tty_features); i++) { |
| if ((term->features & (1 << i)) || (~feat & (1 << i))) |
| continue; |
| tf = tty_features[i]; |
| |
| log_debug("applying terminal feature: %s", tf->name); |
| if (tf->capabilities != NULL) { |
| capability = tf->capabilities; |
| while (*capability != NULL) { |
| log_debug("adding capability: %s", *capability); |
| tty_term_apply(term, *capability, 1); |
| capability++; |
| } |
| } |
| term->flags |= tf->flags; |
| } |
| if ((term->features | feat) == term->features) |
| return (0); |
| term->features |= feat; |
| return (1); |
| } |
| |
| void |
| tty_default_features(int *feat, const char *name, u_int version) |
| { |
| static const struct { |
| const char *name; |
| u_int version; |
| const char *features; |
| } table[] = { |
| #define TTY_FEATURES_BASE_MODERN_XTERM \ |
| "256,RGB,bpaste,clipboard,mouse,strikethrough,title" |
| { .name = "mintty", |
| .features = TTY_FEATURES_BASE_MODERN_XTERM |
| ",ccolour,cstyle,extkeys,margins,overline,usstyle" |
| }, |
| { .name = "tmux", |
| .features = TTY_FEATURES_BASE_MODERN_XTERM |
| ",ccolour,cstyle,focus,overline,usstyle,hyperlinks" |
| }, |
| { .name = "rxvt-unicode", |
| .features = "256,bpaste,ccolour,cstyle,mouse,title,ignorefkeys" |
| }, |
| { .name = "iTerm2", |
| .features = TTY_FEATURES_BASE_MODERN_XTERM |
| ",cstyle,extkeys,margins,usstyle,sync,osc7,hyperlinks" |
| }, |
| { .name = "XTerm", |
| /* |
| * xterm also supports DECSLRM and DECFRA, but they can be |
| * disabled so not set it here - they will be added if |
| * secondary DA shows VT420. |
| */ |
| .features = TTY_FEATURES_BASE_MODERN_XTERM |
| ",ccolour,cstyle,extkeys,focus" |
| } |
| }; |
| u_int i; |
| |
| for (i = 0; i < nitems(table); i++) { |
| if (strcmp(table[i].name, name) != 0) |
| continue; |
| if (version != 0 && version < table[i].version) |
| continue; |
| tty_add_features(feat, table[i].features, ","); |
| } |
| } |