patch 8.0.1309: cannot use 'balloonexpr' in a terminal

Problem:    Cannot use 'balloonexpr' in a terminal.
Solution:   Add 'balloonevalterm' and add code to handle mouse movements in a
            terminal. Initial implementation for Unix with GUI.
diff --git a/src/edit.c b/src/edit.c
index 435e1ed..65c53f5 100644
--- a/src/edit.c
+++ b/src/edit.c
@@ -1177,6 +1177,7 @@
 	case K_LEFTDRAG:
 	case K_LEFTRELEASE:
 	case K_LEFTRELEASE_NM:
+	case K_MOUSEMOVE:
 	case K_MIDDLEMOUSE:
 	case K_MIDDLEDRAG:
 	case K_MIDDLERELEASE:
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 25b28bb..bd5cf8d 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -1410,7 +1410,7 @@
 f_balloon_show(typval_T *argvars, typval_T *rettv UNUSED)
 {
     if (balloonEval != NULL)
-	gui_mch_post_balloon(balloonEval, get_tv_string_chk(&argvars[0]));
+	post_balloon(balloonEval, get_tv_string_chk(&argvars[0]));
 }
 #endif
 
@@ -5589,6 +5589,9 @@
 	"balloon_multiline",
 # endif
 #endif
+#ifdef FEAT_BEVALTERM
+	"balloon_eval_term",
+#endif
 #if defined(SOME_BUILTIN_TCAPS) || defined(ALL_BUILTIN_TCAPS)
 	"builtin_terms",
 # ifdef ALL_BUILTIN_TCAPS
diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c
index 2eef050..c6a6dbe 100644
--- a/src/ex_cmds2.c
+++ b/src/ex_cmds2.c
@@ -1093,21 +1093,21 @@
 static long	last_timer_id = 0;
 
     static long
-timer_time_left(timer_T *timer, proftime_T *now)
+proftime_time_left(proftime_T *due, proftime_T *now)
 {
 #  ifdef WIN3264
     LARGE_INTEGER fr;
 
-    if (now->QuadPart > timer->tr_due.QuadPart)
+    if (now->QuadPart > due->QuadPart)
 	return 0;
     QueryPerformanceFrequency(&fr);
-    return (long)(((double)(timer->tr_due.QuadPart - now->QuadPart)
+    return (long)(((double)(due->QuadPart - now->QuadPart)
 		   / (double)fr.QuadPart) * 1000);
 #  else
-    if (now->tv_sec > timer->tr_due.tv_sec)
+    if (now->tv_sec > due->tv_sec)
 	return 0;
-    return (timer->tr_due.tv_sec - now->tv_sec) * 1000
-	+ (timer->tr_due.tv_usec - now->tv_usec) / 1000;
+    return (due->tv_sec - now->tv_sec) * 1000
+	+ (due->tv_usec - now->tv_usec) / 1000;
 #  endif
 }
 
@@ -1219,7 +1219,7 @@
 
 	if (timer->tr_id == -1 || timer->tr_firing || timer->tr_paused)
 	    continue;
-	this_due = timer_time_left(timer, &now);
+	this_due = proftime_time_left(&timer->tr_due, &now);
 	if (this_due <= 1)
 	{
 	    int save_timer_busy = timer_busy;
@@ -1271,7 +1271,7 @@
 		    && timer->tr_emsg_count < 3)
 	    {
 		profile_setlimit(timer->tr_interval, &timer->tr_due);
-		this_due = timer_time_left(timer, &now);
+		this_due = proftime_time_left(&timer->tr_due, &now);
 		if (this_due < 1)
 		    this_due = 1;
 		if (timer->tr_repeat > 0)
@@ -1291,6 +1291,27 @@
     if (did_one)
 	redraw_after_callback(need_update_screen);
 
+#ifdef FEAT_BEVALTERM
+    if (bevalexpr_due_set)
+    {
+	this_due = proftime_time_left(&bevalexpr_due, &now);
+	if (this_due <= 1)
+	{
+	    bevalexpr_due_set = FALSE;
+
+	    if (balloonEval == NULL)
+	    {
+		balloonEval = (BalloonEval *)alloc(sizeof(BalloonEval));
+		balloonEvalForTerm = TRUE;
+	    }
+	    if (balloonEval != NULL)
+		general_beval_cb(balloonEval, 0);
+	}
+	else if (this_due > 0 && (next_due == -1 || next_due > this_due))
+	    next_due = this_due;
+    }
+#endif
+
     return current_id != last_timer_id ? 1 : next_due;
 }
 
@@ -1358,7 +1379,7 @@
     dict_add_nr_str(dict, "time", (long)timer->tr_interval, NULL);
 
     profile_start(&now);
-    remaining = timer_time_left(timer, &now);
+    remaining = proftime_time_left(&timer->tr_due, &now);
     dict_add_nr_str(dict, "remaining", (long)remaining, NULL);
 
     dict_add_nr_str(dict, "repeat",
diff --git a/src/ex_getln.c b/src/ex_getln.c
index 7c0db89..f8a193d 100644
--- a/src/ex_getln.c
+++ b/src/ex_getln.c
@@ -1452,6 +1452,7 @@
 	case K_X2MOUSE:
 	case K_X2DRAG:
 	case K_X2RELEASE:
+	case K_MOUSEMOVE:
 		goto cmdline_not_changed;
 
 #endif	/* FEAT_MOUSE */
diff --git a/src/feature.h b/src/feature.h
index a6a1599..f17a5c1 100644
--- a/src/feature.h
+++ b/src/feature.h
@@ -1328,6 +1328,13 @@
 # define FEAT_BEVAL_TIP		/* balloon eval used for toolbar tooltip */
 #endif
 
+/*
+ * +balloon_eval_term	Allow balloon expression evaluation in the terminal.
+ */
+#if defined(FEAT_BEVAL) && defined(UNIX) && defined(FEAT_TIMERS)
+# define FEAT_BEVALTERM
+#endif
+
 /* both Motif and Athena are X11 and share some code */
 #if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)
 # define FEAT_GUI_X11
diff --git a/src/getchar.c b/src/getchar.c
index 455c013..2e91c24 100644
--- a/src/getchar.c
+++ b/src/getchar.c
@@ -1792,6 +1792,14 @@
      */
     may_garbage_collect = FALSE;
 #endif
+#ifdef FEAT_BEVALTERM
+    if (c != K_MOUSEMOVE && c != K_IGNORE)
+    {
+	/* Don't trigger 'balloonexpr' unless only the mouse was moved. */
+	bevalexpr_due_set = FALSE;
+	ui_remove_balloon();
+    }
+#endif
 
     return c;
 }
diff --git a/src/globals.h b/src/globals.h
index ae62710..c198664 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -1231,6 +1231,7 @@
 
 #if defined(FEAT_BEVAL) && !defined(NO_X11_INCLUDES)
 EXTERN BalloonEval	*balloonEval INIT(= NULL);
+EXTERN int		balloonEvalForTerm INIT(= FALSE);
 # if defined(FEAT_NETBEANS_INTG) || defined(FEAT_SUN_WORKSHOP)
 EXTERN int bevalServers INIT(= 0);
 #  define BEVAL_NETBEANS		0x01
@@ -1648,6 +1649,11 @@
 EXTERN int  timer_busy INIT(= 0);   /* when timer is inside vgetc() then > 0 */
 #endif
 
+#ifdef FEAT_BEVALTERM
+EXTERN int  bevalexpr_due_set INIT(= FALSE);
+EXTERN proftime_T bevalexpr_due;
+#endif
+
 #ifdef FEAT_EVAL
 EXTERN time_T time_for_testing INIT(= 0);
 
diff --git a/src/gui.c b/src/gui.c
index 7ef1c95..926750c 100644
--- a/src/gui.c
+++ b/src/gui.c
@@ -740,7 +740,10 @@
 
 #ifdef FEAT_BEVAL
 	/* Always create the Balloon Evaluation area, but disable it when
-	 * 'ballooneval' is off */
+	 * 'ballooneval' is off. */
+	if (balloonEval != NULL)
+	    vim_free(balloonEval);
+	balloonEvalForTerm = FALSE;
 # ifdef FEAT_GUI_GTK
 	balloonEval = gui_mch_create_beval_area(gui.drawarea, NULL,
 						     &general_beval_cb, NULL);
diff --git a/src/gui_beval.c b/src/gui_beval.c
index 1c01ecc..ada048c 100644
--- a/src/gui_beval.c
+++ b/src/gui_beval.c
@@ -35,7 +35,11 @@
 
     /* Don't do anything when 'ballooneval' is off, messages scrolled the
      * windows up or we have no beval area. */
-    if (!p_beval || balloonEval == NULL || msg_scrolled > 0)
+    if (!((gui.in_use && p_beval)
+# ifdef FEAT_BEVALTERM
+		|| (!gui.in_use && p_bevalterm)
+# endif
+		) || beval == NULL || msg_scrolled > 0)
 	return;
 
     /* Don't do this recursively.  Happens when the expression evaluation
@@ -45,7 +49,7 @@
     recursive = TRUE;
 
 #ifdef FEAT_EVAL
-    if (get_beval_info(balloonEval, TRUE, &wp, &lnum, &text, &col) == OK)
+    if (get_beval_info(beval, TRUE, &wp, &lnum, &text, &col) == OK)
     {
 	bexpr = (*wp->w_buffer->b_p_bexpr == NUL) ? p_bexpr
 						    : wp->w_buffer->b_p_bexpr;
@@ -96,7 +100,7 @@
 	    set_vim_var_string(VV_BEVAL_TEXT, NULL, -1);
 	    if (result != NULL && result[0] != NUL)
 	    {
-		gui_mch_post_balloon(beval, result);
+		post_balloon(beval, result);
 		recursive = FALSE;
 		return;
 	    }
@@ -335,8 +339,18 @@
     linenr_T	lnum;
 
     *textp = NULL;
-    row = Y_2_ROW(beval->y);
-    col = X_2_COL(beval->x);
+# ifdef FEAT_BEVALTERM
+    if (!gui.in_use)
+    {
+	row = mouse_row;
+	col = mouse_col;
+    }
+    else
+# endif
+    {
+	row = Y_2_ROW(beval->y);
+	col = X_2_COL(beval->x);
+    }
     wp = mouse_find_win(&row, &col);
     if (wp != NULL && row < wp->w_height && col < wp->w_width)
     {
@@ -421,6 +435,20 @@
     return FAIL;
 }
 
+/*
+ * Show a balloon with "mesg".
+ */
+    void
+post_balloon(BalloonEval *beval, char_u *mesg)
+{
+# ifdef FEAT_BEVALTERM
+    if (!gui.in_use)
+	ui_post_balloon(mesg);
+    else
+# endif
+	gui_mch_post_balloon(beval, mesg);
+}
+
 # if !defined(FEAT_GUI_W32) || defined(PROTO)
 
 /*
@@ -451,10 +479,6 @@
 #endif
 
 #ifdef FEAT_GUI_GTK
-/*
- * We can unconditionally use ANSI-style prototypes here since
- * GTK+ requires an ANSI C compiler anyway.
- */
     static void
 addEventHandler(GtkWidget *target, BalloonEval *beval)
 {
diff --git a/src/keymap.h b/src/keymap.h
index 7cb5c69..8a34f0e 100644
--- a/src/keymap.h
+++ b/src/keymap.h
@@ -269,6 +269,7 @@
     , KE_NOP = 97		/* doesn't do something */
     , KE_FOCUSGAINED = 98	/* focus gained */
     , KE_FOCUSLOST = 99		/* focus lost */
+    , KE_MOUSEMOVE = 100	/* mouse moved with no button down */
 };
 
 /*
@@ -437,6 +438,7 @@
 #define K_LEFTDRAG	TERMCAP2KEY(KS_EXTRA, KE_LEFTDRAG)
 #define K_LEFTRELEASE	TERMCAP2KEY(KS_EXTRA, KE_LEFTRELEASE)
 #define K_LEFTRELEASE_NM TERMCAP2KEY(KS_EXTRA, KE_LEFTRELEASE_NM)
+#define K_MOUSEMOVE	TERMCAP2KEY(KS_EXTRA, KE_MOUSEMOVE)
 #define K_MIDDLEMOUSE	TERMCAP2KEY(KS_EXTRA, KE_MIDDLEMOUSE)
 #define K_MIDDLEDRAG	TERMCAP2KEY(KS_EXTRA, KE_MIDDLEDRAG)
 #define K_MIDDLERELEASE	TERMCAP2KEY(KS_EXTRA, KE_MIDDLERELEASE)
diff --git a/src/message.c b/src/message.c
index 221e3d8..c40b6cf 100644
--- a/src/message.c
+++ b/src/message.c
@@ -1179,6 +1179,7 @@
 				|| c == K_RIGHTDRAG  || c == K_RIGHTRELEASE
 				|| c == K_MOUSELEFT  || c == K_MOUSERIGHT
 				|| c == K_MOUSEDOWN  || c == K_MOUSEUP
+				|| c == K_MOUSEMOVE
 				|| (!mouse_has(MOUSE_RETURN)
 				    && mouse_row < msg_row
 				    && (c == K_LEFTMOUSE
diff --git a/src/misc1.c b/src/misc1.c
index f33fd3b..4c691bb 100644
--- a/src/misc1.c
+++ b/src/misc1.c
@@ -3345,6 +3345,7 @@
 	|| c == K_LEFTDRAG
 	|| c == K_LEFTRELEASE
 	|| c == K_LEFTRELEASE_NM
+	|| c == K_MOUSEMOVE
 	|| c == K_MIDDLEMOUSE
 	|| c == K_MIDDLEDRAG
 	|| c == K_MIDDLERELEASE
diff --git a/src/misc2.c b/src/misc2.c
index 63d9e81..87b79fa 100644
--- a/src/misc2.c
+++ b/src/misc2.c
@@ -2453,6 +2453,7 @@
     {K_LEFTDRAG,	(char_u *)"LeftDrag"},
     {K_LEFTRELEASE,	(char_u *)"LeftRelease"},
     {K_LEFTRELEASE_NM,	(char_u *)"LeftReleaseNM"},
+    {K_MOUSEMOVE,	(char_u *)"MouseMove"},
     {K_MIDDLEMOUSE,	(char_u *)"MiddleMouse"},
     {K_MIDDLEDRAG,	(char_u *)"MiddleDrag"},
     {K_MIDDLERELEASE,	(char_u *)"MiddleRelease"},
@@ -2515,7 +2516,7 @@
     {(int)KE_X2DRAG,		MOUSE_X2,	FALSE,	TRUE},
     {(int)KE_X2RELEASE,		MOUSE_X2,	FALSE,	FALSE},
     /* DRAG without CLICK */
-    {(int)KE_IGNORE,		MOUSE_RELEASE,	FALSE,	TRUE},
+    {(int)KE_MOUSEMOVE,		MOUSE_RELEASE,	FALSE,	TRUE},
     /* RELEASE without CLICK */
     {(int)KE_IGNORE,		MOUSE_RELEASE,	FALSE,	FALSE},
     {0,				0,		0,	0},
diff --git a/src/normal.c b/src/normal.c
index e781cd7..81bedfd 100644
--- a/src/normal.c
+++ b/src/normal.c
@@ -358,6 +358,7 @@
     {K_LEFTDRAG, nv_mouse,	0,			0},
     {K_LEFTRELEASE, nv_mouse,	0,			0},
     {K_LEFTRELEASE_NM, nv_mouse, 0,			0},
+    {K_MOUSEMOVE, nv_mouse,	0,			0},
     {K_MIDDLEMOUSE, nv_mouse,	0,			0},
     {K_MIDDLEDRAG, nv_mouse,	0,			0},
     {K_MIDDLERELEASE, nv_mouse,	0,			0},
@@ -2396,6 +2397,20 @@
 	break;
     }
 
+    if (c == K_MOUSEMOVE)
+    {
+	/* Mouse moved without a button pressed. */
+#ifdef FEAT_BEVALTERM
+	ui_may_remove_balloon();
+	if (p_bevalterm && !VIsual_active)
+	{
+	    profile_setlimit(p_bdlay, &bevalexpr_due);
+	    bevalexpr_due_set = TRUE;
+	}
+#endif
+	return FALSE;
+    }
+
 #ifdef FEAT_MOUSESHAPE
     /* May have stopped dragging the status or separator line.  The pointer is
      * most likely still on the status or separator line. */
@@ -3843,7 +3858,7 @@
 	K_LEFTMOUSE_NM, K_LEFTRELEASE_NM,
 # endif
 	K_IGNORE, K_PS,
-	K_LEFTMOUSE, K_LEFTDRAG, K_LEFTRELEASE,
+	K_LEFTMOUSE, K_LEFTDRAG, K_LEFTRELEASE, K_MOUSEMOVE,
 	K_MIDDLEMOUSE, K_MIDDLEDRAG, K_MIDDLERELEASE,
 	K_RIGHTMOUSE, K_RIGHTDRAG, K_RIGHTRELEASE,
 	K_MOUSEDOWN, K_MOUSEUP, K_MOUSELEFT, K_MOUSERIGHT,
@@ -8358,6 +8373,7 @@
     case K_LEFTMOUSE:
     case K_LEFTDRAG:
     case K_LEFTRELEASE:
+    case K_MOUSEMOVE:
     case K_RIGHTMOUSE:
     case K_RIGHTDRAG:
     case K_RIGHTRELEASE:
diff --git a/src/option.c b/src/option.c
index 4f25c1f..495a89d 100644
--- a/src/option.c
+++ b/src/option.c
@@ -642,6 +642,15 @@
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCRIPTID_INIT},
+    {"balloonevalterm", "bevalterm",P_BOOL|P_VI_DEF|P_NO_MKRC,
+#ifdef FEAT_BEVALTERM
+			    (char_u *)&p_bevalterm, PV_NONE,
+			    {(char_u *)FALSE, (char_u *)0L}
+#else
+			    (char_u *)NULL, PV_NONE,
+			    {(char_u *)0L, (char_u *)0L}
+#endif
+			    SCRIPTID_INIT},
     {"balloonexpr", "bexpr", P_STRING|P_ALLOCED|P_VI_DEF|P_VIM,
 #if defined(FEAT_BEVAL) && defined(FEAT_EVAL)
 			    (char_u *)&p_bexpr, PV_BEXPR,
@@ -8423,12 +8432,21 @@
 #ifdef FEAT_BEVAL
     else if ((int *)varp == &p_beval)
     {
-	if (p_beval && !old_value)
-	    gui_mch_enable_beval_area(balloonEval);
-	else if (!p_beval && old_value)
-	    gui_mch_disable_beval_area(balloonEval);
+	if (!balloonEvalForTerm)
+	{
+	    if (p_beval && !old_value)
+		gui_mch_enable_beval_area(balloonEval);
+	    else if (!p_beval && old_value)
+		gui_mch_disable_beval_area(balloonEval);
+	}
     }
 #endif
+# ifdef FEAT_BEVALTERM
+    else if ((int *)varp == &p_bevalterm)
+    {
+	mch_bevalterm_changed();
+    }
+# endif
 
 #ifdef FEAT_AUTOCHDIR
     else if ((int *)varp == &p_acd)
diff --git a/src/option.h b/src/option.h
index f9972f2..2084517 100644
--- a/src/option.h
+++ b/src/option.h
@@ -382,6 +382,9 @@
 EXTERN char_u	*p_bexpr;
 # endif
 #endif
+# ifdef FEAT_BEVALTERM
+EXTERN int	p_bevalterm;	/* 'balloonevalterm' */
+# endif
 #ifdef FEAT_BROWSE
 EXTERN char_u	*p_bsdir;	/* 'browsedir' */
 #endif
diff --git a/src/os_unix.c b/src/os_unix.c
index d2f0805..1c2a902 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -3564,16 +3564,25 @@
 #endif /* VMS  */
 
 #if defined(FEAT_MOUSE_TTY) || defined(PROTO)
+static int	mouse_ison = FALSE;
+
 /*
  * Set mouse clicks on or off.
  */
     void
 mch_setmouse(int on)
 {
-    static int	ison = FALSE;
+# ifdef FEAT_BEVALTERM
+    static int	bevalterm_ison = FALSE;
+# endif
     int		xterm_mouse_vers;
 
-    if (on == ison)	/* return quickly if nothing to do */
+    if (on == mouse_ison
+# ifdef FEAT_BEVALTERM
+	    && p_bevalterm == bevalterm_ison
+# endif
+	    )
+	/* return quickly if nothing to do */
 	return;
 
     xterm_mouse_vers = use_xterm_mouse();
@@ -3585,18 +3594,30 @@
 		   (on
 		   ? IF_EB("\033[?1015h", ESC_STR "[?1015h")
 		   : IF_EB("\033[?1015l", ESC_STR "[?1015l")));
-	ison = on;
+	mouse_ison = on;
     }
 # endif
 
 # ifdef FEAT_MOUSE_SGR
     if (ttym_flags == TTYM_SGR)
     {
+	/* SGR mode supports columns above 223 */
 	out_str_nf((char_u *)
 		   (on
 		   ? IF_EB("\033[?1006h", ESC_STR "[?1006h")
 		   : IF_EB("\033[?1006l", ESC_STR "[?1006l")));
-	ison = on;
+	mouse_ison = on;
+    }
+# endif
+
+# ifdef FEAT_BEVALTERM
+    if (bevalterm_ison != (p_bevalterm && on))
+    {
+	bevalterm_ison = (p_bevalterm && on);
+	if (xterm_mouse_vers > 1 && !bevalterm_ison)
+	    /* disable mouse movement events, enabling is below */
+	    out_str_nf((char_u *)
+			(IF_EB("\033[?1003l", ESC_STR "[?1003l")));
     }
 # endif
 
@@ -3605,14 +3626,19 @@
 	if (on)	/* enable mouse events, use mouse tracking if available */
 	    out_str_nf((char_u *)
 		       (xterm_mouse_vers > 1
-			? IF_EB("\033[?1002h", ESC_STR "[?1002h")
+			? (
+# ifdef FEAT_BEVALTERM
+			    bevalterm_ison
+			       ? IF_EB("\033[?1003h", ESC_STR "[?1003h") :
+# endif
+			      IF_EB("\033[?1002h", ESC_STR "[?1002h"))
 			: IF_EB("\033[?1000h", ESC_STR "[?1000h")));
 	else	/* disable mouse events, could probably always send the same */
 	    out_str_nf((char_u *)
 		       (xterm_mouse_vers > 1
 			? IF_EB("\033[?1002l", ESC_STR "[?1002l")
 			: IF_EB("\033[?1000l", ESC_STR "[?1000l")));
-	ison = on;
+	mouse_ison = on;
     }
 
 # ifdef FEAT_MOUSE_DEC
@@ -3622,7 +3648,7 @@
 	    out_str_nf((char_u *)"\033[1;2'z\033[1;3'{");
 	else	/* disable mouse events */
 	    out_str_nf((char_u *)"\033['z");
-	ison = on;
+	mouse_ison = on;
     }
 # endif
 
@@ -3632,12 +3658,12 @@
 	if (on)
 	{
 	    if (gpm_open())
-		ison = TRUE;
+		mouse_ison = TRUE;
 	}
 	else
 	{
 	    gpm_close();
-	    ison = FALSE;
+	    mouse_ison = FALSE;
 	}
     }
 # endif
@@ -3648,12 +3674,12 @@
 	if (on)
 	{
 	    if (sysmouse_open() == OK)
-		ison = TRUE;
+		mouse_ison = TRUE;
 	}
 	else
 	{
 	    sysmouse_close();
-	    ison = FALSE;
+	    mouse_ison = FALSE;
 	}
     }
 # endif
@@ -3686,13 +3712,13 @@
 	    out_str_nf((char_u *)IF_EB("\033[0~ZwLMRK+1Q\033\\",
 					ESC_STR "[0~ZwLMRK+1Q" ESC_STR "\\"));
 #  endif
-	    ison = TRUE;
+	    mouse_ison = TRUE;
 	}
 	else
 	{
 	    out_str_nf((char_u *)IF_EB("\033[0~ZwQ\033\\",
 					      ESC_STR "[0~ZwQ" ESC_STR "\\"));
-	    ison = FALSE;
+	    mouse_ison = FALSE;
 	}
     }
 # endif
@@ -3704,11 +3730,22 @@
 	    out_str_nf("\033[>1h\033[>6h\033[>7h\033[>1h\033[>9l");
 	else
 	    out_str_nf("\033[>1l\033[>6l\033[>7l\033[>1l\033[>9h");
-	ison = on;
+	mouse_ison = on;
     }
 # endif
 }
 
+#if defined(FEAT_BEVALTERM) || defined(PROTO)
+/*
+ * Called when 'balloonevalterm' changed.
+ */
+    void
+mch_bevalterm_changed(void)
+{
+    mch_setmouse(mouse_ison);
+}
+#endif
+
 /*
  * Set the mouse termcode, depending on the 'term' and 'ttymouse' options.
  */
diff --git a/src/popupmnu.c b/src/popupmnu.c
index ec75281..dc66e75 100644
--- a/src/popupmnu.c
+++ b/src/popupmnu.c
@@ -23,6 +23,7 @@
 static int pum_width;			/* width of displayed pum items */
 static int pum_base_width;		/* width of pum items base */
 static int pum_kind_width;		/* width of pum items kind column */
+static int pum_extra_width;		/* width of extra stuff */
 static int pum_scrollbar;		/* TRUE when scrollbar present */
 
 static int pum_row;			/* top row of pum */
@@ -35,6 +36,36 @@
 #define PUM_DEF_HEIGHT 10
 #define PUM_DEF_WIDTH  15
 
+    static void
+pum_compute_size(void)
+{
+    int	i;
+    int	w;
+
+    /* Compute the width of the widest match and the widest extra. */
+    pum_base_width = 0;
+    pum_kind_width = 0;
+    pum_extra_width = 0;
+    for (i = 0; i < pum_size; ++i)
+    {
+	w = vim_strsize(pum_array[i].pum_text);
+	if (pum_base_width < w)
+	    pum_base_width = w;
+	if (pum_array[i].pum_kind != NULL)
+	{
+	    w = vim_strsize(pum_array[i].pum_kind) + 1;
+	    if (pum_kind_width < w)
+		pum_kind_width = w;
+	}
+	if (pum_array[i].pum_extra != NULL)
+	{
+	    w = vim_strsize(pum_array[i].pum_extra) + 1;
+	    if (pum_extra_width < w)
+		pum_extra_width = w;
+	}
+    }
+}
+
 /*
  * Show the popup menu with items "array[size]".
  * "array" must remain valid until pum_undisplay() is called!
@@ -48,12 +79,8 @@
     int		selected)	/* index of initially selected item, none if
 				   out of range */
 {
-    int		w;
     int		def_width;
     int		max_width;
-    int		kind_width;
-    int		extra_width;
-    int		i;
     int		row;
     int		context_lines;
     int		col;
@@ -67,9 +94,6 @@
     do
     {
 	def_width = PUM_DEF_WIDTH;
-	max_width = 0;
-	kind_width = 0;
-	extra_width = 0;
 	above_row = 0;
 	below_row = cmdline_row;
 
@@ -107,7 +131,7 @@
 	/* Put the pum below "row" if possible.  If there are few lines decide
 	 * on where there is more room. */
 	if (row + 2 >= below_row - pum_height
-				&& row - above_row > (below_row - above_row) / 2)
+			      && row - above_row > (below_row - above_row) / 2)
 	{
 	    /* pum above "row" */
 
@@ -167,27 +191,10 @@
 	}
 #endif
 
-	/* Compute the width of the widest match and the widest extra. */
-	for (i = 0; i < size; ++i)
-	{
-	    w = vim_strsize(array[i].pum_text);
-	    if (max_width < w)
-		max_width = w;
-	    if (array[i].pum_kind != NULL)
-	    {
-		w = vim_strsize(array[i].pum_kind) + 1;
-		if (kind_width < w)
-		    kind_width = w;
-	    }
-	    if (array[i].pum_extra != NULL)
-	    {
-		w = vim_strsize(array[i].pum_extra) + 1;
-		if (extra_width < w)
-		    extra_width = w;
-	    }
-	}
-	pum_base_width = max_width;
-	pum_kind_width = kind_width;
+	pum_array = array;
+	pum_size = size;
+	pum_compute_size();
+	max_width = pum_base_width;
 
 	/* Calculate column */
 #ifdef FEAT_RIGHTLEFT
@@ -226,10 +233,10 @@
 #endif
 		pum_width = Columns - pum_col - pum_scrollbar;
 
-	    if (pum_width > max_width + kind_width + extra_width + 1
-						     && pum_width > PUM_DEF_WIDTH)
+	    if (pum_width > max_width + pum_kind_width + pum_extra_width + 1
+						  && pum_width > PUM_DEF_WIDTH)
 	    {
-		pum_width = max_width + kind_width + extra_width + 1;
+		pum_width = max_width + pum_kind_width + pum_extra_width + 1;
 		if (pum_width < PUM_DEF_WIDTH)
 		    pum_width = PUM_DEF_WIDTH;
 	    }
@@ -258,9 +265,6 @@
 	    pum_width = max_width - pum_scrollbar;
 	}
 
-	pum_array = array;
-	pum_size = size;
-
 	/* Set selected item and redraw.  If the window size changed need to
 	 * redo the positioning.  Limit this to two times, when there is not
 	 * much room the window size will keep changing. */
@@ -756,4 +760,97 @@
     return pum_height;
 }
 
+# if defined(FEAT_BEVALTERM) || defined(PROTO)
+static pumitem_T *balloon_array = NULL;
+static int balloon_arraysize;
+static int balloon_mouse_row = 0;
+static int balloon_mouse_col = 0;
+
+#define BALLOON_MIN_WIDTH 40
+#define BALLOON_MIN_HEIGHT 10
+
+    void
+ui_remove_balloon(void)
+{
+    if (balloon_array != NULL)
+    {
+	pum_undisplay();
+	while (balloon_arraysize > 0)
+	    vim_free(balloon_array[--balloon_arraysize].pum_text);
+	vim_free(balloon_array);
+	balloon_array = NULL;
+    }
+}
+
+/*
+ * Terminal version of a balloon, uses the popup menu code.
+ */
+    void
+ui_post_balloon(char_u *mesg)
+{
+    ui_remove_balloon();
+
+    /* TODO: split the text in multiple lines. */
+    balloon_arraysize = 3;
+    balloon_array = (pumitem_T *)alloc_clear(
+			      (unsigned)sizeof(pumitem_T) * balloon_arraysize);
+    if (balloon_array != NULL)
+    {
+	/* Add an empty line above and below, looks better. */
+	balloon_array[0].pum_text = vim_strsave((char_u *)"");
+	balloon_array[1].pum_text = vim_strsave(mesg);
+	balloon_array[2].pum_text = vim_strsave((char_u *)"");
+
+	pum_array = balloon_array;
+	pum_size = balloon_arraysize;
+	pum_compute_size();
+	pum_scrollbar = 0;
+	pum_height = balloon_arraysize;
+
+	if (Rows - mouse_row > BALLOON_MIN_HEIGHT)
+	{
+	    /* Enough space below the mouse row. */
+	    pum_row = mouse_row + 1;
+	    if (pum_height > Rows - pum_row)
+		pum_height = Rows - pum_row;
+	}
+	else
+	{
+	    /* Show above the mouse row, reduce height if it does not fit. */
+	    pum_row = mouse_row - 1 - pum_size;
+	    if (pum_row < 0)
+	    {
+		pum_height += pum_row;
+		pum_row = 0;
+	    }
+	}
+	if (Columns - mouse_col >= pum_base_width
+		|| Columns - mouse_col > BALLOON_MIN_WIDTH)
+	    /* Enough space to show at mouse column. */
+	    pum_col = mouse_col;
+	else
+	    /* Not enough space, right align with window. */
+	    pum_col = Columns - (pum_base_width > BALLOON_MIN_WIDTH
+					 ? BALLOON_MIN_WIDTH : pum_base_width);
+
+	pum_width = Columns - pum_col;
+	if (pum_width > pum_base_width + 1)
+	    pum_width = pum_base_width + 1;
+
+	pum_selected = -1;
+	pum_first = 0;
+	pum_redraw();
+    }
+}
+
+/*
+ * Called when the mouse moved, may remove any displayed balloon.
+ */
+    void
+ui_may_remove_balloon(void)
+{
+    if (mouse_row != balloon_mouse_row || mouse_col != balloon_mouse_col)
+	ui_remove_balloon();
+}
+# endif
 #endif
diff --git a/src/proto/gui_beval.pro b/src/proto/gui_beval.pro
index b0067a0..aa0553f 100644
--- a/src/proto/gui_beval.pro
+++ b/src/proto/gui_beval.pro
@@ -6,6 +6,7 @@
 void gui_mch_disable_beval_area(BalloonEval *beval);
 BalloonEval *gui_mch_currently_showing_beval(void);
 int get_beval_info(BalloonEval *beval, int getword, win_T **winp, linenr_T *lnump, char_u **textp, int *colp);
+void post_balloon(BalloonEval *beval, char_u *mesg);
 void gui_mch_post_balloon(BalloonEval *beval, char_u *mesg);
 void gui_mch_unpost_balloon(BalloonEval *beval);
 /* vim: set ft=c : */
diff --git a/src/proto/os_unix.pro b/src/proto/os_unix.pro
index d3a00b5..fbdad15 100644
--- a/src/proto/os_unix.pro
+++ b/src/proto/os_unix.pro
@@ -53,6 +53,7 @@
 void get_stty(void);
 int get_tty_info(int fd, ttyinfo_T *info);
 void mch_setmouse(int on);
+void mch_bevalterm_changed(void);
 void check_mouse_termcode(void);
 int mch_screenmode(char_u *arg);
 int mch_get_shellsize(void);
diff --git a/src/proto/popupmnu.pro b/src/proto/popupmnu.pro
index 423076c..5779533 100644
--- a/src/proto/popupmnu.pro
+++ b/src/proto/popupmnu.pro
@@ -5,4 +5,7 @@
 void pum_clear(void);
 int pum_visible(void);
 int pum_get_height(void);
+void ui_remove_balloon(void);
+void ui_post_balloon(char_u *mesg);
+void ui_may_remove_balloon(void);
 /* vim: set ft=c : */
diff --git a/src/term.c b/src/term.c
index 83b54e1..359c171 100644
--- a/src/term.c
+++ b/src/term.c
@@ -5625,6 +5625,7 @@
 		    modifiers |= MOD_MASK_ALT;
 		key_name[1] = (wheel_code & 1)
 					? (int)KE_MOUSEUP : (int)KE_MOUSEDOWN;
+		held_button = MOUSE_RELEASE;
 	    }
 	    else
 		key_name[1] = get_pseudo_mouse_code(current_button,
diff --git a/src/terminal.c b/src/terminal.c
index b416e20..6cbe343 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -52,6 +52,7 @@
  * - Termdebug does not work when Vim build with mzscheme.  gdb hangs.
  * - MS-Windows GUI: WinBar has  tearoff item
  * - MS-Windows GUI: still need to type a key after shell exits?  #1924
+ * - After executing a shell command the status line isn't redraw.
  * - What to store in a session file?  Shell at the prompt would be OK to
  *   restore, but others may not.  Open the window and let the user start the
  *   command?
@@ -717,7 +718,8 @@
 
     vterm_mouse_move(vterm, mouse_row - W_WINROW(curwin),
 					    mouse_col - curwin->w_wincol, mod);
-    vterm_mouse_button(vterm, button, pressed, mod);
+    if (button != 0)
+	vterm_mouse_button(vterm, button, pressed, mod);
     return TRUE;
 }
 
@@ -818,6 +820,7 @@
 	case K_LEFTDRAG:	other = term_send_mouse(vterm, 1, 1); break;
 	case K_LEFTRELEASE:
 	case K_LEFTRELEASE_NM:	other = term_send_mouse(vterm, 1, 0); break;
+	case K_MOUSEMOVE:	other = term_send_mouse(vterm, 0, 0); break;
 	case K_MIDDLEMOUSE:	other = term_send_mouse(vterm, 2, 1); break;
 	case K_MIDDLEDRAG:	other = term_send_mouse(vterm, 2, 1); break;
 	case K_MIDDLERELEASE:	other = term_send_mouse(vterm, 2, 0); break;
@@ -1284,6 +1287,7 @@
 	case K_LEFTMOUSE_NM:
 	case K_LEFTRELEASE:
 	case K_LEFTRELEASE_NM:
+	case K_MOUSEMOVE:
 	case K_MIDDLEMOUSE:
 	case K_MIDDLERELEASE:
 	case K_RIGHTMOUSE:
diff --git a/src/version.c b/src/version.c
index c81d7a3..3d0b5cd 100644
--- a/src/version.c
+++ b/src/version.c
@@ -93,6 +93,11 @@
 #else
 	"-balloon_eval",
 #endif
+#ifdef FEAT_BEVALTERM
+	"+balloon_eval_term",
+#else
+	"-balloon_eval_term",
+#endif
 #ifdef FEAT_BROWSE
 	"+browse",
 #else
@@ -767,6 +772,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1309,
+/**/
     1308,
 /**/
     1307,