]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0483: popup: terminal embedded in an opacity popup freezes Vim on input v9.2.0483
authorHirohito Higashi <h.east.727@gmail.com>
Fri, 15 May 2026 15:02:48 +0000 (15:02 +0000)
committerChristian Brabandt <cb@256bit.org>
Fri, 15 May 2026 15:02:48 +0000 (15:02 +0000)
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) <noreply@anthropic.com>
Signed-off-by: Hirohito Higashi <h.east.727@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
src/drawscreen.c
src/popupwin.c
src/proto/drawscreen.pro
src/testdir/test_popupwin.vim
src/version.c

index 751e09aa3f276e32039301f091b44b7b8d94cd57..94b616214d73b713df03ace162c44865983784c2 100644 (file)
@@ -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;
 
index 0767fa9138a83e1c421f379e3b4983ee9b41fff4..772148f61dcad72012b5bbbee54a718a5c8f9ac0 100644 (file)
@@ -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
index dcf98cb6fbf55d1ff2ec5966709b705f58158d8a..da2662aca74a3285bffee83e15309814a607bcdb 100644 (file)
@@ -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 : */
index 56cce4fb4ee932a9271152b2187212c8d702179d..783138b7e5adfc927f7123c2b0d4334c949d0963 100644 (file)
@@ -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})
index 692c510e6b75594412ba4844eb8b0e909b856dfa..acf47f67a3eda30b2db40b3c25e37c5068d842bc 100644 (file)
@@ -729,6 +729,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    483,
 /**/
     482,
 /**/