diff options
Diffstat (limited to 'src/nvim/tui/tui.c')
-rw-r--r-- | src/nvim/tui/tui.c | 105 |
1 files changed, 72 insertions, 33 deletions
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 2a9530defb..fa50a8252d 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -109,12 +109,14 @@ struct TUIData { bool set_cursor_color_as_str; bool cursor_color_changed; bool is_starting; + bool did_set_grapheme_cluster_mode; FILE *screenshot; cursorentry_T cursor_shapes[SHAPE_IDX_COUNT]; HlAttrs clear_attrs; kvec_t(HlAttrs) attrs; int print_attr_id; bool default_attr; + bool set_default_colors; bool can_clear_attr; ModeShape showing_mode; Integer verbose; @@ -166,18 +168,11 @@ void tui_start(TUIData **tui_p, int *width, int *height, char **term, bool *rgb) tui->seen_error_exit = 0; tui->loop = &main_loop; tui->url = -1; - // Because setting the default colors is delayed until after startup to avoid - // flickering with the default colorscheme background, any flush that happens - // during startup in turn would result in clearing invalidated regions with - // uninitialized attrs(black). Instead initialize clear_attrs with current - // terminal background so that it is at least not perceived as flickering, even - // though it may be different from the colorscheme that is set during startup. - tui->clear_attrs.rgb_bg_color = normal_bg; - tui->clear_attrs.cterm_bg_color = (int16_t)cterm_normal_bg_color; kv_init(tui->invalid_regions); kv_init(tui->urlbuf); signal_watcher_init(tui->loop, &tui->winch_handle, tui); + signal_watcher_start(&tui->winch_handle, sigwinch_cb, SIGWINCH); // TODO(bfredl): zero hl is empty, send this explicitly? kv_push(tui->attrs, HLATTRS_INIT); @@ -212,10 +207,21 @@ static void tui_request_term_mode(TUIData *tui, TermMode mode) out(tui, buf, (size_t)len); } +/// Set (DECSET) or reset (DECRST) a terminal mode. +static void tui_set_term_mode(TUIData *tui, TermMode mode, bool set) + FUNC_ATTR_NONNULL_ALL +{ + char buf[12]; + int len = snprintf(buf, sizeof(buf), "\x1b[?%d%c", (int)mode, set ? 'h' : 'l'); + assert((len > 0) && (len < (int)sizeof(buf))); + out(tui, buf, (size_t)len); +} + /// Handle a mode report (DECRPM) from the terminal. void tui_handle_term_mode(TUIData *tui, TermMode mode, TermModeState state) FUNC_ATTR_NONNULL_ALL { + bool is_set = false; switch (state) { case kTermModeNotRecognized: case kTermModePermanentlySet: @@ -224,6 +230,8 @@ void tui_handle_term_mode(TUIData *tui, TermMode mode, TermModeState state) // then there is nothing to do break; case kTermModeSet: + is_set = true; + FALLTHROUGH; case kTermModeReset: // The terminal supports changing the given mode switch (mode) { @@ -231,6 +239,17 @@ void tui_handle_term_mode(TUIData *tui, TermMode mode, TermModeState state) // Ref: https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036 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; } } } @@ -424,6 +443,8 @@ static void terminfo_start(TUIData *tui) // 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); } // Don't use DECRQSS in screen or tmux, as they behave strangely when receiving it. @@ -482,6 +503,11 @@ static void terminfo_stop(TUIData *tui) // Reset the key encoding tui_reset_key_encoding(tui); + // Disable resize events + tui_set_term_mode(tui, kTermModeResizeEvents, false); + 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) { @@ -517,7 +543,6 @@ static void tui_terminal_start(TUIData *tui) 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); } @@ -546,7 +571,6 @@ static void tui_terminal_stop(TUIData *tui) return; } tinput_stop(&tui->input); - signal_watcher_stop(&tui->winch_handle); // Position the cursor on the last screen line, below all the text cursor_goto(tui, tui->height - 1, 0); terminfo_stop(tui); @@ -563,6 +587,7 @@ void tui_stop(TUIData *tui) stream_set_blocking(tui->input.in_fd, true); // normalize stream (#2598) tinput_destroy(&tui->input); tui->stopped = true; + signal_watcher_stop(&tui->winch_handle); signal_watcher_close(&tui->winch_handle, NULL); uv_close((uv_handle_t *)&tui->startup_delay_timer, NULL); } @@ -781,7 +806,12 @@ static void update_attrs(TUIData *tui, int attr_id) if (attrs.url >= 0) { const char *url = urls.keys[attrs.url]; kv_size(tui->urlbuf) = 0; - kv_printf(tui->urlbuf, "\x1b]8;;%s\x1b\\", url); + + // Add some fixed offset to the URL ID to deconflict with other + // applications which may set their own IDs + const uint64_t id = 0xE1EA0000U + (uint32_t)attrs.url; + + kv_printf(tui->urlbuf, "\x1b]8;id=%" PRIu64 ";%s\x1b\\", id, url); out(tui, tui->urlbuf.items, kv_size(tui->urlbuf)); } else { out(tui, S_LEN("\x1b]8;;\x1b\\")); @@ -871,6 +901,7 @@ static void cursor_goto(TUIData *tui, int row, int col) if (tui->url >= 0) { out(tui, S_LEN("\x1b]8;;\x1b\\")); tui->url = -1; + tui->print_attr_id = -1; } if (0 == row && 0 == col) { @@ -992,7 +1023,7 @@ static void print_cell_at_pos(TUIData *tui, int row, int col, UCell *cell, bool char buf[MAX_SCHAR_SIZE]; schar_get(buf, cell->data); int c = utf_ptr2char(buf); - bool is_ambiwidth = utf_ambiguous_width(c); + bool is_ambiwidth = utf_ambiguous_width(buf); if (is_doublewidth && (is_ambiwidth || utf_char2cells(c) == 1)) { // If the server used setcellwidths() to treat a single-width char as double-width, // it needs to be treated like an ambiguous-width char. @@ -1016,7 +1047,16 @@ static void clear_region(TUIData *tui, int top, int bot, int left, int right, in { UGrid *grid = &tui->grid; - update_attrs(tui, attr_id); + // Setting the default colors is delayed until after startup to avoid flickering + // with the default colorscheme background. Consequently, any flush that happens + // during startup would result in clearing invalidated regions with zeroed + // clear_attrs, perceived as a black flicker. Reset attributes to clear with + // current terminal background instead (#28667, #28668). + if (tui->set_default_colors) { + update_attrs(tui, attr_id); + } else { + unibi_out(tui, unibi_exit_attribute_mode); + } // 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. @@ -1159,7 +1199,7 @@ static CursorShape tui_cursor_decode_shape(const char *shape_str) return shape; } -static cursorentry_T decode_cursor_entry(Dictionary args) +static cursorentry_T decode_cursor_entry(Dict args) { cursorentry_T r = shape_table[0]; @@ -1191,8 +1231,8 @@ void tui_mode_info_set(TUIData *tui, bool guicursor_enabled, Array args) // cursor style entries as defined by `shape_table`. 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); + assert(args.items[i].type == kObjectTypeDict); + cursorentry_T r = decode_cursor_entry(args.items[i].data.dict); tui->cursor_shapes[i] = r; } @@ -1419,6 +1459,7 @@ void tui_default_colors_set(TUIData *tui, Integer rgb_fg, Integer rgb_bg, Intege tui->clear_attrs.cterm_bg_color = (int16_t)cterm_bg; tui->print_attr_id = -1; + tui->set_default_colors = true; invalidate(tui, 0, tui->grid.height, 0, tui->grid.width); } @@ -1498,7 +1539,7 @@ static void show_verbose_terminfo(TUIData *tui) ADD_C(args, BOOLEAN_OBJ(true)); // history MAXSIZE_TEMP_DICT(opts, 1); PUT_C(opts, "verbose", BOOLEAN_OBJ(true)); - ADD_C(args, DICTIONARY_OBJ(opts)); + ADD_C(args, DICT_OBJ(opts)); rpc_send_event(ui_client_channel_id, "nvim_echo", args); xfree(str.data); } @@ -1694,6 +1735,14 @@ static void ensure_space_buf_size(TUIData *tui, size_t len) } } +void tui_set_size(TUIData *tui, int width, int height) + FUNC_ATTR_NONNULL_ALL +{ + tui->width = width; + tui->height = height; + ensure_space_buf_size(tui, (size_t)tui->width); +} + /// Tries to get the user's wanted dimensions (columns and rows) for the entire /// application (i.e., the host terminal). void tui_guess_size(TUIData *tui) @@ -1701,7 +1750,7 @@ void tui_guess_size(TUIData *tui) int width = 0; int height = 0; - // 1 - try from a system call(ioctl/TIOCGWINSZ on unix) + // 1 - try from a system call (ioctl/TIOCGWINSZ on unix) if (tui->out_isatty && !uv_tty_get_winsize(&tui->output_handle.tty, &width, &height)) { goto end; @@ -1728,9 +1777,7 @@ void tui_guess_size(TUIData *tui) height = DFLT_ROWS; } - tui->width = width; - tui->height = height; - ensure_space_buf_size(tui, (size_t)tui->width); + tui_set_size(tui, width, height); // Redraw on SIGWINCH event if size didn't change. #23411 ui_client_set_size(width, height); @@ -1852,20 +1899,12 @@ static int unibi_find_ext_bool(unibi_term *ut, const char *name) return -1; } -/// Determine if the terminal supports truecolor or not: +/// Determine if the terminal supports truecolor or not. /// -/// 1. If $COLORTERM is "24bit" or "truecolor", return true -/// 2. Else, check terminfo for Tc, RGB, setrgbf, or setrgbb capabilities. If -/// found, return true -/// 3. Else, return false +/// If terminfo contains Tc, RGB, or both setrgbf and setrgbb capabilities, return true. static bool term_has_truecolor(TUIData *tui, const char *colorterm) { - // Check $COLORTERM - if (strequal(colorterm, "truecolor") || strequal(colorterm, "24bit")) { - return true; - } - - // Check for Tc and RGB + // Check for Tc or RGB for (size_t i = 0; i < unibi_count_ext_bool(tui->ut); i++) { const char *n = unibi_get_ext_bool_name(tui->ut, i); if (n && (!strcmp(n, "Tc") || !strcmp(n, "RGB"))) { @@ -2505,7 +2544,7 @@ static const char *tui_get_stty_erase(int fd) struct termios t; if (tcgetattr(fd, &t) != -1) { stty_erase[0] = (char)t.c_cc[VERASE]; - stty_erase[1] = '\0'; + stty_erase[1] = NUL; DLOG("stty/termios:erase=%s", stty_erase); } #endif |