From bcf5ee328e228d5a536b4de2069a79234f9f3e9e Mon Sep 17 00:00:00 2001 From: bfredl Date: Tue, 23 Aug 2022 10:36:46 +0200 Subject: refactor(arena): use a shared block freelist This is both simpler in client code and more effective (always reuse block hottest in cache) --- src/nvim/tui/input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 61a59bcf06..6afe7defe3 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -238,7 +238,7 @@ static void tinput_wait_enqueue(void **argv) ArenaMem res_mem = NULL; Object result = rpc_send_call(ui_client_channel_id, "nvim_input", args, &res_mem, &err); consumed = result.type == kObjectTypeInteger ? (size_t)result.data.integer : 0; - arena_mem_free(res_mem, NULL); + arena_mem_free(res_mem); } else { consumed = input_enqueue(keys); } -- cgit From 7784dc9e0d90284b0d9c9cf0833c27d4de22b7f4 Mon Sep 17 00:00:00 2001 From: bfredl Date: Tue, 23 Aug 2022 13:52:09 +0200 Subject: refactor(api): provide a temporary copy solution for nvim_call_atomic Make the copy_object() family accept an optional arena. More than half of the callsites should be refactored to use an arena later anyway. --- src/nvim/tui/input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 6afe7defe3..03cfb830e6 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -233,7 +233,7 @@ static void tinput_wait_enqueue(void **argv) if (ui_client_channel_id) { Array args = ARRAY_DICT_INIT; Error err = ERROR_INIT; - ADD(args, STRING_OBJ(copy_string(keys))); + ADD(args, STRING_OBJ(copy_string(keys, NULL))); // TODO(bfredl): could be non-blocking now with paste? ArenaMem res_mem = NULL; Object result = rpc_send_call(ui_client_channel_id, "nvim_input", args, &res_mem, &err); -- cgit From 568737d5b39a4b58cab05d4edc2599653979770c Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 30 Aug 2022 10:55:00 +0800 Subject: feat(tui): recognize sidescroll events (#19992) Ref https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Other-buttons This works in xterm and kitty. CSI < 66 ; x ; y M sequence is for ScrollWheelLeft. CSI < 67 ; x ; y M sequence is for ScrollWheelRight. --- src/nvim/tui/input.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 03cfb830e6..d269878f46 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -398,6 +398,14 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key) button = last_pressed_button; } + if (ev == TERMKEY_MOUSE_UNKNOWN && !(key->code.mouse[0] & 0x20)) { + int code = key->code.mouse[0] & ~0x3c; + if (code == 66 || code == 67) { + ev = TERMKEY_MOUSE_PRESS; + button = code - 60; + } + } + if (button == 0 || (ev != TERMKEY_MOUSE_PRESS && ev != TERMKEY_MOUSE_DRAG && ev != TERMKEY_MOUSE_RELEASE)) { return; @@ -431,8 +439,11 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key) if (button == 4) { len += (size_t)snprintf(buf + len, sizeof(buf) - len, "ScrollWheelUp"); } else if (button == 5) { - len += (size_t)snprintf(buf + len, sizeof(buf) - len, - "ScrollWheelDown"); + len += (size_t)snprintf(buf + len, sizeof(buf) - len, "ScrollWheelDown"); + } else if (button == 6) { + len += (size_t)snprintf(buf + len, sizeof(buf) - len, "ScrollWheelLeft"); + } else if (button == 7) { + len += (size_t)snprintf(buf + len, sizeof(buf) - len, "ScrollWheelRight"); } else { len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Mouse"); last_pressed_button = button; -- cgit From fb1edb2f5728d74ae811c6ab32395598cea5609b Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Fri, 26 Aug 2022 23:11:25 +0200 Subject: refactor: replace char_u with char Work on https://github.com/neovim/neovim/issues/459 --- src/nvim/tui/terminfo.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/terminfo.c b/src/nvim/tui/terminfo.c index ce48059b94..229e340dc3 100644 --- a/src/nvim/tui/terminfo.c +++ b/src/nvim/tui/terminfo.c @@ -189,7 +189,7 @@ void terminfo_info_msg(const unibi_term *const ut) msg_printf_attr(0, " %-25s %-10s = ", unibi_name_str(i), unibi_short_name_str(i)); // Most of these strings will contain escape sequences. - msg_outtrans_special((char_u *)s, false, 0); + msg_outtrans_special(s, false, 0); msg_putchar('\n'); } } @@ -216,7 +216,7 @@ void terminfo_info_msg(const unibi_term *const ut) msg_puts("Extended string capabilities:\n"); for (size_t i = 0; i < unibi_count_ext_str(ut); i++) { msg_printf_attr(0, " %-25s = ", unibi_get_ext_str_name(ut, i)); - msg_outtrans_special((char_u *)unibi_get_ext_str(ut, i), false, 0); + msg_outtrans_special(unibi_get_ext_str(ut, i), false, 0); msg_putchar('\n'); } } -- cgit From 04bd700ac3bc2bdea0e0d8747de95dab2034aa11 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 24 Jul 2022 11:26:54 +0800 Subject: feat(tui): support 'mousemoveevent' --- src/nvim/tui/input.c | 7 ++++--- src/nvim/tui/tui.c | 27 +++++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 5 deletions(-) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index d269878f46..728520c20c 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -406,8 +406,8 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key) } } - if (button == 0 || (ev != TERMKEY_MOUSE_PRESS && ev != TERMKEY_MOUSE_DRAG - && ev != TERMKEY_MOUSE_RELEASE)) { + if ((button == 0 && ev != TERMKEY_MOUSE_RELEASE) + || (ev != TERMKEY_MOUSE_PRESS && ev != TERMKEY_MOUSE_DRAG && ev != TERMKEY_MOUSE_RELEASE)) { return; } @@ -453,7 +453,8 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key) len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Drag"); break; case TERMKEY_MOUSE_RELEASE: - len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Release"); + len += (size_t)snprintf(buf + len, sizeof(buf) - len, button ? "Release" : "MouseMove"); + last_pressed_button = 0; break; case TERMKEY_MOUSE_UNKNOWN: abort(); diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 38e8c15762..471a3fb859 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -103,6 +103,7 @@ struct TUIData { bool immediate_wrap_after_last_column; bool bce; bool mouse_enabled; + bool mouse_move_enabled; bool busy, is_invisible, want_invisible; bool cork, overflow; bool cursor_color_changed; @@ -117,6 +118,7 @@ struct TUIData { ModeShape showing_mode; struct { int enable_mouse, disable_mouse; + int enable_mouse_move, disable_mouse_move; int enable_bracketed_paste, disable_bracketed_paste; int enable_lr_margin, disable_lr_margin; int enter_strikethrough_mode; @@ -236,6 +238,8 @@ static void terminfo_start(UI *ui) data->showing_mode = SHAPE_IDX_N; data->unibi_ext.enable_mouse = -1; data->unibi_ext.disable_mouse = -1; + data->unibi_ext.enable_mouse_move = -1; + data->unibi_ext.disable_mouse_move = -1; data->unibi_ext.set_cursor_color = -1; data->unibi_ext.reset_cursor_color = -1; data->unibi_ext.enable_bracketed_paste = -1; @@ -1138,6 +1142,9 @@ static void tui_mouse_on(UI *ui) TUIData *data = ui->data; if (!data->mouse_enabled) { unibi_out_ext(ui, data->unibi_ext.enable_mouse); + if (data->mouse_move_enabled) { + unibi_out_ext(ui, data->unibi_ext.enable_mouse_move); + } data->mouse_enabled = true; } } @@ -1146,6 +1153,9 @@ static void tui_mouse_off(UI *ui) { TUIData *data = ui->data; if (data->mouse_enabled) { + if (data->mouse_move_enabled) { + unibi_out_ext(ui, data->unibi_ext.disable_mouse_move); + } unibi_out_ext(ui, data->unibi_ext.disable_mouse); data->mouse_enabled = false; } @@ -1457,9 +1467,18 @@ static void tui_screenshot(UI *ui, String path) static void tui_option_set(UI *ui, String name, Object value) { TUIData *data = ui->data; - if (strequal(name.data, "termguicolors")) { + if (strequal(name.data, "mousemoveevent")) { + if (data->mouse_move_enabled != value.data.boolean) { + if (data->mouse_enabled) { + tui_mouse_off(ui); + data->mouse_move_enabled = value.data.boolean; + tui_mouse_on(ui); + } else { + data->mouse_move_enabled = value.data.boolean; + } + } + } else if (strequal(name.data, "termguicolors")) { ui->rgb = value.data.boolean; - data->print_attr_id = -1; invalidate(ui, 0, data->grid.height, 0, data->grid.width); } else if (strequal(name.data, "ttimeout")) { @@ -2135,6 +2154,10 @@ static void augment_terminfo(TUIData *data, const char *term, long vte_version, "\x1b[?1002h\x1b[?1006h"); data->unibi_ext.disable_mouse = (int)unibi_add_ext_str(ut, "ext.disable_mouse", "\x1b[?1002l\x1b[?1006l"); + data->unibi_ext.enable_mouse_move = (int)unibi_add_ext_str(ut, "ext.enable_mouse_move", + "\x1b[?1003h"); + data->unibi_ext.disable_mouse_move = (int)unibi_add_ext_str(ut, "ext.disable_mouse_move", + "\x1b[?1003l"); // Extended underline. // terminfo will have Smulx for this (but no support for colors yet). -- cgit From a0642ec75e5986af03829ad4f448e300fa40ebb4 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 16 Sep 2022 22:40:04 +0800 Subject: feat(tui): support undercurl when Konsole version >= 221170 (#20219) Support was added in https://invent.kde.org/utilities/konsole/-/commit/76f879cd70fb494ab2334d2660b34679546f3d9d --- src/nvim/tui/tui.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 471a3fb859..73f47f2b71 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -2164,7 +2164,7 @@ static void augment_terminfo(TUIData *data, const char *term, long vte_version, data->unibi_ext.set_underline_style = unibi_find_ext_str(ut, "Smulx"); if (data->unibi_ext.set_underline_style == -1) { int ext_bool_Su = unibi_find_ext_bool(ut, "Su"); // used by kitty - if (vte_version >= 5102 + if (vte_version >= 5102 || konsolev >= 221170 || (ext_bool_Su != -1 && unibi_get_ext_bool(ut, (size_t)ext_bool_Su))) { data->unibi_ext.set_underline_style = (int)unibi_add_ext_str(ut, "ext.set_underline_style", -- cgit From 6d557e324fd4223fff3279a0112f40431c540163 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sun, 18 Sep 2022 03:17:15 +0200 Subject: vim-patch:8.1.0941: macros for MS-Windows are inconsistent (#20215) Problem: Macros for MS-Windows are inconsistent, using "32", "3264 and others. Solution: Use MSWIN for all MS-Windows builds. Use FEAT_GUI_MSWIN for the GUI build. (Hirohito Higashi, closes vim/vim#3932) https://github.com/vim/vim/commit/4f97475d326c2773a78561fb874e4f23c25cbcd9 --- src/nvim/tui/input.c | 4 ++-- src/nvim/tui/tui.c | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 728520c20c..998e8cfed0 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -15,7 +15,7 @@ #include "nvim/tui/input.h" #include "nvim/tui/tui.h" #include "nvim/vim.h" -#ifdef WIN32 +#ifdef MSWIN # include "nvim/os/os_win_console.h" #endif #include "nvim/event/rstream.h" @@ -143,7 +143,7 @@ void tinput_init(TermInput *input, Loop *loop) // If stdin is not a pty, switch to stderr. For cases like: // echo q | nvim -es // ls *.md | xargs nvim -#ifdef WIN32 +#ifdef MSWIN if (!os_isatty(input->in_fd)) { input->in_fd = os_get_conin_fd(); } diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 73f47f2b71..fd92873508 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -31,7 +31,7 @@ #include "nvim/os/tty.h" #include "nvim/ui.h" #include "nvim/vim.h" -#ifdef WIN32 +#ifdef MSWIN # include "nvim/os/os_win_console.h" #endif #include "nvim/cursor_shape.h" @@ -263,7 +263,7 @@ static void terminfo_start(UI *ui) data->input.tui_data = data; const char *term = os_getenv("TERM"); -#ifdef WIN32 +#ifdef MSWIN os_tty_guess_term(&term, data->out_fd); os_setenv("TERM", term, 1); // Old os_getenv() pointer is invalid after os_setenv(), fetch it again. @@ -353,7 +353,7 @@ static void terminfo_start(UI *ui) if (ret) { ELOG("uv_tty_init failed: %s", uv_strerror(ret)); } -#ifdef WIN32 +#ifdef MSWIN ret = uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_RAW); if (ret) { ELOG("uv_tty_set_mode failed: %s", uv_strerror(ret)); @@ -1810,7 +1810,7 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col } } -#ifdef WIN32 +#ifdef MSWIN // XXX: workaround libuv implicit LF => CRLF conversion. #10558 unibi_set_str(ut, unibi_cursor_down, "\x1b[B"); #endif -- cgit From a0e6e767a617d79983ac4982850dee6d95ed5b56 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 20 Sep 2022 11:47:04 +0800 Subject: fix(tui): handle padding requirements for visual bell (#20238) --- src/nvim/tui/tui.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index fd92873508..5a331463e3 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -1616,7 +1616,7 @@ static void unibi_goto(UI *ui, int row, int col) memset(&vars, 0, sizeof(vars)); \ data->cork = true; \ retry: \ - unibi_format(vars, vars + 26, str, data->params, out, ui, NULL, NULL); \ + unibi_format(vars, vars + 26, str, data->params, out, ui, pad, ui); \ if (data->overflow) { \ data->bufpos = orig_pos; \ flush_buf(ui); \ @@ -1647,6 +1647,7 @@ static void out(void *ctx, const char *str, size_t len) if (len > available) { if (data->cork) { + // Called by unibi_format(): avoid flush_buf() halfway an escape sequence. data->overflow = true; return; } else { @@ -1658,6 +1659,30 @@ static void out(void *ctx, const char *str, size_t len) data->bufpos += len; } +/// Called by unibi_format() for padding instructions. +/// The following parameter descriptions are extracted from unibi_format(3) and terminfo(5). +/// +/// @param ctx the same as `ctx2` passed to unibi_format() +/// @param delay the delay in tenths of milliseconds +/// @param scale padding is proportional to the number of lines affected +/// @param force padding is mandatory +static void pad(void *ctx, size_t delay, int scale FUNC_ATTR_UNUSED, int force) +{ + if (!force) { + return; + } + + UI *ui = ctx; + TUIData *data = ui->data; + + if (data->overflow) { + return; + } + + flush_buf(ui); + loop_uv_run(data->loop, (int)(delay / 10), false); +} + static void unibi_set_if_empty(unibi_term *ut, enum unibi_string str, const char *val) { if (!unibi_get_str(ut, str)) { -- cgit From 91e912f8d40284c74d4a997c8c95961eebb35d91 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sun, 25 Sep 2022 15:26:37 +0200 Subject: refactor: move klib out of src/nvim/ #20341 It's confusing to mix vendored dependencies with neovim source code. A clean separation is simpler to keep track of and simpler to document. --- src/nvim/tui/tui.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 5a331463e3..4eabecb74a 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -13,13 +13,13 @@ # include #endif +#include "klib/kvec.h" #include "nvim/api/private/helpers.h" #include "nvim/api/vim.h" #include "nvim/ascii.h" #include "nvim/event/loop.h" #include "nvim/event/signal.h" #include "nvim/highlight.h" -#include "nvim/lib/kvec.h" #include "nvim/log.h" #include "nvim/main.h" #include "nvim/map.h" -- cgit From 6427dc8ab66e5885c133698e3e96e82ab74a89f3 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Wed, 28 Sep 2022 09:28:15 +0200 Subject: build(deps): require libtermkey version 0.22 Reduces #ifdef code. --- src/nvim/tui/input.c | 4 ---- src/nvim/tui/input.h | 2 -- src/nvim/tui/tui.c | 8 ++------ 3 files changed, 2 insertions(+), 12 deletions(-) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 998e8cfed0..fbeca26274 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -159,14 +159,10 @@ void tinput_init(TermInput *input, Loop *loop) term = ""; // termkey_new_abstract assumes non-null (#2745) } -#if TERMKEY_VERSION_MAJOR > 0 || TERMKEY_VERSION_MINOR > 18 input->tk = termkey_new_abstract(term, TERMKEY_FLAG_UTF8 | TERMKEY_FLAG_NOSTART); termkey_hook_terminfo_getstr(input->tk, input->tk_ti_hook_fn, NULL); termkey_start(input->tk); -#else - input->tk = termkey_new_abstract(term, TERMKEY_FLAG_UTF8); -#endif int curflags = termkey_get_canonflags(input->tk); termkey_set_canonflags(input->tk, curflags | TERMKEY_CANON_DELBS); diff --git a/src/nvim/tui/input.h b/src/nvim/tui/input.h index 51df57938c..0b60394850 100644 --- a/src/nvim/tui/input.h +++ b/src/nvim/tui/input.h @@ -26,9 +26,7 @@ typedef struct term_input { ExtkeysType extkeys_type; long ttimeoutlen; TermKey *tk; -#if TERMKEY_VERSION_MAJOR > 0 || TERMKEY_VERSION_MINOR > 18 TermKey_Terminfo_Getstr_Hook *tk_ti_hook_fn; ///< libtermkey terminfo hook -#endif TimeWatcher timer_handle; Loop *loop; Stream read_stream; diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 4eabecb74a..b483ad3486 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -485,9 +485,7 @@ static void tui_main(UIBridgeData *bridge, UI *ui) // TODO(bfredl): zero hl is empty, send this explicitly? kv_push(data->attrs, HLATTRS_INIT); -#if TERMKEY_VERSION_MAJOR > 0 || TERMKEY_VERSION_MINOR > 18 data->input.tk_ti_hook_fn = tui_tk_ti_getstr; -#endif tinput_init(&data->input, &tui_loop); tui_terminal_start(ui); @@ -2278,7 +2276,6 @@ static void flush_buf(UI *ui) data->overflow = false; } -#if TERMKEY_VERSION_MAJOR > 0 || TERMKEY_VERSION_MINOR > 18 /// Try to get "kbs" code from stty because "the terminfo kbs entry is extremely /// unreliable." (Vim, Bash, and tmux also do this.) /// @@ -2287,14 +2284,14 @@ static void flush_buf(UI *ui) static const char *tui_get_stty_erase(void) { static char stty_erase[2] = { 0 }; -# if defined(HAVE_TERMIOS_H) +#if defined(HAVE_TERMIOS_H) struct termios t; if (tcgetattr(input_global_fd(), &t) != -1) { stty_erase[0] = (char)t.c_cc[VERASE]; stty_erase[1] = '\0'; DLOG("stty/termios:erase=%s", stty_erase); } -# endif +#endif return stty_erase; } @@ -2328,4 +2325,3 @@ static const char *tui_tk_ti_getstr(const char *name, const char *value, void *d return value; } -#endif -- cgit From fad1022cafd971c59e28ba34ec58d4866d2d5297 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 8 Oct 2022 20:17:53 +0800 Subject: fix(tui): resume main thread if suspending isn't implemented (#20523) Not doing anything is better than hanging. --- src/nvim/tui/tui.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index b483ad3486..1cb1c34ad3 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -1413,13 +1413,16 @@ static void suspend_event(void **argv) static void tui_suspend(UI *ui) { -#ifdef UNIX TUIData *data = ui->data; +#ifdef UNIX // kill(0, SIGTSTP) won't stop the UI thread, so we must poll for SIGCONT // before continuing. This is done in another callback to avoid // loop_poll_events recursion multiqueue_put_event(data->loop->fast_events, event_create(suspend_event, 1, ui)); +#else + // Resume the main thread as suspending isn't implemented. + CONTINUE(data->bridge); #endif } -- cgit From 6884f017b53369d6c9b06ddd3aedeb642dbd24a8 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 29 Oct 2022 17:41:22 +0200 Subject: vim-patch:partial:6ebe4f970b8b (#20860) Update runtime files https://github.com/vim/vim/commit/6ebe4f970b8b398087076a72a7aae6e680fb92da Co-authored-by: Bram Moolenaar --- src/nvim/tui/tui.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 1cb1c34ad3..083677a58e 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -2099,7 +2099,7 @@ static void augment_terminfo(TUIData *data, const char *term, long vte_version, // Dickey ncurses terminfo does not include the setrgbf and setrgbb // capabilities, proposed by Rüdiger Sonderfeld on 2013-10-15. Adding // them here when terminfo lacks them is an augmentation, not a fixup. - // https://gist.github.com/XVilka/8346728 + // https://github.com/termstandard/colors // At this time (2017-07-12) it seems like all terminals that support rgb // color codes can use semicolons in the terminal code and be fine. -- cgit From 731cdde28ea8d48cc23ba2752a08c261c87eee92 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Sat, 22 Oct 2022 12:36:38 +0200 Subject: refactor: fix clang-tidy warnings Enable and fix bugprone-misplaced-widening-cast warning. Fix some modernize-macro-to-enum and readability-else-after-return warnings, but don't enable them. While the warnings can be useful, they are in general too noisy to enable. --- src/nvim/tui/input.c | 44 +++++++++++++++++++++----------------------- src/nvim/tui/tui.c | 3 +-- 2 files changed, 22 insertions(+), 25 deletions(-) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index fbeca26274..2089686e5e 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -324,15 +324,14 @@ static void forward_simple_utf8(TermInput *input, TermKeyKey *key) && map_has(KittyKey, cstr_t)(&kitty_key_map, (KittyKey)key->code.codepoint)) { handle_kitty_key_protocol(input, key); return; - } else { - while (*ptr) { - if (*ptr == '<') { - len += (size_t)snprintf(buf + len, sizeof(buf) - len, ""); - } else { - buf[len++] = *ptr; - } - ptr++; + } + while (*ptr) { + if (*ptr == '<') { + len += (size_t)snprintf(buf + len, sizeof(buf) - len, ""); + } else { + buf[len++] = *ptr; } + ptr++; } tinput_enqueue(input, buf, len); @@ -355,21 +354,20 @@ static void forward_modified_utf8(TermInput *input, TermKeyKey *key) (KittyKey)key->code.codepoint)) { handle_kitty_key_protocol(input, key); return; - } else { - // Termkey doesn't include the S- modifier for ASCII characters (e.g., - // ctrl-shift-l is instead of . Vim, on the other hand, - // treats and the same, requiring the S- modifier. - len = termkey_strfkey(input->tk, buf, sizeof(buf), key, TERMKEY_FORMAT_VIM); - if ((key->modifiers & TERMKEY_KEYMOD_CTRL) - && !(key->modifiers & TERMKEY_KEYMOD_SHIFT) - && ASCII_ISUPPER(key->code.codepoint)) { - assert(len <= 62); - // Make room for the S- - memmove(buf + 3, buf + 1, len - 1); - buf[1] = 'S'; - buf[2] = '-'; - len += 2; - } + } + // Termkey doesn't include the S- modifier for ASCII characters (e.g., + // ctrl-shift-l is instead of . Vim, on the other hand, + // treats and the same, requiring the S- modifier. + len = termkey_strfkey(input->tk, buf, sizeof(buf), key, TERMKEY_FORMAT_VIM); + if ((key->modifiers & TERMKEY_KEYMOD_CTRL) + && !(key->modifiers & TERMKEY_KEYMOD_SHIFT) + && ASCII_ISUPPER(key->code.codepoint)) { + assert(len <= 62); + // Make room for the S- + memmove(buf + 3, buf + 1, len - 1); + buf[1] = 'S'; + buf[2] = '-'; + len += 2; } } diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 083677a58e..e2d37860c4 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -1651,9 +1651,8 @@ static void out(void *ctx, const char *str, size_t len) // Called by unibi_format(): avoid flush_buf() halfway an escape sequence. data->overflow = true; return; - } else { - flush_buf(ui); } + flush_buf(ui); } memcpy(data->buf + data->bufpos, str, len); -- cgit From 66360675cf4d091b7460e4a8e1435c13216c1929 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Sun, 11 Sep 2022 17:12:44 +0200 Subject: build: allow IWYU to fix includes for all .c files Allow Include What You Use to remove unnecessary includes and only include what is necessary. This helps with reducing compilation times and makes it easier to visualise which dependencies are actually required. Work on https://github.com/neovim/neovim/issues/549, but doesn't close it since this only works fully for .c files and not headers. --- src/nvim/tui/input.c | 16 ++++++++++++++-- src/nvim/tui/input.h | 4 ++++ src/nvim/tui/terminfo.c | 6 ++++-- src/nvim/tui/tui.c | 24 +++++++++++++----------- 4 files changed, 35 insertions(+), 15 deletions(-) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 2089686e5e..9171f79c37 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -1,20 +1,32 @@ // This is an open source non-commercial project. Dear PVS-Studio, please check // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +#include +#include +#include +#include + +#include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/api/vim.h" #include "nvim/ascii.h" #include "nvim/autocmd.h" #include "nvim/charset.h" -#include "nvim/ex_docmd.h" +#include "nvim/event/defs.h" +#include "nvim/event/multiqueue.h" +#include "nvim/globals.h" +#include "nvim/log.h" #include "nvim/macros.h" #include "nvim/main.h" +#include "nvim/map.h" +#include "nvim/memory.h" +#include "nvim/message.h" #include "nvim/option.h" #include "nvim/os/input.h" #include "nvim/os/os.h" #include "nvim/tui/input.h" +#include "nvim/tui/input_defs.h" #include "nvim/tui/tui.h" -#include "nvim/vim.h" #ifdef MSWIN # include "nvim/os/os_win_console.h" #endif diff --git a/src/nvim/tui/input.h b/src/nvim/tui/input.h index 0b60394850..5df108b107 100644 --- a/src/nvim/tui/input.h +++ b/src/nvim/tui/input.h @@ -2,10 +2,14 @@ #define NVIM_TUI_INPUT_H #include +#include #include +#include +#include "nvim/event/loop.h" #include "nvim/event/stream.h" #include "nvim/event/time.h" +#include "nvim/rbuffer.h" #include "nvim/tui/input_defs.h" #include "nvim/tui/tui.h" diff --git a/src/nvim/tui/terminfo.c b/src/nvim/tui/terminfo.c index 229e340dc3..0f6ae03d35 100644 --- a/src/nvim/tui/terminfo.c +++ b/src/nvim/tui/terminfo.c @@ -8,14 +8,16 @@ #include #include "nvim/globals.h" -#include "nvim/log.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/option.h" -#include "nvim/os/os.h" #include "nvim/tui/terminfo.h" #include "nvim/tui/terminfo_defs.h" +#ifdef __FreeBSD__ +# include "nvim/os/os.h" +#endif + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "tui/terminfo.c.generated.h" #endif diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index e2d37860c4..be7658616f 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -4,31 +4,35 @@ // Terminal UI functions. Invoked (by ui_bridge.c) on the TUI thread. #include -#include +#include #include #include +#include +#include #include #include -#if defined(HAVE_TERMIOS_H) -# include -#endif +#include "auto/config.h" #include "klib/kvec.h" -#include "nvim/api/private/helpers.h" -#include "nvim/api/vim.h" +#include "nvim/api/private/defs.h" #include "nvim/ascii.h" +#include "nvim/event/defs.h" #include "nvim/event/loop.h" +#include "nvim/event/multiqueue.h" #include "nvim/event/signal.h" -#include "nvim/highlight.h" +#include "nvim/event/stream.h" +#include "nvim/globals.h" +#include "nvim/grid_defs.h" +#include "nvim/highlight_defs.h" #include "nvim/log.h" #include "nvim/main.h" -#include "nvim/map.h" +#include "nvim/mbyte.h" #include "nvim/memory.h" +#include "nvim/message.h" #include "nvim/option.h" #include "nvim/os/input.h" #include "nvim/os/os.h" #include "nvim/os/signal.h" -#include "nvim/os/tty.h" #include "nvim/ui.h" #include "nvim/vim.h" #ifdef MSWIN @@ -36,8 +40,6 @@ #endif #include "nvim/cursor_shape.h" #include "nvim/macros.h" -#include "nvim/strings.h" -#include "nvim/syntax.h" #include "nvim/tui/input.h" #include "nvim/tui/terminfo.h" #include "nvim/tui/tui.h" -- cgit From a7332ba9b429f9676723eb88cffb2068f5a95c9b Mon Sep 17 00:00:00 2001 From: Tom Churchman Date: Mon, 19 Dec 2022 00:10:47 +0100 Subject: fix(tui): set cursor color param as string when required #21407 Problem: See #20628. Terminals supporting cursor color changing usually set the "user-defined" `Cs` terminfo capability. Most terminals expect the parameter to the capability to be a string (in hex format like `#0099ff` or like `rgb:00/99/ff`), others may expect a number. Nvim currently can't handle string parameters, causing terminals to receive a bogus command. Unfortunately, as the `Cs` capability is "user-defined", there's no strict format. The parameter it takes isn't really standardized. It seems most terminals in use follow xterm; iTerm appears to be an exception. Solution: Use the `Cs` capability more reliable by following terminfo and sending the color in hex format, at the cost of using unibilium string vars. Alternatively, could revert https://github.com/neovim/neovim/commit/34d41baf8a8e4ab8c006b7f29a8106e60e311aa2 and hardcode the specific format required by terminals, instead of reading terminfo. Fixes #20628 Fixes #19607 --- src/nvim/tui/tui.c | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index be7658616f..14ad4ada36 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -65,8 +65,21 @@ do { \ (var) = unibi_var_from_num((num)); \ } while (0) +# define UNIBI_SET_STR_VAR(var, str) \ + do { \ + (var) = unibi_var_from_str((str)); \ + } while (0) #else -# define UNIBI_SET_NUM_VAR(var, num) (var).i = (num); +# define UNIBI_SET_NUM_VAR(var, num) \ + do { \ + (var).p = NULL; \ + (var).i = (num); \ + } while (0) +# define UNIBI_SET_STR_VAR(var, str) \ + do { \ + (var).i = INT_MIN; \ + (var).p = str; \ + } while (0) #endif typedef struct { @@ -108,6 +121,7 @@ struct TUIData { bool mouse_move_enabled; bool busy, is_invisible, want_invisible; bool cork, overflow; + bool set_cursor_color_as_str; bool cursor_color_changed; bool is_starting; FILE *screenshot; @@ -236,6 +250,7 @@ static void terminfo_start(UI *ui) data->busy = false; data->cork = false; data->overflow = false; + data->set_cursor_color_as_str = false; data->cursor_color_changed = false; data->showing_mode = SHAPE_IDX_N; data->unibi_ext.enable_mouse = -1; @@ -1180,7 +1195,13 @@ static void tui_set_mode(UI *ui, ModeShape mode) // Hopefully the user's default cursor color is inverse. unibi_out_ext(ui, data->unibi_ext.reset_cursor_color); } else { - UNIBI_SET_NUM_VAR(data->params[0], aep.rgb_bg_color); + if (data->set_cursor_color_as_str) { + char hexbuf[8]; + snprintf(hexbuf, 7 + 1, "#%06x", aep.rgb_bg_color); + UNIBI_SET_STR_VAR(data->params[0], hexbuf); + } else { + UNIBI_SET_NUM_VAR(data->params[0], aep.rgb_bg_color); + } unibi_out_ext(ui, data->unibi_ext.set_cursor_color); data->cursor_color_changed = true; } @@ -2148,10 +2169,20 @@ static void augment_terminfo(TUIData *data, const char *term, long vte_version, && (vte_version == 0 || vte_version >= 3900)) { // Supported in urxvt, newer VTE. data->unibi_ext.set_cursor_color = (int)unibi_add_ext_str(ut, "ext.set_cursor_color", - "\033]12;#%p1%06x\007"); + "\033]12;%p1%s\007"); } } if (-1 != data->unibi_ext.set_cursor_color) { + // Some terminals supporting cursor color changing specify their Cs + // capability to take a string parameter. Others take a numeric parameter. + // If and only if the format string contains `%s` we assume a string + // parameter. #20628 + const char *set_cursor_color = + unibi_get_ext_str(ut, (unsigned)data->unibi_ext.set_cursor_color); + if (set_cursor_color) { + data->set_cursor_color_as_str = strstr(set_cursor_color, "%s") != NULL; + } + data->unibi_ext.reset_cursor_color = unibi_find_ext_str(ut, "Cr"); if (-1 == data->unibi_ext.reset_cursor_color) { data->unibi_ext.reset_cursor_color = (int)unibi_add_ext_str(ut, "ext.reset_cursor_color", -- cgit From b42d8a43b9f1b3316e73108ebefc4850b1a2c65b Mon Sep 17 00:00:00 2001 From: bfredl Date: Fri, 16 Dec 2022 13:50:12 +0100 Subject: refactor(tui): use nvim_echo() for verbose terminfo This is needed for #18375 for the obvious reasons. note: verbose_terminfo_event is only temporarily needed until the full TUI process refactor is merged. --- src/nvim/tui/terminfo.c | 75 +++++++++++++++++++++++++------------------------ src/nvim/tui/terminfo.h | 2 ++ src/nvim/tui/tui.c | 72 ++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 98 insertions(+), 51 deletions(-) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/terminfo.c b/src/nvim/tui/terminfo.c index 0f6ae03d35..507e9df21e 100644 --- a/src/nvim/tui/terminfo.c +++ b/src/nvim/tui/terminfo.c @@ -7,10 +7,13 @@ #include #include +#include "nvim/api/private/helpers.h" +#include "nvim/charset.h" #include "nvim/globals.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/option.h" +#include "nvim/strings.h" #include "nvim/tui/terminfo.h" #include "nvim/tui/terminfo_defs.h" @@ -147,82 +150,80 @@ unibi_term *terminfo_from_builtin(const char *term, char **termname) /// Serves a similar purpose as Vim `:set termcap` (removed in Nvim). /// /// @note adapted from unibilium unibi-dump.c -void terminfo_info_msg(const unibi_term *const ut) +/// @return allocated string +String terminfo_info_msg(const unibi_term *ut, const char *termname) { - if (exiting) { - return; - } - msg_puts_title("\n\n--- Terminal info --- {{{\n"); + StringBuilder data = KV_INITIAL_VALUE; - char *term; - get_tty_option("term", &term); - msg_printf_attr(0, "&term: %s\n", term); - msg_printf_attr(0, "Description: %s\n", unibi_get_name(ut)); + kv_printf(data, "&term: %s\n", termname); + kv_printf(data, "Description: %s\n", unibi_get_name(ut)); const char **a = unibi_get_aliases(ut); if (*a) { - msg_puts("Aliases: "); + kv_printf(data, "Aliases: "); do { - msg_printf_attr(0, "%s%s\n", *a, a[1] ? " | " : ""); + kv_printf(data, "%s%s\n", *a, a[1] ? " | " : ""); a++; } while (*a); } - msg_puts("Boolean capabilities:\n"); + kv_printf(data, "Boolean capabilities:\n"); for (enum unibi_boolean i = unibi_boolean_begin_ + 1; i < unibi_boolean_end_; i++) { - msg_printf_attr(0, " %-25s %-10s = %s\n", unibi_name_bool(i), - unibi_short_name_bool(i), - unibi_get_bool(ut, i) ? "true" : "false"); + kv_printf(data, " %-25s %-10s = %s\n", unibi_name_bool(i), + unibi_short_name_bool(i), + unibi_get_bool(ut, i) ? "true" : "false"); } - msg_puts("Numeric capabilities:\n"); + kv_printf(data, "Numeric capabilities:\n"); for (enum unibi_numeric i = unibi_numeric_begin_ + 1; i < unibi_numeric_end_; i++) { int n = unibi_get_num(ut, i); // -1 means "empty" - msg_printf_attr(0, " %-25s %-10s = %d\n", unibi_name_num(i), - unibi_short_name_num(i), n); + kv_printf(data, " %-25s %-10s = %d\n", unibi_name_num(i), + unibi_short_name_num(i), n); } - msg_puts("String capabilities:\n"); + kv_printf(data, "String capabilities:\n"); for (enum unibi_string i = unibi_string_begin_ + 1; i < unibi_string_end_; i++) { const char *s = unibi_get_str(ut, i); if (s) { - msg_printf_attr(0, " %-25s %-10s = ", unibi_name_str(i), - unibi_short_name_str(i)); + kv_printf(data, " %-25s %-10s = ", unibi_name_str(i), + unibi_short_name_str(i)); // Most of these strings will contain escape sequences. - msg_outtrans_special(s, false, 0); - msg_putchar('\n'); + kv_transstr(&data, s, false); + kv_push(data, '\n'); } } if (unibi_count_ext_bool(ut)) { - msg_puts("Extended boolean capabilities:\n"); + kv_printf(data, "Extended boolean capabilities:\n"); for (size_t i = 0; i < unibi_count_ext_bool(ut); i++) { - msg_printf_attr(0, " %-25s = %s\n", - unibi_get_ext_bool_name(ut, i), - unibi_get_ext_bool(ut, i) ? "true" : "false"); + kv_printf(data, " %-25s = %s\n", + unibi_get_ext_bool_name(ut, i), + unibi_get_ext_bool(ut, i) ? "true" : "false"); } } if (unibi_count_ext_num(ut)) { - msg_puts("Extended numeric capabilities:\n"); + kv_printf(data, "Extended numeric capabilities:\n"); for (size_t i = 0; i < unibi_count_ext_num(ut); i++) { - msg_printf_attr(0, " %-25s = %d\n", - unibi_get_ext_num_name(ut, i), - unibi_get_ext_num(ut, i)); + kv_printf(data, " %-25s = %d\n", + unibi_get_ext_num_name(ut, i), + unibi_get_ext_num(ut, i)); } } if (unibi_count_ext_str(ut)) { - msg_puts("Extended string capabilities:\n"); + kv_printf(data, "Extended string capabilities:\n"); for (size_t i = 0; i < unibi_count_ext_str(ut); i++) { - msg_printf_attr(0, " %-25s = ", unibi_get_ext_str_name(ut, i)); - msg_outtrans_special(unibi_get_ext_str(ut, i), false, 0); - msg_putchar('\n'); + kv_printf(data, " %-25s = ", unibi_get_ext_str_name(ut, i)); + // NOTE: unibi_get_ext_str(ut, i) might be NULL, as termcap + // might include junk data on mac os. kv_transstr will handle this. + kv_transstr(&data, unibi_get_ext_str(ut, i), false); + kv_push(data, '\n'); } } + kv_push(data, NUL); - msg_puts("}}}\n"); - xfree(term); + return cbuf_as_string(data.items, data.size - 1); } diff --git a/src/nvim/tui/terminfo.h b/src/nvim/tui/terminfo.h index 099df8967f..178d384457 100644 --- a/src/nvim/tui/terminfo.h +++ b/src/nvim/tui/terminfo.h @@ -3,6 +3,8 @@ #include +#include "nvim/api/private/defs.h" + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "tui/terminfo.h.generated.h" #endif diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 14ad4ada36..3010a7b612 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -15,6 +15,8 @@ #include "auto/config.h" #include "klib/kvec.h" #include "nvim/api/private/defs.h" +#include "nvim/api/private/helpers.h" +#include "nvim/api/vim.h" #include "nvim/ascii.h" #include "nvim/event/defs.h" #include "nvim/event/loop.h" @@ -25,10 +27,12 @@ #include "nvim/grid_defs.h" #include "nvim/highlight_defs.h" #include "nvim/log.h" +#include "nvim/macros.h" #include "nvim/main.h" #include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/message.h" +#include "nvim/msgpack_rpc/channel.h" #include "nvim/option.h" #include "nvim/os/input.h" #include "nvim/os/os.h" @@ -98,6 +102,7 @@ struct TUIData { TermInput input; uv_loop_t write_loop; unibi_term *ut; + char *term; // value of $TERM union { uv_tty_t tty; uv_pipe_t pipe; @@ -132,6 +137,7 @@ struct TUIData { bool default_attr; bool can_clear_attr; ModeShape showing_mode; + Integer verbose; struct { int enable_mouse, disable_mouse; int enable_mouse_move, disable_mouse_move; @@ -295,6 +301,7 @@ static void terminfo_start(UI *ui) os_env_var_unlock(); if (data->ut) { termname = xstrdup(term); + data->term = xstrdup(term); } } if (!data->ut) { @@ -509,9 +516,6 @@ static void tui_main(UIBridgeData *bridge, UI *ui) // Allow main thread to continue, we are ready to handle UI callbacks. CONTINUE(bridge); - loop_schedule_deferred(&main_loop, - event_create(show_termcap_event, 1, data->ut)); - // "Active" loop: first ~100 ms of startup. for (size_t ms = 0; ms < 100 && !tui_is_stopped(ui);) { ms += (loop_poll_events(&tui_loop, 20) ? 20 : 1); @@ -533,6 +537,7 @@ static void tui_main(UIBridgeData *bridge, UI *ui) kv_destroy(data->invalid_regions); kv_destroy(data->attrs); xfree(data->space_buf); + xfree(data->term); xfree(data); } @@ -1246,6 +1251,11 @@ static void tui_mode_change(UI *ui, String mode, Integer mode_idx) } #endif tui_set_mode(ui, (ModeShape)mode_idx); + if (data->is_starting) { + if (data->verbose >= 3) { + show_verbose_terminfo(data); + } + } data->is_starting = false; // mode entered, no longer starting data->showing_mode = (ModeShape)mode_idx; } @@ -1390,21 +1400,53 @@ static void tui_flush(UI *ui) } /// Dumps termcap info to the messages area, if 'verbose' >= 3. -static void show_termcap_event(void **argv) +static void show_verbose_terminfo(TUIData *data) { - if (p_verbose < 3) { - return; - } - const unibi_term *const ut = argv[0]; + const unibi_term *const ut = data->ut; if (!ut) { abort(); } - verbose_enter(); - // XXX: (future) if unibi_term is modified (e.g. after a terminal - // query-response) this is a race condition. - terminfo_info_msg(ut); - verbose_leave(); - verbose_stop(); // flush now + + Array chunks = ARRAY_DICT_INIT; + Array title = ARRAY_DICT_INIT; + ADD(title, STRING_OBJ(cstr_to_string("\n\n--- Terminal info --- {{{\n"))); + ADD(title, STRING_OBJ(cstr_to_string("Title"))); + ADD(chunks, ARRAY_OBJ(title)); + Array info = ARRAY_DICT_INIT; + String str = terminfo_info_msg(ut, data->term); + ADD(info, STRING_OBJ(str)); + ADD(chunks, ARRAY_OBJ(info)); + Array end_fold = ARRAY_DICT_INIT; + ADD(end_fold, STRING_OBJ(cstr_to_string("}}}\n"))); + ADD(end_fold, STRING_OBJ(cstr_to_string("Title"))); + ADD(chunks, ARRAY_OBJ(end_fold)); + + if (ui_client_channel_id) { + Array args = ARRAY_DICT_INIT; + ADD(args, ARRAY_OBJ(chunks)); + ADD(args, BOOLEAN_OBJ(true)); // history + Dictionary opts = ARRAY_DICT_INIT; + PUT(opts, "verbose", BOOLEAN_OBJ(true)); + ADD(args, DICTIONARY_OBJ(opts)); + rpc_send_event(ui_client_channel_id, "nvim_echo", args); + } else { + loop_schedule_deferred(&main_loop, event_create(verbose_terminfo_event, 2, + chunks.items, chunks.size)); + } +} + +static void verbose_terminfo_event(void **argv) +{ + Array chunks = { .items = argv[0], .size = (size_t)argv[1] }; + Dict(echo_opts) opts = { .verbose = BOOLEAN_OBJ(true) }; + Error err = ERROR_INIT; + nvim_echo(chunks, true, &opts, &err); + api_free_array(chunks); + if (ERROR_SET(&err)) { + fprintf(stderr, "TUI bought the farm: %s\n", err.msg); + exit(1); + } + api_clear_error(&err); } #ifdef UNIX @@ -1509,6 +1551,8 @@ static void tui_option_set(UI *ui, String name, Object value) data->input.ttimeout = value.data.boolean; } else if (strequal(name.data, "ttimeoutlen")) { data->input.ttimeoutlen = (long)value.data.integer; + } else if (strequal(name.data, "verbose")) { + data->verbose = value.data.integer; } } -- cgit From 09370eae7756b07bb8f1f116b690b22aec7ec563 Mon Sep 17 00:00:00 2001 From: bfredl Date: Fri, 30 Dec 2022 19:49:59 +0100 Subject: refactor(sleep): simplify rube goldberg implementation of :sleep As neovim does have event handling, we are checking for CTRL-C all the time, not once per second. Also, do_sleep() reimplements the same loop as LOOP_PROCESS_EVENTS_UNTIL() already contains internally. Fix the latter to use the right integer type, so we do not need the extra indirection. --- src/nvim/tui/tui.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 3010a7b612..e32e48ce91 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -1747,7 +1747,7 @@ static void pad(void *ctx, size_t delay, int scale FUNC_ATTR_UNUSED, int force) } flush_buf(ui); - loop_uv_run(data->loop, (int)(delay / 10), false); + loop_uv_run(data->loop, (int64_t)(delay / 10), false); } static void unibi_set_if_empty(unibi_term *ut, enum unibi_string str, const char *val) -- cgit From 24488169564c39a506c235bf6a33b8e23a8cb528 Mon Sep 17 00:00:00 2001 From: hlpr98 Date: Mon, 27 May 2019 22:04:24 +0530 Subject: feat(tui): run TUI as external process --- src/nvim/tui/input.c | 55 ++++++++------------- src/nvim/tui/tui.c | 135 +++++++++++++++++++++++++++++---------------------- 2 files changed, 98 insertions(+), 92 deletions(-) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 9171f79c37..ca1f7c25d7 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -32,6 +32,7 @@ #endif #include "nvim/event/rstream.h" #include "nvim/msgpack_rpc/channel.h" +#include "nvim/ui.h" #define KEY_BUFFER_SIZE 0xfff @@ -226,8 +227,9 @@ static void tinput_wait_enqueue(void **argv) ADD(args, INTEGER_OBJ(input->paste)); // 'phase' rpc_send_event(ui_client_channel_id, "nvim_paste", args); } else { - multiqueue_put(main_loop.events, tinput_paste_event, 3, - keys.data, keys.size, (intptr_t)input->paste); + // TODO + // multiqueue_put(main_loop.events, tinput_paste_event, 3, + // keys.data, keys.size, (intptr_t)input->paste); } if (input->paste == 1) { // Paste phase: "continue" @@ -248,7 +250,9 @@ static void tinput_wait_enqueue(void **argv) consumed = result.type == kObjectTypeInteger ? (size_t)result.data.integer : 0; arena_mem_free(res_mem); } else { - consumed = input_enqueue(keys); + // TODO + // consumed = input_enqueue(keys); + abort(); } if (consumed) { rbuffer_consumed(input->key_buffer, consumed); @@ -259,38 +263,15 @@ static void tinput_wait_enqueue(void **argv) } } } - uv_mutex_lock(&input->key_buffer_mutex); - input->waiting = false; - uv_cond_signal(&input->key_buffer_cond); - uv_mutex_unlock(&input->key_buffer_mutex); } -static void tinput_paste_event(void **argv) -{ - String keys = { .data = argv[0], .size = (size_t)argv[1] }; - intptr_t phase = (intptr_t)argv[2]; - - Error err = ERROR_INIT; - nvim_paste(keys, true, phase, &err); - if (ERROR_SET(&err)) { - semsg("paste: %s", err.msg); - api_clear_error(&err); - } - - api_free_string(keys); -} static void tinput_flush(TermInput *input, bool wait_until_empty) { size_t drain_boundary = wait_until_empty ? 0 : 0xff; + // TODO: fuuuuuuuuuuuuuuu do { - uv_mutex_lock(&input->key_buffer_mutex); - loop_schedule_fast(&main_loop, event_create(tinput_wait_enqueue, 1, input)); - input->waiting = true; - while (input->waiting) { - uv_cond_wait(&input->key_buffer_cond, &input->key_buffer_mutex); - } - uv_mutex_unlock(&input->key_buffer_mutex); + tinput_wait_enqueue((void**)&input); } while (rbuffer_size(input->key_buffer) > drain_boundary); } @@ -569,8 +550,10 @@ static bool handle_focus_event(TermInput *input) || !rbuffer_cmp(input->read_stream.buffer, "\x1b[O", 3))) { bool focus_gained = *rbuffer_get(input->read_stream.buffer, 2) == 'I'; // Advance past the sequence - rbuffer_consumed(input->read_stream.buffer, 3); - autocmd_schedule_focusgained(focus_gained); + + Array args = ARRAY_DICT_INIT; + ADD(args, BOOLEAN_OBJ(focus_gained)); + rpc_send_event(ui_client_channel_id, "nvim_ui_set_focus", args); return true; } return false; @@ -617,10 +600,13 @@ static HandleState handle_bracketed_paste(TermInput *input) return kNotApplicable; } -static void set_bg_deferred(void **argv) +static void set_bg(char *bgvalue) { - char *bgvalue = argv[0]; - set_tty_background(bgvalue); + Array args = ARRAY_DICT_INIT; + ADD(args, STRING_OBJ(cstr_to_string("term_background"))); + ADD(args, STRING_OBJ(cstr_as_string(xstrdup(bgvalue)))); + + rpc_send_event(ui_client_channel_id, "nvim_ui_set_option", args); } // During startup, tui.c requests the background color (see `ext.get_bg`). @@ -704,8 +690,7 @@ static HandleState handle_background_color(TermInput *input) double luminance = (0.299 * r) + (0.587 * g) + (0.114 * b); // CCIR 601 char *bgvalue = luminance < 0.5 ? "dark" : "light"; DLOG("bg response: %s", bgvalue); - loop_schedule_deferred(&main_loop, - event_create(set_bg_deferred, 1, bgvalue)); + set_bg(bgvalue); input->waiting_for_bg_response = 0; } else if (!done && !bad) { // An incomplete sequence was found, waiting for the next input. diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 3010a7b612..4634c77a1f 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -1,7 +1,7 @@ // This is an open source non-commercial project. Dear PVS-Studio, please check // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// Terminal UI functions. Invoked (by ui_bridge.c) on the TUI thread. +// Terminal UI functions. Invoked (by UI_CALL) on the UI process. #include #include @@ -48,7 +48,7 @@ #include "nvim/tui/terminfo.h" #include "nvim/tui/tui.h" #include "nvim/ugrid.h" -#include "nvim/ui_bridge.h" +#include "nvim/msgpack_rpc/channel.h" // Space reserved in two output buffers to make the cursor normal or invisible // when flushing. No existing terminal will require 32 bytes to do that. @@ -91,7 +91,7 @@ typedef struct { } Rect; struct TUIData { - UIBridgeData *bridge; + UI *ui; Loop *loop; unibi_var_t params[9]; char buf[OUTBUF_SIZE]; @@ -159,18 +159,19 @@ struct TUIData { int get_extkeys; } unibi_ext; char *space_buf; + bool stopped; }; static int got_winch = 0; static bool cursor_style_enabled = false; - +char *termname_local; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "tui/tui.c.generated.h" #endif UI *tui_start(void) { - UI *ui = xcalloc(1, sizeof(UI)); // Freed by ui_bridge_stop(). + UI *ui = xcalloc(1, sizeof(UI)); // Freed by tui_data_destroy(). ui->stop = tui_stop; ui->grid_resize = tui_grid_resize; ui->grid_clear = tui_grid_clear; @@ -198,15 +199,18 @@ UI *tui_start(void) CLEAR_FIELD(ui->ui_ext); ui->ui_ext[kUILinegrid] = true; ui->ui_ext[kUITermColors] = true; + + tui_main(ui); + ui_attach_impl(ui, 0); - return ui_bridge_attach(ui, tui_main, tui_scheduler); + return ui; } void tui_enable_extkeys(TUIData *data) { TermInput input = data->input; unibi_term *ut = data->ut; - UI *ui = data->bridge->ui; + UI *ui = data->ui; switch (input.extkeys_type) { case kExtkeysCSIu: @@ -237,13 +241,6 @@ static size_t unibi_pre_fmt_str(TUIData *data, unsigned int unibi_index, char *b return unibi_run(str, data->params, buf, len); } -static void termname_set_event(void **argv) -{ - char *termname = argv[0]; - set_tty_option("term", termname); - // Do not free termname, it is freed by set_tty_option. -} - static void terminfo_start(UI *ui) { TUIData *data = ui->data; @@ -294,22 +291,19 @@ static void terminfo_start(UI *ui) #endif // Set up unibilium/terminfo. - char *termname = NULL; + termname_local = NULL; if (term) { os_env_var_lock(); data->ut = unibi_from_term(term); os_env_var_unlock(); if (data->ut) { - termname = xstrdup(term); + termname_local = xstrdup(term); data->term = xstrdup(term); } } if (!data->ut) { - data->ut = terminfo_from_builtin(term, &termname); + data->ut = terminfo_from_builtin(term, &termname_local); } - // Update 'term' option. - loop_schedule_deferred(&main_loop, - event_create(termname_set_event, 1, termname)); // None of the following work over SSH; see :help TERM . const char *colorterm = os_getenv("COLORTERM"); @@ -467,7 +461,7 @@ static void tui_terminal_stop(UI *ui) if (uv_is_closing(STRUCT_CAST(uv_handle_t, &data->output_handle))) { // Race between SIGCONT (tui.c) and SIGHUP (os/signal.c)? #8075 ELOG("TUI already stopped (race?)"); - ui->data = NULL; // Flag UI as "stopped". + data->stopped = true; return; } tinput_stop(&data->input); @@ -479,26 +473,27 @@ static void tui_terminal_stop(UI *ui) static void tui_stop(UI *ui) { tui_terminal_stop(ui); - ui->data = NULL; // Flag UI as "stopped". + TUIData *data = ui->data; + data->stopped = true; } /// Returns true if UI `ui` is stopped. static bool tui_is_stopped(UI *ui) { - return ui->data == NULL; + TUIData *data = ui->data; + return data->stopped; } -/// Main function of the TUI thread. -static void tui_main(UIBridgeData *bridge, UI *ui) +// Main function for TUI +static void tui_main(UI *ui) { - Loop tui_loop; - loop_init(&tui_loop, NULL); TUIData *data = xcalloc(1, sizeof(TUIData)); ui->data = data; - data->bridge = bridge; - data->loop = &tui_loop; + data->ui = ui; data->is_starting = true; data->screenshot = NULL; + data->stopped = false; + data->loop = &main_loop; kv_init(data->invalid_regions); signal_watcher_init(data->loop, &data->winch_handle, ui); signal_watcher_init(data->loop, &data->cont_handle, data); @@ -510,43 +505,54 @@ static void tui_main(UIBridgeData *bridge, UI *ui) kv_push(data->attrs, HLATTRS_INIT); data->input.tk_ti_hook_fn = tui_tk_ti_getstr; - tinput_init(&data->input, &tui_loop); + tinput_init(&data->input, &main_loop); tui_terminal_start(ui); + // TODO: borked! + // loop_schedule(&main_loop, event_create(show_termcap_event, 1, data->ut)); - // Allow main thread to continue, we are ready to handle UI callbacks. - CONTINUE(bridge); +} - // "Active" loop: first ~100 ms of startup. - for (size_t ms = 0; ms < 100 && !tui_is_stopped(ui);) { - ms += (loop_poll_events(&tui_loop, 20) ? 20 : 1); - } - if (!tui_is_stopped(ui)) { - tui_terminal_after_startup(ui); - } - // "Passive" (I/O-driven) loop: TUI thread "main loop". +void tui_execute(void) + FUNC_ATTR_NORETURN +{ + UI *ui = ui_get_by_index(1); + LOOP_PROCESS_EVENTS(&main_loop, main_loop.events, -1); + tui_io_driven_loop(ui); + tui_exit_safe(ui); + getout(0); +} + +// Doesn't return until the TUI is closed (by call of tui_stop()) +static void tui_io_driven_loop(UI *ui){ + // "Passive" (I/O-driven) loop: TUI process's "main loop". while (!tui_is_stopped(ui)) { - loop_poll_events(&tui_loop, -1); // tui_loop.events is never processed + loop_poll_events(&main_loop, -1); } +} - ui_bridge_stopped(bridge); - tinput_destroy(&data->input); - signal_watcher_stop(&data->cont_handle); - signal_watcher_close(&data->cont_handle, NULL); - signal_watcher_close(&data->winch_handle, NULL); - loop_close(&tui_loop, false); +// TODO: call me when EXITFREE +#if 0 +static void tui_data_destroy(void **argv) { + UI *ui = argv[0]; + TUIData *data = ui->data; kv_destroy(data->invalid_regions); kv_destroy(data->attrs); xfree(data->space_buf); xfree(data->term); xfree(data); + xfree(ui); } +#endif -/// Handoff point between the main (ui_bridge) thread and the TUI thread. -static void tui_scheduler(Event event, void *d) -{ - UI *ui = d; +void tui_exit_safe(UI *ui) { TUIData *data = ui->data; - loop_schedule_fast(data->loop, event); // `tui_loop` local to tui_main(). + if (!tui_is_stopped(ui)) { + tui_stop(ui); + } + tinput_destroy(&data->input); + signal_watcher_stop(&data->cont_handle); + signal_watcher_close(&data->cont_handle, NULL); + signal_watcher_close(&data->winch_handle, NULL); } #ifdef UNIX @@ -1324,6 +1330,9 @@ static void tui_grid_scroll(UI *ui, Integer g, Integer startrow, // -V751 static void tui_hl_attr_define(UI *ui, Integer id, HlAttrs attrs, HlAttrs cterm_attrs, Array info) { TUIData *data = ui->data; + attrs.cterm_ae_attr = cterm_attrs.cterm_ae_attr; + attrs.cterm_fg_color = cterm_attrs.cterm_fg_color; + attrs.cterm_bg_color = cterm_attrs.cterm_bg_color; kv_a(data->attrs, (size_t)id) = attrs; } @@ -1399,6 +1408,7 @@ static void tui_flush(UI *ui) flush_buf(ui); } +#if 0 /// Dumps termcap info to the messages area, if 'verbose' >= 3. static void show_verbose_terminfo(TUIData *data) { @@ -1448,6 +1458,7 @@ static void verbose_terminfo_event(void **argv) } api_clear_error(&err); } +#endif #ifdef UNIX static void suspend_event(void **argv) @@ -1459,20 +1470,19 @@ static void suspend_event(void **argv) data->cont_received = false; stream_set_blocking(input_global_fd(), true); // normalize stream (#2598) signal_stop(); - kill(0, SIGTSTP); + kill(0, SIGTSTP); // make TUI process run in background signal_start(); while (!data->cont_received) { // poll the event loop until SIGCONT is received loop_poll_events(data->loop, -1); } + tui_terminal_start(ui); tui_terminal_after_startup(ui); if (enable_mouse) { tui_mouse_on(ui); } stream_set_blocking(input_global_fd(), false); // libuv expects this - // resume the main thread - CONTINUE(data->bridge); } #endif @@ -1547,6 +1557,13 @@ static void tui_option_set(UI *ui, String name, Object value) ui->rgb = value.data.boolean; data->print_attr_id = -1; invalidate(ui, 0, data->grid.height, 0, data->grid.width); + + if (ui_client_channel_id) { + Array args = ARRAY_DICT_INIT; + ADD(args, STRING_OBJ(cstr_as_string(xstrdup("rgb")))); + ADD(args, BOOLEAN_OBJ(value.data.boolean)); + rpc_send_event(ui_client_channel_id, "nvim_ui_set_option", args); + } } else if (strequal(name.data, "ttimeout")) { data->input.ttimeout = value.data.boolean; } else if (strequal(name.data, "ttimeoutlen")) { @@ -1595,6 +1612,10 @@ static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol, I // printed immediately without an intervening newline. final_column_wrap(ui); } + + // TODO: wat + //xfree((void *) chunk); + //xfree((void *) attrs); } static void invalidate(UI *ui, int top, int bot, int left, int right) @@ -1659,8 +1680,8 @@ static void tui_guess_size(UI *ui) height = DFLT_ROWS; } - data->bridge->bridge.width = ui->width = width; - data->bridge->bridge.height = ui->height = height; + ui->width = width; + ui->height = height; } static void unibi_goto(UI *ui, int row, int col) -- cgit From 43e8ec92de9e0850e7d202cb7ff9051bc408447e Mon Sep 17 00:00:00 2001 From: bfredl Date: Mon, 2 May 2022 21:10:01 +0200 Subject: fix(tui): more work in the TUI --- src/nvim/tui/input.c | 66 +++++++---------- src/nvim/tui/tui.c | 198 +++++++++++++++++---------------------------------- 2 files changed, 89 insertions(+), 175 deletions(-) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index ca1f7c25d7..c6066597f0 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -220,17 +220,11 @@ static void tinput_wait_enqueue(void **argv) const size_t len = rbuffer_size(input->key_buffer); String keys = { .data = xmallocz(len), .size = len }; rbuffer_read(input->key_buffer, keys.data, len); - if (ui_client_channel_id) { - Array args = ARRAY_DICT_INIT; - ADD(args, STRING_OBJ(keys)); // 'data' - ADD(args, BOOLEAN_OBJ(true)); // 'crlf' - ADD(args, INTEGER_OBJ(input->paste)); // 'phase' - rpc_send_event(ui_client_channel_id, "nvim_paste", args); - } else { - // TODO - // multiqueue_put(main_loop.events, tinput_paste_event, 3, - // keys.data, keys.size, (intptr_t)input->paste); - } + Array args = ARRAY_DICT_INIT; + ADD(args, STRING_OBJ(keys)); // 'data' + ADD(args, BOOLEAN_OBJ(true)); // 'crlf' + ADD(args, INTEGER_OBJ(input->paste)); // 'phase' + rpc_send_event(ui_client_channel_id, "nvim_paste", args); if (input->paste == 1) { // Paste phase: "continue" input->paste = 2; @@ -239,39 +233,22 @@ static void tinput_wait_enqueue(void **argv) } else { // enqueue input for the main thread or Nvim server RBUFFER_UNTIL_EMPTY(input->key_buffer, buf, len) { const String keys = { .data = buf, .size = len }; - size_t consumed; - if (ui_client_channel_id) { - Array args = ARRAY_DICT_INIT; - Error err = ERROR_INIT; - ADD(args, STRING_OBJ(copy_string(keys, NULL))); - // TODO(bfredl): could be non-blocking now with paste? - ArenaMem res_mem = NULL; - Object result = rpc_send_call(ui_client_channel_id, "nvim_input", args, &res_mem, &err); - consumed = result.type == kObjectTypeInteger ? (size_t)result.data.integer : 0; - arena_mem_free(res_mem); - } else { - // TODO - // consumed = input_enqueue(keys); - abort(); - } - if (consumed) { - rbuffer_consumed(input->key_buffer, consumed); - } + Array args = ARRAY_DICT_INIT; + ADD(args, STRING_OBJ(copy_string(keys, NULL))); + // NOTE: This is non-blocking and won't check partially processed input, + // but should be fine as all big sends are handled with nvim_paste, not nvim_input + rpc_send_event(ui_client_channel_id, "nvim_input", args); + rbuffer_consumed(input->key_buffer, len); rbuffer_reset(input->key_buffer); - if (consumed < len) { - break; - } } } } - static void tinput_flush(TermInput *input, bool wait_until_empty) { size_t drain_boundary = wait_until_empty ? 0 : 0xff; - // TODO: fuuuuuuuuuuuuuuu do { - tinput_wait_enqueue((void**)&input); + tinput_wait_enqueue((void **)&input); } while (rbuffer_size(input->key_buffer) > drain_boundary); } @@ -550,7 +527,8 @@ static bool handle_focus_event(TermInput *input) || !rbuffer_cmp(input->read_stream.buffer, "\x1b[O", 3))) { bool focus_gained = *rbuffer_get(input->read_stream.buffer, 2) == 'I'; // Advance past the sequence - + rbuffer_consumed(input->read_stream.buffer, 3); + Array args = ARRAY_DICT_INIT; ADD(args, BOOLEAN_OBJ(focus_gained)); rpc_send_event(ui_client_channel_id, "nvim_ui_set_focus", args); @@ -602,11 +580,13 @@ static HandleState handle_bracketed_paste(TermInput *input) static void set_bg(char *bgvalue) { - Array args = ARRAY_DICT_INIT; - ADD(args, STRING_OBJ(cstr_to_string("term_background"))); - ADD(args, STRING_OBJ(cstr_as_string(xstrdup(bgvalue)))); - - rpc_send_event(ui_client_channel_id, "nvim_ui_set_option", args); + if (ui_client_attached) { + Array args = ARRAY_DICT_INIT; + ADD(args, STRING_OBJ(cstr_to_string("term_background"))); + ADD(args, STRING_OBJ(cstr_as_string(xstrdup(bgvalue)))); + + rpc_send_event(ui_client_channel_id, "nvim_ui_set_option", args); + } } // During startup, tui.c requests the background color (see `ext.get_bg`). @@ -688,8 +668,10 @@ static HandleState handle_background_color(TermInput *input) double g = (double)rgb[1] / (double)rgb_max[1]; double b = (double)rgb[2] / (double)rgb_max[2]; double luminance = (0.299 * r) + (0.587 * g) + (0.114 * b); // CCIR 601 - char *bgvalue = luminance < 0.5 ? "dark" : "light"; + bool is_dark = luminance < 0.5; + char *bgvalue = is_dark ? "dark" : "light"; DLOG("bg response: %s", bgvalue); + ui_client_bg_respose = is_dark ? kTrue : kFalse; set_bg(bgvalue); input->waiting_for_bg_response = 0; } else if (!done && !bad) { diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 4634c77a1f..89ca77a09a 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -18,6 +18,7 @@ #include "nvim/api/private/helpers.h" #include "nvim/api/vim.h" #include "nvim/ascii.h" +#include "nvim/cursor_shape.h" #include "nvim/event/defs.h" #include "nvim/event/loop.h" #include "nvim/event/multiqueue.h" @@ -37,18 +38,15 @@ #include "nvim/os/input.h" #include "nvim/os/os.h" #include "nvim/os/signal.h" -#include "nvim/ui.h" -#include "nvim/vim.h" #ifdef MSWIN # include "nvim/os/os_win_console.h" #endif -#include "nvim/cursor_shape.h" -#include "nvim/macros.h" #include "nvim/tui/input.h" #include "nvim/tui/terminfo.h" #include "nvim/tui/tui.h" #include "nvim/ugrid.h" -#include "nvim/msgpack_rpc/channel.h" +#include "nvim/ui.h" +#include "nvim/vim.h" // Space reserved in two output buffers to make the cursor normal or invisible // when flushing. No existing terminal will require 32 bytes to do that. @@ -108,8 +106,8 @@ struct TUIData { uv_pipe_t pipe; } output_handle; bool out_isatty; - SignalWatcher winch_handle, cont_handle; - bool cont_received; + SignalWatcher winch_handle; + uv_timer_t startup_delay_timer; UGrid grid; kvec_t(Rect) invalid_regions; int row, col; @@ -164,7 +162,6 @@ struct TUIData { static int got_winch = 0; static bool cursor_style_enabled = false; -char *termname_local; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "tui/tui.c.generated.h" #endif @@ -199,8 +196,30 @@ UI *tui_start(void) CLEAR_FIELD(ui->ui_ext); ui->ui_ext[kUILinegrid] = true; ui->ui_ext[kUITermColors] = true; - - tui_main(ui); + + TUIData *data = xcalloc(1, sizeof(TUIData)); + ui->data = data; + data->ui = ui; + data->is_starting = true; + data->screenshot = NULL; + data->stopped = false; + data->loop = &main_loop; + kv_init(data->invalid_regions); + signal_watcher_init(data->loop, &data->winch_handle, ui); + + // TODO(bfredl): zero hl is empty, send this explicitly? + kv_push(data->attrs, HLATTRS_INIT); + + data->input.tk_ti_hook_fn = tui_tk_ti_getstr; + tinput_init(&data->input, &main_loop); + ugrid_init(&data->grid); + tui_terminal_start(ui); + + uv_timer_init(&data->loop->uv, &data->startup_delay_timer); + data->startup_delay_timer.data = ui; + uv_timer_start(&data->startup_delay_timer, after_startup_cb, + 100, 0); + ui_attach_impl(ui, 0); return ui; @@ -291,18 +310,18 @@ static void terminfo_start(UI *ui) #endif // Set up unibilium/terminfo. - termname_local = NULL; + ui_client_termname = NULL; if (term) { - os_env_var_lock(); data->ut = unibi_from_term(term); - os_env_var_unlock(); if (data->ut) { - termname_local = xstrdup(term); - data->term = xstrdup(term); + ui_client_termname = xstrdup(term); + if (!data->term) { + data->term = xstrdup(term); + } } } if (!data->ut) { - data->ut = terminfo_from_builtin(term, &termname_local); + data->ut = terminfo_from_builtin(term, &ui_client_termname); } // None of the following work over SSH; see :help TERM . @@ -437,13 +456,18 @@ static void tui_terminal_start(UI *ui) { TUIData *data = ui->data; data->print_attr_id = -1; - ugrid_init(&data->grid); terminfo_start(ui); tui_guess_size(ui); signal_watcher_start(&data->winch_handle, sigwinch_cb, SIGWINCH); tinput_start(&data->input); } +static void after_startup_cb(uv_timer_t *handle) +{ + UI *ui = handle->data; + tui_terminal_after_startup(ui); +} + static void tui_terminal_after_startup(UI *ui) FUNC_ATTR_NONNULL_ALL { @@ -455,26 +479,30 @@ static void tui_terminal_after_startup(UI *ui) flush_buf(ui); } +/// stop the terminal but allow it to restart later (like after suspend) static void tui_terminal_stop(UI *ui) { TUIData *data = ui->data; if (uv_is_closing(STRUCT_CAST(uv_handle_t, &data->output_handle))) { // Race between SIGCONT (tui.c) and SIGHUP (os/signal.c)? #8075 ELOG("TUI already stopped (race?)"); - data->stopped = true; + data->stopped = true; return; } tinput_stop(&data->input); signal_watcher_stop(&data->winch_handle); terminfo_stop(ui); - ugrid_free(&data->grid); } static void tui_stop(UI *ui) { - tui_terminal_stop(ui); TUIData *data = ui->data; + tui_terminal_stop(ui); + tinput_destroy(&data->input); data->stopped = true; + signal_watcher_close(&data->winch_handle, NULL); + uv_close((uv_handle_t *)&data->startup_delay_timer, NULL); + ui_detach_impl(ui, 0); } /// Returns true if UI `ui` is stopped. @@ -484,57 +512,11 @@ static bool tui_is_stopped(UI *ui) return data->stopped; } -// Main function for TUI -static void tui_main(UI *ui) -{ - TUIData *data = xcalloc(1, sizeof(TUIData)); - ui->data = data; - data->ui = ui; - data->is_starting = true; - data->screenshot = NULL; - data->stopped = false; - data->loop = &main_loop; - kv_init(data->invalid_regions); - signal_watcher_init(data->loop, &data->winch_handle, ui); - signal_watcher_init(data->loop, &data->cont_handle, data); -#ifdef UNIX - signal_watcher_start(&data->cont_handle, sigcont_cb, SIGCONT); -#endif - - // TODO(bfredl): zero hl is empty, send this explicitly? - kv_push(data->attrs, HLATTRS_INIT); - - data->input.tk_ti_hook_fn = tui_tk_ti_getstr; - tinput_init(&data->input, &main_loop); - tui_terminal_start(ui); - // TODO: borked! - // loop_schedule(&main_loop, event_create(show_termcap_event, 1, data->ut)); - -} - -void tui_execute(void) - FUNC_ATTR_NORETURN +#ifdef EXITFREE +void tui_free_all_mem(UI *ui) { - UI *ui = ui_get_by_index(1); - LOOP_PROCESS_EVENTS(&main_loop, main_loop.events, -1); - tui_io_driven_loop(ui); - tui_exit_safe(ui); - getout(0); -} - -// Doesn't return until the TUI is closed (by call of tui_stop()) -static void tui_io_driven_loop(UI *ui){ - // "Passive" (I/O-driven) loop: TUI process's "main loop". - while (!tui_is_stopped(ui)) { - loop_poll_events(&main_loop, -1); - } -} - -// TODO: call me when EXITFREE -#if 0 -static void tui_data_destroy(void **argv) { - UI *ui = argv[0]; TUIData *data = ui->data; + ugrid_free(&data->grid); kv_destroy(data->invalid_regions); kv_destroy(data->attrs); xfree(data->space_buf); @@ -544,24 +526,6 @@ static void tui_data_destroy(void **argv) { } #endif -void tui_exit_safe(UI *ui) { - TUIData *data = ui->data; - if (!tui_is_stopped(ui)) { - tui_stop(ui); - } - tinput_destroy(&data->input); - signal_watcher_stop(&data->cont_handle); - signal_watcher_close(&data->cont_handle, NULL); - signal_watcher_close(&data->winch_handle, NULL); -} - -#ifdef UNIX -static void sigcont_cb(SignalWatcher *watcher, int signum, void *data) -{ - ((TUIData *)data)->cont_received = true; -} -#endif - static void sigwinch_cb(SignalWatcher *watcher, int signum, void *data) { got_winch++; @@ -571,7 +535,6 @@ static void sigwinch_cb(SignalWatcher *watcher, int signum, void *data) } tui_guess_size(ui); - ui_schedule_refresh(); } static bool attrs_differ(UI *ui, int id1, int id2, bool rgb) @@ -1408,7 +1371,6 @@ static void tui_flush(UI *ui) flush_buf(ui); } -#if 0 /// Dumps termcap info to the messages area, if 'verbose' >= 3. static void show_verbose_terminfo(TUIData *data) { @@ -1431,35 +1393,15 @@ static void show_verbose_terminfo(TUIData *data) ADD(end_fold, STRING_OBJ(cstr_to_string("Title"))); ADD(chunks, ARRAY_OBJ(end_fold)); - if (ui_client_channel_id) { - Array args = ARRAY_DICT_INIT; - ADD(args, ARRAY_OBJ(chunks)); - ADD(args, BOOLEAN_OBJ(true)); // history - Dictionary opts = ARRAY_DICT_INIT; - PUT(opts, "verbose", BOOLEAN_OBJ(true)); - ADD(args, DICTIONARY_OBJ(opts)); - rpc_send_event(ui_client_channel_id, "nvim_echo", args); - } else { - loop_schedule_deferred(&main_loop, event_create(verbose_terminfo_event, 2, - chunks.items, chunks.size)); - } + Array args = ARRAY_DICT_INIT; + ADD(args, ARRAY_OBJ(chunks)); + ADD(args, BOOLEAN_OBJ(true)); // history + Dictionary opts = ARRAY_DICT_INIT; + PUT(opts, "verbose", BOOLEAN_OBJ(true)); + ADD(args, DICTIONARY_OBJ(opts)); + rpc_send_event(ui_client_channel_id, "nvim_echo", args); } -static void verbose_terminfo_event(void **argv) -{ - Array chunks = { .items = argv[0], .size = (size_t)argv[1] }; - Dict(echo_opts) opts = { .verbose = BOOLEAN_OBJ(true) }; - Error err = ERROR_INIT; - nvim_echo(chunks, true, &opts, &err); - api_free_array(chunks); - if (ERROR_SET(&err)) { - fprintf(stderr, "TUI bought the farm: %s\n", err.msg); - exit(1); - } - api_clear_error(&err); -} -#endif - #ifdef UNIX static void suspend_event(void **argv) { @@ -1467,15 +1409,9 @@ static void suspend_event(void **argv) TUIData *data = ui->data; bool enable_mouse = data->mouse_enabled; tui_terminal_stop(ui); - data->cont_received = false; stream_set_blocking(input_global_fd(), true); // normalize stream (#2598) - signal_stop(); - kill(0, SIGTSTP); // make TUI process run in background - signal_start(); - while (!data->cont_received) { - // poll the event loop until SIGCONT is received - loop_poll_events(data->loop, -1); - } + + kill(0, SIGTSTP); tui_terminal_start(ui); tui_terminal_after_startup(ui); @@ -1488,16 +1424,13 @@ static void suspend_event(void **argv) static void tui_suspend(UI *ui) { - TUIData *data = ui->data; +// on a non-UNIX system, this is a no-op #ifdef UNIX // kill(0, SIGTSTP) won't stop the UI thread, so we must poll for SIGCONT // before continuing. This is done in another callback to avoid // loop_poll_events recursion - multiqueue_put_event(data->loop->fast_events, + multiqueue_put_event(resize_events, event_create(suspend_event, 1, ui)); -#else - // Resume the main thread as suspending isn't implemented. - CONTINUE(data->bridge); #endif } @@ -1612,10 +1545,6 @@ static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol, I // printed immediately without an intervening newline. final_column_wrap(ui); } - - // TODO: wat - //xfree((void *) chunk); - //xfree((void *) attrs); } static void invalidate(UI *ui, int top, int bot, int left, int right) @@ -1682,6 +1611,9 @@ static void tui_guess_size(UI *ui) ui->width = width; ui->height = height; + + // TODO(bfredl): only if different from last value + ui_schedule_refresh(); } static void unibi_goto(UI *ui, int row, int col) -- cgit From c590641febf4d03e89c46f8e7ef4c3fb2a455520 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 1 Jan 2023 23:18:19 +0800 Subject: fix(tui): do not set ui_client_termname if it is already set (#21607) It is fine to initialize ui_client_termname to NULL as it is only used after tui_start(). --- src/nvim/tui/tui.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 984f96354a..0d01cd44cd 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -310,11 +310,12 @@ static void terminfo_start(UI *ui) #endif // Set up unibilium/terminfo. - ui_client_termname = NULL; if (term) { data->ut = unibi_from_term(term); if (data->ut) { - ui_client_termname = xstrdup(term); + if (!ui_client_termname) { + ui_client_termname = xstrdup(term); + } if (!data->term) { data->term = xstrdup(term); } -- cgit From b2295ac4ec80e141628cea33ebee81c96e168989 Mon Sep 17 00:00:00 2001 From: bfredl Date: Tue, 3 Jan 2023 15:24:41 +0100 Subject: refactor(api): do not allocate temporaries for internal events --- src/nvim/tui/input.c | 24 ++++++++++++------------ src/nvim/tui/tui.c | 7 ++++--- 2 files changed, 16 insertions(+), 15 deletions(-) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index c6066597f0..b837a380d5 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -220,11 +220,12 @@ static void tinput_wait_enqueue(void **argv) const size_t len = rbuffer_size(input->key_buffer); String keys = { .data = xmallocz(len), .size = len }; rbuffer_read(input->key_buffer, keys.data, len); - Array args = ARRAY_DICT_INIT; - ADD(args, STRING_OBJ(keys)); // 'data' - ADD(args, BOOLEAN_OBJ(true)); // 'crlf' - ADD(args, INTEGER_OBJ(input->paste)); // 'phase' + MAXSIZE_TEMP_ARRAY(args, 3); + ADD_C(args, STRING_OBJ(keys)); // 'data' + ADD_C(args, BOOLEAN_OBJ(true)); // 'crlf' + ADD_C(args, INTEGER_OBJ(input->paste)); // 'phase' rpc_send_event(ui_client_channel_id, "nvim_paste", args); + api_free_string(keys); if (input->paste == 1) { // Paste phase: "continue" input->paste = 2; @@ -233,8 +234,8 @@ static void tinput_wait_enqueue(void **argv) } else { // enqueue input for the main thread or Nvim server RBUFFER_UNTIL_EMPTY(input->key_buffer, buf, len) { const String keys = { .data = buf, .size = len }; - Array args = ARRAY_DICT_INIT; - ADD(args, STRING_OBJ(copy_string(keys, NULL))); + MAXSIZE_TEMP_ARRAY(args, 1); + ADD_C(args, STRING_OBJ(keys)); // NOTE: This is non-blocking and won't check partially processed input, // but should be fine as all big sends are handled with nvim_paste, not nvim_input rpc_send_event(ui_client_channel_id, "nvim_input", args); @@ -529,8 +530,8 @@ static bool handle_focus_event(TermInput *input) // Advance past the sequence rbuffer_consumed(input->read_stream.buffer, 3); - Array args = ARRAY_DICT_INIT; - ADD(args, BOOLEAN_OBJ(focus_gained)); + MAXSIZE_TEMP_ARRAY(args, 1); + ADD_C(args, BOOLEAN_OBJ(focus_gained)); rpc_send_event(ui_client_channel_id, "nvim_ui_set_focus", args); return true; } @@ -581,10 +582,9 @@ static HandleState handle_bracketed_paste(TermInput *input) static void set_bg(char *bgvalue) { if (ui_client_attached) { - Array args = ARRAY_DICT_INIT; - ADD(args, STRING_OBJ(cstr_to_string("term_background"))); - ADD(args, STRING_OBJ(cstr_as_string(xstrdup(bgvalue)))); - + MAXSIZE_TEMP_ARRAY(args, 2); + ADD_C(args, STRING_OBJ(cstr_as_string("term_background"))); + ADD_C(args, STRING_OBJ(cstr_as_string(bgvalue))); rpc_send_event(ui_client_channel_id, "nvim_ui_set_option", args); } } diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 0d01cd44cd..3205547324 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -1401,6 +1401,7 @@ static void show_verbose_terminfo(TUIData *data) PUT(opts, "verbose", BOOLEAN_OBJ(true)); ADD(args, DICTIONARY_OBJ(opts)); rpc_send_event(ui_client_channel_id, "nvim_echo", args); + api_free_array(args); } #ifdef UNIX @@ -1493,9 +1494,9 @@ static void tui_option_set(UI *ui, String name, Object value) invalidate(ui, 0, data->grid.height, 0, data->grid.width); if (ui_client_channel_id) { - Array args = ARRAY_DICT_INIT; - ADD(args, STRING_OBJ(cstr_as_string(xstrdup("rgb")))); - ADD(args, BOOLEAN_OBJ(value.data.boolean)); + MAXSIZE_TEMP_ARRAY(args, 2); + ADD_C(args, STRING_OBJ(cstr_as_string("rgb"))); + ADD_C(args, BOOLEAN_OBJ(value.data.boolean)); rpc_send_event(ui_client_channel_id, "nvim_ui_set_option", args); } } else if (strequal(name.data, "ttimeout")) { -- cgit From 89232b8b4890824f93121483626af582f13758fe Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 5 Jan 2023 00:25:25 +0800 Subject: fix(tui): make a copy of data->params before unibi_format() (#21643) Problem: When unibi_format() modifies params and data->buf overflows, unibi_format() is called again, causing the params to be modified twice. This can happen for escapes sequences that use the %i terminfo format specifier (e.g. cursor_address), which makes unibi_format() increase the param by 1. Solution: Make a copy of data->params before calling unibi_format(). --- src/nvim/tui/tui.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 3205547324..066567a87f 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -1635,11 +1635,13 @@ static void unibi_goto(UI *ui, int row, int col) } \ if (str) { \ unibi_var_t vars[26 + 26]; \ + unibi_var_t params[9]; \ size_t orig_pos = data->bufpos; \ memset(&vars, 0, sizeof(vars)); \ data->cork = true; \ retry: \ - unibi_format(vars, vars + 26, str, data->params, out, ui, pad, ui); \ + memcpy(params, data->params, sizeof(params)); \ + unibi_format(vars, vars + 26, str, params, out, ui, pad, ui); \ if (data->overflow) { \ data->bufpos = orig_pos; \ flush_buf(ui); \ -- cgit From 47ba78f89a1f0bba8168b4408bc55a3024d5ab97 Mon Sep 17 00:00:00 2001 From: bfredl Date: Fri, 30 Dec 2022 22:17:01 +0100 Subject: refactor(ui): devirtualize the ui layer - The defined interface for the UI is only the RPC protocol. The original UI interface as an array of function pointers fill no function. - On the server, all the UI:s are all RPC channels. - ui.c is only used on the server. - The compositor is a preprocessing step for single-grid UI:s - on the client, ui_client and tui talk directly to each other - we still do module separation, as ui_client.c could form the basis of a libnvim client module later. Items for later PR:s - vim.ui_attach is still an unhappy child, reconsider based on plugin experience. - the flags in ui_events.in.h are still a mess. Can be simplified now. - UX for remote attachment needs more work. - startup for client can be simplified further (think of the millisecs we can save) --- src/nvim/tui/tui.c | 1469 +++++++++++++++++++++++++--------------------------- 1 file changed, 695 insertions(+), 774 deletions(-) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 066567a87f..162f54b6c1 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -1,7 +1,7 @@ // This is an open source non-commercial project. Dear PVS-Studio, please check // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// Terminal UI functions. Invoked (by UI_CALL) on the UI process. +// Terminal UI functions. Invoked (by ui_client.c) on the UI process. #include #include @@ -89,7 +89,6 @@ typedef struct { } Rect; struct TUIData { - UI *ui; Loop *loop; unibi_var_t params[9]; char buf[OUTBUF_SIZE]; @@ -158,6 +157,9 @@ struct TUIData { } unibi_ext; char *space_buf; bool stopped; + int width; + int height; + bool rgb; }; static int got_winch = 0; @@ -166,144 +168,113 @@ static bool cursor_style_enabled = false; # include "tui/tui.c.generated.h" #endif -UI *tui_start(void) -{ - UI *ui = xcalloc(1, sizeof(UI)); // Freed by tui_data_destroy(). - ui->stop = tui_stop; - ui->grid_resize = tui_grid_resize; - ui->grid_clear = tui_grid_clear; - ui->grid_cursor_goto = tui_grid_cursor_goto; - ui->mode_info_set = tui_mode_info_set; - ui->update_menu = tui_update_menu; - ui->busy_start = tui_busy_start; - ui->busy_stop = tui_busy_stop; - ui->mouse_on = tui_mouse_on; - ui->mouse_off = tui_mouse_off; - ui->mode_change = tui_mode_change; - ui->grid_scroll = tui_grid_scroll; - ui->hl_attr_define = tui_hl_attr_define; - ui->bell = tui_bell; - ui->visual_bell = tui_visual_bell; - ui->default_colors_set = tui_default_colors_set; - ui->flush = tui_flush; - ui->suspend = tui_suspend; - ui->set_title = tui_set_title; - ui->set_icon = tui_set_icon; - ui->screenshot = tui_screenshot; - ui->option_set = tui_option_set; - ui->raw_line = tui_raw_line; - - CLEAR_FIELD(ui->ui_ext); - ui->ui_ext[kUILinegrid] = true; - ui->ui_ext[kUITermColors] = true; - - TUIData *data = xcalloc(1, sizeof(TUIData)); - ui->data = data; - data->ui = ui; - data->is_starting = true; - data->screenshot = NULL; - data->stopped = false; - data->loop = &main_loop; - kv_init(data->invalid_regions); - signal_watcher_init(data->loop, &data->winch_handle, ui); +TUIData *tui_start(int *width, int *height, char **term) +{ + TUIData *tui = xcalloc(1, sizeof(TUIData)); + tui->is_starting = true; + tui->screenshot = NULL; + tui->stopped = false; + tui->loop = &main_loop; + kv_init(tui->invalid_regions); + signal_watcher_init(tui->loop, &tui->winch_handle, tui); // TODO(bfredl): zero hl is empty, send this explicitly? - kv_push(data->attrs, HLATTRS_INIT); + kv_push(tui->attrs, HLATTRS_INIT); - data->input.tk_ti_hook_fn = tui_tk_ti_getstr; - tinput_init(&data->input, &main_loop); - ugrid_init(&data->grid); - tui_terminal_start(ui); + tui->input.tk_ti_hook_fn = tui_tk_ti_getstr; + tinput_init(&tui->input, &main_loop); + ugrid_init(&tui->grid); + tui_terminal_start(tui); - uv_timer_init(&data->loop->uv, &data->startup_delay_timer); - data->startup_delay_timer.data = ui; - uv_timer_start(&data->startup_delay_timer, after_startup_cb, + uv_timer_init(&tui->loop->uv, &tui->startup_delay_timer); + tui->startup_delay_timer.data = tui; + uv_timer_start(&tui->startup_delay_timer, after_startup_cb, 100, 0); - ui_attach_impl(ui, 0); - - return ui; + loop_poll_events(&main_loop, 1); + *width = tui->width; + *height = tui->height; + *term = tui->term; + return tui; } -void tui_enable_extkeys(TUIData *data) +void tui_enable_extkeys(TUIData *tui) { - TermInput input = data->input; - unibi_term *ut = data->ut; - UI *ui = data->ui; + TermInput input = tui->input; + unibi_term *ut = tui->ut; switch (input.extkeys_type) { case kExtkeysCSIu: - data->unibi_ext.enable_extended_keys = (int)unibi_add_ext_str(ut, "ext.enable_extended_keys", - "\x1b[>1u"); - data->unibi_ext.disable_extended_keys = (int)unibi_add_ext_str(ut, "ext.disable_extended_keys", - "\x1b[<1u"); + tui->unibi_ext.enable_extended_keys = (int)unibi_add_ext_str(ut, "ext.enable_extended_keys", + "\x1b[>1u"); + tui->unibi_ext.disable_extended_keys = (int)unibi_add_ext_str(ut, "ext.disable_extended_keys", + "\x1b[<1u"); break; case kExtkeysXterm: - data->unibi_ext.enable_extended_keys = (int)unibi_add_ext_str(ut, "ext.enable_extended_keys", - "\x1b[>4;2m"); - data->unibi_ext.disable_extended_keys = (int)unibi_add_ext_str(ut, "ext.disable_extended_keys", - "\x1b[>4;0m"); + tui->unibi_ext.enable_extended_keys = (int)unibi_add_ext_str(ut, "ext.enable_extended_keys", + "\x1b[>4;2m"); + tui->unibi_ext.disable_extended_keys = (int)unibi_add_ext_str(ut, "ext.disable_extended_keys", + "\x1b[>4;0m"); break; default: break; } - unibi_out_ext(ui, data->unibi_ext.enable_extended_keys); + unibi_out_ext(tui, tui->unibi_ext.enable_extended_keys); } -static size_t unibi_pre_fmt_str(TUIData *data, unsigned int unibi_index, char *buf, size_t len) +static size_t unibi_pre_fmt_str(TUIData *tui, unsigned int unibi_index, char *buf, size_t len) { - const char *str = unibi_get_str(data->ut, unibi_index); + const char *str = unibi_get_str(tui->ut, unibi_index); if (!str) { return 0U; } - return unibi_run(str, data->params, buf, len); -} - -static void terminfo_start(UI *ui) -{ - TUIData *data = ui->data; - data->scroll_region_is_full_screen = true; - data->bufpos = 0; - data->default_attr = false; - data->can_clear_attr = false; - data->is_invisible = true; - data->want_invisible = false; - data->busy = false; - data->cork = false; - data->overflow = false; - data->set_cursor_color_as_str = false; - data->cursor_color_changed = false; - data->showing_mode = SHAPE_IDX_N; - data->unibi_ext.enable_mouse = -1; - data->unibi_ext.disable_mouse = -1; - data->unibi_ext.enable_mouse_move = -1; - data->unibi_ext.disable_mouse_move = -1; - data->unibi_ext.set_cursor_color = -1; - data->unibi_ext.reset_cursor_color = -1; - data->unibi_ext.enable_bracketed_paste = -1; - data->unibi_ext.disable_bracketed_paste = -1; - data->unibi_ext.enter_strikethrough_mode = -1; - data->unibi_ext.enable_lr_margin = -1; - data->unibi_ext.disable_lr_margin = -1; - data->unibi_ext.enable_focus_reporting = -1; - data->unibi_ext.disable_focus_reporting = -1; - data->unibi_ext.resize_screen = -1; - data->unibi_ext.reset_scroll_region = -1; - data->unibi_ext.set_cursor_style = -1; - data->unibi_ext.reset_cursor_style = -1; - data->unibi_ext.get_bg = -1; - data->unibi_ext.set_underline_color = -1; - data->unibi_ext.enable_extended_keys = -1; - data->unibi_ext.disable_extended_keys = -1; - data->unibi_ext.get_extkeys = -1; - data->out_fd = STDOUT_FILENO; - data->out_isatty = os_isatty(data->out_fd); - data->input.tui_data = data; + return unibi_run(str, tui->params, buf, len); +} + +static void terminfo_start(TUIData *tui) +{ + tui->scroll_region_is_full_screen = true; + tui->bufpos = 0; + tui->default_attr = false; + tui->can_clear_attr = false; + tui->is_invisible = true; + tui->want_invisible = false; + tui->busy = false; + tui->cork = false; + tui->overflow = false; + tui->set_cursor_color_as_str = false; + tui->cursor_color_changed = false; + tui->showing_mode = SHAPE_IDX_N; + tui->unibi_ext.enable_mouse = -1; + tui->unibi_ext.disable_mouse = -1; + tui->unibi_ext.enable_mouse_move = -1; + tui->unibi_ext.disable_mouse_move = -1; + tui->unibi_ext.set_cursor_color = -1; + tui->unibi_ext.reset_cursor_color = -1; + tui->unibi_ext.enable_bracketed_paste = -1; + tui->unibi_ext.disable_bracketed_paste = -1; + tui->unibi_ext.enter_strikethrough_mode = -1; + tui->unibi_ext.enable_lr_margin = -1; + tui->unibi_ext.disable_lr_margin = -1; + tui->unibi_ext.enable_focus_reporting = -1; + tui->unibi_ext.disable_focus_reporting = -1; + tui->unibi_ext.resize_screen = -1; + tui->unibi_ext.reset_scroll_region = -1; + tui->unibi_ext.set_cursor_style = -1; + tui->unibi_ext.reset_cursor_style = -1; + tui->unibi_ext.get_bg = -1; + tui->unibi_ext.set_underline_color = -1; + tui->unibi_ext.enable_extended_keys = -1; + tui->unibi_ext.disable_extended_keys = -1; + tui->unibi_ext.get_extkeys = -1; + tui->out_fd = STDOUT_FILENO; + tui->out_isatty = os_isatty(tui->out_fd); + tui->input.tui_data = tui; const char *term = os_getenv("TERM"); #ifdef MSWIN - os_tty_guess_term(&term, data->out_fd); + os_tty_guess_term(&term, tui->out_fd); os_setenv("TERM", term, 1); // Old os_getenv() pointer is invalid after os_setenv(), fetch it again. term = os_getenv("TERM"); @@ -311,18 +282,15 @@ static void terminfo_start(UI *ui) // Set up unibilium/terminfo. if (term) { - data->ut = unibi_from_term(term); - if (data->ut) { - if (!ui_client_termname) { - ui_client_termname = xstrdup(term); - } - if (!data->term) { - data->term = xstrdup(term); + tui->ut = unibi_from_term(term); + if (tui->ut) { + if (!tui->term) { + tui->term = xstrdup(term); } } } - if (!data->ut) { - data->ut = terminfo_from_builtin(term, &ui_client_termname); + if (!tui->ut) { + tui->ut = terminfo_from_builtin(term, &tui->term); } // None of the following work over SSH; see :help TERM . @@ -340,59 +308,59 @@ static void terminfo_start(UI *ui) long konsolev = konsolev_env ? strtol(konsolev_env, NULL, 10) : (konsole ? 1 : 0); - patch_terminfo_bugs(data, term, colorterm, vtev, konsolev, iterm_env, nsterm); - augment_terminfo(data, term, vtev, konsolev, iterm_env, nsterm); - data->can_change_scroll_region = - !!unibi_get_str(data->ut, unibi_change_scroll_region); - data->can_set_lr_margin = - !!unibi_get_str(data->ut, unibi_set_lr_margin); - data->can_set_left_right_margin = - !!unibi_get_str(data->ut, unibi_set_left_margin_parm) - && !!unibi_get_str(data->ut, unibi_set_right_margin_parm); - data->can_scroll = - !!unibi_get_str(data->ut, unibi_delete_line) - && !!unibi_get_str(data->ut, unibi_parm_delete_line) - && !!unibi_get_str(data->ut, unibi_insert_line) - && !!unibi_get_str(data->ut, unibi_parm_insert_line); - data->can_erase_chars = !!unibi_get_str(data->ut, unibi_erase_chars); - data->immediate_wrap_after_last_column = + patch_terminfo_bugs(tui, term, colorterm, vtev, konsolev, iterm_env, nsterm); + augment_terminfo(tui, term, vtev, konsolev, iterm_env, nsterm); + tui->can_change_scroll_region = + !!unibi_get_str(tui->ut, unibi_change_scroll_region); + tui->can_set_lr_margin = + !!unibi_get_str(tui->ut, unibi_set_lr_margin); + tui->can_set_left_right_margin = + !!unibi_get_str(tui->ut, unibi_set_left_margin_parm) + && !!unibi_get_str(tui->ut, unibi_set_right_margin_parm); + tui->can_scroll = + !!unibi_get_str(tui->ut, unibi_delete_line) + && !!unibi_get_str(tui->ut, unibi_parm_delete_line) + && !!unibi_get_str(tui->ut, unibi_insert_line) + && !!unibi_get_str(tui->ut, unibi_parm_insert_line); + tui->can_erase_chars = !!unibi_get_str(tui->ut, unibi_erase_chars); + tui->immediate_wrap_after_last_column = terminfo_is_term_family(term, "conemu") || terminfo_is_term_family(term, "cygwin") || terminfo_is_term_family(term, "win32con") || terminfo_is_term_family(term, "interix"); - data->bce = unibi_get_bool(data->ut, unibi_back_color_erase); - data->normlen = unibi_pre_fmt_str(data, unibi_cursor_normal, - data->norm, sizeof data->norm); - data->invislen = unibi_pre_fmt_str(data, unibi_cursor_invisible, - data->invis, sizeof data->invis); + tui->bce = unibi_get_bool(tui->ut, unibi_back_color_erase); + tui->normlen = unibi_pre_fmt_str(tui, unibi_cursor_normal, + tui->norm, sizeof tui->norm); + tui->invislen = unibi_pre_fmt_str(tui, unibi_cursor_invisible, + tui->invis, sizeof tui->invis); // Set 't_Co' from the result of unibilium & fix_terminfo. - t_colors = unibi_get_num(data->ut, unibi_max_colors); + t_colors = unibi_get_num(tui->ut, unibi_max_colors); // Enter alternate screen, save title, and clear. // NOTE: Do this *before* changing terminal settings. #6433 - unibi_out(ui, unibi_enter_ca_mode); + unibi_out(tui, unibi_enter_ca_mode); // Save title/icon to the "stack". #4063 - unibi_out_ext(ui, data->unibi_ext.save_title); - unibi_out(ui, unibi_keypad_xmit); - unibi_out(ui, unibi_clear_screen); + unibi_out_ext(tui, tui->unibi_ext.save_title); + unibi_out(tui, unibi_keypad_xmit); + unibi_out(tui, unibi_clear_screen); // Ask the terminal to send us the background color. - data->input.waiting_for_bg_response = 5; - unibi_out_ext(ui, data->unibi_ext.get_bg); + tui->input.waiting_for_bg_response = 5; + unibi_out_ext(tui, tui->unibi_ext.get_bg); // Enable bracketed paste - unibi_out_ext(ui, data->unibi_ext.enable_bracketed_paste); + unibi_out_ext(tui, tui->unibi_ext.enable_bracketed_paste); // Query the terminal to see if it supports CSI u - data->input.waiting_for_csiu_response = 5; - unibi_out_ext(ui, data->unibi_ext.get_extkeys); + tui->input.waiting_for_csiu_response = 5; + unibi_out_ext(tui, tui->unibi_ext.get_extkeys); int ret; - uv_loop_init(&data->write_loop); - if (data->out_isatty) { - ret = uv_tty_init(&data->write_loop, &data->output_handle.tty, data->out_fd, 0); + uv_loop_init(&tui->write_loop); + if (tui->out_isatty) { + ret = uv_tty_init(&tui->write_loop, &tui->output_handle.tty, tui->out_fd, 0); if (ret) { ELOG("uv_tty_init failed: %s", uv_strerror(ret)); } #ifdef MSWIN - ret = uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_RAW); + ret = uv_tty_set_mode(&tui->output_handle.tty, UV_TTY_MODE_RAW); if (ret) { ELOG("uv_tty_set_mode failed: %s", uv_strerror(ret)); } @@ -400,7 +368,7 @@ static void terminfo_start(UI *ui) int retry_count = 10; // A signal may cause uv_tty_set_mode() to fail (e.g., SIGCONT). Retry a // few times. #12322 - while ((ret = uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_IO)) == UV_EINTR + while ((ret = uv_tty_set_mode(&tui->output_handle.tty, UV_TTY_MODE_IO)) == UV_EINTR && retry_count > 0) { retry_count--; } @@ -409,145 +377,134 @@ static void terminfo_start(UI *ui) } #endif } else { - ret = uv_pipe_init(&data->write_loop, &data->output_handle.pipe, 0); + ret = uv_pipe_init(&tui->write_loop, &tui->output_handle.pipe, 0); if (ret) { ELOG("uv_pipe_init failed: %s", uv_strerror(ret)); } - ret = uv_pipe_open(&data->output_handle.pipe, data->out_fd); + ret = uv_pipe_open(&tui->output_handle.pipe, tui->out_fd); if (ret) { ELOG("uv_pipe_open failed: %s", uv_strerror(ret)); } } - flush_buf(ui); + flush_buf(tui); } -static void terminfo_stop(UI *ui) +static void terminfo_stop(TUIData *tui) { - TUIData *data = ui->data; // Destroy output stuff - tui_mode_change(ui, (String)STRING_INIT, SHAPE_IDX_N); - tui_mouse_off(ui); - unibi_out(ui, unibi_exit_attribute_mode); + tui_mode_change(tui, (String)STRING_INIT, SHAPE_IDX_N); + tui_mouse_off(tui); + unibi_out(tui, unibi_exit_attribute_mode); // Reset cursor to normal before exiting alternate screen. - unibi_out(ui, unibi_cursor_normal); - unibi_out(ui, unibi_keypad_local); + unibi_out(tui, unibi_cursor_normal); + unibi_out(tui, unibi_keypad_local); // Disable extended keys before exiting alternate screen. - unibi_out_ext(ui, data->unibi_ext.disable_extended_keys); - unibi_out(ui, unibi_exit_ca_mode); + unibi_out_ext(tui, tui->unibi_ext.disable_extended_keys); + unibi_out(tui, unibi_exit_ca_mode); // Restore title/icon from the "stack". #4063 - unibi_out_ext(ui, data->unibi_ext.restore_title); - if (data->cursor_color_changed) { - unibi_out_ext(ui, data->unibi_ext.reset_cursor_color); + unibi_out_ext(tui, tui->unibi_ext.restore_title); + if (tui->cursor_color_changed) { + unibi_out_ext(tui, tui->unibi_ext.reset_cursor_color); } // Disable bracketed paste - unibi_out_ext(ui, data->unibi_ext.disable_bracketed_paste); + unibi_out_ext(tui, tui->unibi_ext.disable_bracketed_paste); // Disable focus reporting - unibi_out_ext(ui, data->unibi_ext.disable_focus_reporting); - flush_buf(ui); + unibi_out_ext(tui, tui->unibi_ext.disable_focus_reporting); + flush_buf(tui); uv_tty_reset_mode(); - uv_close((uv_handle_t *)&data->output_handle, NULL); - uv_run(&data->write_loop, UV_RUN_DEFAULT); - if (uv_loop_close(&data->write_loop)) { + uv_close((uv_handle_t *)&tui->output_handle, NULL); + uv_run(&tui->write_loop, UV_RUN_DEFAULT); + if (uv_loop_close(&tui->write_loop)) { abort(); } - unibi_destroy(data->ut); + unibi_destroy(tui->ut); } -static void tui_terminal_start(UI *ui) +static void tui_terminal_start(TUIData *tui) { - TUIData *data = ui->data; - data->print_attr_id = -1; - terminfo_start(ui); - tui_guess_size(ui); - signal_watcher_start(&data->winch_handle, sigwinch_cb, SIGWINCH); - tinput_start(&data->input); + tui->print_attr_id = -1; + terminfo_start(tui); + tui_guess_size(tui); + signal_watcher_start(&tui->winch_handle, sigwinch_cb, SIGWINCH); + tinput_start(&tui->input); } static void after_startup_cb(uv_timer_t *handle) { - UI *ui = handle->data; - tui_terminal_after_startup(ui); + TUIData *tui = handle->data; + tui_terminal_after_startup(tui); } -static void tui_terminal_after_startup(UI *ui) +static void tui_terminal_after_startup(TUIData *tui) FUNC_ATTR_NONNULL_ALL { - TUIData *data = ui->data; - // Emit this after Nvim startup, not during. This works around a tmux // 2.3 bug(?) which caused slow drawing during startup. #7649 - unibi_out_ext(ui, data->unibi_ext.enable_focus_reporting); - flush_buf(ui); + unibi_out_ext(tui, tui->unibi_ext.enable_focus_reporting); + flush_buf(tui); } /// stop the terminal but allow it to restart later (like after suspend) -static void tui_terminal_stop(UI *ui) +static void tui_terminal_stop(TUIData *tui) { - TUIData *data = ui->data; - if (uv_is_closing(STRUCT_CAST(uv_handle_t, &data->output_handle))) { + if (uv_is_closing(STRUCT_CAST(uv_handle_t, &tui->output_handle))) { // Race between SIGCONT (tui.c) and SIGHUP (os/signal.c)? #8075 ELOG("TUI already stopped (race?)"); - data->stopped = true; + tui->stopped = true; return; } - tinput_stop(&data->input); - signal_watcher_stop(&data->winch_handle); - terminfo_stop(ui); + tinput_stop(&tui->input); + signal_watcher_stop(&tui->winch_handle); + terminfo_stop(tui); } -static void tui_stop(UI *ui) +void tui_stop(TUIData *tui) { - TUIData *data = ui->data; - tui_terminal_stop(ui); - tinput_destroy(&data->input); - data->stopped = true; - signal_watcher_close(&data->winch_handle, NULL); - uv_close((uv_handle_t *)&data->startup_delay_timer, NULL); - ui_detach_impl(ui, 0); + tui_terminal_stop(tui); + tinput_destroy(&tui->input); + tui->stopped = true; + signal_watcher_close(&tui->winch_handle, NULL); + uv_close((uv_handle_t *)&tui->startup_delay_timer, NULL); } /// Returns true if UI `ui` is stopped. -static bool tui_is_stopped(UI *ui) +static bool tui_is_stopped(TUIData *tui) { - TUIData *data = ui->data; - return data->stopped; + return tui->stopped; } #ifdef EXITFREE -void tui_free_all_mem(UI *ui) +void tui_free_all_mem(TUIData *tui) { - TUIData *data = ui->data; - ugrid_free(&data->grid); - kv_destroy(data->invalid_regions); - kv_destroy(data->attrs); - xfree(data->space_buf); - xfree(data->term); - xfree(data); - xfree(ui); + ugrid_free(&tui->grid); + kv_destroy(tui->invalid_regions); + kv_destroy(tui->attrs); + xfree(tui->space_buf); + xfree(tui->term); + xfree(tui); } #endif -static void sigwinch_cb(SignalWatcher *watcher, int signum, void *data) +static void sigwinch_cb(SignalWatcher *watcher, int signum, void *cbdata) { got_winch++; - UI *ui = data; - if (tui_is_stopped(ui)) { + TUIData *tui = cbdata; + if (tui_is_stopped(tui)) { return; } - tui_guess_size(ui); + tui_guess_size(tui); } -static bool attrs_differ(UI *ui, int id1, int id2, bool rgb) +static bool attrs_differ(TUIData *tui, int id1, int id2, bool rgb) { - TUIData *data = ui->data; if (id1 == id2) { return false; } else if (id1 < 0 || id2 < 0) { return true; } - HlAttrs a1 = kv_A(data->attrs, (size_t)id1); - HlAttrs a2 = kv_A(data->attrs, (size_t)id2); + HlAttrs a1 = kv_A(tui->attrs, (size_t)id1); + HlAttrs a2 = kv_A(tui->attrs, (size_t)id2); if (rgb) { return a1.rgb_fg_color != a2.rgb_fg_color @@ -563,17 +520,15 @@ static bool attrs_differ(UI *ui, int id1, int id2, bool rgb) } } -static void update_attrs(UI *ui, int attr_id) +static void update_attrs(TUIData *tui, int attr_id) { - TUIData *data = ui->data; - - if (!attrs_differ(ui, attr_id, data->print_attr_id, ui->rgb)) { - data->print_attr_id = attr_id; + if (!attrs_differ(tui, attr_id, tui->print_attr_id, tui->rgb)) { + tui->print_attr_id = attr_id; return; } - data->print_attr_id = attr_id; - HlAttrs attrs = kv_A(data->attrs, (size_t)attr_id); - int attr = ui->rgb ? attrs.rgb_ae_attr : attrs.cterm_ae_attr; + tui->print_attr_id = attr_id; + HlAttrs attrs = kv_A(tui->attrs, (size_t)attr_id); + int attr = tui->rgb ? attrs.rgb_ae_attr : attrs.cterm_ae_attr; bool bold = attr & HL_BOLD; bool italic = attr & HL_ITALIC; @@ -586,7 +541,7 @@ static void update_attrs(UI *ui, int attr_id) bool underdouble; bool underdotted; bool underdashed; - if (data->unibi_ext.set_underline_style != -1) { + if (tui->unibi_ext.set_underline_style != -1) { underline = attr & HL_UNDERLINE; undercurl = attr & HL_UNDERCURL; underdouble = attr & HL_UNDERDOUBLE; @@ -603,126 +558,125 @@ static void update_attrs(UI *ui, int attr_id) bool has_any_underline = undercurl || underline || underdouble || underdotted || underdashed; - if (unibi_get_str(data->ut, unibi_set_attributes)) { + if (unibi_get_str(tui->ut, unibi_set_attributes)) { if (bold || reverse || underline || standout) { - UNIBI_SET_NUM_VAR(data->params[0], standout); - UNIBI_SET_NUM_VAR(data->params[1], underline); - UNIBI_SET_NUM_VAR(data->params[2], reverse); - UNIBI_SET_NUM_VAR(data->params[3], 0); // blink - UNIBI_SET_NUM_VAR(data->params[4], 0); // dim - UNIBI_SET_NUM_VAR(data->params[5], bold); - UNIBI_SET_NUM_VAR(data->params[6], 0); // blank - UNIBI_SET_NUM_VAR(data->params[7], 0); // protect - UNIBI_SET_NUM_VAR(data->params[8], 0); // alternate character set - unibi_out(ui, unibi_set_attributes); - } else if (!data->default_attr) { - unibi_out(ui, unibi_exit_attribute_mode); + UNIBI_SET_NUM_VAR(tui->params[0], standout); + UNIBI_SET_NUM_VAR(tui->params[1], underline); + UNIBI_SET_NUM_VAR(tui->params[2], reverse); + UNIBI_SET_NUM_VAR(tui->params[3], 0); // blink + UNIBI_SET_NUM_VAR(tui->params[4], 0); // dim + UNIBI_SET_NUM_VAR(tui->params[5], bold); + UNIBI_SET_NUM_VAR(tui->params[6], 0); // blank + UNIBI_SET_NUM_VAR(tui->params[7], 0); // protect + UNIBI_SET_NUM_VAR(tui->params[8], 0); // alternate character set + unibi_out(tui, unibi_set_attributes); + } else if (!tui->default_attr) { + unibi_out(tui, unibi_exit_attribute_mode); } } else { - if (!data->default_attr) { - unibi_out(ui, unibi_exit_attribute_mode); + if (!tui->default_attr) { + unibi_out(tui, unibi_exit_attribute_mode); } if (bold) { - unibi_out(ui, unibi_enter_bold_mode); + unibi_out(tui, unibi_enter_bold_mode); } if (underline) { - unibi_out(ui, unibi_enter_underline_mode); + unibi_out(tui, unibi_enter_underline_mode); } if (standout) { - unibi_out(ui, unibi_enter_standout_mode); + unibi_out(tui, unibi_enter_standout_mode); } if (reverse) { - unibi_out(ui, unibi_enter_reverse_mode); + unibi_out(tui, unibi_enter_reverse_mode); } } if (italic) { - unibi_out(ui, unibi_enter_italics_mode); + unibi_out(tui, unibi_enter_italics_mode); } - if (strikethrough && data->unibi_ext.enter_strikethrough_mode != -1) { - unibi_out_ext(ui, data->unibi_ext.enter_strikethrough_mode); + if (strikethrough && tui->unibi_ext.enter_strikethrough_mode != -1) { + unibi_out_ext(tui, tui->unibi_ext.enter_strikethrough_mode); } - if (undercurl && data->unibi_ext.set_underline_style != -1) { - UNIBI_SET_NUM_VAR(data->params[0], 3); - unibi_out_ext(ui, data->unibi_ext.set_underline_style); + if (undercurl && tui->unibi_ext.set_underline_style != -1) { + UNIBI_SET_NUM_VAR(tui->params[0], 3); + unibi_out_ext(tui, tui->unibi_ext.set_underline_style); } - if (underdouble && data->unibi_ext.set_underline_style != -1) { - UNIBI_SET_NUM_VAR(data->params[0], 2); - unibi_out_ext(ui, data->unibi_ext.set_underline_style); + if (underdouble && tui->unibi_ext.set_underline_style != -1) { + UNIBI_SET_NUM_VAR(tui->params[0], 2); + unibi_out_ext(tui, tui->unibi_ext.set_underline_style); } - if (underdotted && data->unibi_ext.set_underline_style != -1) { - UNIBI_SET_NUM_VAR(data->params[0], 4); - unibi_out_ext(ui, data->unibi_ext.set_underline_style); + if (underdotted && tui->unibi_ext.set_underline_style != -1) { + UNIBI_SET_NUM_VAR(tui->params[0], 4); + unibi_out_ext(tui, tui->unibi_ext.set_underline_style); } - if (underdashed && data->unibi_ext.set_underline_style != -1) { - UNIBI_SET_NUM_VAR(data->params[0], 5); - unibi_out_ext(ui, data->unibi_ext.set_underline_style); + if (underdashed && tui->unibi_ext.set_underline_style != -1) { + UNIBI_SET_NUM_VAR(tui->params[0], 5); + unibi_out_ext(tui, tui->unibi_ext.set_underline_style); } - if (has_any_underline && data->unibi_ext.set_underline_color != -1) { + if (has_any_underline && tui->unibi_ext.set_underline_color != -1) { int color = attrs.rgb_sp_color; if (color != -1) { - UNIBI_SET_NUM_VAR(data->params[0], (color >> 16) & 0xff); // red - UNIBI_SET_NUM_VAR(data->params[1], (color >> 8) & 0xff); // green - UNIBI_SET_NUM_VAR(data->params[2], color & 0xff); // blue - unibi_out_ext(ui, data->unibi_ext.set_underline_color); + UNIBI_SET_NUM_VAR(tui->params[0], (color >> 16) & 0xff); // red + UNIBI_SET_NUM_VAR(tui->params[1], (color >> 8) & 0xff); // green + UNIBI_SET_NUM_VAR(tui->params[2], color & 0xff); // blue + unibi_out_ext(tui, tui->unibi_ext.set_underline_color); } } int fg, bg; - if (ui->rgb && !(attr & HL_FG_INDEXED)) { + if (tui->rgb && !(attr & HL_FG_INDEXED)) { fg = ((attrs.rgb_fg_color != -1) - ? attrs.rgb_fg_color : data->clear_attrs.rgb_fg_color); + ? attrs.rgb_fg_color : tui->clear_attrs.rgb_fg_color); if (fg != -1) { - UNIBI_SET_NUM_VAR(data->params[0], (fg >> 16) & 0xff); // red - UNIBI_SET_NUM_VAR(data->params[1], (fg >> 8) & 0xff); // green - UNIBI_SET_NUM_VAR(data->params[2], fg & 0xff); // blue - unibi_out_ext(ui, data->unibi_ext.set_rgb_foreground); + UNIBI_SET_NUM_VAR(tui->params[0], (fg >> 16) & 0xff); // red + UNIBI_SET_NUM_VAR(tui->params[1], (fg >> 8) & 0xff); // green + UNIBI_SET_NUM_VAR(tui->params[2], fg & 0xff); // blue + unibi_out_ext(tui, tui->unibi_ext.set_rgb_foreground); } } else { fg = (attrs.cterm_fg_color - ? attrs.cterm_fg_color - 1 : (data->clear_attrs.cterm_fg_color - 1)); + ? attrs.cterm_fg_color - 1 : (tui->clear_attrs.cterm_fg_color - 1)); if (fg != -1) { - UNIBI_SET_NUM_VAR(data->params[0], fg); - unibi_out(ui, unibi_set_a_foreground); + UNIBI_SET_NUM_VAR(tui->params[0], fg); + unibi_out(tui, unibi_set_a_foreground); } } - if (ui->rgb && !(attr & HL_BG_INDEXED)) { + if (tui->rgb && !(attr & HL_BG_INDEXED)) { bg = ((attrs.rgb_bg_color != -1) - ? attrs.rgb_bg_color : data->clear_attrs.rgb_bg_color); + ? attrs.rgb_bg_color : tui->clear_attrs.rgb_bg_color); if (bg != -1) { - UNIBI_SET_NUM_VAR(data->params[0], (bg >> 16) & 0xff); // red - UNIBI_SET_NUM_VAR(data->params[1], (bg >> 8) & 0xff); // green - UNIBI_SET_NUM_VAR(data->params[2], bg & 0xff); // blue - unibi_out_ext(ui, data->unibi_ext.set_rgb_background); + UNIBI_SET_NUM_VAR(tui->params[0], (bg >> 16) & 0xff); // red + UNIBI_SET_NUM_VAR(tui->params[1], (bg >> 8) & 0xff); // green + UNIBI_SET_NUM_VAR(tui->params[2], bg & 0xff); // blue + unibi_out_ext(tui, tui->unibi_ext.set_rgb_background); } } else { bg = (attrs.cterm_bg_color - ? attrs.cterm_bg_color - 1 : (data->clear_attrs.cterm_bg_color - 1)); + ? attrs.cterm_bg_color - 1 : (tui->clear_attrs.cterm_bg_color - 1)); if (bg != -1) { - UNIBI_SET_NUM_VAR(data->params[0], bg); - unibi_out(ui, unibi_set_a_background); + UNIBI_SET_NUM_VAR(tui->params[0], bg); + unibi_out(tui, unibi_set_a_background); } } - data->default_attr = fg == -1 && bg == -1 - && !bold && !italic && !has_any_underline && !reverse && !standout - && !strikethrough; + tui->default_attr = fg == -1 && bg == -1 + && !bold && !italic && !has_any_underline && !reverse && !standout + && !strikethrough; // Non-BCE terminals can't clear with non-default background color. Some BCE // terminals don't support attributes either, so don't rely on it. But assume // italic and bold has no effect if there is no text. - data->can_clear_attr = !reverse && !standout && !has_any_underline - && !strikethrough && (data->bce || bg == -1); + tui->can_clear_attr = !reverse && !standout && !has_any_underline + && !strikethrough && (tui->bce || bg == -1); } -static void final_column_wrap(UI *ui) +static void final_column_wrap(TUIData *tui) { - TUIData *data = ui->data; - UGrid *grid = &data->grid; - if (grid->row != -1 && grid->col == ui->width) { + UGrid *grid = &tui->grid; + if (grid->row != -1 && grid->col == tui->width) { grid->col = 0; - if (grid->row < MIN(ui->height, grid->height - 1)) { + if (grid->row < MIN(tui->height, grid->height - 1)) { grid->row++; } } @@ -731,33 +685,31 @@ static void final_column_wrap(UI *ui) /// It is undocumented, but in the majority of terminals and terminal emulators /// printing at the right margin does not cause an automatic wrap until the /// next character is printed, holding the cursor in place until then. -static void print_cell(UI *ui, UCell *ptr) +static void print_cell(TUIData *tui, UCell *ptr) { - TUIData *data = ui->data; - UGrid *grid = &data->grid; - if (!data->immediate_wrap_after_last_column) { + UGrid *grid = &tui->grid; + if (!tui->immediate_wrap_after_last_column) { // Printing the next character finally advances the cursor. - final_column_wrap(ui); + final_column_wrap(tui); } - update_attrs(ui, ptr->attr); - out(ui, ptr->data, strlen(ptr->data)); + update_attrs(tui, ptr->attr); + out(tui, ptr->data, strlen(ptr->data)); grid->col++; - if (data->immediate_wrap_after_last_column) { + if (tui->immediate_wrap_after_last_column) { // Printing at the right margin immediately advances the cursor. - final_column_wrap(ui); + final_column_wrap(tui); } } -static bool cheap_to_print(UI *ui, int row, int col, int next) +static bool cheap_to_print(TUIData *tui, int row, int col, int next) { - TUIData *data = ui->data; - UGrid *grid = &data->grid; + UGrid *grid = &tui->grid; UCell *cell = grid->cells[row] + col; while (next) { next--; - if (attrs_differ(ui, cell->attr, - data->print_attr_id, ui->rgb)) { - if (data->default_attr) { + if (attrs_differ(tui, cell->attr, + tui->print_attr_id, tui->rgb)) { + if (tui->default_attr) { return false; } } @@ -778,15 +730,14 @@ static bool cheap_to_print(UI *ui, int row, int col, int next) /// (ASCII 0/12) means the same thing and does not mean home. VT, CVT, and /// TAB also stop at software-defined tabulation stops, not at a fixed set /// of row/column positions. -static void cursor_goto(UI *ui, int row, int col) +static void cursor_goto(TUIData *tui, int row, int col) { - TUIData *data = ui->data; - UGrid *grid = &data->grid; + UGrid *grid = &tui->grid; if (row == grid->row && col == grid->col) { return; } if (0 == row && 0 == col) { - unibi_out(ui, unibi_cursor_home); + unibi_out(tui, unibi_cursor_home); ugrid_goto(grid, row, col); return; } @@ -795,12 +746,12 @@ static void cursor_goto(UI *ui, int row, int col) } if (0 == col ? col != grid->col : row != grid->row ? false : - 1 == col ? 2 < grid->col && cheap_to_print(ui, grid->row, 0, col) : - 2 == col ? 5 < grid->col && cheap_to_print(ui, grid->row, 0, col) : + 1 == col ? 2 < grid->col && cheap_to_print(tui, grid->row, 0, col) : + 2 == col ? 5 < grid->col && cheap_to_print(tui, grid->row, 0, col) : false) { // Motion to left margin from anywhere else, or CR + printing chars is // even less expensive than using BSes or CUB. - unibi_out(ui, unibi_carriage_return); + unibi_out(tui, unibi_carriage_return); ugrid_goto(grid, grid->row, 0); } if (row == grid->row) { @@ -810,15 +761,15 @@ static void cursor_goto(UI *ui, int row, int col) // motion calculations have OBOEs that cannot be compensated for, // because two terminals that claim to be the same will implement // different cursor positioning rules. - && (data->immediate_wrap_after_last_column || grid->col < ui->width)) { + && (tui->immediate_wrap_after_last_column || grid->col < tui->width)) { int n = grid->col - col; if (n <= 4) { // This might be just BS, so it is considered really cheap. while (n--) { - unibi_out(ui, unibi_cursor_left); + unibi_out(tui, unibi_cursor_left); } } else { - UNIBI_SET_NUM_VAR(data->params[0], n); - unibi_out(ui, unibi_parm_left_cursor); + UNIBI_SET_NUM_VAR(tui->params[0], n); + unibi_out(tui, unibi_parm_left_cursor); } ugrid_goto(grid, row, col); return; @@ -826,11 +777,11 @@ static void cursor_goto(UI *ui, int row, int col) int n = col - grid->col; if (n <= 2) { while (n--) { - unibi_out(ui, unibi_cursor_right); + unibi_out(tui, unibi_cursor_right); } } else { - UNIBI_SET_NUM_VAR(data->params[0], n); - unibi_out(ui, unibi_parm_right_cursor); + UNIBI_SET_NUM_VAR(tui->params[0], n); + unibi_out(tui, unibi_parm_right_cursor); } ugrid_goto(grid, row, col); return; @@ -841,11 +792,11 @@ static void cursor_goto(UI *ui, int row, int col) int n = row - grid->row; if (n <= 4) { // This might be just LF, so it is considered really cheap. while (n--) { - unibi_out(ui, unibi_cursor_down); + unibi_out(tui, unibi_cursor_down); } } else { - UNIBI_SET_NUM_VAR(data->params[0], n); - unibi_out(ui, unibi_parm_down_cursor); + UNIBI_SET_NUM_VAR(tui->params[0], n); + unibi_out(tui, unibi_parm_down_cursor); } ugrid_goto(grid, row, col); return; @@ -853,11 +804,11 @@ static void cursor_goto(UI *ui, int row, int col) int n = grid->row - row; if (n <= 2) { while (n--) { - unibi_out(ui, unibi_cursor_up); + unibi_out(tui, unibi_cursor_up); } } else { - UNIBI_SET_NUM_VAR(data->params[0], n); - unibi_out(ui, unibi_parm_up_cursor); + UNIBI_SET_NUM_VAR(tui->params[0], n); + unibi_out(tui, unibi_parm_up_cursor); } ugrid_goto(grid, row, col); return; @@ -865,20 +816,19 @@ static void cursor_goto(UI *ui, int row, int col) } safe_move: - unibi_goto(ui, row, col); + unibi_goto(tui, row, col); ugrid_goto(grid, row, col); } -static void print_spaces(UI *ui, int width) +static void print_spaces(TUIData *tui, int width) { - TUIData *data = ui->data; - UGrid *grid = &data->grid; + UGrid *grid = &tui->grid; - out(ui, data->space_buf, (size_t)width); + out(tui, tui->space_buf, (size_t)width); grid->col += width; - if (data->immediate_wrap_after_last_column) { + if (tui->immediate_wrap_after_last_column) { // Printing at the right margin immediately advances the cursor. - final_column_wrap(ui); + final_column_wrap(tui); } } @@ -887,28 +837,27 @@ static void print_spaces(UI *ui, int width) /// /// @param is_doublewidth whether the character is double-width on the grid. /// If true and the character is ambiguous-width, clear two cells. -static void print_cell_at_pos(UI *ui, int row, int col, UCell *cell, bool is_doublewidth) +static void print_cell_at_pos(TUIData *tui, int row, int col, UCell *cell, bool is_doublewidth) { - TUIData *data = ui->data; - UGrid *grid = &data->grid; + UGrid *grid = &tui->grid; if (grid->row == -1 && cell->data[0] == NUL) { // If cursor needs to repositioned and there is nothing to print, don't move cursor. return; } - cursor_goto(ui, row, col); + cursor_goto(tui, row, col); bool is_ambiwidth = utf_ambiguous_width(utf_ptr2char(cell->data)); if (is_ambiwidth && is_doublewidth) { // Clear the two screen cells. // If the character is single-width in the host terminal it won't change the second cell. - update_attrs(ui, cell->attr); - print_spaces(ui, 2); - cursor_goto(ui, row, col); + update_attrs(tui, cell->attr); + print_spaces(tui, 2); + cursor_goto(tui, row, col); } - print_cell(ui, cell); + print_cell(tui, cell); if (is_ambiwidth) { // Force repositioning cursor after printing an ambiguous-width character. @@ -916,120 +865,116 @@ static void print_cell_at_pos(UI *ui, int row, int col, UCell *cell, bool is_dou } } -static void clear_region(UI *ui, int top, int bot, int left, int right, int attr_id) +static void clear_region(TUIData *tui, int top, int bot, int left, int right, int attr_id) { - TUIData *data = ui->data; - UGrid *grid = &data->grid; + UGrid *grid = &tui->grid; - update_attrs(ui, attr_id); + update_attrs(tui, attr_id); // Background is set to the default color and the right edge matches the // screen end, try to use terminal codes for clearing the requested area. - if (data->can_clear_attr - && left == 0 && right == ui->width && bot == ui->height) { + if (tui->can_clear_attr + && left == 0 && right == tui->width && bot == tui->height) { if (top == 0) { - unibi_out(ui, unibi_clear_screen); + unibi_out(tui, unibi_clear_screen); ugrid_goto(grid, top, left); } else { - cursor_goto(ui, top, 0); - unibi_out(ui, unibi_clr_eos); + cursor_goto(tui, top, 0); + unibi_out(tui, unibi_clr_eos); } } else { int width = right - left; // iterate through each line and clear for (int row = top; row < bot; row++) { - cursor_goto(ui, row, left); - if (data->can_clear_attr && right == ui->width) { - unibi_out(ui, unibi_clr_eol); - } else if (data->can_erase_chars && data->can_clear_attr && width >= 5) { - UNIBI_SET_NUM_VAR(data->params[0], width); - unibi_out(ui, unibi_erase_chars); + cursor_goto(tui, row, left); + if (tui->can_clear_attr && right == tui->width) { + unibi_out(tui, unibi_clr_eol); + } else if (tui->can_erase_chars && tui->can_clear_attr && width >= 5) { + UNIBI_SET_NUM_VAR(tui->params[0], width); + unibi_out(tui, unibi_erase_chars); } else { - print_spaces(ui, width); + print_spaces(tui, width); } } } } -static void set_scroll_region(UI *ui, int top, int bot, int left, int right) +static void set_scroll_region(TUIData *tui, int top, int bot, int left, int right) { - TUIData *data = ui->data; - UGrid *grid = &data->grid; + UGrid *grid = &tui->grid; - UNIBI_SET_NUM_VAR(data->params[0], top); - UNIBI_SET_NUM_VAR(data->params[1], bot); - unibi_out(ui, unibi_change_scroll_region); - if (left != 0 || right != ui->width - 1) { - unibi_out_ext(ui, data->unibi_ext.enable_lr_margin); - if (data->can_set_lr_margin) { - UNIBI_SET_NUM_VAR(data->params[0], left); - UNIBI_SET_NUM_VAR(data->params[1], right); - unibi_out(ui, unibi_set_lr_margin); + UNIBI_SET_NUM_VAR(tui->params[0], top); + UNIBI_SET_NUM_VAR(tui->params[1], bot); + unibi_out(tui, unibi_change_scroll_region); + if (left != 0 || right != tui->width - 1) { + unibi_out_ext(tui, tui->unibi_ext.enable_lr_margin); + if (tui->can_set_lr_margin) { + UNIBI_SET_NUM_VAR(tui->params[0], left); + UNIBI_SET_NUM_VAR(tui->params[1], right); + unibi_out(tui, unibi_set_lr_margin); } else { - UNIBI_SET_NUM_VAR(data->params[0], left); - unibi_out(ui, unibi_set_left_margin_parm); - UNIBI_SET_NUM_VAR(data->params[0], right); - unibi_out(ui, unibi_set_right_margin_parm); + UNIBI_SET_NUM_VAR(tui->params[0], left); + unibi_out(tui, unibi_set_left_margin_parm); + UNIBI_SET_NUM_VAR(tui->params[0], right); + unibi_out(tui, unibi_set_right_margin_parm); } } grid->row = -1; } -static void reset_scroll_region(UI *ui, bool fullwidth) +static void reset_scroll_region(TUIData *tui, bool fullwidth) { - TUIData *data = ui->data; - UGrid *grid = &data->grid; + UGrid *grid = &tui->grid; - if (0 <= data->unibi_ext.reset_scroll_region) { - unibi_out_ext(ui, data->unibi_ext.reset_scroll_region); + if (0 <= tui->unibi_ext.reset_scroll_region) { + unibi_out_ext(tui, tui->unibi_ext.reset_scroll_region); } else { - UNIBI_SET_NUM_VAR(data->params[0], 0); - UNIBI_SET_NUM_VAR(data->params[1], ui->height - 1); - unibi_out(ui, unibi_change_scroll_region); + UNIBI_SET_NUM_VAR(tui->params[0], 0); + UNIBI_SET_NUM_VAR(tui->params[1], tui->height - 1); + unibi_out(tui, unibi_change_scroll_region); } if (!fullwidth) { - if (data->can_set_lr_margin) { - UNIBI_SET_NUM_VAR(data->params[0], 0); - UNIBI_SET_NUM_VAR(data->params[1], ui->width - 1); - unibi_out(ui, unibi_set_lr_margin); + if (tui->can_set_lr_margin) { + UNIBI_SET_NUM_VAR(tui->params[0], 0); + UNIBI_SET_NUM_VAR(tui->params[1], tui->width - 1); + unibi_out(tui, unibi_set_lr_margin); } else { - UNIBI_SET_NUM_VAR(data->params[0], 0); - unibi_out(ui, unibi_set_left_margin_parm); - UNIBI_SET_NUM_VAR(data->params[0], ui->width - 1); - unibi_out(ui, unibi_set_right_margin_parm); + UNIBI_SET_NUM_VAR(tui->params[0], 0); + unibi_out(tui, unibi_set_left_margin_parm); + UNIBI_SET_NUM_VAR(tui->params[0], tui->width - 1); + unibi_out(tui, unibi_set_right_margin_parm); } - unibi_out_ext(ui, data->unibi_ext.disable_lr_margin); + unibi_out_ext(tui, tui->unibi_ext.disable_lr_margin); } grid->row = -1; } -static void tui_grid_resize(UI *ui, Integer g, Integer width, Integer height) +void tui_grid_resize(TUIData *tui, Integer g, Integer width, Integer height) { - TUIData *data = ui->data; - UGrid *grid = &data->grid; + UGrid *grid = &tui->grid; ugrid_resize(grid, (int)width, (int)height); - xfree(data->space_buf); - data->space_buf = xmalloc((size_t)width * sizeof(*data->space_buf)); - memset(data->space_buf, ' ', (size_t)width); + xfree(tui->space_buf); + tui->space_buf = xmalloc((size_t)width * sizeof(*tui->space_buf)); + memset(tui->space_buf, ' ', (size_t)width); // resize might not always be followed by a clear before flush // so clip the invalid region - for (size_t i = 0; i < kv_size(data->invalid_regions); i++) { - Rect *r = &kv_A(data->invalid_regions, i); + for (size_t i = 0; i < kv_size(tui->invalid_regions); i++) { + Rect *r = &kv_A(tui->invalid_regions, i); r->bot = MIN(r->bot, grid->height); r->right = MIN(r->right, grid->width); } - if (!got_winch && !data->is_starting) { + if (!got_winch && !tui->is_starting) { // Resize the _host_ terminal. - UNIBI_SET_NUM_VAR(data->params[0], (int)height); - UNIBI_SET_NUM_VAR(data->params[1], (int)width); - unibi_out_ext(ui, data->unibi_ext.resize_screen); + UNIBI_SET_NUM_VAR(tui->params[0], (int)height); + UNIBI_SET_NUM_VAR(tui->params[1], (int)width); + unibi_out_ext(tui, tui->unibi_ext.resize_screen); // DECSLPP does not reset the scroll region. - if (data->scroll_region_is_full_screen) { - reset_scroll_region(ui, ui->width == grid->width); + if (tui->scroll_region_is_full_screen) { + reset_scroll_region(tui, tui->width == grid->width); } } else { // Already handled the SIGWINCH signal; avoid double-resize. got_winch = got_winch > 0 ? got_winch - 1 : 0; @@ -1037,22 +982,19 @@ static void tui_grid_resize(UI *ui, Integer g, Integer width, Integer height) } } -static void tui_grid_clear(UI *ui, Integer g) +void tui_grid_clear(TUIData *tui, Integer g) { - TUIData *data = ui->data; - UGrid *grid = &data->grid; + UGrid *grid = &tui->grid; ugrid_clear(grid); - kv_size(data->invalid_regions) = 0; - clear_region(ui, 0, grid->height, 0, grid->width, 0); + kv_size(tui->invalid_regions) = 0; + clear_region(tui, 0, grid->height, 0, grid->width, 0); } -static void tui_grid_cursor_goto(UI *ui, Integer grid, Integer row, Integer col) +void tui_grid_cursor_goto(TUIData *tui, Integer grid, Integer row, Integer col) { - TUIData *data = ui->data; - // cursor position is validated in tui_flush - data->row = (int)row; - data->col = (int)col; + tui->row = (int)row; + tui->col = (int)col; } CursorShape tui_cursor_decode_shape(const char *shape_str) @@ -1092,13 +1034,12 @@ static cursorentry_T decode_cursor_entry(Dictionary args) return r; } -static void tui_mode_info_set(UI *ui, bool guicursor_enabled, Array args) +void tui_mode_info_set(TUIData *tui, bool guicursor_enabled, Array args) { cursor_style_enabled = guicursor_enabled; if (!guicursor_enabled) { return; // Do not send cursor style control codes. } - TUIData *data = ui->data; assert(args.size); @@ -1106,84 +1047,81 @@ static void tui_mode_info_set(UI *ui, bool guicursor_enabled, Array args) for (size_t i = 0; i < args.size; i++) { assert(args.items[i].type == kObjectTypeDictionary); cursorentry_T r = decode_cursor_entry(args.items[i].data.dictionary); - data->cursor_shapes[i] = r; + tui->cursor_shapes[i] = r; } - tui_set_mode(ui, data->showing_mode); + tui_set_mode(tui, tui->showing_mode); } -static void tui_update_menu(UI *ui) +void tui_update_menu(TUIData *tui) { // Do nothing; menus are for GUI only } -static void tui_busy_start(UI *ui) +void tui_busy_start(TUIData *tui) { - ((TUIData *)ui->data)->busy = true; + tui->busy = true; } -static void tui_busy_stop(UI *ui) +void tui_busy_stop(TUIData *tui) { - ((TUIData *)ui->data)->busy = false; + tui->busy = false; } -static void tui_mouse_on(UI *ui) +void tui_mouse_on(TUIData *tui) { - TUIData *data = ui->data; - if (!data->mouse_enabled) { - unibi_out_ext(ui, data->unibi_ext.enable_mouse); - if (data->mouse_move_enabled) { - unibi_out_ext(ui, data->unibi_ext.enable_mouse_move); + if (!tui->mouse_enabled) { + unibi_out_ext(tui, tui->unibi_ext.enable_mouse); + if (tui->mouse_move_enabled) { + unibi_out_ext(tui, tui->unibi_ext.enable_mouse_move); } - data->mouse_enabled = true; + tui->mouse_enabled = true; } } -static void tui_mouse_off(UI *ui) +void tui_mouse_off(TUIData *tui) { - TUIData *data = ui->data; - if (data->mouse_enabled) { - if (data->mouse_move_enabled) { - unibi_out_ext(ui, data->unibi_ext.disable_mouse_move); + if (tui->mouse_enabled) { + if (tui->mouse_move_enabled) { + unibi_out_ext(tui, tui->unibi_ext.disable_mouse_move); } - unibi_out_ext(ui, data->unibi_ext.disable_mouse); - data->mouse_enabled = false; + unibi_out_ext(tui, tui->unibi_ext.disable_mouse); + tui->mouse_enabled = false; } } -static void tui_set_mode(UI *ui, ModeShape mode) +void tui_set_mode(TUIData *tui, ModeShape mode) { if (!cursor_style_enabled) { return; } - TUIData *data = ui->data; - cursorentry_T c = data->cursor_shapes[mode]; + cursorentry_T c = tui->cursor_shapes[mode]; - if (c.id != 0 && c.id < (int)kv_size(data->attrs) && ui->rgb) { - HlAttrs aep = kv_A(data->attrs, c.id); + if (c.id != 0 && c.id < (int)kv_size(tui->attrs) && tui->rgb) { + HlAttrs aep = kv_A(tui->attrs, c.id); - data->want_invisible = aep.hl_blend == 100; - if (data->want_invisible) { - unibi_out(ui, unibi_cursor_invisible); + tui->want_invisible = aep.hl_blend == 100; + if (tui->want_invisible) { + unibi_out(tui, unibi_cursor_invisible); } else if (aep.rgb_ae_attr & HL_INVERSE) { // We interpret "inverse" as "default" (no termcode for "inverse"...). // Hopefully the user's default cursor color is inverse. - unibi_out_ext(ui, data->unibi_ext.reset_cursor_color); + unibi_out_ext(tui, tui->unibi_ext.reset_cursor_color); } else { - if (data->set_cursor_color_as_str) { + if (tui->set_cursor_color_as_str) { char hexbuf[8]; snprintf(hexbuf, 7 + 1, "#%06x", aep.rgb_bg_color); - UNIBI_SET_STR_VAR(data->params[0], hexbuf); + UNIBI_SET_STR_VAR(tui->params[0], hexbuf); } else { - UNIBI_SET_NUM_VAR(data->params[0], aep.rgb_bg_color); + UNIBI_SET_NUM_VAR(tui->params[0], aep.rgb_bg_color); } - unibi_out_ext(ui, data->unibi_ext.set_cursor_color); - data->cursor_color_changed = true; + unibi_out_ext(tui, tui->unibi_ext.set_cursor_color); + tui->cursor_color_changed = true; } } else if (c.id == 0) { // No cursor color for this mode; reset to default. - data->want_invisible = false; - unibi_out_ext(ui, data->unibi_ext.reset_cursor_color); + tui->want_invisible = false; + unibi_out_ext(tui, tui->unibi_ext.reset_cursor_color); } int shape; @@ -1197,88 +1135,86 @@ static void tui_set_mode(UI *ui, ModeShape mode) case SHAPE_VER: shape = 5; break; } - UNIBI_SET_NUM_VAR(data->params[0], shape + (int)(c.blinkon == 0)); - unibi_out_ext(ui, data->unibi_ext.set_cursor_style); + UNIBI_SET_NUM_VAR(tui->params[0], shape + (int)(c.blinkon == 0)); + unibi_out_ext(tui, tui->unibi_ext.set_cursor_style); } /// @param mode editor mode -static void tui_mode_change(UI *ui, String mode, Integer mode_idx) +void tui_mode_change(TUIData *tui, String mode, Integer mode_idx) { - TUIData *data = ui->data; #ifdef UNIX // If stdin is not a TTY, the LHS of pipe may change the state of the TTY // after calling uv_tty_set_mode. So, set the mode of the TTY again here. // #13073 - if (data->is_starting && data->input.in_fd == STDERR_FILENO) { - int ret = uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_NORMAL); + if (tui->is_starting && tui->input.in_fd == STDERR_FILENO) { + int ret = uv_tty_set_mode(&tui->output_handle.tty, UV_TTY_MODE_NORMAL); if (ret) { ELOG("uv_tty_set_mode failed: %s", uv_strerror(ret)); } - ret = uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_IO); + ret = uv_tty_set_mode(&tui->output_handle.tty, UV_TTY_MODE_IO); if (ret) { ELOG("uv_tty_set_mode failed: %s", uv_strerror(ret)); } } #endif - tui_set_mode(ui, (ModeShape)mode_idx); - if (data->is_starting) { - if (data->verbose >= 3) { - show_verbose_terminfo(data); + tui_set_mode(tui, (ModeShape)mode_idx); + if (tui->is_starting) { + if (tui->verbose >= 3) { + show_verbose_terminfo(tui); } } - data->is_starting = false; // mode entered, no longer starting - data->showing_mode = (ModeShape)mode_idx; + tui->is_starting = false; // mode entered, no longer starting + tui->showing_mode = (ModeShape)mode_idx; } -static void tui_grid_scroll(UI *ui, Integer g, Integer startrow, // -V751 - Integer endrow, Integer startcol, Integer endcol, Integer rows, - Integer cols FUNC_ATTR_UNUSED) +void tui_grid_scroll(TUIData *tui, Integer g, Integer startrow, // -V751 + Integer endrow, Integer startcol, Integer endcol, Integer rows, + Integer cols FUNC_ATTR_UNUSED) { - TUIData *data = ui->data; - UGrid *grid = &data->grid; + UGrid *grid = &tui->grid; int top = (int)startrow, bot = (int)endrow - 1; int left = (int)startcol, right = (int)endcol - 1; - bool fullwidth = left == 0 && right == ui->width - 1; - data->scroll_region_is_full_screen = fullwidth - && top == 0 && bot == ui->height - 1; + bool fullwidth = left == 0 && right == tui->width - 1; + tui->scroll_region_is_full_screen = fullwidth + && top == 0 && bot == tui->height - 1; ugrid_scroll(grid, top, bot, left, right, (int)rows); - bool can_scroll = data->can_scroll - && (data->scroll_region_is_full_screen - || (data->can_change_scroll_region - && ((left == 0 && right == ui->width - 1) - || data->can_set_lr_margin - || data->can_set_left_right_margin))); + bool can_scroll = tui->can_scroll + && (tui->scroll_region_is_full_screen + || (tui->can_change_scroll_region + && ((left == 0 && right == tui->width - 1) + || tui->can_set_lr_margin + || tui->can_set_left_right_margin))); if (can_scroll) { // Change terminal scroll region and move cursor to the top - if (!data->scroll_region_is_full_screen) { - set_scroll_region(ui, top, bot, left, right); + if (!tui->scroll_region_is_full_screen) { + set_scroll_region(tui, top, bot, left, right); } - cursor_goto(ui, top, left); - update_attrs(ui, 0); + cursor_goto(tui, top, left); + update_attrs(tui, 0); if (rows > 0) { if (rows == 1) { - unibi_out(ui, unibi_delete_line); + unibi_out(tui, unibi_delete_line); } else { - UNIBI_SET_NUM_VAR(data->params[0], (int)rows); - unibi_out(ui, unibi_parm_delete_line); + UNIBI_SET_NUM_VAR(tui->params[0], (int)rows); + unibi_out(tui, unibi_parm_delete_line); } } else { if (rows == -1) { - unibi_out(ui, unibi_insert_line); + unibi_out(tui, unibi_insert_line); } else { - UNIBI_SET_NUM_VAR(data->params[0], -(int)rows); - unibi_out(ui, unibi_parm_insert_line); + UNIBI_SET_NUM_VAR(tui->params[0], -(int)rows); + unibi_out(tui, unibi_parm_insert_line); } } // Restore terminal scroll region and cursor - if (!data->scroll_region_is_full_screen) { - reset_scroll_region(ui, fullwidth); + if (!tui->scroll_region_is_full_screen) { + reset_scroll_region(tui, fullwidth); } } else { // Mark the moved region as invalid for redrawing later @@ -1287,50 +1223,46 @@ static void tui_grid_scroll(UI *ui, Integer g, Integer startrow, // -V751 } else { startrow = startrow - rows; } - invalidate(ui, (int)startrow, (int)endrow, (int)startcol, (int)endcol); + invalidate(tui, (int)startrow, (int)endrow, (int)startcol, (int)endcol); } } -static void tui_hl_attr_define(UI *ui, Integer id, HlAttrs attrs, HlAttrs cterm_attrs, Array info) +void tui_hl_attr_define(TUIData *tui, Integer id, HlAttrs attrs, HlAttrs cterm_attrs, Array info) { - TUIData *data = ui->data; attrs.cterm_ae_attr = cterm_attrs.cterm_ae_attr; attrs.cterm_fg_color = cterm_attrs.cterm_fg_color; attrs.cterm_bg_color = cterm_attrs.cterm_bg_color; - kv_a(data->attrs, (size_t)id) = attrs; + kv_a(tui->attrs, (size_t)id) = attrs; } -static void tui_bell(UI *ui) +void tui_bell(TUIData *tui) { - unibi_out(ui, unibi_bell); + unibi_out(tui, unibi_bell); } -static void tui_visual_bell(UI *ui) +void tui_visual_bell(TUIData *tui) { - unibi_out(ui, unibi_flash_screen); + unibi_out(tui, unibi_flash_screen); } -static void tui_default_colors_set(UI *ui, Integer rgb_fg, Integer rgb_bg, Integer rgb_sp, - Integer cterm_fg, Integer cterm_bg) +void tui_default_colors_set(TUIData *tui, Integer rgb_fg, Integer rgb_bg, Integer rgb_sp, + Integer cterm_fg, Integer cterm_bg) { - TUIData *data = ui->data; + tui->clear_attrs.rgb_fg_color = (int)rgb_fg; + tui->clear_attrs.rgb_bg_color = (int)rgb_bg; + tui->clear_attrs.rgb_sp_color = (int)rgb_sp; + tui->clear_attrs.cterm_fg_color = (int)cterm_fg; + tui->clear_attrs.cterm_bg_color = (int)cterm_bg; - data->clear_attrs.rgb_fg_color = (int)rgb_fg; - data->clear_attrs.rgb_bg_color = (int)rgb_bg; - data->clear_attrs.rgb_sp_color = (int)rgb_sp; - data->clear_attrs.cterm_fg_color = (int)cterm_fg; - data->clear_attrs.cterm_bg_color = (int)cterm_bg; - - data->print_attr_id = -1; - invalidate(ui, 0, data->grid.height, 0, data->grid.width); + tui->print_attr_id = -1; + invalidate(tui, 0, tui->grid.height, 0, tui->grid.width); } -static void tui_flush(UI *ui) +void tui_flush(TUIData *tui) { - TUIData *data = ui->data; - UGrid *grid = &data->grid; + UGrid *grid = &tui->grid; - size_t nrevents = loop_size(data->loop); + size_t nrevents = loop_size(tui->loop); if (nrevents > TOO_MANY_EVENTS) { WLOG("TUI event-queue flooded (thread_events=%zu); purging", nrevents); // Back-pressure: UI events may accumulate much faster than the terminal @@ -1338,12 +1270,12 @@ static void tui_flush(UI *ui) // wait for the TUI event-queue to drain, and if there are ~millions of // events in the queue, it could take hours. Clearing the queue allows the // UI to recover. #1234 #5396 - loop_purge(data->loop); - tui_busy_stop(ui); // avoid hidden cursor + loop_purge(tui->loop); + tui_busy_stop(tui); // avoid hidden cursor } - while (kv_size(data->invalid_regions)) { - Rect r = kv_pop(data->invalid_regions); + while (kv_size(tui->invalid_regions)) { + Rect r = kv_pop(tui->invalid_regions); assert(r.bot <= grid->height && r.right <= grid->width); for (int row = r.top; row < r.bot; row++) { @@ -1358,24 +1290,24 @@ static void tui_flush(UI *ui) } UGRID_FOREACH_CELL(grid, row, r.left, clear_col, { - print_cell_at_pos(ui, row, curcol, cell, + print_cell_at_pos(tui, row, curcol, cell, curcol < clear_col - 1 && (cell + 1)->data[0] == NUL); }); if (clear_col < r.right) { - clear_region(ui, row, row + 1, clear_col, r.right, clear_attr); + clear_region(tui, row, row + 1, clear_col, r.right, clear_attr); } } } - cursor_goto(ui, data->row, data->col); + cursor_goto(tui, tui->row, tui->col); - flush_buf(ui); + flush_buf(tui); } /// Dumps termcap info to the messages area, if 'verbose' >= 3. -static void show_verbose_terminfo(TUIData *data) +static void show_verbose_terminfo(TUIData *tui) { - const unibi_term *const ut = data->ut; + const unibi_term *const ut = tui->ut; if (!ut) { abort(); } @@ -1386,7 +1318,7 @@ static void show_verbose_terminfo(TUIData *data) ADD(title, STRING_OBJ(cstr_to_string("Title"))); ADD(chunks, ARRAY_OBJ(title)); Array info = ARRAY_DICT_INIT; - String str = terminfo_info_msg(ut, data->term); + String str = terminfo_info_msg(ut, tui->term); ADD(info, STRING_OBJ(str)); ADD(chunks, ARRAY_OBJ(info)); Array end_fold = ARRAY_DICT_INIT; @@ -1407,24 +1339,23 @@ static void show_verbose_terminfo(TUIData *data) #ifdef UNIX static void suspend_event(void **argv) { - UI *ui = argv[0]; - TUIData *data = ui->data; - bool enable_mouse = data->mouse_enabled; - tui_terminal_stop(ui); + TUIData *tui = argv[0]; + bool enable_mouse = tui->mouse_enabled; + tui_terminal_stop(tui); stream_set_blocking(input_global_fd(), true); // normalize stream (#2598) kill(0, SIGTSTP); - tui_terminal_start(ui); - tui_terminal_after_startup(ui); + tui_terminal_start(tui); + tui_terminal_after_startup(tui); if (enable_mouse) { - tui_mouse_on(ui); + tui_mouse_on(tui); } stream_set_blocking(input_global_fd(), false); // libuv expects this } #endif -static void tui_suspend(UI *ui) +void tui_suspend(TUIData *tui) { // on a non-UNIX system, this is a no-op #ifdef UNIX @@ -1432,66 +1363,63 @@ static void tui_suspend(UI *ui) // before continuing. This is done in another callback to avoid // loop_poll_events recursion multiqueue_put_event(resize_events, - event_create(suspend_event, 1, ui)); + event_create(suspend_event, 1, tui)); #endif } -static void tui_set_title(UI *ui, String title) +void tui_set_title(TUIData *tui, String title) { - TUIData *data = ui->data; - if (!(title.data && unibi_get_str(data->ut, unibi_to_status_line) - && unibi_get_str(data->ut, unibi_from_status_line))) { + if (!(title.data && unibi_get_str(tui->ut, unibi_to_status_line) + && unibi_get_str(tui->ut, unibi_from_status_line))) { return; } - unibi_out(ui, unibi_to_status_line); - out(ui, title.data, title.size); - unibi_out(ui, unibi_from_status_line); + unibi_out(tui, unibi_to_status_line); + out(tui, title.data, title.size); + unibi_out(tui, unibi_from_status_line); } -static void tui_set_icon(UI *ui, String icon) +void tui_set_icon(TUIData *tui, String icon) {} -static void tui_screenshot(UI *ui, String path) +void tui_screenshot(TUIData *tui, String path) { - TUIData *data = ui->data; - UGrid *grid = &data->grid; - flush_buf(ui); + UGrid *grid = &tui->grid; + flush_buf(tui); grid->row = 0; grid->col = 0; FILE *f = fopen(path.data, "w"); - data->screenshot = f; + tui->screenshot = f; fprintf(f, "%d,%d\n", grid->height, grid->width); - unibi_out(ui, unibi_clear_screen); + unibi_out(tui, unibi_clear_screen); for (int i = 0; i < grid->height; i++) { - cursor_goto(ui, i, 0); + cursor_goto(tui, i, 0); for (int j = 0; j < grid->width; j++) { - print_cell(ui, &grid->cells[i][j]); + print_cell(tui, &grid->cells[i][j]); } } - flush_buf(ui); - data->screenshot = NULL; + flush_buf(tui); + tui->screenshot = NULL; fclose(f); } -static void tui_option_set(UI *ui, String name, Object value) +void tui_option_set(TUIData *tui, String name, Object value) { - TUIData *data = ui->data; if (strequal(name.data, "mousemoveevent")) { - if (data->mouse_move_enabled != value.data.boolean) { - if (data->mouse_enabled) { - tui_mouse_off(ui); - data->mouse_move_enabled = value.data.boolean; - tui_mouse_on(ui); + if (tui->mouse_move_enabled != value.data.boolean) { + if (tui->mouse_enabled) { + tui_mouse_off(tui); + tui->mouse_move_enabled = value.data.boolean; + tui_mouse_on(tui); } else { - data->mouse_move_enabled = value.data.boolean; + tui->mouse_move_enabled = value.data.boolean; } } } else if (strequal(name.data, "termguicolors")) { - ui->rgb = value.data.boolean; - data->print_attr_id = -1; - invalidate(ui, 0, data->grid.height, 0, data->grid.width); + tui->rgb = value.data.boolean; + tui->print_attr_id = -1; + invalidate(tui, 0, tui->grid.height, 0, tui->grid.width); if (ui_client_channel_id) { MAXSIZE_TEMP_ARRAY(args, 2); @@ -1500,38 +1428,37 @@ static void tui_option_set(UI *ui, String name, Object value) rpc_send_event(ui_client_channel_id, "nvim_ui_set_option", args); } } else if (strequal(name.data, "ttimeout")) { - data->input.ttimeout = value.data.boolean; + tui->input.ttimeout = value.data.boolean; } else if (strequal(name.data, "ttimeoutlen")) { - data->input.ttimeoutlen = (long)value.data.integer; + tui->input.ttimeoutlen = (long)value.data.integer; } else if (strequal(name.data, "verbose")) { - data->verbose = value.data.integer; + tui->verbose = value.data.integer; } } -static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol, Integer endcol, - Integer clearcol, Integer clearattr, LineFlags flags, const schar_T *chunk, - const sattr_T *attrs) +void tui_raw_line(TUIData *tui, Integer g, Integer linerow, Integer startcol, Integer endcol, + Integer clearcol, Integer clearattr, LineFlags flags, const schar_T *chunk, + const sattr_T *attrs) { - TUIData *data = ui->data; - UGrid *grid = &data->grid; + UGrid *grid = &tui->grid; for (Integer c = startcol; c < endcol; c++) { memcpy(grid->cells[linerow][c].data, chunk[c - startcol], sizeof(schar_T)); - assert((size_t)attrs[c - startcol] < kv_size(data->attrs)); + assert((size_t)attrs[c - startcol] < kv_size(tui->attrs)); grid->cells[linerow][c].attr = attrs[c - startcol]; } UGRID_FOREACH_CELL(grid, (int)linerow, (int)startcol, (int)endcol, { - print_cell_at_pos(ui, (int)linerow, curcol, cell, + print_cell_at_pos(tui, (int)linerow, curcol, cell, curcol < endcol - 1 && (cell + 1)->data[0] == NUL); }); if (clearcol > endcol) { ugrid_clear_chunk(grid, (int)linerow, (int)endcol, (int)clearcol, (sattr_T)clearattr); - clear_region(ui, (int)linerow, (int)linerow + 1, (int)endcol, (int)clearcol, + clear_region(tui, (int)linerow, (int)linerow + 1, (int)endcol, (int)clearcol, (int)clearattr); } - if (flags & kLineFlagWrap && ui->width == grid->width + if (flags & kLineFlagWrap && tui->width == grid->width && linerow + 1 < grid->height) { // Only do line wrapping if the grid width is equal to the terminal // width and the line continuation is within the grid. @@ -1539,23 +1466,22 @@ static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol, I if (endcol != grid->width) { // Print the last char of the row, if we haven't already done so. int size = grid->cells[linerow][grid->width - 1].data[0] == NUL ? 2 : 1; - print_cell_at_pos(ui, (int)linerow, grid->width - size, + print_cell_at_pos(tui, (int)linerow, grid->width - size, &grid->cells[linerow][grid->width - size], size == 2); } // Wrap the cursor over to the next line. The next line will be // printed immediately without an intervening newline. - final_column_wrap(ui); + final_column_wrap(tui); } } -static void invalidate(UI *ui, int top, int bot, int left, int right) +static void invalidate(TUIData *tui, int top, int bot, int left, int right) { - TUIData *data = ui->data; Rect *intersects = NULL; - for (size_t i = 0; i < kv_size(data->invalid_regions); i++) { - Rect *r = &kv_A(data->invalid_regions, i); + for (size_t i = 0; i < kv_size(tui->invalid_regions); i++) { + Rect *r = &kv_A(tui->invalid_regions, i); // adjacent regions are treated as overlapping if (!(top > r->bot || bot < r->top) && !(left > r->right || right < r->left)) { @@ -1573,20 +1499,19 @@ static void invalidate(UI *ui, int top, int bot, int left, int right) intersects->right = MAX(right, intersects->right); } else { // Else just add a new entry; - kv_push(data->invalid_regions, ((Rect) { top, bot, left, right })); + kv_push(tui->invalid_regions, ((Rect) { top, bot, left, right })); } } /// Tries to get the user's wanted dimensions (columns and rows) for the entire /// application (i.e., the host terminal). -static void tui_guess_size(UI *ui) +void tui_guess_size(TUIData *tui) { - TUIData *data = ui->data; int width = 0, height = 0; // 1 - try from a system call(ioctl/TIOCGWINSZ on unix) - if (data->out_isatty - && !uv_tty_get_winsize(&data->output_handle.tty, &width, &height)) { + if (tui->out_isatty + && !uv_tty_get_winsize(&tui->output_handle.tty, &width, &height)) { goto end; } @@ -1601,8 +1526,8 @@ static void tui_guess_size(UI *ui) } // 3 - read from terminfo if available - height = unibi_get_num(data->ut, unibi_lines); - width = unibi_get_num(data->ut, unibi_columns); + height = unibi_get_num(tui->ut, unibi_lines); + width = unibi_get_num(tui->ut, unibi_columns); end: if (width <= 0 || height <= 0) { @@ -1611,50 +1536,49 @@ static void tui_guess_size(UI *ui) height = DFLT_ROWS; } - ui->width = width; - ui->height = height; + if (tui->width != width || tui->height != height) { + tui->width = width; + tui->height = height; - // TODO(bfredl): only if different from last value - ui_schedule_refresh(); + ui_client_set_size(width, height); + } } -static void unibi_goto(UI *ui, int row, int col) +static void unibi_goto(TUIData *tui, int row, int col) { - TUIData *data = ui->data; - UNIBI_SET_NUM_VAR(data->params[0], row); - UNIBI_SET_NUM_VAR(data->params[1], col); - unibi_out(ui, unibi_cursor_address); + UNIBI_SET_NUM_VAR(tui->params[0], row); + UNIBI_SET_NUM_VAR(tui->params[1], col); + unibi_out(tui, unibi_cursor_address); } #define UNIBI_OUT(fn) \ do { \ - TUIData *data = ui->data; \ const char *str = NULL; \ if (unibi_index >= 0) { \ - str = fn(data->ut, (unsigned)unibi_index); \ + str = fn(tui->ut, (unsigned)unibi_index); \ } \ if (str) { \ unibi_var_t vars[26 + 26]; \ unibi_var_t params[9]; \ - size_t orig_pos = data->bufpos; \ + size_t orig_pos = tui->bufpos; \ memset(&vars, 0, sizeof(vars)); \ - data->cork = true; \ + tui->cork = true; \ retry: \ - memcpy(params, data->params, sizeof(params)); \ - unibi_format(vars, vars + 26, str, params, out, ui, pad, ui); \ - if (data->overflow) { \ - data->bufpos = orig_pos; \ - flush_buf(ui); \ + memcpy(params, tui->params, sizeof(params)); \ + unibi_format(vars, vars + 26, str, params, out, tui, pad, tui); \ + if (tui->overflow) { \ + tui->bufpos = orig_pos; \ + flush_buf(tui); \ goto retry; \ } \ - data->cork = false; \ + tui->cork = false; \ } \ } while (0) -static void unibi_out(UI *ui, int unibi_index) +static void unibi_out(TUIData *tui, int unibi_index) { UNIBI_OUT(unibi_get_str); } -static void unibi_out_ext(UI *ui, int unibi_index) +static void unibi_out_ext(TUIData *tui, int unibi_index) { UNIBI_OUT(unibi_get_ext_str); } @@ -1662,25 +1586,24 @@ static void unibi_out_ext(UI *ui, int unibi_index) static void out(void *ctx, const char *str, size_t len) { - UI *ui = ctx; - TUIData *data = ui->data; - size_t available = sizeof(data->buf) - data->bufpos; + TUIData *tui = ctx; + size_t available = sizeof(tui->buf) - tui->bufpos; - if (data->cork && data->overflow) { + if (tui->cork && tui->overflow) { return; } if (len > available) { - if (data->cork) { + if (tui->cork) { // Called by unibi_format(): avoid flush_buf() halfway an escape sequence. - data->overflow = true; + tui->overflow = true; return; } - flush_buf(ui); + flush_buf(tui); } - memcpy(data->buf + data->bufpos, str, len); - data->bufpos += len; + memcpy(tui->buf + tui->bufpos, str, len); + tui->bufpos += len; } /// Called by unibi_format() for padding instructions. @@ -1696,15 +1619,14 @@ static void pad(void *ctx, size_t delay, int scale FUNC_ATTR_UNUSED, int force) return; } - UI *ui = ctx; - TUIData *data = ui->data; + TUIData *tui = ctx; - if (data->overflow) { + if (tui->overflow) { return; } - flush_buf(ui); - loop_uv_run(data->loop, (int64_t)(delay / 10), false); + flush_buf(tui); + loop_uv_run(tui->loop, (int64_t)(delay / 10), false); } static void unibi_set_if_empty(unibi_term *ut, enum unibi_string str, const char *val) @@ -1741,10 +1663,10 @@ static int unibi_find_ext_bool(unibi_term *ut, const char *name) /// Patches the terminfo records after loading from system or built-in db. /// Several entries in terminfo are known to be deficient or outright wrong; /// and several terminal emulators falsely announce incorrect terminal types. -static void patch_terminfo_bugs(TUIData *data, const char *term, const char *colorterm, +static void patch_terminfo_bugs(TUIData *tui, const char *term, const char *colorterm, long vte_version, long konsolev, bool iterm_env, bool nsterm) { - unibi_term *ut = data->ut; + unibi_term *ut = tui->ut; const char *xterm_version = os_getenv("XTERM_VERSION"); #if 0 // We don't need to identify this specifically, for now. bool roxterm = !!os_getenv("ROXTERM_ID"); @@ -1941,14 +1863,14 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col #define XTERM_SETAB_16 \ "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e39%;m" - data->unibi_ext.get_bg = (int)unibi_add_ext_str(ut, "ext.get_bg", - "\x1b]11;?\x07"); + tui->unibi_ext.get_bg = (int)unibi_add_ext_str(ut, "ext.get_bg", + "\x1b]11;?\x07"); // Query the terminal to see if it supports CSI u key encoding by writing CSI // ? u followed by a request for the primary device attributes (CSI c) // See https://sw.kovidgoyal.net/kitty/keyboard-protocol/#detection-of-support-for-this-protocol - data->unibi_ext.get_extkeys = (int)unibi_add_ext_str(ut, "ext.get_extkeys", - "\x1b[?u\x1b[c"); + tui->unibi_ext.get_extkeys = (int)unibi_add_ext_str(ut, "ext.get_extkeys", + "\x1b[?u\x1b[c"); // Terminals with 256-colour SGR support despite what terminfo says. if (unibi_get_num(ut, unibi_max_colors) < 256) { @@ -1979,14 +1901,14 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col // Blacklist of terminals that cannot be trusted to report DECSCUSR support. if (!(st || (vte_version != 0 && vte_version < 3900) || konsolev)) { - data->unibi_ext.reset_cursor_style = unibi_find_ext_str(ut, "Se"); - data->unibi_ext.set_cursor_style = unibi_find_ext_str(ut, "Ss"); + tui->unibi_ext.reset_cursor_style = unibi_find_ext_str(ut, "Se"); + tui->unibi_ext.set_cursor_style = unibi_find_ext_str(ut, "Ss"); } // Dickey ncurses terminfo includes Ss/Se capabilities since 2011-07-14. So // adding them to terminal types, that have such control sequences but lack // the correct terminfo entries, is a fixup, not an augmentation. - if (-1 == data->unibi_ext.set_cursor_style) { + if (-1 == tui->unibi_ext.set_cursor_style) { // DECSCUSR (cursor shape) is widely supported. // https://github.com/gnachman/iTerm2/pull/92 if ((!bsdvt && (!konsolev || konsolev >= 180770)) @@ -2012,59 +1934,59 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col // Example: console-terminal-emulator from the nosh toolset. || (linuxvt && (xterm_version || (vte_version > 0) || colorterm)))) { - data->unibi_ext.set_cursor_style = + tui->unibi_ext.set_cursor_style = (int)unibi_add_ext_str(ut, "Ss", "\x1b[%p1%d q"); - if (-1 == data->unibi_ext.reset_cursor_style) { - data->unibi_ext.reset_cursor_style = (int)unibi_add_ext_str(ut, "Se", - ""); + if (-1 == tui->unibi_ext.reset_cursor_style) { + tui->unibi_ext.reset_cursor_style = (int)unibi_add_ext_str(ut, "Se", + ""); } - unibi_set_ext_str(ut, (size_t)data->unibi_ext.reset_cursor_style, + unibi_set_ext_str(ut, (size_t)tui->unibi_ext.reset_cursor_style, "\x1b[ q"); } else if (linuxvt) { // Linux uses an idiosyncratic escape code to set the cursor shape and // does not support DECSCUSR. // See http://linuxgazette.net/137/anonymous.html for more info - data->unibi_ext.set_cursor_style = (int)unibi_add_ext_str(ut, "Ss", - "\x1b[?" - "%?" - // The parameter passed to Ss is the DECSCUSR parameter, so the - // terminal capability has to translate into the Linux idiosyncratic - // parameter. - // - // linuxvt only supports block and underline. It is also only - // possible to have a steady block (no steady underline) - "%p1%{2}%<" "%t%{8}" // blink block - "%e%p1%{2}%=" "%t%{112}" // steady block - "%e%p1%{3}%=" "%t%{4}" // blink underline (set to half block) - "%e%p1%{4}%=" "%t%{4}" // steady underline - "%e%p1%{5}%=" "%t%{2}" // blink bar (set to underline) - "%e%p1%{6}%=" "%t%{2}" // steady bar - "%e%{0}" // anything else - "%;" "%dc"); - if (-1 == data->unibi_ext.reset_cursor_style) { - data->unibi_ext.reset_cursor_style = (int)unibi_add_ext_str(ut, "Se", - ""); + tui->unibi_ext.set_cursor_style = (int)unibi_add_ext_str(ut, "Ss", + "\x1b[?" + "%?" + // The parameter passed to Ss is the DECSCUSR parameter, so the + // terminal capability has to translate into the Linux idiosyncratic + // parameter. + // + // linuxvt only supports block and underline. It is also only + // possible to have a steady block (no steady underline) + "%p1%{2}%<" "%t%{8}" // blink block + "%e%p1%{2}%=" "%t%{112}" // steady block + "%e%p1%{3}%=" "%t%{4}" // blink underline (set to half block) + "%e%p1%{4}%=" "%t%{4}" // steady underline + "%e%p1%{5}%=" "%t%{2}" // blink bar (set to underline) + "%e%p1%{6}%=" "%t%{2}" // steady bar + "%e%{0}" // anything else + "%;" "%dc"); + if (-1 == tui->unibi_ext.reset_cursor_style) { + tui->unibi_ext.reset_cursor_style = (int)unibi_add_ext_str(ut, "Se", + ""); } - unibi_set_ext_str(ut, (size_t)data->unibi_ext.reset_cursor_style, + unibi_set_ext_str(ut, (size_t)tui->unibi_ext.reset_cursor_style, "\x1b[?c"); } else if (konsolev > 0 && konsolev < 180770) { // Konsole before version 18.07.70: set up a nonce profile. This has // side effects on temporary font resizing. #6798 - data->unibi_ext.set_cursor_style = (int)unibi_add_ext_str(ut, "Ss", - TMUX_WRAP(tmux, - "\x1b]50;CursorShape=%?" - "%p1%{3}%<" "%t%{0}" // block - "%e%p1%{5}%<" "%t%{2}" // underline - "%e%{1}" // everything else is bar - "%;%d;BlinkingCursorEnabled=%?" - "%p1%{1}%<" "%t%{1}" // Fortunately if we exclude zero as special, - "%e%p1%{1}%&" // in all other cases we can treat bit #0 as a flag. - "%;%d\x07")); - if (-1 == data->unibi_ext.reset_cursor_style) { - data->unibi_ext.reset_cursor_style = (int)unibi_add_ext_str(ut, "Se", - ""); + tui->unibi_ext.set_cursor_style = (int)unibi_add_ext_str(ut, "Ss", + TMUX_WRAP(tmux, + "\x1b]50;CursorShape=%?" + "%p1%{3}%<" "%t%{0}" // block + "%e%p1%{5}%<" "%t%{2}" // underline + "%e%{1}" // everything else is bar + "%;%d;BlinkingCursorEnabled=%?" + "%p1%{1}%<" "%t%{1}" // Fortunately if we exclude zero as special, + "%e%p1%{1}%&" // in all other cases we can treat bit #0 as a flag. + "%;%d\x07")); + if (-1 == tui->unibi_ext.reset_cursor_style) { + tui->unibi_ext.reset_cursor_style = (int)unibi_add_ext_str(ut, "Se", + ""); } - unibi_set_ext_str(ut, (size_t)data->unibi_ext.reset_cursor_style, + unibi_set_ext_str(ut, (size_t)tui->unibi_ext.reset_cursor_style, "\x1b]50;\x07"); } } @@ -2072,10 +1994,10 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col /// This adds stuff that is not in standard terminfo as extended unibilium /// capabilities. -static void augment_terminfo(TUIData *data, const char *term, long vte_version, long konsolev, +static void augment_terminfo(TUIData *tui, const char *term, long vte_version, long konsolev, bool iterm_env, bool nsterm) { - unibi_term *ut = data->ut; + unibi_term *ut = tui->ut; bool xterm = terminfo_is_term_family(term, "xterm") // Treat Terminal.app as generic xterm-like, for now. || nsterm; @@ -2105,19 +2027,19 @@ static void augment_terminfo(TUIData *data, const char *term, long vte_version, || konsolev // per commentary in VT102Emulation.cpp || teraterm // per TeraTerm "Supported Control Functions" doco || rxvt) { // per command.C - data->unibi_ext.resize_screen = (int)unibi_add_ext_str(ut, - "ext.resize_screen", - "\x1b[8;%p1%d;%p2%dt"); + tui->unibi_ext.resize_screen = (int)unibi_add_ext_str(ut, + "ext.resize_screen", + "\x1b[8;%p1%d;%p2%dt"); } if (putty || xterm || hterm || rxvt) { - data->unibi_ext.reset_scroll_region = (int)unibi_add_ext_str(ut, - "ext.reset_scroll_region", - "\x1b[r"); + tui->unibi_ext.reset_scroll_region = (int)unibi_add_ext_str(ut, + "ext.reset_scroll_region", + "\x1b[r"); } // terminfo describes strikethrough modes as rmxx/smxx with respect // to the ECMA-48 strikeout/crossed-out attributes. - data->unibi_ext.enter_strikethrough_mode = unibi_find_ext_str(ut, "smxx"); + tui->unibi_ext.enter_strikethrough_mode = unibi_find_ext_str(ut, "smxx"); // Dickey ncurses terminfo does not include the setrgbf and setrgbb // capabilities, proposed by Rüdiger Sonderfeld on 2013-10-15. Adding @@ -2137,120 +2059,119 @@ static void augment_terminfo(TUIData *data, const char *term, long vte_version, // per http://invisible-island.net/xterm/xterm.log.html#xterm_282 || true_xterm); - data->unibi_ext.set_rgb_foreground = unibi_find_ext_str(ut, "setrgbf"); - if (-1 == data->unibi_ext.set_rgb_foreground) { + tui->unibi_ext.set_rgb_foreground = unibi_find_ext_str(ut, "setrgbf"); + if (-1 == tui->unibi_ext.set_rgb_foreground) { if (has_colon_rgb) { - data->unibi_ext.set_rgb_foreground = (int)unibi_add_ext_str(ut, "setrgbf", - "\x1b[38:2:%p1%d:%p2%d:%p3%dm"); + tui->unibi_ext.set_rgb_foreground = (int)unibi_add_ext_str(ut, "setrgbf", + "\x1b[38:2:%p1%d:%p2%d:%p3%dm"); } else { - data->unibi_ext.set_rgb_foreground = (int)unibi_add_ext_str(ut, "setrgbf", - "\x1b[38;2;%p1%d;%p2%d;%p3%dm"); + tui->unibi_ext.set_rgb_foreground = (int)unibi_add_ext_str(ut, "setrgbf", + "\x1b[38;2;%p1%d;%p2%d;%p3%dm"); } } - data->unibi_ext.set_rgb_background = unibi_find_ext_str(ut, "setrgbb"); - if (-1 == data->unibi_ext.set_rgb_background) { + tui->unibi_ext.set_rgb_background = unibi_find_ext_str(ut, "setrgbb"); + if (-1 == tui->unibi_ext.set_rgb_background) { if (has_colon_rgb) { - data->unibi_ext.set_rgb_background = (int)unibi_add_ext_str(ut, "setrgbb", - "\x1b[48:2:%p1%d:%p2%d:%p3%dm"); + tui->unibi_ext.set_rgb_background = (int)unibi_add_ext_str(ut, "setrgbb", + "\x1b[48:2:%p1%d:%p2%d:%p3%dm"); } else { - data->unibi_ext.set_rgb_background = (int)unibi_add_ext_str(ut, "setrgbb", - "\x1b[48;2;%p1%d;%p2%d;%p3%dm"); + tui->unibi_ext.set_rgb_background = (int)unibi_add_ext_str(ut, "setrgbb", + "\x1b[48;2;%p1%d;%p2%d;%p3%dm"); } } - data->unibi_ext.set_cursor_color = unibi_find_ext_str(ut, "Cs"); - if (-1 == data->unibi_ext.set_cursor_color) { + tui->unibi_ext.set_cursor_color = unibi_find_ext_str(ut, "Cs"); + if (-1 == tui->unibi_ext.set_cursor_color) { if (iterm || iterm_pretending_xterm) { // FIXME: Bypassing tmux like this affects the cursor colour globally, in // all panes, which is not particularly desirable. A better approach // would use a tmux control sequence and an extra if(screen) test. - data->unibi_ext.set_cursor_color = + tui->unibi_ext.set_cursor_color = (int)unibi_add_ext_str(ut, NULL, TMUX_WRAP(tmux, "\033]Pl%p1%06x\033\\")); } else if ((xterm || hterm || rxvt || tmux || alacritty) && (vte_version == 0 || vte_version >= 3900)) { // Supported in urxvt, newer VTE. - data->unibi_ext.set_cursor_color = (int)unibi_add_ext_str(ut, "ext.set_cursor_color", - "\033]12;%p1%s\007"); + tui->unibi_ext.set_cursor_color = (int)unibi_add_ext_str(ut, "ext.set_cursor_color", + "\033]12;%p1%s\007"); } } - if (-1 != data->unibi_ext.set_cursor_color) { + if (-1 != tui->unibi_ext.set_cursor_color) { // Some terminals supporting cursor color changing specify their Cs // capability to take a string parameter. Others take a numeric parameter. // If and only if the format string contains `%s` we assume a string // parameter. #20628 const char *set_cursor_color = - unibi_get_ext_str(ut, (unsigned)data->unibi_ext.set_cursor_color); + unibi_get_ext_str(ut, (unsigned)tui->unibi_ext.set_cursor_color); if (set_cursor_color) { - data->set_cursor_color_as_str = strstr(set_cursor_color, "%s") != NULL; + tui->set_cursor_color_as_str = strstr(set_cursor_color, "%s") != NULL; } - data->unibi_ext.reset_cursor_color = unibi_find_ext_str(ut, "Cr"); - if (-1 == data->unibi_ext.reset_cursor_color) { - data->unibi_ext.reset_cursor_color = (int)unibi_add_ext_str(ut, "ext.reset_cursor_color", - "\x1b]112\x07"); + tui->unibi_ext.reset_cursor_color = unibi_find_ext_str(ut, "Cr"); + if (-1 == tui->unibi_ext.reset_cursor_color) { + tui->unibi_ext.reset_cursor_color = (int)unibi_add_ext_str(ut, "ext.reset_cursor_color", + "\x1b]112\x07"); } } - data->unibi_ext.save_title = (int)unibi_add_ext_str(ut, "ext.save_title", "\x1b[22;0t"); - data->unibi_ext.restore_title = (int)unibi_add_ext_str(ut, "ext.restore_title", "\x1b[23;0t"); + tui->unibi_ext.save_title = (int)unibi_add_ext_str(ut, "ext.save_title", "\x1b[22;0t"); + tui->unibi_ext.restore_title = (int)unibi_add_ext_str(ut, "ext.restore_title", "\x1b[23;0t"); /// Terminals usually ignore unrecognized private modes, and there is no /// known ambiguity with these. So we just set them unconditionally. - data->unibi_ext.enable_lr_margin = + tui->unibi_ext.enable_lr_margin = (int)unibi_add_ext_str(ut, "ext.enable_lr_margin", "\x1b[?69h"); - data->unibi_ext.disable_lr_margin = (int)unibi_add_ext_str(ut, "ext.disable_lr_margin", - "\x1b[?69l"); - data->unibi_ext.enable_bracketed_paste = (int)unibi_add_ext_str(ut, "ext.enable_bpaste", - "\x1b[?2004h"); - data->unibi_ext.disable_bracketed_paste = (int)unibi_add_ext_str(ut, "ext.disable_bpaste", - "\x1b[?2004l"); + tui->unibi_ext.disable_lr_margin = (int)unibi_add_ext_str(ut, "ext.disable_lr_margin", + "\x1b[?69l"); + tui->unibi_ext.enable_bracketed_paste = (int)unibi_add_ext_str(ut, "ext.enable_bpaste", + "\x1b[?2004h"); + tui->unibi_ext.disable_bracketed_paste = (int)unibi_add_ext_str(ut, "ext.disable_bpaste", + "\x1b[?2004l"); // For urxvt send BOTH xterm and old urxvt sequences. #8695 - data->unibi_ext.enable_focus_reporting = (int)unibi_add_ext_str(ut, "ext.enable_focus", - rxvt ? "\x1b[?1004h\x1b]777;focus;on\x7" : "\x1b[?1004h"); - data->unibi_ext.disable_focus_reporting = (int)unibi_add_ext_str(ut, "ext.disable_focus", - rxvt ? "\x1b[?1004l\x1b]777;focus;off\x7" : "\x1b[?1004l"); - data->unibi_ext.enable_mouse = (int)unibi_add_ext_str(ut, "ext.enable_mouse", - "\x1b[?1002h\x1b[?1006h"); - data->unibi_ext.disable_mouse = (int)unibi_add_ext_str(ut, "ext.disable_mouse", - "\x1b[?1002l\x1b[?1006l"); - data->unibi_ext.enable_mouse_move = (int)unibi_add_ext_str(ut, "ext.enable_mouse_move", - "\x1b[?1003h"); - data->unibi_ext.disable_mouse_move = (int)unibi_add_ext_str(ut, "ext.disable_mouse_move", - "\x1b[?1003l"); + tui->unibi_ext.enable_focus_reporting = (int)unibi_add_ext_str(ut, "ext.enable_focus", + rxvt ? "\x1b[?1004h\x1b]777;focus;on\x7" : "\x1b[?1004h"); + tui->unibi_ext.disable_focus_reporting = (int)unibi_add_ext_str(ut, "ext.disable_focus", + rxvt ? "\x1b[?1004l\x1b]777;focus;off\x7" : "\x1b[?1004l"); + tui->unibi_ext.enable_mouse = (int)unibi_add_ext_str(ut, "ext.enable_mouse", + "\x1b[?1002h\x1b[?1006h"); + tui->unibi_ext.disable_mouse = (int)unibi_add_ext_str(ut, "ext.disable_mouse", + "\x1b[?1002l\x1b[?1006l"); + tui->unibi_ext.enable_mouse_move = (int)unibi_add_ext_str(ut, "ext.enable_mouse_move", + "\x1b[?1003h"); + tui->unibi_ext.disable_mouse_move = (int)unibi_add_ext_str(ut, "ext.disable_mouse_move", + "\x1b[?1003l"); // Extended underline. // terminfo will have Smulx for this (but no support for colors yet). - data->unibi_ext.set_underline_style = unibi_find_ext_str(ut, "Smulx"); - if (data->unibi_ext.set_underline_style == -1) { + tui->unibi_ext.set_underline_style = unibi_find_ext_str(ut, "Smulx"); + if (tui->unibi_ext.set_underline_style == -1) { int ext_bool_Su = unibi_find_ext_bool(ut, "Su"); // used by kitty if (vte_version >= 5102 || konsolev >= 221170 || (ext_bool_Su != -1 && unibi_get_ext_bool(ut, (size_t)ext_bool_Su))) { - data->unibi_ext.set_underline_style = (int)unibi_add_ext_str(ut, "ext.set_underline_style", - "\x1b[4:%p1%dm"); + tui->unibi_ext.set_underline_style = (int)unibi_add_ext_str(ut, "ext.set_underline_style", + "\x1b[4:%p1%dm"); } } - if (data->unibi_ext.set_underline_style != -1) { + if (tui->unibi_ext.set_underline_style != -1) { // Only support colon syntax. #9270 - data->unibi_ext.set_underline_color = (int)unibi_add_ext_str(ut, "ext.set_underline_color", - "\x1b[58:2::%p1%d:%p2%d:%p3%dm"); + tui->unibi_ext.set_underline_color = (int)unibi_add_ext_str(ut, "ext.set_underline_color", + "\x1b[58:2::%p1%d:%p2%d:%p3%dm"); } if (!kitty && (vte_version == 0 || vte_version >= 5400)) { // Fallback to Xterm's modifyOtherKeys if terminal does not support CSI u - data->input.extkeys_type = kExtkeysXterm; + tui->input.extkeys_type = kExtkeysXterm; } } -static void flush_buf(UI *ui) +static void flush_buf(TUIData *tui) { uv_write_t req; uv_buf_t bufs[3]; uv_buf_t *bufp = &bufs[0]; - TUIData *data = ui->data; // The content of the output for each condition is shown in the following - // table. Therefore, if data->bufpos == 0 and N/A or invis + norm, there is + // table. Therefore, if tui->bufpos == 0 and N/A or invis + norm, there is // no need to output it. // // | is_invisible | !is_invisible @@ -2262,54 +2183,54 @@ static void flush_buf(UI *ui) // | !want_invisible | norm | invis + norm // ------+-----------------+--------------+--------------- // - if (data->bufpos <= 0 - && ((data->is_invisible && data->busy) - || (data->is_invisible && !data->busy && data->want_invisible) - || (!data->is_invisible && !data->busy && !data->want_invisible))) { + if (tui->bufpos <= 0 + && ((tui->is_invisible && tui->busy) + || (tui->is_invisible && !tui->busy && tui->want_invisible) + || (!tui->is_invisible && !tui->busy && !tui->want_invisible))) { return; } - if (!data->is_invisible) { + if (!tui->is_invisible) { // cursor is visible. Write a "cursor invisible" command before writing the // buffer. - bufp->base = data->invis; - bufp->len = UV_BUF_LEN(data->invislen); + bufp->base = tui->invis; + bufp->len = UV_BUF_LEN(tui->invislen); bufp++; - data->is_invisible = true; + tui->is_invisible = true; } - if (data->bufpos > 0) { - bufp->base = data->buf; - bufp->len = UV_BUF_LEN(data->bufpos); + if (tui->bufpos > 0) { + bufp->base = tui->buf; + bufp->len = UV_BUF_LEN(tui->bufpos); bufp++; } - if (!data->busy) { - assert(data->is_invisible); + if (!tui->busy) { + assert(tui->is_invisible); // not busy and the cursor is invisible. Write a "cursor normal" command // after writing the buffer. - if (!data->want_invisible) { - bufp->base = data->norm; - bufp->len = UV_BUF_LEN(data->normlen); + if (!tui->want_invisible) { + bufp->base = tui->norm; + bufp->len = UV_BUF_LEN(tui->normlen); bufp++; - data->is_invisible = false; + tui->is_invisible = false; } } - if (data->screenshot) { + if (tui->screenshot) { for (size_t i = 0; i < (size_t)(bufp - bufs); i++) { - fwrite(bufs[i].base, bufs[i].len, 1, data->screenshot); + fwrite(bufs[i].base, bufs[i].len, 1, tui->screenshot); } } else { - int ret = uv_write(&req, STRUCT_CAST(uv_stream_t, &data->output_handle), + int ret = uv_write(&req, STRUCT_CAST(uv_stream_t, &tui->output_handle), bufs, (unsigned)(bufp - bufs), NULL); if (ret) { ELOG("uv_write failed: %s", uv_strerror(ret)); } - uv_run(&data->write_loop, UV_RUN_DEFAULT); + uv_run(&tui->write_loop, UV_RUN_DEFAULT); } - data->bufpos = 0; - data->overflow = false; + tui->bufpos = 0; + tui->overflow = false; } /// Try to get "kbs" code from stty because "the terminfo kbs entry is extremely -- cgit From b303ab9a7d5f695e53c3f261b000e0e748ed8654 Mon Sep 17 00:00:00 2001 From: bfredl Date: Fri, 6 Jan 2023 10:40:56 +0100 Subject: fix(tui): do not invoke loop recursively for pad() fixes #21610 --- src/nvim/tui/tui.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 162f54b6c1..44f6718039 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -1626,7 +1626,7 @@ static void pad(void *ctx, size_t delay, int scale FUNC_ATTR_UNUSED, int force) } flush_buf(tui); - loop_uv_run(tui->loop, (int64_t)(delay / 10), false); + uv_sleep((unsigned int)(delay/10)); } static void unibi_set_if_empty(unibi_term *ut, enum unibi_string str, const char *val) -- cgit From 3269902a13df3bccf8705db73488c0a47f495514 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sun, 15 Jan 2023 14:16:33 +0100 Subject: refactor: fix IWYU mapping file and use IWYU (#21802) Also add the EXITFREE definition to main_lib rather than the nvim target, as the header generation needs the EXITFREE flag to work properly. --- src/nvim/tui/input.c | 8 ++------ src/nvim/tui/terminfo.c | 6 +++--- src/nvim/tui/tui.c | 6 +----- 3 files changed, 6 insertions(+), 14 deletions(-) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index b837a380d5..55a8b3666e 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -8,31 +8,27 @@ #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" -#include "nvim/api/vim.h" #include "nvim/ascii.h" -#include "nvim/autocmd.h" #include "nvim/charset.h" #include "nvim/event/defs.h" -#include "nvim/event/multiqueue.h" -#include "nvim/globals.h" #include "nvim/log.h" #include "nvim/macros.h" #include "nvim/main.h" #include "nvim/map.h" #include "nvim/memory.h" -#include "nvim/message.h" #include "nvim/option.h" #include "nvim/os/input.h" #include "nvim/os/os.h" #include "nvim/tui/input.h" #include "nvim/tui/input_defs.h" #include "nvim/tui/tui.h" +#include "nvim/types.h" +#include "nvim/ui_client.h" #ifdef MSWIN # include "nvim/os/os_win_console.h" #endif #include "nvim/event/rstream.h" #include "nvim/msgpack_rpc/channel.h" -#include "nvim/ui.h" #define KEY_BUFFER_SIZE 0xfff diff --git a/src/nvim/tui/terminfo.c b/src/nvim/tui/terminfo.c index 507e9df21e..d630a34ce2 100644 --- a/src/nvim/tui/terminfo.c +++ b/src/nvim/tui/terminfo.c @@ -7,12 +7,12 @@ #include #include +#include "klib/kvec.h" +#include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" +#include "nvim/ascii.h" #include "nvim/charset.h" -#include "nvim/globals.h" #include "nvim/memory.h" -#include "nvim/message.h" -#include "nvim/option.h" #include "nvim/strings.h" #include "nvim/tui/terminfo.h" #include "nvim/tui/terminfo_defs.h" diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 44f6718039..5232bcad19 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -16,7 +16,6 @@ #include "klib/kvec.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" -#include "nvim/api/vim.h" #include "nvim/ascii.h" #include "nvim/cursor_shape.h" #include "nvim/event/defs.h" @@ -32,12 +31,10 @@ #include "nvim/main.h" #include "nvim/mbyte.h" #include "nvim/memory.h" -#include "nvim/message.h" #include "nvim/msgpack_rpc/channel.h" -#include "nvim/option.h" #include "nvim/os/input.h" #include "nvim/os/os.h" -#include "nvim/os/signal.h" +#include "nvim/ui_client.h" #ifdef MSWIN # include "nvim/os/os_win_console.h" #endif @@ -46,7 +43,6 @@ #include "nvim/tui/tui.h" #include "nvim/ugrid.h" #include "nvim/ui.h" -#include "nvim/vim.h" // Space reserved in two output buffers to make the cursor normal or invisible // when flushing. No existing terminal will require 32 bytes to do that. -- cgit From 160c69b655ce2e47fbedcc87fcb4949c2bc04dce Mon Sep 17 00:00:00 2001 From: bfredl Date: Tue, 10 Jan 2023 14:03:15 +0100 Subject: fix(ui): re-organize tty fd handling and fix issues - Use the correct fd to replace stdin on windows (CONIN) - Don't start the TUI if there are no tty fd (not a regression, but makes sense regardless) - De-mythologize "global input fd". it is just STDIN. --- src/nvim/tui/input.c | 16 ++-------------- src/nvim/tui/tui.c | 11 ++++++----- 2 files changed, 8 insertions(+), 19 deletions(-) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index b837a380d5..91fbdf7886 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -153,19 +153,7 @@ void tinput_init(TermInput *input, Loop *loop) kitty_key_map_entry[i].name); } - // If stdin is not a pty, switch to stderr. For cases like: - // echo q | nvim -es - // ls *.md | xargs nvim -#ifdef MSWIN - if (!os_isatty(input->in_fd)) { - input->in_fd = os_get_conin_fd(); - } -#else - if (!os_isatty(input->in_fd) && os_isatty(STDERR_FILENO)) { - input->in_fd = STDERR_FILENO; - } -#endif - input_global_fd_init(input->in_fd); + input->in_fd = STDIN_FILENO; const char *term = os_getenv("TERM"); if (!term) { @@ -174,7 +162,7 @@ void tinput_init(TermInput *input, Loop *loop) input->tk = termkey_new_abstract(term, TERMKEY_FLAG_UTF8 | TERMKEY_FLAG_NOSTART); - termkey_hook_terminfo_getstr(input->tk, input->tk_ti_hook_fn, NULL); + termkey_hook_terminfo_getstr(input->tk, input->tk_ti_hook_fn, input); termkey_start(input->tk); int curflags = termkey_get_canonflags(input->tk); diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 44f6718039..e46dc892ea 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -1342,7 +1342,7 @@ static void suspend_event(void **argv) TUIData *tui = argv[0]; bool enable_mouse = tui->mouse_enabled; tui_terminal_stop(tui); - stream_set_blocking(input_global_fd(), true); // normalize stream (#2598) + stream_set_blocking(tui->input.in_fd, true); // normalize stream (#2598) kill(0, SIGTSTP); @@ -1351,7 +1351,7 @@ static void suspend_event(void **argv) if (enable_mouse) { tui_mouse_on(tui); } - stream_set_blocking(input_global_fd(), false); // libuv expects this + stream_set_blocking(tui->input.in_fd, false); // libuv expects this } #endif @@ -2238,12 +2238,12 @@ static void flush_buf(TUIData *tui) /// /// @see tmux/tty-keys.c fe4e9470bb504357d073320f5d305b22663ee3fd /// @see https://bugzilla.redhat.com/show_bug.cgi?id=142659 -static const char *tui_get_stty_erase(void) +static const char *tui_get_stty_erase(int fd) { static char stty_erase[2] = { 0 }; #if defined(HAVE_TERMIOS_H) struct termios t; - if (tcgetattr(input_global_fd(), &t) != -1) { + if (tcgetattr(fd, &t) != -1) { stty_erase[0] = (char)t.c_cc[VERASE]; stty_erase[1] = '\0'; DLOG("stty/termios:erase=%s", stty_erase); @@ -2256,9 +2256,10 @@ static const char *tui_get_stty_erase(void) /// @see TermInput.tk_ti_hook_fn static const char *tui_tk_ti_getstr(const char *name, const char *value, void *data) { + TermInput *input = data; static const char *stty_erase = NULL; if (stty_erase == NULL) { - stty_erase = tui_get_stty_erase(); + stty_erase = tui_get_stty_erase(input->in_fd); } if (strequal(name, "key_backspace")) { -- cgit From 6bfbb4db1d3708ce4ea69d29f3afe73def5a9f2a Mon Sep 17 00:00:00 2001 From: bfredl Date: Wed, 18 Jan 2023 13:59:40 +0100 Subject: fix(unittests): fix TUI broken test previously ignored --- src/nvim/tui/input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 5325ae3e4d..2cb39ab26b 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -655,7 +655,7 @@ static HandleState handle_background_color(TermInput *input) bool is_dark = luminance < 0.5; char *bgvalue = is_dark ? "dark" : "light"; DLOG("bg response: %s", bgvalue); - ui_client_bg_respose = is_dark ? kTrue : kFalse; + ui_client_bg_response = is_dark ? kTrue : kFalse; set_bg(bgvalue); input->waiting_for_bg_response = 0; } else if (!done && !bad) { -- cgit From f5d357de553c1aa61cdb25b047f984f6414b1967 Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Thu, 19 Jan 2023 17:51:56 +0000 Subject: refactor(highlight): reshape the HL_UNDER* bits into a 3-bit integer mask Saves two bits for reuse for new features --- src/nvim/tui/tui.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 44b99f6c84..3b1cc1e420 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -511,7 +511,7 @@ static bool attrs_differ(TUIData *tui, int id1, int id2, bool rgb) return a1.cterm_fg_color != a2.cterm_fg_color || a1.cterm_bg_color != a2.cterm_bg_color || a1.cterm_ae_attr != a2.cterm_ae_attr - || (a1.cterm_ae_attr & HL_ANY_UNDERLINE + || (a1.cterm_ae_attr & HL_UNDERLINE_MASK && a1.rgb_sp_color != a2.rgb_sp_color); } } @@ -538,13 +538,14 @@ static void update_attrs(TUIData *tui, int attr_id) bool underdotted; bool underdashed; if (tui->unibi_ext.set_underline_style != -1) { - underline = attr & HL_UNDERLINE; - undercurl = attr & HL_UNDERCURL; - underdouble = attr & HL_UNDERDOUBLE; - underdashed = attr & HL_UNDERDASHED; - underdotted = attr & HL_UNDERDOTTED; + int ul = attr & HL_UNDERLINE_MASK; + underline = ul == HL_UNDERLINE; + undercurl = ul == HL_UNDERCURL; + underdouble = ul == HL_UNDERDOUBLE; + underdashed = ul == HL_UNDERDASHED; + underdotted = ul == HL_UNDERDOTTED; } else { - underline = attr & HL_ANY_UNDERLINE; + underline = attr & HL_UNDERLINE_MASK; undercurl = false; underdouble = false; underdotted = false; -- cgit From a56dc7a7f6f4cae9461921cf9253152a40cd7619 Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Wed, 18 Jan 2023 18:19:21 +0000 Subject: feat(tui): support altfont mode in tui.c --- src/nvim/tui/tui.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 3b1cc1e420..3130cd27fe 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -137,6 +137,7 @@ struct TUIData { int enable_bracketed_paste, disable_bracketed_paste; int enable_lr_margin, disable_lr_margin; int enter_strikethrough_mode; + int enter_altfont_mode; int set_rgb_foreground, set_rgb_background; int set_cursor_color; int reset_cursor_color; @@ -251,6 +252,7 @@ static void terminfo_start(TUIData *tui) tui->unibi_ext.enable_bracketed_paste = -1; tui->unibi_ext.disable_bracketed_paste = -1; tui->unibi_ext.enter_strikethrough_mode = -1; + tui->unibi_ext.enter_altfont_mode = -1; tui->unibi_ext.enable_lr_margin = -1; tui->unibi_ext.disable_lr_margin = -1; tui->unibi_ext.enable_focus_reporting = -1; @@ -531,6 +533,7 @@ static void update_attrs(TUIData *tui, int attr_id) bool reverse = attr & HL_INVERSE; bool standout = attr & HL_STANDOUT; bool strikethrough = attr & HL_STRIKETHROUGH; + bool altfont = attr & HL_ALTFONT; bool underline; bool undercurl; @@ -590,6 +593,9 @@ static void update_attrs(TUIData *tui, int attr_id) if (italic) { unibi_out(tui, unibi_enter_italics_mode); } + if (altfont && tui->unibi_ext.enter_altfont_mode != -1) { + unibi_out_ext(tui, tui->unibi_ext.enter_altfont_mode); + } if (strikethrough && tui->unibi_ext.enter_strikethrough_mode != -1) { unibi_out_ext(tui, tui->unibi_ext.enter_strikethrough_mode); } @@ -2038,6 +2044,11 @@ static void augment_terminfo(TUIData *tui, const char *term, long vte_version, l // to the ECMA-48 strikeout/crossed-out attributes. tui->unibi_ext.enter_strikethrough_mode = unibi_find_ext_str(ut, "smxx"); + // It should be pretty safe to always enable this, as terminals will ignore + // unrecognised SGR numbers. + tui->unibi_ext.enter_altfont_mode = (int)unibi_add_ext_str(ut, "ext.enter_altfont_mode", + "\x1b[11m"); + // Dickey ncurses terminfo does not include the setrgbf and setrgbb // capabilities, proposed by Rüdiger Sonderfeld on 2013-10-15. Adding // them here when terminfo lacks them is an augmentation, not a fixup. -- cgit From 51b39f816ca4a44660e7ab7906520ae908492734 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 25 Jan 2023 07:34:28 +0800 Subject: fix(tui): set stdin as "blocking" on exit (#21973) This fixes a regression from #21605 that stdin is no longer set as "blocking" after Nvim TUI exits, and the problems described in #2598 happen again. I'm not sure if this should be done in TUI code or common exiting code. I added this call in tui_stop() as it is also present in tui_suspend(). --- src/nvim/tui/tui.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/tui') diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 3130cd27fe..a50e44f7a3 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -459,6 +459,7 @@ static void tui_terminal_stop(TUIData *tui) void tui_stop(TUIData *tui) { tui_terminal_stop(tui); + stream_set_blocking(tui->input.in_fd, true); // normalize stream (#2598) tinput_destroy(&tui->input); tui->stopped = true; signal_watcher_close(&tui->winch_handle, NULL); -- cgit