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.

Raspunde prin e-mail lui