patch 8.2.4674: cannot force getting MouseMove events

Problem:    Cannot force getting MouseMove events.
Solution:   Add the 'mousemoveevent' option with implementaiton for the GUI.
            (Ernie Rael, closes #10044)
diff --git a/src/gui.c b/src/gui.c
index 3e383a4..3b8c0d0 100644
--- a/src/gui.c
+++ b/src/gui.c
@@ -3142,13 +3142,26 @@
 		if (hold_gui_events)
 		    return;
 
+		row = gui_xy2colrow(x, y, &col);
+		// Don't report a mouse move unless moved to a
+		// different character position.
+		if (button == MOUSE_MOVE)
+		{
+		    if (row == prev_row && col == prev_col)
+			return;
+		    else
+		    {
+			prev_row = row >= 0 ? row : 0;
+			prev_col = col;
+		    }
+		}
+
 		string[3] = CSI;
 		string[4] = KS_EXTRA;
 		string[5] = (int)button_char;
 
 		// Pass the pointer coordinates of the scroll event so that we
 		// know which window to scroll.
-		row = gui_xy2colrow(x, y, &col);
 		string[6] = (char_u)(col / 128 + ' ' + 1);
 		string[7] = (char_u)(col % 128 + ' ' + 1);
 		string[8] = (char_u)(row / 128 + ' ' + 1);
@@ -4967,12 +4980,14 @@
     // apply 'mousefocus' and pointer shape
     gui_mouse_focus(x, y);
 
+    if (p_mousemev
 #ifdef FEAT_PROP_POPUP
-    if (popup_uses_mouse_move)
-	// Generate a mouse-moved event, so that the popup can perhaps be
-	// closed, just like in the terminal.
-	gui_send_mouse_event(MOUSE_MOVE, x, y, FALSE, 0);
+	|| popup_uses_mouse_move
 #endif
+   )
+	// Generate a mouse-moved event. For a <MouseMove> mapping. Or so the
+	// popup can perhaps be closed, just like in the terminal.
+	gui_send_mouse_event(MOUSE_MOVE, x, y, FALSE, 0);
 }
 
 /*
diff --git a/src/option.h b/src/option.h
index e344675..85f2fa8 100644
--- a/src/option.h
+++ b/src/option.h
@@ -760,6 +760,9 @@
 EXTERN int	p_mh;		// 'mousehide'
 #endif
 EXTERN char_u	*p_mousem;	// 'mousemodel'
+#ifdef FEAT_GUI
+EXTERN int	p_mousemev;	// 'mousemoveevent'
+#endif
 EXTERN long	p_mouset;	// 'mousetime'
 EXTERN int	p_more;		// 'more'
 #ifdef FEAT_MZSCHEME
diff --git a/src/optiondefs.h b/src/optiondefs.h
index 650c622..9eb0e78 100644
--- a/src/optiondefs.h
+++ b/src/optiondefs.h
@@ -1746,6 +1746,13 @@
 # endif
 #endif
 				(char_u *)0L} SCTX_INIT},
+    {"mousemoveevent",   "mousemev",   P_BOOL|P_VI_DEF,
+#ifdef FEAT_GUI
+			    (char_u *)&p_mousemev, PV_NONE,
+#else
+			    (char_u *)NULL, PV_NONE,
+#endif
+			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"mouseshape",  "mouses",  P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
 #ifdef FEAT_MOUSESHAPE
 			    (char_u *)&p_mouseshape, PV_NONE,
diff --git a/src/testdir/test_gui.vim b/src/testdir/test_gui.vim
index 7947288..c5332b8 100644
--- a/src/testdir/test_gui.vim
+++ b/src/testdir/test_gui.vim
@@ -1194,6 +1194,78 @@
   set mousemodel&
 endfunc
 
+func Test_gui_mouse_move_event()
+  let args = #{move: 1, button: 0, multiclick: 0, modifiers: 0}
+
+  " default, do not generate mouse move events
+  set mousemev&
+  call assert_false(&mousemev)
+
+  let n_event = 0
+  nnoremap <special> <MouseMove> :let n_event += 1<CR>
+
+  " start at mouse pos (1,1), clear counter
+  call extend(args, #{row: 1, col:1})
+  call test_gui_event('mouse', args)
+  call feedkeys('', 'Lx!')
+  let n_event = 0
+
+  call extend(args, #{row: 30, col:300})
+  call test_gui_event('mouse', args)
+  call feedkeys('', 'Lx!')
+
+  call extend(args, #{row: 100, col:300})
+  call test_gui_event('mouse', args)
+  call feedkeys('', 'Lx!')
+
+  " no events since mousemev off
+  call assert_equal(0, n_event)
+
+  " turn on mouse events and try the same thing
+  set mousemev
+  call extend(args, #{row: 1, col:1})
+  call test_gui_event('mouse', args)
+  call feedkeys('', 'Lx!')
+  let n_event = 0
+
+  call extend(args, #{row: 30, col:300})
+  call test_gui_event('mouse', args)
+  call feedkeys('', 'Lx!')
+
+  call extend(args, #{row: 100, col:300})
+  call test_gui_event('mouse', args)
+  call feedkeys('', 'Lx!')
+
+  call assert_equal(2, n_event)
+
+  " wiggle the mouse around, shouldn't get events
+  call extend(args, #{row: 1, col:1})
+  call test_gui_event('mouse', args)
+  call feedkeys('', 'Lx!')
+  let n_event = 0
+
+  call extend(args, #{row: 1, col:2})
+  call test_gui_event('mouse', args)
+  call feedkeys('', 'Lx!')
+
+  call extend(args, #{row: 2, col:2})
+  call test_gui_event('mouse', args)
+  call feedkeys('', 'Lx!')
+
+  call extend(args, #{row: 2, col:1})
+  call test_gui_event('mouse', args)
+  call feedkeys('', 'Lx!')
+
+  call extend(args, #{row: 1, col:1})
+  call test_gui_event('mouse', args)
+  call feedkeys('', 'Lx!')
+
+  call assert_equal(0, n_event)
+
+  unmap <MouseMove>
+  set mousemev&
+endfunc
+
 " Test for 'guitablabel' and 'guitabtooltip' options
 func TestGuiTabLabel()
   call add(g:TabLabels, v:lnum + 100)
diff --git a/src/testing.c b/src/testing.c
index 48ba14d..c053487 100644
--- a/src/testing.c
+++ b/src/testing.c
@@ -1368,22 +1368,35 @@
     int		col;
     int		repeated_click;
     int_u	mods;
+    int		move;
 
-    if (dict_find(args, (char_u *)"button", -1) == NULL
-	    || dict_find(args, (char_u *)"row", -1) == NULL
-	    || dict_find(args, (char_u *)"col", -1) == NULL
-	    || dict_find(args, (char_u *)"multiclick", -1) == NULL
-	    || dict_find(args, (char_u *)"modifiers", -1) == NULL)
+    if (dict_find(args, (char_u *)"row", -1) == NULL
+	    || dict_find(args, (char_u *)"col", -1) == NULL)
 	return FALSE;
 
-    button = (int)dict_get_number(args, (char_u *)"button");
+    // Note: "move" is optional, requires fewer arguments
+    move = (int)dict_get_bool(args, (char_u *)"move", FALSE);
+
+    if (!move && (dict_find(args, (char_u *)"button", -1) == NULL
+	    || dict_find(args, (char_u *)"multiclick", -1) == NULL
+	    || dict_find(args, (char_u *)"modifiers", -1) == NULL))
+	return FALSE;
+
     row = (int)dict_get_number(args, (char_u *)"row");
     col = (int)dict_get_number(args, (char_u *)"col");
-    repeated_click = (int)dict_get_number(args, (char_u *)"multiclick");
-    mods = (int)dict_get_number(args, (char_u *)"modifiers");
 
-    gui_send_mouse_event(button, TEXT_X(col - 1), TEXT_Y(row - 1),
+    if (move)
+	gui_mouse_moved(col, row);
+    else
+    {
+	button = (int)dict_get_number(args, (char_u *)"button");
+	repeated_click = (int)dict_get_number(args, (char_u *)"multiclick");
+	mods = (int)dict_get_number(args, (char_u *)"modifiers");
+
+	gui_send_mouse_event(button, TEXT_X(col - 1), TEXT_Y(row - 1),
 							repeated_click, mods);
+    }
+
     return TRUE;
 }
 
diff --git a/src/version.c b/src/version.c
index 9b92724..602366f 100644
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    4674,
+/**/
     4673,
 /**/
     4672,