diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2025-02-05 23:09:29 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2025-02-05 23:09:29 +0000 |
commit | d5f194ce780c95821a855aca3c19426576d28ae0 (patch) | |
tree | d45f461b19f9118ad2bb1f440a7a08973ad18832 /src/nvim/tui | |
parent | c5d770d311841ea5230426cc4c868e8db27300a8 (diff) | |
parent | 44740e561fc93afe3ebecfd3618bda2d2abeafb0 (diff) | |
download | rneovim-rahm.tar.gz rneovim-rahm.tar.bz2 rneovim-rahm.zip |
Diffstat (limited to 'src/nvim/tui')
-rw-r--r-- | src/nvim/tui/input.c | 53 | ||||
-rw-r--r-- | src/nvim/tui/input.h | 2 | ||||
-rw-r--r-- | src/nvim/tui/termkey/driver-csi.c | 57 | ||||
-rw-r--r-- | src/nvim/tui/termkey/driver-csi.h | 2 | ||||
-rw-r--r-- | src/nvim/tui/termkey/driver-ti.c | 6 | ||||
-rw-r--r-- | src/nvim/tui/termkey/driver-ti.h | 2 | ||||
-rw-r--r-- | src/nvim/tui/termkey/termkey-internal.h | 2 | ||||
-rw-r--r-- | src/nvim/tui/termkey/termkey.c | 55 | ||||
-rw-r--r-- | src/nvim/tui/termkey/termkey.h | 6 | ||||
-rw-r--r-- | src/nvim/tui/termkey/termkey_defs.h | 9 | ||||
-rw-r--r-- | src/nvim/tui/tui.c | 63 | ||||
-rw-r--r-- | src/nvim/tui/tui_defs.h | 1 |
12 files changed, 180 insertions, 78 deletions
diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 98dd7b4b45..1f73f2d135 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -8,7 +8,6 @@ #include "nvim/api/private/helpers.h" #include "nvim/event/loop.h" #include "nvim/event/rstream.h" -#include "nvim/event/stream.h" #include "nvim/macros_defs.h" #include "nvim/main.h" #include "nvim/map_defs.h" @@ -160,12 +159,16 @@ void tinput_init(TermInput *input, Loop *loop) // initialize a timer handle for handling ESC with libtermkey uv_timer_init(&loop->uv, &input->timer_handle); input->timer_handle.data = input; + + uv_timer_init(&loop->uv, &input->bg_query_timer); + input->bg_query_timer.data = input; } void tinput_destroy(TermInput *input) { map_destroy(int, &kitty_key_map); uv_close((uv_handle_t *)&input->timer_handle, NULL); + uv_close((uv_handle_t *)&input->bg_query_timer, NULL); rstream_may_close(&input->read_stream); termkey_destroy(input->tk); } @@ -179,6 +182,7 @@ void tinput_stop(TermInput *input) { rstream_stop(&input->read_stream); uv_timer_stop(&input->timer_handle); + uv_timer_stop(&input->bg_query_timer); } static void tinput_done_event(void **argv) @@ -383,6 +387,10 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key) len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Middle"); } else if (button == 3) { len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Right"); + } else if (button == 8) { + len += (size_t)snprintf(buf + len, sizeof(buf) - len, "X1"); + } else if (button == 9) { + len += (size_t)snprintf(buf + len, sizeof(buf) - len, "X2"); } switch (ev) { @@ -427,6 +435,15 @@ static void tk_getkeys(TermInput *input, bool force) TermKeyResult result; while ((result = tk_getkey(input->tk, &key, force)) == TERMKEY_RES_KEY) { + // Only press and repeat events are handled for now + switch (key.event) { + case TERMKEY_EVENT_PRESS: + case TERMKEY_EVENT_REPEAT: + break; + default: + continue; + } + if (key.type == TERMKEY_TYPE_UNICODE && !key.modifiers) { forward_simple_utf8(input, &key); } else if (key.type == TERMKEY_TYPE_UNICODE @@ -474,6 +491,13 @@ static void tinput_timer_cb(uv_timer_t *handle) tinput_flush(input); } +static void bg_query_timer_cb(uv_timer_t *handle) + FUNC_ATTR_NONNULL_ALL +{ + TermInput *input = handle->data; + tui_query_bg_color(input->tui_data); +} + /// Handle focus events. /// /// If the upcoming sequence of bytes in the input stream matches the termcode @@ -660,6 +684,33 @@ static void handle_unknown_csi(TermInput *input, const TermKeyKey *key) } } break; + case 'n': + // Device Status Report (DSR) + if (nparams == 2) { + int args[2]; + for (size_t i = 0; i < ARRAY_SIZE(args); i++) { + if (termkey_interpret_csi_param(params[i], &args[i], NULL, NULL) != TERMKEY_RES_KEY) { + return; + } + } + + if (args[0] == 997) { + // Theme update notification + // https://github.com/contour-terminal/contour/blob/master/docs/vt-extensions/color-palette-update-notifications.md + // The second argument tells us whether the OS theme is set to light + // mode or dark mode, but all we care about is the background color of + // the terminal emulator. We query for that with OSC 11 and the response + // is handled by the autocommand created in _defaults.lua. The terminal + // may send us multiple notifications all at once so we use a timer to + // coalesce the queries. + if (uv_timer_get_due_in(&input->bg_query_timer) > 0) { + return; + } + + uv_timer_start(&input->bg_query_timer, bg_query_timer_cb, 100, 0); + } + } + break; default: break; } diff --git a/src/nvim/tui/input.h b/src/nvim/tui/input.h index 4c2baf908e..e48982f9a4 100644 --- a/src/nvim/tui/input.h +++ b/src/nvim/tui/input.h @@ -1,6 +1,7 @@ #pragma once #include <stdbool.h> +#include <stddef.h> #include <stdint.h> #include <uv.h> @@ -32,6 +33,7 @@ typedef struct { TermKey *tk; TermKey_Terminfo_Getstr_Hook *tk_ti_hook_fn; ///< libtermkey terminfo hook uv_timer_t timer_handle; + uv_timer_t bg_query_timer; ///< timer used to batch background color queries Loop *loop; RStream read_stream; TUIData *tui_data; diff --git a/src/nvim/tui/termkey/driver-csi.c b/src/nvim/tui/termkey/driver-csi.c index 28c7eaccfd..d427be50ff 100644 --- a/src/nvim/tui/termkey/driver-csi.c +++ b/src/nvim/tui/termkey/driver-csi.c @@ -1,11 +1,10 @@ #include <assert.h> -#include <stdio.h> +#include <stdint.h> #include <string.h> #include "nvim/memory.h" #include "nvim/tui/termkey/driver-csi.h" #include "nvim/tui/termkey/termkey-internal.h" -#include "nvim/tui/termkey/termkey.h" #include "nvim/tui/termkey/termkey_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -32,11 +31,20 @@ static TermKeyResult handle_csi_ss3_full(TermKey *tk, TermKeyKey *key, int cmd, if (nparams > 1 && params[1].param != NULL) { int arg = 0; - result = termkey_interpret_csi_param(params[1], &arg, NULL, NULL); + int subparam = 0; + size_t nsubparams = 1; + result = termkey_interpret_csi_param(params[1], &arg, &subparam, &nsubparams); if (result != TERMKEY_RES_KEY) { return result; } + if (nsubparams > 0) { + key->event = parse_key_event(subparam); + if (key->event == TERMKEY_EVENT_UNKNOWN) { + return TERMKEY_RES_NONE; + } + } + key->modifiers = arg - 1; } else { key->modifiers = 0; @@ -104,11 +112,20 @@ static TermKeyResult handle_csifunc(TermKey *tk, TermKeyKey *key, int cmd, TermK int args[3]; if (nparams > 1 && params[1].param != NULL) { - result = termkey_interpret_csi_param(params[1], &args[1], NULL, NULL); + int subparam = 0; + size_t nsubparams = 1; + result = termkey_interpret_csi_param(params[1], &args[1], &subparam, &nsubparams); if (result != TERMKEY_RES_KEY) { return result; } + if (nsubparams > 0) { + key->event = parse_key_event(subparam); + if (key->event == TERMKEY_EVENT_UNKNOWN) { + return TERMKEY_RES_NONE; + } + } + key->modifiers = args[1] - 1; } else { key->modifiers = 0; @@ -178,9 +195,11 @@ static TermKeyResult handle_csi_u(TermKey *tk, TermKeyKey *key, int cmd, TermKey return TERMKEY_RES_ERROR; } - if (nsubparams > 0 && subparam != 1) { - // Not a press event. Ignore for now - return TERMKEY_RES_NONE; + if (nsubparams > 0) { + key->event = parse_key_event(subparam); + if (key->event == TERMKEY_EVENT_UNKNOWN) { + return TERMKEY_RES_NONE; + } } key->modifiers = args[1] - 1; @@ -308,6 +327,12 @@ TermKeyResult termkey_interpret_mouse(TermKey *tk, const TermKeyKey *key, TermKe btn = code + 4 - 64; break; + case 128: + case 129: + *event = drag ? TERMKEY_MOUSE_DRAG : TERMKEY_MOUSE_PRESS; + btn = code + 8 - 128; + break; + default: *event = TERMKEY_MOUSE_UNKNOWN; } @@ -419,6 +444,20 @@ TermKeyResult termkey_interpret_modereport(TermKey *tk, const TermKeyKey *key, i #define CHARAT(i) (tk->buffer[tk->buffstart + (i)]) +static TermKeyEvent parse_key_event(int n) +{ + switch (n) { + case 1: + return TERMKEY_EVENT_PRESS; + case 2: + return TERMKEY_EVENT_REPEAT; + case 3: + return TERMKEY_EVENT_RELEASE; + default: + return TERMKEY_EVENT_UNKNOWN; + } +} + static TermKeyResult parse_csi(TermKey *tk, size_t introlen, size_t *csi_len, TermKeyCsiParam params[], size_t *nargs, unsigned *commandp) { @@ -529,7 +568,7 @@ TermKeyResult termkey_interpret_csi_param(TermKeyCsiParam param, int *paramp, in if (c == ':') { if (length == 0) { *paramp = arg; - } else { + } else if (subparams != NULL) { subparams[length - 1] = arg; } @@ -544,7 +583,7 @@ TermKeyResult termkey_interpret_csi_param(TermKeyCsiParam param, int *paramp, in if (length == 0) { *paramp = arg; - } else { + } else if (subparams != NULL) { subparams[length - 1] = arg; } diff --git a/src/nvim/tui/termkey/driver-csi.h b/src/nvim/tui/termkey/driver-csi.h index 0abd8b5c2e..644cc9d58b 100644 --- a/src/nvim/tui/termkey/driver-csi.h +++ b/src/nvim/tui/termkey/driver-csi.h @@ -1,6 +1,6 @@ #pragma once -#include "nvim/tui/termkey/termkey_defs.h" +#include "nvim/tui/termkey/termkey_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "tui/termkey/driver-csi.h.generated.h" diff --git a/src/nvim/tui/termkey/driver-ti.c b/src/nvim/tui/termkey/driver-ti.c index 745ee9902f..e402f93e93 100644 --- a/src/nvim/tui/termkey/driver-ti.c +++ b/src/nvim/tui/termkey/driver-ti.c @@ -1,16 +1,16 @@ -#include <ctype.h> #include <errno.h> #include <stdbool.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include <sys/stat.h> -#include <sys/types.h> #include <unibilium.h> +#include <uv.h> #include "nvim/memory.h" #include "nvim/tui/termkey/driver-ti.h" #include "nvim/tui/termkey/termkey-internal.h" -#include "nvim/tui/termkey/termkey.h" +#include "nvim/tui/termkey/termkey_defs.h" #ifndef _WIN32 # include <unistd.h> diff --git a/src/nvim/tui/termkey/driver-ti.h b/src/nvim/tui/termkey/driver-ti.h index df9bd72d5b..6dbcb11344 100644 --- a/src/nvim/tui/termkey/driver-ti.h +++ b/src/nvim/tui/termkey/driver-ti.h @@ -1,6 +1,6 @@ #pragma once -#include "nvim/tui/termkey/termkey_defs.h" +#include "nvim/tui/termkey/termkey_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "tui/termkey/driver-ti.h.generated.h" diff --git a/src/nvim/tui/termkey/termkey-internal.h b/src/nvim/tui/termkey/termkey-internal.h index 107591f950..97fae939c5 100644 --- a/src/nvim/tui/termkey/termkey-internal.h +++ b/src/nvim/tui/termkey/termkey-internal.h @@ -47,7 +47,7 @@ struct TermKey { int canonflags; unsigned char *buffer; size_t buffstart; // First offset in buffer - size_t buffcount; // NUMBER of entires valid in buffer + size_t buffcount; // NUMBER of entries valid in buffer size_t buffsize; // Total malloc'ed size size_t hightide; // Position beyond buffstart at which peekkey() should next start // normally 0, but see also termkey_interpret_csi diff --git a/src/nvim/tui/termkey/termkey.c b/src/nvim/tui/termkey/termkey.c index e6440118f3..eabde2f9f7 100644 --- a/src/nvim/tui/termkey/termkey.c +++ b/src/nvim/tui/termkey/termkey.c @@ -1,9 +1,9 @@ #include <ctype.h> #include <errno.h> -#include <stdbool.h> #include <stdio.h> #include <string.h> +#include "nvim/macros_defs.h" #include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/tui/termkey/driver-csi.h" @@ -13,9 +13,10 @@ #include "nvim/tui/termkey/termkey_defs.h" #ifndef _WIN32 -# include <poll.h> -# include <strings.h> -# include <unistd.h> +// Include these directly instead of <termios.h> which is system-dependent. #31704 +# include <poll.h> // IWYU pragma: keep +# include <strings.h> // IWYU pragma: keep +# include <unistd.h> // IWYU pragma: keep #else # include <io.h> #endif @@ -633,40 +634,13 @@ static void eat_bytes(TermKey *tk, size_t count) tk->buffcount -= count; } -// TODO(dundargoc): we should be able to replace this with utf_char2bytes from mbyte.c int fill_utf8(int codepoint, char *str) { - int nbytes = utf_char2len(codepoint); - + int nbytes = utf_char2bytes(codepoint, str); str[nbytes] = 0; - - // This is easier done backwards - int b = nbytes; - while (b > 1) { - b--; - str[b] = (char)0x80 | (codepoint & 0x3f); - codepoint >>= 6; - } - - switch (nbytes) { - case 1: - str[0] = (codepoint & 0x7f); break; - case 2: - str[0] = (char)0xc0 | (codepoint & 0x1f); break; - case 3: - str[0] = (char)0xe0 | (codepoint & 0x0f); break; - case 4: - str[0] = (char)0xf0 | (codepoint & 0x07); break; - case 5: - str[0] = (char)0xf8 | (codepoint & 0x03); break; - case 6: - str[0] = (char)0xfc | (codepoint & 0x01); break; - } - return nbytes; } -#define UTF8_INVALID 0xFFFD static TermKeyResult parse_utf8(const unsigned char *bytes, size_t len, int *cp, size_t *nbytep) { unsigned nbytes; @@ -680,7 +654,7 @@ static TermKeyResult parse_utf8(const unsigned char *bytes, size_t len, int *cp, return TERMKEY_RES_KEY; } else if (b0 < 0xc0) { // Starts with a continuation byte - that's not right - *cp = UTF8_INVALID; + *cp = UNICODE_INVALID; *nbytep = 1; return TERMKEY_RES_KEY; } else if (b0 < 0xe0) { @@ -699,7 +673,7 @@ static TermKeyResult parse_utf8(const unsigned char *bytes, size_t len, int *cp, nbytes = 6; *cp = b0 & 0x01; } else { - *cp = UTF8_INVALID; + *cp = UNICODE_INVALID; *nbytep = 1; return TERMKEY_RES_KEY; } @@ -713,7 +687,7 @@ static TermKeyResult parse_utf8(const unsigned char *bytes, size_t len, int *cp, cb = bytes[b]; if (cb < 0x80 || cb >= 0xc0) { - *cp = UTF8_INVALID; + *cp = UNICODE_INVALID; *nbytep = b; return TERMKEY_RES_KEY; } @@ -724,14 +698,14 @@ static TermKeyResult parse_utf8(const unsigned char *bytes, size_t len, int *cp, // Check for overlong sequences if ((int)nbytes > utf_char2len(*cp)) { - *cp = UTF8_INVALID; + *cp = UNICODE_INVALID; } // Check for UTF-16 surrogates or invalid *cps if ((*cp >= 0xD800 && *cp <= 0xDFFF) || *cp == 0xFFFE || *cp == 0xFFFF) { - *cp = UTF8_INVALID; + *cp = UNICODE_INVALID; } *nbytep = nbytes; @@ -833,6 +807,9 @@ static TermKeyResult peekkey(TermKey *tk, TermKeyKey *key, int force, size_t *nb return TERMKEY_RES_ERROR; } + // Press is the default event type. + key->event = TERMKEY_EVENT_PRESS; + #ifdef DEBUG fprintf(stderr, "getkey(force=%d): buffer ", force); print_buffer(tk); @@ -958,9 +935,9 @@ static TermKeyResult peekkey_simple(TermKey *tk, TermKeyKey *key, int force, siz if (res == TERMKEY_RES_AGAIN && force) { // There weren't enough bytes for a complete UTF-8 sequence but caller // demands an answer. About the best thing we can do here is eat as many - // bytes as we have, and emit a UTF8_INVALID. If the remaining bytes + // bytes as we have, and emit a UNICODE_INVALID. If the remaining bytes // arrive later, they'll be invalid too. - codepoint = UTF8_INVALID; + codepoint = UNICODE_INVALID; *nbytep = tk->buffcount; res = TERMKEY_RES_KEY; } diff --git a/src/nvim/tui/termkey/termkey.h b/src/nvim/tui/termkey/termkey.h index 21ed141346..5cf9894cac 100644 --- a/src/nvim/tui/termkey/termkey.h +++ b/src/nvim/tui/termkey/termkey.h @@ -1,9 +1,9 @@ #pragma once -#include <stdint.h> -#include <stdlib.h> +#include <stdint.h> // IWYU pragma: keep +#include <stdlib.h> // IWYU pragma: keep -#include "nvim/tui/termkey/termkey_defs.h" +#include "nvim/tui/termkey/termkey_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "tui/termkey/termkey.h.generated.h" diff --git a/src/nvim/tui/termkey/termkey_defs.h b/src/nvim/tui/termkey/termkey_defs.h index 7c218ba7c2..87d3f63447 100644 --- a/src/nvim/tui/termkey/termkey_defs.h +++ b/src/nvim/tui/termkey/termkey_defs.h @@ -123,6 +123,13 @@ typedef enum { TERMKEY_MOUSE_RELEASE, } TermKeyMouseEvent; +typedef enum { + TERMKEY_EVENT_UNKNOWN, + TERMKEY_EVENT_PRESS, + TERMKEY_EVENT_REPEAT, + TERMKEY_EVENT_RELEASE, +} TermKeyEvent; + enum { TERMKEY_KEYMOD_SHIFT = 1 << 0, TERMKEY_KEYMOD_ALT = 1 << 1, @@ -163,6 +170,8 @@ typedef struct { int modifiers; + TermKeyEvent event; + // Any Unicode character can be UTF-8 encoded in no more than 6 bytes, plus // terminating NUL char utf8[7]; diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 2839a665da..31f95a1006 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -1,9 +1,9 @@ // Terminal UI functions. Invoked (by ui_client.c) on the UI process. #include <assert.h> +#include <inttypes.h> #include <signal.h> #include <stdbool.h> -#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -22,8 +22,6 @@ #include "nvim/event/stream.h" #include "nvim/globals.h" #include "nvim/grid.h" -#include "nvim/grid_defs.h" -#include "nvim/highlight.h" #include "nvim/highlight_defs.h" #include "nvim/log.h" #include "nvim/macros_defs.h" @@ -107,7 +105,7 @@ struct TUIData { bool busy, is_invisible, want_invisible; bool cork, overflow; bool set_cursor_color_as_str; - bool cursor_color_changed; + bool cursor_has_color; bool is_starting; bool did_set_grapheme_cluster_mode; FILE *screenshot; @@ -241,16 +239,19 @@ void tui_handle_term_mode(TUIData *tui, TermMode mode, TermModeState state) tui->unibi_ext.sync = (int)unibi_add_ext_str(tui->ut, "Sync", "\x1b[?2026%?%p1%{1}%-%tl%eh%;"); break; - case kTermModeResizeEvents: - signal_watcher_stop(&tui->winch_handle); - tui_set_term_mode(tui, mode, true); - break; case kTermModeGraphemeClusters: if (!is_set) { tui_set_term_mode(tui, mode, true); tui->did_set_grapheme_cluster_mode = true; } break; + case kTermModeThemeUpdates: + tui_set_term_mode(tui, mode, true); + break; + case kTermModeResizeEvents: + signal_watcher_stop(&tui->winch_handle); + tui_set_term_mode(tui, mode, true); + break; } } } @@ -295,7 +296,10 @@ void tui_set_key_encoding(TUIData *tui) { switch (tui->input.key_encoding) { case kKeyEncodingKitty: - out(tui, S_LEN("\x1b[>1u")); + // Progressive enhancement flags: + // 0b01 (1) Disambiguate escape codes + // 0b10 (2) Report event types + out(tui, S_LEN("\x1b[>3u")); break; case kKeyEncodingXterm: out(tui, S_LEN("\x1b[>4;2m")); @@ -310,7 +314,7 @@ static void tui_reset_key_encoding(TUIData *tui) { switch (tui->input.key_encoding) { case kKeyEncodingKitty: - out(tui, S_LEN("\x1b[<1u")); + out(tui, S_LEN("\x1b[<u")); break; case kKeyEncodingXterm: out(tui, S_LEN("\x1b[>4;0m")); @@ -320,6 +324,18 @@ static void tui_reset_key_encoding(TUIData *tui) } } +/// Write the OSC 11 sequence to the terminal emulator to query the current +/// background color. +/// +/// The response will be handled by the TermResponse autocommand created in +/// _defaults.lua. +void tui_query_bg_color(TUIData *tui) + FUNC_ATTR_NONNULL_ALL +{ + out(tui, S_LEN("\x1b]11;?\x07")); + flush_buf(tui); +} + /// Enable the alternate screen and emit other control sequences to start the TUI. /// /// This is also called when the TUI is resumed after being suspended. We reinitialize all state @@ -336,7 +352,7 @@ static void terminfo_start(TUIData *tui) tui->cork = false; tui->overflow = false; tui->set_cursor_color_as_str = false; - tui->cursor_color_changed = false; + tui->cursor_has_color = false; tui->showing_mode = SHAPE_IDX_N; tui->unibi_ext.enable_mouse = -1; tui->unibi_ext.disable_mouse = -1; @@ -438,14 +454,13 @@ static void terminfo_start(TUIData *tui) // Enable bracketed paste unibi_out_ext(tui, tui->unibi_ext.enable_bracketed_paste); - // Query support for mode 2026 (Synchronized Output). Some terminals also - // support an older DCS sequence for synchronized output, but we will only use - // mode 2026. + // Query support for private DEC modes that Nvim can take advantage of. // Some terminals (such as Terminal.app) do not support DECRQM, so skip the query. if (!nsterm) { tui_request_term_mode(tui, kTermModeSynchronizedOutput); - tui_request_term_mode(tui, kTermModeResizeEvents); tui_request_term_mode(tui, kTermModeGraphemeClusters); + tui_request_term_mode(tui, kTermModeThemeUpdates); + tui_request_term_mode(tui, kTermModeResizeEvents); } // Don't use DECRQSS in screen or tmux, as they behave strangely when receiving it. @@ -493,6 +508,10 @@ static void terminfo_start(TUIData *tui) /// Disable the alternate screen and prepare for the TUI to close. static void terminfo_stop(TUIData *tui) { + // Disable theme update notifications. We do this first to avoid getting any + // more notifications after we reset the cursor and any color palette changes. + tui_set_term_mode(tui, kTermModeThemeUpdates, false); + // Destroy output stuff tui_mode_change(tui, NULL_STRING, SHAPE_IDX_N); tui_mouse_off(tui); @@ -509,6 +528,7 @@ static void terminfo_stop(TUIData *tui) if (tui->did_set_grapheme_cluster_mode) { tui_set_term_mode(tui, kTermModeGraphemeClusters, false); } + // May restore old title before exiting alternate screen. tui_set_title(tui, NULL_STRING); if (ui_client_exit_status == 0) { @@ -521,7 +541,7 @@ static void terminfo_stop(TUIData *tui) // Exit alternate screen. unibi_out(tui, unibi_exit_ca_mode); } - if (tui->cursor_color_changed) { + if (tui->cursor_has_color) { unibi_out_ext(tui, tui->unibi_ext.reset_cursor_color); } // Disable bracketed paste @@ -1302,11 +1322,12 @@ static void tui_set_mode(TUIData *tui, ModeShape mode) UNIBI_SET_NUM_VAR(tui->params[0], aep.rgb_bg_color); } unibi_out_ext(tui, tui->unibi_ext.set_cursor_color); - tui->cursor_color_changed = true; + tui->cursor_has_color = true; } - } else if (c.id == 0) { + } else if (c.id == 0 && (tui->want_invisible || tui->cursor_has_color)) { // No cursor color for this mode; reset to default. tui->want_invisible = false; + tui->cursor_has_color = false; unibi_out_ext(tui, tui->unibi_ext.reset_cursor_color); } @@ -1319,7 +1340,7 @@ static void tui_set_mode(TUIData *tui, ModeShape mode) case SHAPE_VER: shape = 5; break; } - UNIBI_SET_NUM_VAR(tui->params[0], shape + (int)(c.blinkon == 0)); + UNIBI_SET_NUM_VAR(tui->params[0], shape + (int)(c.blinkon == 0 || c.blinkoff == 0)); unibi_out_ext(tui, tui->unibi_ext.set_cursor_style); } @@ -2273,6 +2294,7 @@ static void augment_terminfo(TUIData *tui, const char *term, int vte_version, in bool putty = terminfo_is_term_family(term, "putty"); bool screen = terminfo_is_term_family(term, "screen"); bool tmux = terminfo_is_term_family(term, "tmux") || !!os_getenv("TMUX"); + bool st = terminfo_is_term_family(term, "st"); bool iterm = terminfo_is_term_family(term, "iterm") || terminfo_is_term_family(term, "iterm2") || terminfo_is_term_family(term, "iTerm.app") @@ -2357,9 +2379,10 @@ static void augment_terminfo(TUIData *tui, const char *term, int vte_version, in // would use a tmux control sequence and an extra if(screen) test. 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) + } else if ((xterm || hterm || rxvt || tmux || alacritty || st) && (vte_version == 0 || vte_version >= 3900)) { // Supported in urxvt, newer VTE. + // Supported in st, but currently missing in ncurses definitions. #32217 tui->unibi_ext.set_cursor_color = (int)unibi_add_ext_str(ut, "ext.set_cursor_color", "\033]12;%p1%s\007"); } diff --git a/src/nvim/tui/tui_defs.h b/src/nvim/tui/tui_defs.h index bd99d6b0ad..5d6f027bf7 100644 --- a/src/nvim/tui/tui_defs.h +++ b/src/nvim/tui/tui_defs.h @@ -5,6 +5,7 @@ typedef struct TUIData TUIData; typedef enum { kTermModeSynchronizedOutput = 2026, kTermModeGraphemeClusters = 2027, + kTermModeThemeUpdates = 2031, kTermModeResizeEvents = 2048, } TermMode; |