patch 9.2.0483: popup: terminal embedded in an opacity popup freezes Vim on input
Commit: https://github.com/vim/vim/commit/f281493c491c58e2452111e0e9b78edce486f07e Author: Hirohito Higashi <[email protected]> Date: Fri May 15 15:02:48 2026 +0000 patch 9.2.0483: popup: terminal embedded in an opacity popup freezes Vim on input Problem: When a terminal buffer is shown inside a popup with 'opacity' set to a value other than 100, typing into it freezes Vim. Only the first keystroke is drawn; afterwards no input is processed and the screen stops updating. Solution: When marking background lines for redraw to keep opacity blend cells fresh, do not raise must_redraw. This marking happens from inside update_screen() (via may_update_popup_mask()), so raising must_redraw makes terminal_loop()'s "while (must_redraw != 0) update_screen()" loop never terminate. Add redraw_win_range_now() that updates only the per-window state and use it from redraw_win_under_opacity_popup() (Hirohito Higashi) fixes: #20214 closes: #20220 Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> Signed-off-by: Hirohito Higashi <[email protected]> Signed-off-by: Christian Brabandt <[email protected]> diff --git a/src/drawscreen.c b/src/drawscreen.c index 751e09aa3..94b616214 100644 --- a/src/drawscreen.c +++ b/src/drawscreen.c @@ -3497,6 +3497,28 @@ redraw_win_range_later( } } +/* + * Like redraw_win_range_later() but do not raise the global must_redraw. + * Use this from inside an update_screen() pass (where the redraw will be + * picked up this cycle), to avoid triggering an extra full redraw cycle. + */ + void +redraw_win_range_now( + win_T *wp, + linenr_T first, + linenr_T last) +{ + if (last >= wp->w_topline && first < wp->w_botline) + { + if (wp->w_redraw_top == 0 || wp->w_redraw_top > first) + wp->w_redraw_top = first; + if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < last) + wp->w_redraw_bot = last; + if (wp->w_redr_type < UPD_VALID) + wp->w_redr_type = UPD_VALID; + } +} + #ifdef FEAT_EVAL static bool redraw_cb_in_progress = false; diff --git a/src/popupwin.c b/src/popupwin.c index 0767fa913..772148f61 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -5311,7 +5311,9 @@ redraw_win_under_opacity_popup(win_T *wp) linenr_T lnum; (void)mouse_comp_pos(twp, &line_cp, &col_cp, &lnum, NULL); - redrawWinline(twp, lnum); + // Called from inside update_screen(); raising must_redraw + // would loop the outer redraw indefinitely. + redraw_win_range_now(twp, lnum, lnum); } else if (line_cp == twp->w_height) // Status bar line: mark for redraw to prevent diff --git a/src/proto/drawscreen.pro b/src/proto/drawscreen.pro index dcf98cb6f..da2662aca 100644 --- a/src/proto/drawscreen.pro +++ b/src/proto/drawscreen.pro @@ -25,6 +25,7 @@ void redraw_statuslines(void); void win_redraw_last_status(frame_T *frp); void redrawWinline(win_T *wp, linenr_T lnum); void redraw_win_range_later(win_T *wp, linenr_T first, linenr_T last); +void redraw_win_range_now(win_T *wp, linenr_T first, linenr_T last); void f_redraw_listener_add(typval_T *argvars, typval_T *rettv); void f_redraw_listener_remove(typval_T *argvars, typval_T *rettv); /* vim: set ft=c : */ diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim index 56cce4fb4..783138b7e 100644 --- a/src/testdir/test_popupwin.vim +++ b/src/testdir/test_popupwin.vim @@ -5180,6 +5180,32 @@ func Test_popup_opacity_zero() call StopVimInTerminal(buf) endfunc +func Test_popup_opacity_terminal_no_freeze() + CheckFeature terminal + CheckUnix + let g:test_is_flaky = 1 + + let origwin = win_getid() + let termbuf = term_start(&shell, #{hidden: 1}) + let winid = popup_create(termbuf, #{minwidth: 40, minheight: 10, + \ border: [1, 1, 1, 1], opacity: 10}) + call WaitForAssert({-> assert_equal("run", job_status(term_getjob(termbuf)))}) + call WaitForAssert({-> assert_equal(' ', screenstring(screenrow(), screencol() - 1))}) + + " Before the fix typing froze Vim: redraw under an opacity popup raised + " must_redraw every cycle, trapping terminal_loop in its redraw loop. + call feedkeys('x', 'xt') + call term_wait(termbuf) + redraw + call WaitForAssert({-> assert_equal('x', screenstring(screenrow(), screencol() - 1))}) + + call feedkeys("\<BS>", 'xt') + call feedkeys("exit\<CR>", 'xt') + call WaitForAssert({-> assert_equal("dead", job_status(term_getjob(termbuf)))}) + call feedkeys(":quit\<CR>", 'xt') + call assert_equal(origwin, win_getid()) +endfunc + func Test_popup_getwininfo_tabnr() tab split let winid1 = popup_create('sup', #{tabpage: 1}) diff --git a/src/version.c b/src/version.c index 692c510e6..acf47f67a 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 483, /**/ 482, /**/ -- -- You received this message from the "vim_dev" maillist. Do not top-post! Type your reply below the text you are replying to. For more information, visit http://www.vim.org/maillist.php --- You received this message because you are subscribed to the Google Groups "vim_dev" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To view this discussion visit https://groups.google.com/d/msgid/vim_dev/E1wNuFk-006Ozn-LI%40256bit.org.
