From e299430ff5dd35b0e059e0302dde928dfb953dba Mon Sep 17 00:00:00 2001 From: Gregory Anders Date: Fri, 8 Aug 2025 08:50:02 -0500 Subject: [PATCH] fix(tui): do not remove SIGWINCH handler when resize events are enabled (#35221) (#35238) When Nvim is started in one terminal emulator, suspended, and later resumed in a different terminal emulator (as can happen when using e.g. a multiplexer), the new terminal emulator will not have all of the same modes enabled that the original terminal emulator had. This is a problem in particular for in-band resize events because we were disabling the SIGWINCH signal handler when we determined that the terminal supported this mode. However, if the new terminal does not support this mode then the SIGWINCH handler remains disabled, and Neovim no longer properly resizes. Instead of disabling the SIGWINCH handler, we track the state of the resize events mode internally. If the mode is enabled then we return early from the SIGWINCH handler before performing any ioctls or other system calls. But if the mode is not enabled we proceed as normal. (cherry picked from commit b1679f0ab6a3f28ffb1aaa08776eff714d6e00b5) --- src/nvim/tui/tui.c | 48 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 056c93dc99..52e4fd7bca 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -113,7 +113,15 @@ struct TUIData { bool set_cursor_color_as_str; bool cursor_has_color; bool is_starting; - bool did_set_grapheme_cluster_mode; + bool resize_events_enabled; + + // Terminal modes that Nvim enabled that it must disable on exit + struct { + bool grapheme_clusters : 1; + bool theme_updates : 1; + bool resize_events : 1; + } modes; + FILE *screenshot; cursorentry_T cursor_shapes[SHAPE_IDX_COUNT]; HlAttrs clear_attrs; @@ -246,15 +254,25 @@ void tui_handle_term_mode(TUIData *tui, TermMode mode, TermModeState state) case kTermModeGraphemeClusters: if (!is_set) { tui_set_term_mode(tui, mode, true); - tui->did_set_grapheme_cluster_mode = true; + tui->modes.grapheme_clusters = true; } break; case kTermModeThemeUpdates: - tui_set_term_mode(tui, mode, true); + if (!is_set) { + tui_set_term_mode(tui, mode, true); + tui->modes.theme_updates = 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); + tui->modes.resize_events = true; + } + + // We track both whether the mode is enabled AND if Nvim was the one to enable it + tui->resize_events_enabled = true; + break; + default: break; } } @@ -358,7 +376,10 @@ static void terminfo_start(TUIData *tui) tui->overflow = false; tui->set_cursor_color_as_str = false; tui->cursor_has_color = false; - tui->did_set_grapheme_cluster_mode = false; + tui->resize_events_enabled = false; + tui->modes.grapheme_clusters = false; + tui->modes.resize_events = false; + tui->modes.theme_updates = false; tui->showing_mode = SHAPE_IDX_N; tui->unibi_ext.enable_mouse = -1; tui->unibi_ext.disable_mouse = -1; @@ -519,7 +540,9 @@ static void terminfo_disable(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); + if (tui->modes.theme_updates) { + tui_set_term_mode(tui, kTermModeThemeUpdates, false); + } // Destroy output stuff tui_mode_change(tui, NULL_STRING, SHAPE_IDX_N); @@ -532,9 +555,12 @@ static void terminfo_disable(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) { + // Disable terminal modes that we enabled + if (tui->modes.resize_events) { + tui_set_term_mode(tui, kTermModeResizeEvents, false); + } + + if (tui->modes.grapheme_clusters) { tui_set_term_mode(tui, kTermModeGraphemeClusters, false); } @@ -680,7 +706,7 @@ static void sigwinch_cb(SignalWatcher *watcher, int signum, void *cbdata) { got_winch++; TUIData *tui = cbdata; - if (tui_is_stopped(tui)) { + if (tui_is_stopped(tui) || tui->resize_events_enabled) { return; }