diff --git a/src/drawscreen.c b/src/drawscreen.c index 751e09aa3f..94b616214d 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 0767fa9138..772148f61d 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 dcf98cb6fb..da2662aca7 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 56cce4fb4e..783138b7e5 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("\", 'xt') + call feedkeys("exit\", 'xt') + call WaitForAssert({-> assert_equal("dead", job_status(term_getjob(termbuf)))}) + call feedkeys(":quit\", '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 692c510e6b..acf47f67a3 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, /**/