patch 8.2.3763: when editing the cmdline a callback may cause a scroll up

Problem:    When editing the command line a FocusLost callback may cause the
            screen to scroll up.
Solution:   Do not redraw at the last line but at the same place where the
            command line was before. (closes #9295)
diff --git a/src/beval.c b/src/beval.c
index 8e0efb5..8b7570b 100644
--- a/src/beval.c
+++ b/src/beval.c
@@ -308,7 +308,7 @@
 	    // The 'balloonexpr' evaluation may show something on the screen
 	    // that requires a screen update.
 	    if (must_redraw)
-		redraw_after_callback(FALSE);
+		redraw_after_callback(FALSE, FALSE);
 
 	    recursive = FALSE;
 	    return;
diff --git a/src/channel.c b/src/channel.c
index 6343124..05e2489 100644
--- a/src/channel.c
+++ b/src/channel.c
@@ -3205,7 +3205,7 @@
 	      if (channel_need_redraw)
 	      {
 		  channel_need_redraw = FALSE;
-		  redraw_after_callback(TRUE);
+		  redraw_after_callback(TRUE, FALSE);
 	      }
 
 	      if (!channel->ch_drop_never)
@@ -4687,7 +4687,7 @@
     if (channel_need_redraw)
     {
 	channel_need_redraw = FALSE;
-	redraw_after_callback(TRUE);
+	redraw_after_callback(TRUE, FALSE);
     }
 
     --safe_to_invoke_callback;
diff --git a/src/drawscreen.c b/src/drawscreen.c
index f56ef91..106ae95 100644
--- a/src/drawscreen.c
+++ b/src/drawscreen.c
@@ -3019,14 +3019,19 @@
  * it belongs. If highlighting was changed a redraw is needed.
  * If "call_update_screen" is FALSE don't call update_screen() when at the
  * command line.
+ * If "redraw_message" is TRUE.
  */
     void
-redraw_after_callback(int call_update_screen)
+redraw_after_callback(int call_update_screen, int do_message)
 {
     ++redrawing_for_callback;
 
-    if (State == HITRETURN || State == ASKMORE)
-	; // do nothing
+    if (State == HITRETURN || State == ASKMORE || State == SETWSIZE
+	    || State == EXTERNCMD || State == CONFIRM || exmode_active)
+    {
+	if (do_message)
+	    repeat_message();
+    }
     else if (State & CMDLINE)
     {
 	// Don't redraw when in prompt_for_number().
diff --git a/src/ex_getln.c b/src/ex_getln.c
index 2616afc..bffdf65 100644
--- a/src/ex_getln.c
+++ b/src/ex_getln.c
@@ -3730,6 +3730,10 @@
     redrawcmdline_ex(TRUE);
 }
 
+/*
+ * When "do_compute_cmdrow" is TRUE the command line is redrawn at the bottom.
+ * If FALSE cmdline_row is used, which should redraw in the same place.
+ */
     void
 redrawcmdline_ex(int do_compute_cmdrow)
 {
diff --git a/src/job.c b/src/job.c
index 6ee9f44..7329851 100644
--- a/src/job.c
+++ b/src/job.c
@@ -1260,7 +1260,7 @@
     if (channel_need_redraw)
     {
 	channel_need_redraw = FALSE;
-	redraw_after_callback(TRUE);
+	redraw_after_callback(TRUE, FALSE);
     }
     return did_end;
 }
diff --git a/src/popupwin.c b/src/popupwin.c
index 7641827..266e113 100644
--- a/src/popupwin.c
+++ b/src/popupwin.c
@@ -3357,7 +3357,7 @@
 
 	// Reset got_int to avoid a function used in the statusline aborts.
 	got_int = FALSE;
-	redraw_after_callback(FALSE);
+	redraw_after_callback(FALSE, FALSE);
 	got_int |= save_got_int;
     }
     recursive = FALSE;
diff --git a/src/proto/drawscreen.pro b/src/proto/drawscreen.pro
index 528c8b2..c835019 100644
--- a/src/proto/drawscreen.pro
+++ b/src/proto/drawscreen.pro
@@ -8,7 +8,7 @@
 void update_debug_sign(buf_T *buf, linenr_T lnum);
 void updateWindow(win_T *wp);
 int redraw_asap(int type);
-void redraw_after_callback(int call_update_screen);
+void redraw_after_callback(int call_update_screen, int do_message);
 void redraw_later(int type);
 void redraw_win_later(win_T *wp, int type);
 void redraw_later_clear(void);
diff --git a/src/sound.c b/src/sound.c
index e704aed..2f121eb 100644
--- a/src/sound.c
+++ b/src/sound.c
@@ -173,7 +173,7 @@
 	delete_sound_callback(scb->scb_callback);
 	vim_free(scb);
     }
-    redraw_after_callback(TRUE);
+    redraw_after_callback(TRUE, FALSE);
 }
 
     static void
@@ -327,7 +327,7 @@
 		    clear_tv(&rettv);
 
 		    delete_sound_callback(p);
-		    redraw_after_callback(TRUE);
+		    redraw_after_callback(TRUE, FALSE);
 
 		}
 	    break;
diff --git a/src/terminal.c b/src/terminal.c
index c3ce116..7e2f45b 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -1258,7 +1258,7 @@
 		update_cursor(curbuf->b_term, TRUE);
 	}
 	else
-	    redraw_after_callback(TRUE);
+	    redraw_after_callback(TRUE, FALSE);
     }
 }
 
diff --git a/src/testdir/dumps/Test_terminal_focus_1.dump b/src/testdir/dumps/Test_terminal_focus_1.dump
index 96fe303..caf67e7 100644
--- a/src/testdir/dumps/Test_terminal_focus_1.dump
+++ b/src/testdir/dumps/Test_terminal_focus_1.dump
@@ -1,6 +1,6 @@
-> +0&#ffffff0@74
+>I+0&#ffffff0| |a|m| |l|o|s|t| @65
 |~+0#4040ff13&| @73
 |~| @73
 |~| @73
 |~| @73
-|I+0#0000000&| |a|m| |l|o|s|t| @65
+| +0#0000000&@56|0|,|0|-|1| @8|A|l@1| 
diff --git a/src/testdir/dumps/Test_terminal_focus_2.dump b/src/testdir/dumps/Test_terminal_focus_2.dump
index b4e8f4c..d02c151 100644
--- a/src/testdir/dumps/Test_terminal_focus_2.dump
+++ b/src/testdir/dumps/Test_terminal_focus_2.dump
@@ -1,6 +1,6 @@
-> +0&#ffffff0@74
+>I+0&#ffffff0| |a|m| |b|a|c|k| @65
 |~+0#4040ff13&| @73
 |~| @73
 |~| @73
 |~| @73
-|I+0#0000000&| |a|m| |b|a|c|k| @65
+| +0#0000000&@56|0|,|0|-|1| @8|A|l@1| 
diff --git a/src/testdir/dumps/Test_terminal_focus_3.dump b/src/testdir/dumps/Test_terminal_focus_3.dump
new file mode 100644
index 0000000..8ece5a2
--- /dev/null
+++ b/src/testdir/dumps/Test_terminal_focus_3.dump
@@ -0,0 +1,6 @@
+|~+0#4040ff13#ffffff0| @73
+|~| @73
+|~| @73
+|~| @73
+|:+0#0000000&|x@73
+@6> @68
diff --git a/src/testdir/test_terminal.vim b/src/testdir/test_terminal.vim
index 61b1458..3efc6bb 100644
--- a/src/testdir/test_terminal.vim
+++ b/src/testdir/test_terminal.vim
@@ -1135,8 +1135,8 @@
 
   let lines =<< trim END
       set term=xterm ttymouse=xterm2
-      au FocusLost * echo 'I am lost'
-      au FocusGained * echo 'I am back'
+      au FocusLost * call setline(1, 'I am lost') | set nomod
+      au FocusGained * call setline(1, 'I am back') | set nomod
       " FIXME: sometimes this job hangs, exit after a couple of seconds
       call timer_start(2000, {id -> execute('qall')})
   END
@@ -1152,6 +1152,14 @@
   call TermWait(buf)
   call VerifyScreenDump(buf, 'Test_terminal_focus_2', {})
 
+  " check that a command line being edited is redrawn in place
+  call term_sendkeys(buf, ":" .. repeat('x', 80))
+  call TermWait(buf)
+  call feedkeys("\<Esc>[O", "Lx!")
+  call TermWait(buf)
+  call VerifyScreenDump(buf, 'Test_terminal_focus_3', {})
+  call term_sendkeys(buf, "\<Esc>")
+
   call StopVimInTerminal(buf)
   call delete('XtermFocus')
   let &term = save_term
diff --git a/src/time.c b/src/time.c
index ab12691..362fd46 100644
--- a/src/time.c
+++ b/src/time.c
@@ -595,7 +595,7 @@
     }
 
     if (did_one)
-	redraw_after_callback(need_update_screen);
+	redraw_after_callback(need_update_screen, FALSE);
 
 #ifdef FEAT_BEVAL_TERM
     if (bevalexpr_due_set)
diff --git a/src/ui.c b/src/ui.c
index 906d88f..8cbe49b 100644
--- a/src/ui.c
+++ b/src/ui.c
@@ -1156,29 +1156,7 @@
 				: EVENT_FOCUSLOST, NULL, NULL, FALSE, curbuf);
 
     if (need_redraw)
-    {
-	// Something was executed, make sure the cursor is put back where it
-	// belongs.
-	need_wait_return = FALSE;
-
-	if (State & CMDLINE)
-	    redrawcmdline();
-	else if (State == HITRETURN || State == SETWSIZE || State == ASKMORE
-		|| State == EXTERNCMD || State == CONFIRM || exmode_active)
-	    repeat_message();
-	else if ((State & NORMAL) || (State & INSERT))
-	{
-	    if (must_redraw != 0)
-		update_screen(0);
-	    setcursor();
-	}
-	cursor_on();	    // redrawing may have switched it off
-	out_flush_cursor(FALSE, TRUE);
-# ifdef FEAT_GUI
-	if (gui.in_use)
-	    gui_update_scrollbars(FALSE);
-# endif
-    }
+	redraw_after_callback(TRUE, TRUE);
 
     // File may have been changed from 'readonly' to 'noreadonly'
     if (need_maketitle)
diff --git a/src/version.c b/src/version.c
index a8f2b36..a14f9a7 100644
--- a/src/version.c
+++ b/src/version.c
@@ -754,6 +754,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    3763,
+/**/
     3762,
 /**/
     3761,