diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 7566cfe080..92c53ae49c 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -96,6 +96,7 @@ struct TUIData { int row, col; int out_fd; bool can_change_scroll_region; + bool has_left_and_right_margin_mode; bool can_set_lr_margin; // smglr bool can_set_left_right_margin; bool can_scroll; @@ -127,7 +128,6 @@ struct TUIData { 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; int enter_altfont_mode; int set_rgb_foreground, set_rgb_background; @@ -228,16 +228,20 @@ void tui_handle_term_mode(TUIData *tui, TermMode mode, TermModeState state) bool is_set = false; switch (state) { case kTermModeNotRecognized: - case kTermModePermanentlySet: case kTermModePermanentlyReset: + // TODO(bfredl): This is really ILOG but we want it in all builds. + // add to show_verbose_terminfo() without being too racy ???? + WLOG("TUI: terminal mode %d unavailable, state %d", mode, state); // If the mode is not recognized, or if the terminal emulator does not allow it to be changed, // then there is nothing to do break; + case kTermModePermanentlySet: case kTermModeSet: is_set = true; FALLTHROUGH; case kTermModeReset: // The terminal supports changing the given mode + WLOG("TUI: terminal mode %d detected, state %d", mode, state); switch (mode) { case kTermModeSynchronizedOutput: // Ref: https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036 @@ -251,11 +255,20 @@ void tui_handle_term_mode(TUIData *tui, TermMode mode, TermModeState state) } break; case kTermModeThemeUpdates: - tui_set_term_mode(tui, mode, true); + if (!is_set) { + tui_set_term_mode(tui, mode, true); + } break; case kTermModeResizeEvents: signal_watcher_stop(&tui->winch_handle); - tui_set_term_mode(tui, mode, true); + if (!is_set) { + tui_set_term_mode(tui, mode, true); + } + break; + case kTermModeLeftAndRightMargins: + tui->has_left_and_right_margin_mode = true; + break; + default: break; } } @@ -370,8 +383,6 @@ static void terminfo_start(TUIData *tui) 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; tui->unibi_ext.disable_focus_reporting = -1; tui->unibi_ext.resize_screen = -1; @@ -434,6 +445,7 @@ static void terminfo_start(TUIData *tui) augment_terminfo(tui, term, vtev, konsolev, weztermv, iterm_env, nsterm); tui->can_change_scroll_region = !!unibi_get_str(tui->ut, unibi_change_scroll_region); + // note: also gated by tui->has_left_and_right_margin_mode tui->can_set_lr_margin = !!unibi_get_str(tui->ut, unibi_set_lr_margin); tui->can_set_left_right_margin = @@ -462,9 +474,12 @@ static void terminfo_start(TUIData *tui) // Enable bracketed paste unibi_out_ext(tui, tui->unibi_ext.enable_bracketed_paste); + tui->has_left_and_right_margin_mode = false; + // 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, kTermModeLeftAndRightMargins); tui_request_term_mode(tui, kTermModeSynchronizedOutput); tui_request_term_mode(tui, kTermModeGraphemeClusters); tui_request_term_mode(tui, kTermModeThemeUpdates); @@ -1165,7 +1180,7 @@ static void set_scroll_region(TUIData *tui, int top, int bot, int left, int righ 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); + tui_set_term_mode(tui, kTermModeLeftAndRightMargins, true); if (tui->can_set_lr_margin) { UNIBI_SET_NUM_VAR(tui->params[0], left); UNIBI_SET_NUM_VAR(tui->params[1], right); @@ -1197,12 +1212,14 @@ static void reset_scroll_region(TUIData *tui, bool fullwidth) UNIBI_SET_NUM_VAR(tui->params[1], tui->width - 1); unibi_out(tui, unibi_set_lr_margin); } else { + // TODO(bfredl): does there ever exist a terminal which can do this and + // not has unibi_set_lr_margin already set? 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(tui, tui->unibi_ext.disable_lr_margin); + tui_set_term_mode(tui, kTermModeLeftAndRightMargins, false); } grid->row = -1; } @@ -1430,12 +1447,13 @@ void tui_grid_scroll(TUIData *tui, Integer g, Integer startrow, Integer endrow, ugrid_scroll(grid, top, bot, left, right, (int)rows); + bool has_lr_margins = tui->has_left_and_right_margin_mode + && (tui->can_set_lr_margin || tui->can_set_left_right_margin); + bool can_scroll = tui->can_scroll && (full_screen_scroll || (tui->can_change_scroll_region - && ((left == 0 && right == tui->width - 1) - || tui->can_set_lr_margin - || tui->can_set_left_right_margin))); + && ((left == 0 && right == tui->width - 1) || has_lr_margins))); if (can_scroll) { // Change terminal scroll region and move cursor to the top @@ -1591,7 +1609,7 @@ static void show_verbose_terminfo(TUIData *tui) ADD_C(title, CSTR_AS_OBJ("\n\n--- Terminal info --- {{{\n")); ADD_C(title, CSTR_AS_OBJ("Title")); ADD_C(chunks, ARRAY_OBJ(title)); - MAXSIZE_TEMP_ARRAY(info, 2); + MAXSIZE_TEMP_ARRAY(info, 1); String str = terminfo_info_msg(ut, tui->term); ADD_C(info, STRING_OBJ(str)); ADD_C(chunks, ARRAY_OBJ(info)); @@ -2109,29 +2127,12 @@ static void patch_terminfo_bugs(TUIData *tui, const char *term, const char *colo unibi_set_if_empty(ut, unibi_enter_italics_mode, "\x1b[3m"); unibi_set_if_empty(ut, unibi_exit_italics_mode, "\x1b[23m"); - if (true_xterm) { - // 2017-04 terminfo.src lacks these. genuine Xterm has them. - unibi_set_if_empty(ut, unibi_set_lr_margin, "\x1b[%i%p1%d;%p2%ds"); - unibi_set_if_empty(ut, unibi_set_left_margin_parm, "\x1b[%i%p1%ds"); - unibi_set_if_empty(ut, unibi_set_right_margin_parm, "\x1b[%i;%p2%ds"); - } else { - // Fix things advertised via TERM=xterm, for non-xterm. - // - // TODO(aktau): stop patching this out for hterm when it gains support - // (https://crbug.com/1175065). - if (unibi_get_str(ut, unibi_set_lr_margin)) { - ILOG("Disabling smglr with TERM=xterm for non-xterm."); - unibi_set_str(ut, unibi_set_lr_margin, NULL); - } - if (unibi_get_str(ut, unibi_set_left_margin_parm)) { - ILOG("Disabling smglp with TERM=xterm for non-xterm."); - unibi_set_str(ut, unibi_set_left_margin_parm, NULL); - } - if (unibi_get_str(ut, unibi_set_right_margin_parm)) { - ILOG("Disabling smgrp with TERM=xterm for non-xterm."); - unibi_set_str(ut, unibi_set_right_margin_parm, NULL); - } - } + // 2017-04 terminfo.src lacks these. genuine Xterm has them. + // 2025: these are not supported by all xterm-alikes, but they are only + // used when kTermModeLeftAndRightMargins is detected + unibi_set_if_empty(ut, unibi_set_lr_margin, "\x1b[%i%p1%d;%p2%ds"); + unibi_set_if_empty(ut, unibi_set_left_margin_parm, "\x1b[%i%p1%ds"); + unibi_set_if_empty(ut, unibi_set_right_margin_parm, "\x1b[%i;%p2%ds"); #ifdef MSWIN // XXX: workaround libuv implicit LF => CRLF conversion. #10558 @@ -2150,19 +2151,6 @@ static void patch_terminfo_bugs(TUIData *tui, const char *term, const char *colo // per the screen manual; 2017-04 terminfo.src lacks these. unibi_set_if_empty(ut, unibi_to_status_line, "\x1b_"); unibi_set_if_empty(ut, unibi_from_status_line, "\x1b\\"); - // Fix an issue where smglr is inherited by TERM=screen.xterm. - if (unibi_get_str(ut, unibi_set_lr_margin)) { - ILOG("Disabling smglr with TERM=screen.xterm for screen."); - unibi_set_str(ut, unibi_set_lr_margin, NULL); - } - if (unibi_get_str(ut, unibi_set_left_margin_parm)) { - ILOG("Disabling smglp with TERM=screen.xterm for screen."); - unibi_set_str(ut, unibi_set_left_margin_parm, NULL); - } - if (unibi_get_str(ut, unibi_set_right_margin_parm)) { - ILOG("Disabling smgrp with TERM=screen.xterm for screen."); - unibi_set_str(ut, unibi_set_right_margin_parm, NULL); - } } else if (tmux) { unibi_set_if_empty(ut, unibi_to_status_line, "\x1b_"); unibi_set_if_empty(ut, unibi_from_status_line, "\x1b\\"); @@ -2483,10 +2471,6 @@ static void augment_terminfo(TUIData *tui, const char *term, int vte_version, in /// Terminals usually ignore unrecognized private modes, and there is no /// known ambiguity with these. So we just set them unconditionally. - tui->unibi_ext.enable_lr_margin = - (int)unibi_add_ext_str(ut, "ext.enable_lr_margin", "\x1b[?69h"); - 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", diff --git a/src/nvim/tui/tui_defs.h b/src/nvim/tui/tui_defs.h index 5d6f027bf7..97ceb6647a 100644 --- a/src/nvim/tui/tui_defs.h +++ b/src/nvim/tui/tui_defs.h @@ -3,6 +3,7 @@ typedef struct TUIData TUIData; typedef enum { + kTermModeLeftAndRightMargins = 69, kTermModeSynchronizedOutput = 2026, kTermModeGraphemeClusters = 2027, kTermModeThemeUpdates = 2031,