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/runtime/doc/options.txt b/runtime/doc/options.txt
index b9e82d7..f9acff8 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -1,4 +1,4 @@
-*options.txt*	For Vim version 8.0.  Last change: 2017 Nov 11
+*options.txt*	For Vim version 8.0.  Last change: 2017 Nov 18
 
 
 		  VIM REFERENCE MANUAL	  by Bram Moolenaar
@@ -1127,7 +1127,16 @@
 			{not in Vi}
 			{only available when compiled with the |+balloon_eval|
 			feature}
-	Switch on the |balloon-eval| functionality.
+	Switch on the |balloon-eval| functionality for the GUI.
+
+		       *'balloonevalterm'* *'bevalterm'* *'noballoonevalterm'*
+		       *'nobevalterm'*
+'balloonevalterm' 'bevalterm'	boolean	(default off)
+			global
+			{not in Vi}
+			{only available when compiled with the
+			|+balloon_eval_term| feature}
+	Switch on the |balloon-eval| functionality for the terminal.
 
 						     *'balloonexpr'* *'bexpr'*
 'balloonexpr' 'bexpr'	string	(default "")
@@ -3521,6 +3530,8 @@
 	systems without an fsync() implementation, this variable is always
 	off.
 	Also see 'swapsync' for controlling fsync() on swap files.
+	'fsync' also applies to |writefile()|, unless a flag is used to
+	overrule it.
 
 				   *'gdefault'* *'gd'* *'nogdefault'* *'nogd'*
 'gdefault' 'gd'		boolean	(default off)
@@ -3944,7 +3955,7 @@
 		toolbar, tabline, etc.  Instead, the behavior is similar to
 		when the window is maximized and will adjust 'lines' and
 		'columns' to fit to the window.  Without the 'k' flag Vim will
-		try to keep 'lines' and 'columns the same when adding and
+		try to keep 'lines' and 'columns' the same when adding and
 		removing GUI components.
 
 						*'guipty'* *'noguipty'*
@@ -8221,7 +8232,7 @@
 	number, more intelligent detection process runs.
 	The "xterm2" value will be set if the xterm version is reported to be
 	from 95 to 276.  The "sgr" value will be set if the xterm version is
-	277 or highter.
+	277 or highter and when Vim detects Mac Terminal.app or Iterm2.
 	If you do not want 'ttymouse' to be set to "xterm2" or "sgr"
 	automatically, set t_RV to an empty string: >
 		:set t_RV=
diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
index 635bf93..1c3c9df 100644
--- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
+++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
@@ -69,6 +69,11 @@
   endif
   let pty = job_info(term_getjob(s:ptybuf))['tty_out']
   let s:ptywin = win_getid(winnr())
+  if vertical
+    " Assuming the source code window will get a signcolumn, use two more
+    " columns for that, thus one less for the terminal window.
+    exe (&columns / 2 - 1) . "wincmd |"
+  endif
 
   " Create a hidden terminal window to communicate with gdb
   let s:commbuf = term_start('NONE', {
@@ -121,6 +126,15 @@
   call s:InstallCommands()
   call win_gotoid(s:gdbwin)
 
+  " Enable showing a balloon with eval info
+  if has("balloon_eval")
+    set ballooneval
+    set balloonexpr=TermDebugBalloonExpr()
+    if has("balloon_eval_term")
+      set balloonevalterm
+    endif
+  endif
+
   let s:breakpoints = {}
 
   augroup TermDebug
@@ -144,6 +158,14 @@
     let &columns = s:save_columns
   endif
 
+  if has("balloon_eval")
+    set noballooneval
+    set balloonexpr=
+    if has("balloon_eval_term")
+      set noballoonevalterm
+    endif
+  endif
+
   au! TermDebug
 endfunc
 
@@ -279,6 +301,11 @@
   call s:SendCommand('-exec-run')
 endfunc
 
+func s:SendEval(expr)
+  call s:SendCommand('-data-evaluate-expression "' . a:expr . '"')
+  let s:evalexpr = a:expr
+endfunc
+
 " :Evaluate - evaluate what is under the cursor
 func s:Evaluate(range, arg)
   if a:arg != ''
@@ -294,25 +321,54 @@
   else
     let expr = expand('<cexpr>')
   endif
-  call s:SendCommand('-data-evaluate-expression "' . expr . '"')
-  let s:evalexpr = expr
+  call s:SendEval(expr)
 endfunc
 
+let s:evalFromBalloonExpr = 0
+
 " Handle the result of data-evaluate-expression
 func s:HandleEvaluate(msg)
   let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '')
   let value = substitute(value, '\\"', '"', 'g')
-  echomsg '"' . s:evalexpr . '": ' . value
+  if s:evalFromBalloonExpr
+    if s:evalFromBalloonExprResult == ''
+      let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value
+    else
+      let s:evalFromBalloonExprResult .= ' = ' . value
+    endif
+    call balloon_show(s:evalFromBalloonExprResult)
+  else
+    echomsg '"' . s:evalexpr . '": ' . value
+  endif
 
   if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
     " Looks like a pointer, also display what it points to.
-    let s:evalexpr = '*' . s:evalexpr
-    call term_sendkeys(s:commbuf, '-data-evaluate-expression "' . s:evalexpr . "\"\r")
+    call s:SendEval('*' . s:evalexpr)
+  else
+    let s:evalFromBalloonExpr = 0
   endif
 endfunc
 
+" Show a balloon with information of the variable under the mouse pointer,
+" if there is any.
+func TermDebugBalloonExpr()
+  if v:beval_winid != s:startwin
+    return
+  endif
+  call s:SendEval(v:beval_text)
+  let s:evalFromBalloonExpr = 1
+  let s:evalFromBalloonExprResult = ''
+  return ''
+endfunc
+
 " Handle an error.
 func s:HandleError(msg)
+  if a:msg =~ 'No symbol .* in current context'
+	\ || a:msg =~ 'Cannot access memory at address '
+	\ || a:msg =~ 'Attempt to use a type name as an expression'
+    " Result of s:SendEval() failed, ignore.
+    return
+  endif
   echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '')
 endfunc
 
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,