diff --git a/README.md b/README.md new file mode 100644 index 0000000000..4953a6bf0f --- /dev/null +++ b/README.md @@ -0,0 +1,180 @@ +> sixel-tmux : a terminal multiplexer that properly display graphics because it does not eat escape sequences + +### What is sixel-tmux + +sixel-tmux is a fork of tmux, with just one goal: having the most reliable support of graphics. + +In a console, this means: + 1. ANSI blocks and box-drawing characters to display ASCII and ANSI art + 2. ANSI escape codes, with a focus on SGR parameters to render text attributes + 3. and the best of all: sixel graphics to display graphs + +This seems very simple requirements, yet a regular tmux fails on all 3: + 1. ANSI art is corrupted, + 2. some SGR parameters are supported, but the more exotic features like overline do not work, and some sequences may be intercepted and improperly changed + 3. sixels are not supported. + +For a proper support of all these features, sixel-tmux is necessary but +not sufficient: the terminal must be able to decode and render the sequences +passed by sixel-tmux. + +Therefore, a separate test suite +is provided to first check if a terminal can achieve these goals. + +If you are dissatisfied by your current terminal, please consider mlterm + or mintty that are +tested and known to work well. + +Microsoft Window Terminal is very promising, but does not support sixels yet. +Please upvote the feature request + +You can find a longer list of alternative terminals and terminal multiplexers through +related works such as + +## Usage + +First, use to test-suite to see what your terminal supports: + +For the colors: +1. start by 16-colors.sh +2. then try 256-colors.pl +3. see if you are lucky and can use a 24 bits palette with 24-bit-color.sh +4. if it works, try to switch the 16 colors palette between Solarized dark and light with toggle-solarized-dark.sh and toggle-solarized-light.sh + +In the final test, after displaying the 4 continous color lines without bands +or sudden breaks, your screen should switch to a black background then back to +a clear yellowish background. + +For the fonts: +1. start with `cat font-ansi-blocks.txt` to see block drawing characters and a test bear +2. run `font-vt100.sh` to see the basic box drawing characters +2. `cat font-ansi-box.txt` to see the full set of box drawing characters +3. `cat font-dec.txt` to check DEC VT220 extra characters +4. `cat font-test-all.txt` to check unicode support and overall support of the drawing characters + +In the final test, boxes should be properly aligned, and lines should intersect cleanly. + +If the font test fails, you may need to change your font as it may not support every character. + +Alternatively, when using mlterm you can tell it to extend your preferred font with one or more other fonts, for example: + ISO8859_1 = Iosevka SS04 18 + ISO8859_15 = Iosevka SS04 18 + ISO10646_UCS2_1 =Iosevka SS04 18 + ISO10646_UCS2_1_BOLD = Iosevka SS04 18 + U+2500-25ff=Segoe UI Symbol + U+25C6 = Tera Special # Diamond ◆ + U+2409 = Tera Special # Horizontal tab ␉ + U+240C = Tera Special # Form feed ␌ + U+240D = Tera Special # Carrier return ␍ + U+240A = Tera Special # Linefeed ␊ + U+2424 = Tera Special # Newline ␤ + U+240B = Tera Special # Vertical tab ␋ + U+23BA = Tera Special # Horizontal line 1 ⎺ + U+23BB = Tera Special # Horizontal line 3 ⎻ + U+2500 = Tera Special # Horizontal line 5 ─ + U+23BC = Tera Special # Horizontal line 7 ⎼ + U+23BD = Tera Special # Horizontal line 9 ⎽ + +When everything works to your satisfaction, install libevent2 and ncurses, then +compile with: `./configure && make && make -j8 install` + +Then: +1. install sixel-tmux.terminfo: `tic sixel-tmux.terminfo; tic -o ~/.terminfo sixel-tmux.terminfo` +2. start sixel-tmux: `sixel-tmux` +3. select sixel-tmux: `export TERM=sixel-tmux` +4. verify sixel-tmux is used: `tput smglr|base64` should return GzcbWz82OWgbWyVpJXAxJWQ7JXAyJWRzGzg= + +You can then run the test-suite to check if what was working in your terminal before sixel-tmux is still working. + +If it isn't, oops: it means sixel-tmux broke something. Patches are welcome! + +### Current status and tests failed + +Sixels fully work, but images are not redrawn when returning from another buffer (snake.six) + +Overlines are not working yet (ansi-vte52.sh) + +Ansi blocks are separated until you move to another buffer and back (font-ansi-blocks.txt) + +24-bit mode is not supported (24-bit-color.sh) + +dynamic switching of the 16 colors palette is not working (toggle-solarized-dark.sh/toggle-solarized-light.sh) + +vertical split does not plays well with ansi and sixel + +### Example inside mintty + +Here is what my terminal looks like while I am editing this file in VIM + +![mintty running tmux split window and displaying sixels inside](https://raw.githubusercontent.com/csdvrx/sixel-tmux/master/sixel-tmux.jpg) + +### Why a fork + +To support slow terminal with little bandwith, tmux started implementing +some controversial features after version 2.3. For example, when a lot of text +arrives, tmux start silently discarding the output until a redraw can be done. + +Bug reports + mention +serious issues, such as being unable to cat a long text file without some parts +being cut off, so this questionable feature for slow terminals impacts many usecases. + +Among other things, it breaks sixel support. Even when not using sixels, it +seems very wrong for tmux to be taking the initiative of silently discarding +parts of the outputs, without providing any command line flags to override that +"feature". + +As it can't be disabled, this anti-feature is not just a questionable default, +but a perverse feature: I believe breaking the integrity of the output of +whatever command was run, with no recourse, is not acceptable for a terminal multiplexer. + +Also, refusing to even provide a command-line toggle to opt-out is not +respectful of the end user freedom: no one should have to track patches to +allow basic functionality like properly displaying long text files! + +The maintainer explicitely said several times that even if a patch was provided +to support sixels, the functionality would never be added to tmux, and it would +have to be a fork + + +By becoming a permanent fork of tmux, sixel-tmux will do just what was asked. + +sixel-tmux is not just made for sixels but for all graphical things that can no +longer be properly displayed inside tmux, including long text files. + +Unfortunately, a permanent fork seems necessary as the problem seems to be more +general, and spreading: tmux intercept and improperly rewrites various escapes +codes, breaking other things + + +At this point, it is very unlikely that tmux will be fixed: all these +questionable choices start piling up, and make the correct display of what is +not just simple plain text inside tmux harder and harder. + +This is a sad situation as tmux was one of the best terminal multiplexer, and +supported what GNU screen couldn't when too many questionable choices had been +accumulated and frozen in over 32 years of spaghetti code. + +Therefore, all patches adding functionality to sixel-tmux are welcome- +including cleanups, and ports of tmux features. Even the features that may affect the +proper display of graphics and text are welcome, as long as they are off by +default since obscure usecases like limited bandwidth are now as rare as +serial ports. + +### History + +Hayaki Saito wrote a patch and released +a tmux-sixel fork in 2015. As tmux keep adding features, Chris Steinbach + tried to integrate them in +tmux-sixel. Patches were released by others to add to the version of tmux +officialy packaged by distributions to have tmux-sixel features, like Mehdi +Abaakouk + +Yatao Li had a branch that was the closet to upstream +tmux, but the most recent changes dated from 2017 and some sixel isues remained. + +When I tried them, none had full support for ANSI SGR parameters. In 2019, as +there was no recent activity on any of these, I forked the most recent branch, +fixed the remaining issues to have good sixel support at least in the current +buffer, added the missing SGR parameters I needed, and fixed issues brough by +the inclusion of unavoidable anti-features in mainline tmux. diff --git a/README.tmux b/README.tmux new file mode 100644 index 0000000000..073cd80693 --- /dev/null +++ b/README.tmux @@ -0,0 +1,83 @@ +Note on tmux-sixel + +This fork has been created to make it a little easier to keep the sixel support +patch provided by Hayaki Saito working (see https://github.com/saitoha). + +----------- + +Welcome to tmux! + +tmux is a "terminal multiplexer", it enables a number of terminals (or windows) +to be accessed and controlled from a single terminal. tmux is intended to be a +simple, modern, BSD-licensed alternative to programs such as GNU screen. + +This release runs on OpenBSD, FreeBSD, NetBSD, Linux, OS X and Solaris. + +tmux depends on libevent 2.x. Download it from: + + http://libevent.org + +It also depends on ncurses, available from: + + http://invisible-island.net/ncurses/ + +To build and install tmux from a release tarball, use: + + $ ./configure && make + $ sudo make install + +tmux can use the utempter library to update utmp(5), if it is installed - run +configure with --enable-utempter to enable this. + +To get and build the latest from version control: + + $ git clone https://github.com/tmux/tmux.git + $ cd tmux + $ sh autogen.sh + $ ./configure && make + +(Note that this requires at least a working C compiler, make, autoconf, +automake, pkg-config as well as libevent and ncurses libraries and headers.) + +For more information see http://git-scm.com. Patches should be sent by email to +the mailing list at tmux-users@googlegroups.com or submitted through GitHub at +https://github.com/tmux/tmux/issues. + +For documentation on using tmux, see the tmux.1 manpage. It can be viewed from +the source tree with: + + $ nroff -mdoc tmux.1|less + +A small example configuration in example_tmux.conf. + +A vim(1) syntax file is available at: + + https://github.com/ericpruitt/tmux.vim + https://raw.githubusercontent.com/ericpruitt/tmux.vim/master/vim/syntax/tmux.vim + +And a bash(1) completion file at: + + https://github.com/imomaliev/tmux-bash-completion + +For debugging, running tmux with -v or -vv will generate server and client log +files in the current directory. + +tmux mailing lists are available. For general discussion and bug reports: + + https://groups.google.com/forum/#!forum/tmux-users + +And for Git commit emails: + + https://groups.google.com/forum/#!forum/tmux-git + +Subscribe by sending an email to . + +Bug reports, feature suggestions and especially code contributions are most +welcome. Please send by email to: + + tmux-users@googlegroups.com + +This file and the CHANGES, FAQ, SYNCING and TODO files are licensed under the +ISC license. All other files have a license and copyright notice at their start. + +-- Nicholas Marriott diff --git a/attributes.c b/attributes.c index 1e45e584cf..1b8317332a 100644 --- a/attributes.c +++ b/attributes.c @@ -25,13 +25,13 @@ const char * attributes_tostring(int attr) { - static char buf[128]; + static char buf[512]; size_t len; if (attr == 0) return ("none"); - len = xsnprintf(buf, sizeof buf, "%s%s%s%s%s%s%s%s", + len = xsnprintf(buf, sizeof buf, "%s%s%s%s%s%s%s%s%s%s%s%s", (attr & GRID_ATTR_BRIGHT) ? "bright," : "", (attr & GRID_ATTR_DIM) ? "dim," : "", (attr & GRID_ATTR_UNDERSCORE) ? "underscore," : "", @@ -39,7 +39,11 @@ attributes_tostring(int attr) (attr & GRID_ATTR_REVERSE) ? "reverse," : "", (attr & GRID_ATTR_HIDDEN) ? "hidden," : "", (attr & GRID_ATTR_ITALICS) ? "italics," : "", - (attr & GRID_ATTR_STRIKETHROUGH) ? "strikethrough," : ""); + (attr & GRID_ATTR_STRIKETHROUGH) ? "strikethrough," : "", + (attr & GRID_ATTR_UNDERSCORE_2) ? "double-underscore," : "", + (attr & GRID_ATTR_UNDERSCORE_3) ? "curly-underscore," : "", + (attr & GRID_ATTR_UNDERSCORE_4) ? "dotted-underscore," : "", + (attr & GRID_ATTR_UNDERSCORE_5) ? "dashed-underscore," : ""); if (len > 0) buf[len - 1] = '\0'; @@ -52,6 +56,25 @@ attributes_fromstring(const char *str) const char delimiters[] = " ,|"; int attr; size_t end; + u_int i; + struct { + const char* name; + int attr; + } table[] = { + { "bright", GRID_ATTR_BRIGHT }, + { "bold", GRID_ATTR_BRIGHT }, + { "dim", GRID_ATTR_DIM }, + { "underscore", GRID_ATTR_UNDERSCORE }, + { "blink", GRID_ATTR_BLINK }, + { "reverse", GRID_ATTR_REVERSE }, + { "hidden", GRID_ATTR_HIDDEN }, + { "italics", GRID_ATTR_ITALICS }, + { "strikethrough", GRID_ATTR_STRIKETHROUGH }, + { "double-underscore", GRID_ATTR_UNDERSCORE_2 }, + { "curly-underscore", GRID_ATTR_UNDERSCORE_3 }, + { "dotted-underscore", GRID_ATTR_UNDERSCORE_4 }, + { "dashed-underscore", GRID_ATTR_UNDERSCORE_5 } + }; if (*str == '\0' || strcspn(str, delimiters) == 0) return (-1); @@ -64,24 +87,15 @@ attributes_fromstring(const char *str) attr = 0; do { end = strcspn(str, delimiters); - if ((end == 6 && strncasecmp(str, "bright", end) == 0) || - (end == 4 && strncasecmp(str, "bold", end) == 0)) - attr |= GRID_ATTR_BRIGHT; - else if (end == 3 && strncasecmp(str, "dim", end) == 0) - attr |= GRID_ATTR_DIM; - else if (end == 10 && strncasecmp(str, "underscore", end) == 0) - attr |= GRID_ATTR_UNDERSCORE; - else if (end == 5 && strncasecmp(str, "blink", end) == 0) - attr |= GRID_ATTR_BLINK; - else if (end == 7 && strncasecmp(str, "reverse", end) == 0) - attr |= GRID_ATTR_REVERSE; - else if (end == 6 && strncasecmp(str, "hidden", end) == 0) - attr |= GRID_ATTR_HIDDEN; - else if (end == 7 && strncasecmp(str, "italics", end) == 0) - attr |= GRID_ATTR_ITALICS; - else if (end == 13 && strncasecmp(str, "strikethrough", end) == 0) - attr |= GRID_ATTR_STRIKETHROUGH; - else + for (i = 0; i < nitems(table); i++) { + if (end != strlen(table[i].name)) + continue; + if (strncasecmp(str, table[i].name, end) == 0) { + attr |= table[i].attr; + break; + } + } + if (i == nitems(table)) return (-1); str += end + strspn(str + end, delimiters); } while (*str != '\0'); diff --git a/grid.c b/grid.c index aa9aea4c66..43cd96c8ee 100644 --- a/grid.c +++ b/grid.c @@ -753,7 +753,11 @@ grid_string_cells_code(const struct grid_cell *lastgc, { GRID_ATTR_BLINK, 5 }, { GRID_ATTR_REVERSE, 7 }, { GRID_ATTR_HIDDEN, 8 }, - { GRID_ATTR_STRIKETHROUGH, 9 } + { GRID_ATTR_STRIKETHROUGH, 9 }, + { GRID_ATTR_UNDERSCORE_2, 42 }, + { GRID_ATTR_UNDERSCORE_3, 43 }, + { GRID_ATTR_UNDERSCORE_4, 44 }, + { GRID_ATTR_UNDERSCORE_5, 45 }, }; n = 0; @@ -779,11 +783,15 @@ grid_string_cells_code(const struct grid_cell *lastgc, else strlcat(buf, "\033[", len); for (i = 0; i < n; i++) { - if (i + 1 < n) - xsnprintf(tmp, sizeof tmp, "%d;", s[i]); - else + if (s[i] < 10) xsnprintf(tmp, sizeof tmp, "%d", s[i]); + else { + xsnprintf(tmp, sizeof tmp, "%d:%d", s[i] / 10, + s[i] % 10); + } strlcat(buf, tmp, len); + if (i + 1 < n) + strlcat(buf, ";", len); } strlcat(buf, "m", len); } diff --git a/input.c b/input.c index 8b5cae6d8b..ba10435474 100644 --- a/input.c +++ b/input.c @@ -58,6 +58,19 @@ struct input_cell { int g1set; /* 1 if ACS */ }; +/* Input parser argument. */ +struct input_param { + enum { + INPUT_MISSING, + INPUT_NUMBER, + INPUT_STRING + } type; + union { + int num; + char *str; + }; +}; + /* Input parser context. */ struct input_ctx { struct window_pane *wp; @@ -81,7 +94,7 @@ struct input_ctx { size_t input_len; size_t input_space; - int param_list[24]; /* -1 not present */ + struct input_param param_list[24]; u_int param_list_len; struct utf8_data utf8data; @@ -526,7 +539,7 @@ static const struct input_transition input_state_csi_enter_table[] = { { 0x1c, 0x1f, input_c0_dispatch, NULL }, { 0x20, 0x2f, input_intermediate, &input_state_csi_intermediate }, { 0x30, 0x39, input_parameter, &input_state_csi_parameter }, - { 0x3a, 0x3a, NULL, &input_state_csi_ignore }, + { 0x3a, 0x3a, input_parameter, &input_state_csi_parameter }, { 0x3b, 0x3b, input_parameter, &input_state_csi_parameter }, { 0x3c, 0x3f, input_intermediate, &input_state_csi_parameter }, { 0x40, 0x7e, input_csi_dispatch, &input_state_ground }, @@ -544,7 +557,7 @@ static const struct input_transition input_state_csi_parameter_table[] = { { 0x1c, 0x1f, input_c0_dispatch, NULL }, { 0x20, 0x2f, input_intermediate, &input_state_csi_intermediate }, { 0x30, 0x39, input_parameter, NULL }, - { 0x3a, 0x3a, NULL, &input_state_csi_ignore }, + { 0x3a, 0x3a, input_parameter, NULL }, { 0x3b, 0x3b, input_parameter, NULL }, { 0x3c, 0x3f, NULL, &input_state_csi_ignore }, { 0x40, 0x7e, input_csi_dispatch, &input_state_ground }, @@ -780,7 +793,9 @@ input_timer_callback(__unused int fd, __unused short events, void *arg) static void input_start_timer(struct input_ctx *ictx) { - struct timeval tv = { .tv_usec = 100000 }; + // For sixel +// struct timeval tv = { .tv_usec = 100000 }; + struct timeval tv = { .tv_sec = 10, .tv_usec = 100000 }; event_del(&ictx->timer); event_add(&ictx->timer, &tv); @@ -822,6 +837,12 @@ void input_free(struct window_pane *wp) { struct input_ctx *ictx = wp->ictx; + u_int i; + + for (i = 0; i < ictx->param_list_len; i++) { + if (ictx->param_list[i].type == INPUT_STRING) + free(ictx->param_list[i].str); + } event_del(&ictx->timer); @@ -964,29 +985,51 @@ input_parse(struct window_pane *wp) static int input_split(struct input_ctx *ictx) { - const char *errstr; - char *ptr, *out; - int n; + const char *errstr; + char *ptr, *out; + struct input_param *ip; + u_int i; + for (i = 0; i < ictx->param_list_len; i++) { + if (ictx->param_list[i].type == INPUT_STRING) + free(ictx->param_list[i].str); + } ictx->param_list_len = 0; + if (ictx->param_len == 0) return (0); + ip = &ictx->param_list[0]; ptr = ictx->param_buf; while ((out = strsep(&ptr, ";")) != NULL) { if (*out == '\0') - n = -1; + ip->type = INPUT_MISSING; else { - n = strtonum(out, 0, INT_MAX, &errstr); - if (errstr != NULL) - return (-1); + if (strchr(out, ':') != NULL) { + ip->type = INPUT_STRING; + ip->str = xstrdup(out); + } else { + ip->type = INPUT_NUMBER; + ip->num = strtonum(out, 0, INT_MAX, &errstr); + if (errstr != NULL) + return (-1); + } } - - ictx->param_list[ictx->param_list_len++] = n; + ip = &ictx->param_list[++ictx->param_list_len]; if (ictx->param_list_len == nitems(ictx->param_list)) return (-1); } + for (i = 0; i < ictx->param_list_len; i++) { + ip = &ictx->param_list[i]; + if (ip->type == INPUT_MISSING) + log_debug("parameter %u: missing", i); + else if (ip->type == INPUT_STRING) + log_debug("parameter %u: string %s", i, ip->str); + else if (ip->type == INPUT_NUMBER) + log_debug("parameter %u: number %d", i, ip->num); + } + return (0); } @@ -994,14 +1037,17 @@ input_split(struct input_ctx *ictx) static int input_get(struct input_ctx *ictx, u_int validx, int minval, int defval) { - int retval; + struct input_param *ip; + int retval; if (validx >= ictx->param_list_len) return (defval); - - retval = ictx->param_list[validx]; - if (retval == -1) + ip = &ictx->param_list[validx]; + if (ip->type == INPUT_MISSING) return (defval); + if (ip->type == INPUT_STRING) + return (-1); + retval = ip->num; if (retval < minval) return (minval); return (retval); @@ -1264,7 +1310,7 @@ input_csi_dispatch(struct input_ctx *ictx) struct screen *s = sctx->s; struct input_table_entry *entry; int i, n, m; - u_int cx; + u_int cx, bg = ictx->cell.cell.bg; if (ictx->flags & INPUT_DISCARD) return (0); @@ -1289,6 +1335,8 @@ input_csi_dispatch(struct input_ctx *ictx) if (cx > screen_size_x(s) - 1) cx = screen_size_x(s) - 1; n = input_get(ictx, 0, 1, 1); + if (n == -1) + break; while (cx > 0 && n-- > 0) { do cx--; @@ -1297,35 +1345,52 @@ input_csi_dispatch(struct input_ctx *ictx) s->cx = cx; break; case INPUT_CSI_CUB: - screen_write_cursorleft(sctx, input_get(ictx, 0, 1, 1)); + n = input_get(ictx, 0, 1, 1); + if (n != -1) + screen_write_cursorleft(sctx, n); break; case INPUT_CSI_CUD: - screen_write_cursordown(sctx, input_get(ictx, 0, 1, 1)); + n = input_get(ictx, 0, 1, 1); + if (n != -1) + screen_write_cursordown(sctx, n); break; case INPUT_CSI_CUF: - screen_write_cursorright(sctx, input_get(ictx, 0, 1, 1)); + n = input_get(ictx, 0, 1, 1); + if (n != -1) + screen_write_cursorright(sctx, n); break; case INPUT_CSI_CUP: n = input_get(ictx, 0, 1, 1); m = input_get(ictx, 1, 1, 1); - screen_write_cursormove(sctx, m - 1, n - 1); + if (n != -1 && m != -1) + screen_write_cursormove(sctx, m - 1, n - 1); break; case INPUT_CSI_WINOPS: input_csi_dispatch_winops(ictx); break; case INPUT_CSI_CUU: - screen_write_cursorup(sctx, input_get(ictx, 0, 1, 1)); + n = input_get(ictx, 0, 1, 1); + if (n != -1) + screen_write_cursorup(sctx, n); break; case INPUT_CSI_CNL: - screen_write_carriagereturn(sctx); - screen_write_cursordown(sctx, input_get(ictx, 0, 1, 1)); + n = input_get(ictx, 0, 1, 1); + if (n != -1) { + screen_write_carriagereturn(sctx); + screen_write_cursordown(sctx, n); + } break; case INPUT_CSI_CPL: - screen_write_carriagereturn(sctx); - screen_write_cursorup(sctx, input_get(ictx, 0, 1, 1)); + n = input_get(ictx, 0, 1, 1); + if (n != -1) { + screen_write_carriagereturn(sctx); + screen_write_cursorup(sctx, n); + } break; case INPUT_CSI_DA: switch (input_get(ictx, 0, 0, 0)) { + case -1: + break; case 0: input_reply(ictx, "\033[?1;2c"); break; @@ -1336,6 +1401,8 @@ input_csi_dispatch(struct input_ctx *ictx) break; case INPUT_CSI_DA_TWO: switch (input_get(ictx, 0, 0, 0)) { + case -1: + break; case 0: input_reply(ictx, "\033[>84;0;0c"); break; @@ -1345,24 +1412,30 @@ input_csi_dispatch(struct input_ctx *ictx) } break; case INPUT_CSI_ECH: - screen_write_clearcharacter(sctx, input_get(ictx, 0, 1, 1), - ictx->cell.cell.bg); + n = input_get(ictx, 0, 1, 1); + if (n != -1) + screen_write_clearcharacter(sctx, n, bg); break; case INPUT_CSI_DCH: - screen_write_deletecharacter(sctx, input_get(ictx, 0, 1, 1), - ictx->cell.cell.bg); + n = input_get(ictx, 0, 1, 1); + if (n != -1) + screen_write_deletecharacter(sctx, n, bg); break; case INPUT_CSI_DECSTBM: n = input_get(ictx, 0, 1, 1); m = input_get(ictx, 1, 1, screen_size_y(s)); - screen_write_scrollregion(sctx, n - 1, m - 1); + if (n != -1 && m != -1) + screen_write_scrollregion(sctx, n - 1, m - 1); break; case INPUT_CSI_DL: - screen_write_deleteline(sctx, input_get(ictx, 0, 1, 1), - ictx->cell.cell.bg); + n = input_get(ictx, 0, 1, 1); + if (n != -1) + screen_write_deleteline(sctx, n, bg); break; case INPUT_CSI_DSR: switch (input_get(ictx, 0, 0, 0)) { + case -1: + break; case 5: input_reply(ictx, "\033[0n"); break; @@ -1376,24 +1449,24 @@ input_csi_dispatch(struct input_ctx *ictx) break; case INPUT_CSI_ED: switch (input_get(ictx, 0, 0, 0)) { + case -1: + break; case 0: - screen_write_clearendofscreen(sctx, ictx->cell.cell.bg); + screen_write_clearendofscreen(sctx, bg); break; case 1: - screen_write_clearstartofscreen(sctx, ictx->cell.cell.bg); + screen_write_clearstartofscreen(sctx, bg); break; case 2: - screen_write_clearscreen(sctx, ictx->cell.cell.bg); + screen_write_clearscreen(sctx, bg); break; case 3: - switch (input_get(ictx, 1, 0, 0)) { - case 0: + if (input_get(ictx, 1, 0, 0) == 0) { /* * Linux console extension to clear history * (for example before locking the screen). */ screen_write_clearhistory(sctx); - break; } break; default: @@ -1403,14 +1476,16 @@ input_csi_dispatch(struct input_ctx *ictx) break; case INPUT_CSI_EL: switch (input_get(ictx, 0, 0, 0)) { + case -1: + break; case 0: - screen_write_clearendofline(sctx, ictx->cell.cell.bg); + screen_write_clearendofline(sctx, bg); break; case 1: - screen_write_clearstartofline(sctx, ictx->cell.cell.bg); + screen_write_clearstartofline(sctx, bg); break; case 2: - screen_write_clearline(sctx, ictx->cell.cell.bg); + screen_write_clearline(sctx, bg); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); @@ -1419,22 +1494,28 @@ input_csi_dispatch(struct input_ctx *ictx) break; case INPUT_CSI_HPA: n = input_get(ictx, 0, 1, 1); - screen_write_cursormove(sctx, n - 1, s->cy); + if (n != -1) + screen_write_cursormove(sctx, n - 1, s->cy); break; case INPUT_CSI_ICH: - screen_write_insertcharacter(sctx, input_get(ictx, 0, 1, 1), - ictx->cell.cell.bg); + n = input_get(ictx, 0, 1, 1); + if (n != -1) + screen_write_insertcharacter(sctx, n, bg); break; case INPUT_CSI_IL: - screen_write_insertline(sctx, input_get(ictx, 0, 1, 1), - ictx->cell.cell.bg); + n = input_get(ictx, 0, 1, 1); + if (n != -1) + screen_write_insertline(sctx, n, bg); break; case INPUT_CSI_REP: + n = input_get(ictx, 0, 1, 1); + if (n == -1) + break; + if (ictx->last == -1) break; ictx->ch = ictx->last; - n = input_get(ictx, 0, 1, 1); for (i = 0; i < n; i++) input_print(ictx); break; @@ -1463,11 +1544,14 @@ input_csi_dispatch(struct input_ctx *ictx) input_csi_dispatch_sm_private(ictx); break; case INPUT_CSI_SU: - screen_write_scrollup(sctx, input_get(ictx, 0, 1, 1), - ictx->cell.cell.bg); + n = input_get(ictx, 0, 1, 1); + if (n != -1) + screen_write_scrollup(sctx, n, bg); break; case INPUT_CSI_TBC: switch (input_get(ictx, 0, 0, 0)) { + case -1: + break; case 0: if (s->cx < screen_size_x(s)) bit_clear(s->tabs, s->cx); @@ -1482,11 +1566,13 @@ input_csi_dispatch(struct input_ctx *ictx) break; case INPUT_CSI_VPA: n = input_get(ictx, 0, 1, 1); - screen_write_cursormove(sctx, s->cx, n - 1); + if (n != -1) + screen_write_cursormove(sctx, s->cx, n - 1); break; case INPUT_CSI_DECSCUSR: n = input_get(ictx, 0, 0, 0); - screen_set_cursor_style(s, n); + if (n != -1) + screen_set_cursor_style(s, n); break; } @@ -1502,6 +1588,8 @@ input_csi_dispatch_rm(struct input_ctx *ictx) for (i = 0; i < ictx->param_list_len; i++) { switch (input_get(ictx, i, 0, -1)) { + case -1: + break; case 4: /* IRM */ screen_write_mode_clear(&ictx->ctx, MODE_INSERT); break; @@ -1524,6 +1612,8 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx) for (i = 0; i < ictx->param_list_len; i++) { switch (input_get(ictx, i, 0, -1)) { + case -1: + break; case 1: /* DECCKM */ screen_write_mode_clear(&ictx->ctx, MODE_KCURSOR); break; @@ -1581,6 +1671,8 @@ input_csi_dispatch_sm(struct input_ctx *ictx) for (i = 0; i < ictx->param_list_len; i++) { switch (input_get(ictx, i, 0, -1)) { + case -1: + break; case 4: /* IRM */ screen_write_mode_set(&ictx->ctx, MODE_INSERT); break; @@ -1603,6 +1695,8 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) for (i = 0; i < ictx->param_list_len; i++) { switch (input_get(ictx, i, 0, -1)) { + case -1: + break; case 1: /* DECCKM */ screen_write_mode_set(&ictx->ctx, MODE_KCURSOR); break; @@ -1710,16 +1804,13 @@ input_csi_dispatch_winops(struct input_ctx *ictx) } } -/* Handle CSI SGR for 256 colours. */ -static void -input_csi_dispatch_sgr_256(struct input_ctx *ictx, int fgbg, u_int *i) +/* Helper for 256 colour SGR. */ +static int +input_csi_dispatch_sgr_256_do(struct input_ctx *ictx, int fgbg, int c) { struct grid_cell *gc = &ictx->cell.cell; - int c; - (*i)++; - c = input_get(ictx, *i, 0, -1); - if (c == -1) { + if (c == -1 || c > 255) { if (fgbg == 38) gc->fg = 8; else if (fgbg == 48) @@ -1730,32 +1821,125 @@ input_csi_dispatch_sgr_256(struct input_ctx *ictx, int fgbg, u_int *i) else if (fgbg == 48) gc->bg = c | COLOUR_FLAG_256; } + return (1); } -/* Handle CSI SGR for RGB colours. */ +/* Handle CSI SGR for 256 colours. */ static void -input_csi_dispatch_sgr_rgb(struct input_ctx *ictx, int fgbg, u_int *i) +input_csi_dispatch_sgr_256(struct input_ctx *ictx, int fgbg, u_int *i) +{ + int c; + + c = input_get(ictx, (*i) + 1, 0, -1); + if (input_csi_dispatch_sgr_256_do(ictx, fgbg, c)) + (*i)++; +} + +/* Helper for RGB colour SGR. */ +static int +input_csi_dispatch_sgr_rgb_do(struct input_ctx *ictx, int fgbg, int r, int g, + int b) { struct grid_cell *gc = &ictx->cell.cell; - int r, g, b; - (*i)++; - r = input_get(ictx, *i, 0, -1); if (r == -1 || r > 255) - return; - (*i)++; - g = input_get(ictx, *i, 0, -1); + return (0); if (g == -1 || g > 255) - return; - (*i)++; - b = input_get(ictx, *i, 0, -1); + return (0); if (b == -1 || b > 255) - return; + return (0); if (fgbg == 38) gc->fg = colour_join_rgb(r, g, b); else if (fgbg == 48) gc->bg = colour_join_rgb(r, g, b); + return (1); +} + +/* Handle CSI SGR for RGB colours. */ +static void +input_csi_dispatch_sgr_rgb(struct input_ctx *ictx, int fgbg, u_int *i) +{ + int r, g, b; + + r = input_get(ictx, (*i) + 1, 0, -1); + g = input_get(ictx, (*i) + 2, 0, -1); + b = input_get(ictx, (*i) + 3, 0, -1); + if (input_csi_dispatch_sgr_rgb_do(ictx, fgbg, r, g, b)) + (*i) += 3; +} + +/* Handle CSI SGR with a ISO parameter. */ +static void +input_csi_dispatch_sgr_colon(struct input_ctx *ictx, u_int i) +{ + struct grid_cell *gc = &ictx->cell.cell; + char *s = ictx->param_list[i].str, *copy, *ptr, *out; + int p[8]; + u_int n; + const char *errstr; + + for (n = 0; n < nitems(p); n++) + p[n] = -1; + n = 0; + + ptr = copy = xstrdup(s); + while ((out = strsep(&ptr, ":")) != NULL) { + p[n++] = strtonum(out, 0, INT_MAX, &errstr); + if (errstr != NULL || n == nitems(p)) { + free(copy); + return; + } + log_debug("%s: %u = %d", __func__, n - 1, p[n - 1]); + } + free(copy); + + if (n == 0) + return; + if (p[0] == 4) { + if (n != 2) + return; + switch (p[1]) { + case 0: + gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; + break; + case 1: + gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; + gc->attr |= GRID_ATTR_UNDERSCORE; + break; + case 2: + gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; + gc->attr |= GRID_ATTR_UNDERSCORE_2; + break; + case 3: + gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; + gc->attr |= GRID_ATTR_UNDERSCORE_3; + break; + case 4: + gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; + gc->attr |= GRID_ATTR_UNDERSCORE_4; + break; + case 5: + gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; + gc->attr |= GRID_ATTR_UNDERSCORE_5; + break; + } + return; + } + if (p[0] != 38 && p[0] != 48) + return; + switch (p[1]) { + case 2: + if (n != 5) + break; + input_csi_dispatch_sgr_rgb_do(ictx, p[0], p[2], p[3], p[4]); + break; + case 5: + if (n != 3) + break; + input_csi_dispatch_sgr_256_do(ictx, p[0], p[2]); + break; + } } /* Handle CSI SGR. */ @@ -1772,7 +1956,13 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) } for (i = 0; i < ictx->param_list_len; i++) { + if (ictx->param_list[i].type == INPUT_STRING) { + input_csi_dispatch_sgr_colon(ictx, i); + continue; + } n = input_get(ictx, i, 0, 0); + if (n == -1) + continue; if (n == 38 || n == 48) { i++; @@ -1802,6 +1992,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) gc->attr |= GRID_ATTR_ITALICS; break; case 4: + gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; gc->attr |= GRID_ATTR_UNDERSCORE; break; case 5: @@ -1823,7 +2014,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) gc->attr &= ~GRID_ATTR_ITALICS; break; case 24: - gc->attr &= ~GRID_ATTR_UNDERSCORE; + gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; break; case 25: gc->attr &= ~GRID_ATTR_BLINK; diff --git a/screen-write.c b/screen-write.c index b7eeb230fe..34b5d86679 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1027,6 +1027,10 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg) ctx->scrolled++; } else if (s->cy < screen_size_y(s) - 1) s->cy++; + + // for sixel support + // https://github.com/tmux/tmux/issues/1019 + screen_write_collect_flush(ctx, 0); } /* Scroll up. */ diff --git a/sixel-tmux-night.jpg b/sixel-tmux-night.jpg new file mode 100644 index 0000000000..faaa33b903 Binary files /dev/null and b/sixel-tmux-night.jpg differ diff --git a/sixel-tmux.exe b/sixel-tmux.exe new file mode 100644 index 0000000000..ab109efc76 Binary files /dev/null and b/sixel-tmux.exe differ diff --git a/sixel-tmux.jpg b/sixel-tmux.jpg new file mode 100644 index 0000000000..828e88334b Binary files /dev/null and b/sixel-tmux.jpg differ diff --git a/sixel-tmux.terminfo b/sixel-tmux.terminfo new file mode 100644 index 0000000000..d223013f48 --- /dev/null +++ b/sixel-tmux.terminfo @@ -0,0 +1,48 @@ +# A tmux 256color based TERMINFO that adds the escape sequences for italic, +# and sixel, that can be aliased as xterm-256color for compat purposes +# Install: tic sixel-tmux.terminfo; tic -o ~/.terminfo sixel-tmux.terminfo +# Usage: export TERM=sixel-tmux +# Test: tput smglr|base64 +# Assert: GzcbWz82OWgbWyVpJXAxJWQ7JXAyJWRzGzg= +# +# am, km, mir, msgr, xenl, +# am, bce, hs, km, mir, msgr, npc, xenl, +# ich=\E[%p1%d@, il=\E[%p1%dL, il1=\E[L, ind=^J, is2=\E)0, +# sgr=\E[0%?%p6%t;1%;%?%p1%t;7%;%?%p2%t;4%;%?%p3%t;7%;%?%p4%t;5%;%?%p5%t;2%;m%?%p9%t\016%e\017%;, +# sgr=\E[0%?%p6%t;1%;%?%p1%t;3%;%?%p2%t;4%;%?%p3%t;7%;%?%p4%t;5%;%?%p5%t;2%;m%?%p9%t\016%e\017%;, +# rev=\E[7m, ri=\EM, ritm=\E[23m, rmacs=^O, rmcup=\E[?1049l, +# sgr0=\E[m\017, sitm=\E[3m, smacs=^N, smcup=\E[?1049h, +# smir=\E[4h, smkx=\E[?1h\E=, smso=\E[3m, +# rs2=\Ec\E[?1000l\E[?25h, sc=\E7, +# sgr=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p5%t;2%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m, + +sixel-tmux|standalone sixel-tmux with 256colors, BCE, italics and sixel support, + am, bce, ccc, km, mir, msgr, xenl, + colors#0x100, cols#80, it#8, lines#24, pairs#0x010000, + bel=^G, blink=\E[5m, bold=\E[1m, cbt=\E[Z, civis=\E[?25l, + clear=\E[H\E[J, cnorm=\E[34h\E[?25h, cr=\r, + csr=\E[%i%p1%d;%p2%dr, cub=\E[%p1%dD, cub1=^H, + cud=\E[%p1%dB, cud1=\n, cuf=\E[%p1%dC, cuf1=\E[C, + cup=\E[%i%p1%d;%p2%dH, cuu=\E[%p1%dA, cuu1=\EM, + cvvis=\E[34l, dch=\E[%p1%dP, dch1=\E[P, dim=\E[2m, + dl=\E[%p1%dM, dl1=\E[M, ed=\E[J, el=\E[K, el1=\E[1K, + enacs=\E(B\E)0, flash=\Eg, hpa=\E[%i%p1%dG, home=\E[H, ht=^I, + hts=\EH, ich=\E[%p1%d@, il=\E[%p1%dL, il1=\E[L, ind=\n, indn=\E[%p1%dS, + initc=\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\, + invis=\E[8m, is2=\E)0, kbs=^H, kcbt=\E[Z, kcub1=\EOD, kcud1=\EOB, kcuf1=\EOC, + kcuu1=\EOA, kdch1=\E[3~, kend=\E[4~, kf1=\EOP, kf10=\E[21~, + kf11=\E[23~, kf12=\E[24~, kf2=\EOQ, kf3=\EOR, kf4=\EOS, + kf5=\E[15~, kf6=\E[17~, kf7=\E[18~, kf8=\E[19~, kf9=\E[20~, + khome=\E[1~, kich1=\E[2~, kmous=\E[M, knp=\E[6~, kpp=\E[5~, + nel=\EE, oc=\E]104\007, op=\E[39;49m, rc=\E8, rev=\E[7m, ri=\EM, + ritm=\E[23m, rmacs=^O, rmcup=\E[?1049l, rmir=\E[4l, rmkx=\E[?1l\E>, + rmso=\E[23m\E[m\017, rmul=\E[24m, rs2=\Ec\E[?1000l\E[?25h, sc=\E7, + 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, + sgr=\E[0%?%p6%t;1%;%?%p1%t;7%;%?%p2%t;4%;%?%p3%t;7%;%?%p4%t;5%;%?%p5%t;2%;m%?%p9%t\016%e\017%;, + sgr0=\E[m\017, sitm=\E[3m, smacs=^N, smcup=\E[?1049h, + smglr=\E7\E[?69h\E[%i%p1%d;%p2%ds\E8, mgc=\E7\E[?69l\E8, + smir=\E[4h, smkx=\E[?1h\E=, smso=\E[3m\E[7m, smul=\E[4m, + tbc=\E[3g, vpa=\E[%i%p1%dd, + +# vi:ft=terminfo: diff --git a/tmux.1 b/tmux.1 index 49a5350a68..b111c79803 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2712,8 +2712,12 @@ or a comma-delimited list of one or more of: .Ic reverse , .Ic hidden , .Ic italics , +.Ic strikethrough , +.Ic double-underscore +.Ic curly-underscore +.Ic dotted-underscore or -.Ic strikethrough +.Ic dashed-underscore to turn an attribute on, or an attribute prefixed with .Ql no to turn one off. @@ -4306,6 +4310,11 @@ to change the cursor colour from inside .Bd -literal -offset indent $ printf '\e033]12;red\e033\e\e' .Ed +.It Em \&Smulx +Set a styled underline. +The single parameter is one of: 0 for no underline, 1 for normal +underline, 2 for double underline, 3 for curly underline, 4 for dotted +underline and 5 for dashed underline. .It Em \&Ss , Se Set or reset the cursor style. If set, a sequence such as this may be used diff --git a/tmux.c b/tmux.c index 78cb499bb3..31155d871d 100644 --- a/tmux.c +++ b/tmux.c @@ -131,8 +131,12 @@ make_label(const char *label) errno = ENOTDIR; goto fail; } +#ifdef __MSYS__ + if (sb.st_uid != uid) { +#else /* __MSYS__ */ if (sb.st_uid != uid || (sb.st_mode & S_IRWXO) != 0) { - errno = EACCES; +#endif /* __MSYS__ */ + errno = EACCES; goto fail; } diff --git a/tmux.h b/tmux.h index f6484759a5..31f7e10918 100644 --- a/tmux.h +++ b/tmux.h @@ -418,6 +418,7 @@ enum tty_code_code { TTYC_SMGLR, /* set_lr_margin, ML */ TTYC_SMKX, /* keypad_xmit, ks */ TTYC_SMSO, /* enter_standout_mode, so */ + TTYC_SMULX, /* extended underline styles */ TTYC_SMUL, /* enter_underline_mode, us */ TTYC_SS, /* set cursor style, Ss */ TTYC_TSL, /* to_status_line, tsl */ @@ -546,6 +547,18 @@ enum utf8_state { #define GRID_ATTR_ITALICS 0x40 #define GRID_ATTR_CHARSET 0x80 /* alternative character set */ #define GRID_ATTR_STRIKETHROUGH 0x100 +#define GRID_ATTR_UNDERSCORE_2 0x200 +#define GRID_ATTR_UNDERSCORE_3 0x400 +#define GRID_ATTR_UNDERSCORE_4 0x800 +#define GRID_ATTR_UNDERSCORE_5 0x1000 + +/* All underscore attributes. */ +#define GRID_ATTR_ALL_UNDERSCORE \ + (GRID_ATTR_UNDERSCORE| \ + GRID_ATTR_UNDERSCORE_2| \ + GRID_ATTR_UNDERSCORE_3| \ + GRID_ATTR_UNDERSCORE_4| \ + GRID_ATTR_UNDERSCORE_5) /* Grid flags. */ #define GRID_FLAG_FG256 0x1 diff --git a/tty-term.c b/tty-term.c index 06307c56a9..82eb22dc95 100644 --- a/tty-term.c +++ b/tty-term.c @@ -257,6 +257,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_SMGLR] = { TTYCODE_STRING, "smglr" }, [TTYC_SMKX] = { TTYCODE_STRING, "smkx" }, [TTYC_SMSO] = { TTYCODE_STRING, "smso" }, + [TTYC_SMULX] = { TTYCODE_STRING, "Smulx" }, [TTYC_SMUL] = { TTYCODE_STRING, "smul" }, [TTYC_SMXX] = { TTYCODE_STRING, "smxx" }, [TTYC_SS] = { TTYCODE_STRING, "Ss" }, @@ -303,25 +304,53 @@ tty_term_strip(const char *s) return (xstrdup(buf)); } +static char * +tty_term_override_next(const char *s, size_t *offset) +{ + static char value[BUFSIZ]; + size_t n = 0, at = *offset; + + if (s[at] == '\0') + return (NULL); + + while (s[at] != '\0') { + if (s[at] == ':') { + if (s[at + 1] == ':') { + value[n++] = ':'; + at += 2; + } else + break; + } else { + value[n++] = s[at]; + at++; + } + if (n == (sizeof value) - 1) + return (NULL); + } + if (s[at] != '\0') + *offset = at + 1; + else + *offset = at; + value[n] = '\0'; + return (value); +} + static void tty_term_override(struct tty_term *term, const char *override) { const struct tty_term_code_entry *ent; struct tty_code *code; - char *next, *s, *copy, *cp, *value; + size_t offset = 0; + char *cp, *value, *s; const char *errstr; u_int i; int n, remove; - copy = next = xstrdup(override); - - s = strsep(&next, ":"); - if (s == NULL || next == NULL || fnmatch(s, term->name, 0) != 0) { - free(copy); + s = tty_term_override_next(override, &offset); + if (s == NULL || fnmatch(s, term->name, 0) != 0) return; - } - while ((s = strsep(&next, ":")) != NULL) { + while ((s = tty_term_override_next(override, &offset)) != NULL) { if (*s == '\0') continue; value = NULL; @@ -342,6 +371,8 @@ tty_term_override(struct tty_term *term, const char *override) if (remove) log_debug("%s override: %s@", term->name, s); + else if (*value == '\0') + log_debug("%s override: %s", term->name, s); else log_debug("%s override: %s=%s", term->name, s, value); @@ -380,7 +411,6 @@ tty_term_override(struct tty_term *term, const char *override) free(value); } - free(s); } struct tty_term * diff --git a/tty.c b/tty.c index 3c7ec1b32d..3265ca307e 100644 --- a/tty.c +++ b/tty.c @@ -195,6 +195,27 @@ tty_timer_callback(__unused int fd, __unused short events, void *data) evtimer_add(&tty->timer, &tv); } +// Do not redraw a client unless we realistically think it can accept the data +// This defers redraws until the client has nothing else waiting to write. +// https://github.com/tmux/tmux/commit/fa6deb58664739a8073f188a4e3a88a122270537 +// +// Officially: +// Handle slow terminals and fast output better: when the amount of data +// outstanding gets too large, discard output until it is drained and we are +// able to do a full redraw. Prevents tmux sitting on a huge buffer that the +// terminal will take forever to consume. +// +// However: +// This is a "feature" for slow terminals that messes up other cases, +// including sixel support: tmux is basically taking initiative by dropping +// output thus breaking the integrity of the output of whatever command was run +// inside of tmux. +// +// For proper sixel support, it must be disabled. +// +// https://github.com/tmux/tmux/issues/1019#issuecomment-318284445 +// https://github.com/tmux/tmux/issues/1502#issuecomment-429710887 + static int tty_block_maybe(struct tty *tty) { @@ -202,11 +223,19 @@ tty_block_maybe(struct tty *tty) size_t size = EVBUFFER_LENGTH(tty->out); struct timeval tv = { .tv_usec = TTY_BLOCK_INTERVAL }; +// force 0 to support sixels + return (0); +// eventually, could try to make it so that the blocking mechanism is disabled +// until the content of the DCS passthrough escape has been flushed to the +// terminal + + if (size < TTY_BLOCK_START(tty)) return (0); if (tty->flags & TTY_BLOCK) return (1); + tty->flags |= TTY_BLOCK; log_debug("%s: can't keep up, %zu discarded", c->name, size); @@ -1846,8 +1875,19 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc, tty_putcode(tty, TTYC_DIM); if (changed & GRID_ATTR_ITALICS) tty_set_italics(tty); - if (changed & GRID_ATTR_UNDERSCORE) - tty_putcode(tty, TTYC_SMUL); + if (changed & GRID_ATTR_ALL_UNDERSCORE) { + if ((changed & GRID_ATTR_UNDERSCORE) || + !tty_term_has(tty->term, TTYC_SMULX)) + tty_putcode(tty, TTYC_SMUL); + else if (changed & GRID_ATTR_UNDERSCORE_2) + tty_putcode1(tty, TTYC_SMULX, 2); + else if (changed & GRID_ATTR_UNDERSCORE_3) + tty_putcode1(tty, TTYC_SMULX, 3); + else if (changed & GRID_ATTR_UNDERSCORE_4) + tty_putcode1(tty, TTYC_SMULX, 4); + else if (changed & GRID_ATTR_UNDERSCORE_5) + tty_putcode1(tty, TTYC_SMULX, 5); + } if (changed & GRID_ATTR_BLINK) tty_putcode(tty, TTYC_BLINK); if (changed & GRID_ATTR_REVERSE) {