changeset 18150:0ec6521e9d80 v8.1.2070

patch 8.1.2070: mouse code is spread out Commit: https://github.com/vim/vim/commit/b8ff5c271ee5dcef6f63436b77e228e062ff9a0e Author: Bram Moolenaar <Bram@vim.org> Date: Mon Sep 23 21:16:54 2019 +0200 patch 8.1.2070: mouse code is spread out Problem: Mouse code is spread out. Solution: Move mouse terminal code parsing to mouse.c. (Yegappan Lakshmanan, closes #4966)
author Bram Moolenaar <Bram@vim.org>
date Mon, 23 Sep 2019 21:30:04 +0200
parents 275fec3af04a
children 6b00e9517382
files src/mouse.c src/proto/mouse.pro src/proto/term.pro src/term.c src/version.c
diffstat 5 files changed, 771 insertions(+), 725 deletions(-) [+]
line wrap: on
line diff
--- a/src/mouse.c
+++ b/src/mouse.c
@@ -2089,6 +2089,727 @@ nv_mouse(cmdarg_T *cap)
 {
     (void)do_mouse(cap->oap, cap->cmdchar, BACKWARD, cap->count1, 0);
 }
+
+/*
+ * Check if typebuf 'tp' contains a terminal mouse code and returns the
+ * modifiers found in typebuf in 'modifiers'.
+ */
+    int
+check_termcode_mouse(
+    char_u	*tp,
+    int		*slen,
+    char_u	*key_name,
+    char_u	*modifiers_start,
+    int		idx,
+    int		*modifiers)
+{
+    int		j;
+    char_u	*p;
+# if !defined(UNIX) || defined(FEAT_MOUSE_XTERM) || defined(FEAT_GUI) \
+    || defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE)
+    char_u	bytes[6];
+    int		num_bytes;
+# endif
+    int		mouse_code = 0;	    // init for GCC
+    int		is_click, is_drag;
+    int		wheel_code = 0;
+    int		current_button;
+    static int	held_button = MOUSE_RELEASE;
+    static int	orig_num_clicks = 1;
+    static int	orig_mouse_code = 0x0;
+# ifdef CHECK_DOUBLE_CLICK
+    static int	orig_mouse_col = 0;
+    static int	orig_mouse_row = 0;
+    static struct timeval  orig_mouse_time = {0, 0};
+    // time of previous mouse click
+    struct timeval  mouse_time;		// time of current mouse click
+    long	timediff;		// elapsed time in msec
+# endif
+
+    is_click = is_drag = FALSE;
+
+# if !defined(UNIX) || defined(FEAT_MOUSE_XTERM) || defined(FEAT_GUI) \
+    || defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE)
+    if (key_name[0] == KS_MOUSE
+#  ifdef FEAT_MOUSE_GPM
+	    || key_name[0] == KS_GPM_MOUSE
+#  endif
+       )
+    {
+	/*
+	 * For xterm we get "<t_mouse>scr", where
+	 *  s == encoded button state:
+	 *	   0x20 = left button down
+	 *	   0x21 = middle button down
+	 *	   0x22 = right button down
+	 *	   0x23 = any button release
+	 *	   0x60 = button 4 down (scroll wheel down)
+	 *	   0x61 = button 5 down (scroll wheel up)
+	 *	add 0x04 for SHIFT
+	 *	add 0x08 for ALT
+	 *	add 0x10 for CTRL
+	 *	add 0x20 for mouse drag (0x40 is drag with left button)
+	 *	add 0x40 for mouse move (0x80 is move, 0x81 too)
+	 *		 0x43 (drag + release) is also move
+	 *  c == column + ' ' + 1 == column + 33
+	 *  r == row + ' ' + 1 == row + 33
+	 *
+	 * The coordinates are passed on through global variables.
+	 * Ugly, but this avoids trouble with mouse clicks at an
+	 * unexpected moment and allows for mapping them.
+	 */
+	for (;;)
+	{
+#  ifdef FEAT_GUI
+	    if (gui.in_use)
+	    {
+		// GUI uses more bits for columns > 223
+		num_bytes = get_bytes_from_buf(tp + *slen, bytes, 5);
+		if (num_bytes == -1)	// not enough coordinates
+		    return -1;
+		mouse_code = bytes[0];
+		mouse_col = 128 * (bytes[1] - ' ' - 1)
+		    + bytes[2] - ' ' - 1;
+		mouse_row = 128 * (bytes[3] - ' ' - 1)
+		    + bytes[4] - ' ' - 1;
+	    }
+	    else
+#  endif
+	    {
+		num_bytes = get_bytes_from_buf(tp + *slen, bytes, 3);
+		if (num_bytes == -1)	// not enough coordinates
+		    return -1;
+		mouse_code = bytes[0];
+		mouse_col = bytes[1] - ' ' - 1;
+		mouse_row = bytes[2] - ' ' - 1;
+	    }
+	    *slen += num_bytes;
+
+	    // If the following bytes is also a mouse code and it has
+	    // the same code, dump this one and get the next.  This
+	    // makes dragging a whole lot faster.
+#  ifdef FEAT_GUI
+	    if (gui.in_use)
+		j = 3;
+	    else
+#  endif
+		j = get_termcode_len(idx);
+	    if (STRNCMP(tp, tp + *slen, (size_t)j) == 0
+		    && tp[*slen + j] == mouse_code
+		    && tp[*slen + j + 1] != NUL
+		    && tp[*slen + j + 2] != NUL
+#  ifdef FEAT_GUI
+		    && (!gui.in_use
+			|| (tp[*slen + j + 3] != NUL
+			    && tp[*slen + j + 4] != NUL))
+#  endif
+	       )
+		*slen += j;
+	    else
+		break;
+	}
+    }
+
+    if (key_name[0] == KS_URXVT_MOUSE
+	    || key_name[0] == KS_SGR_MOUSE
+	    || key_name[0] == KS_SGR_MOUSE_RELEASE)
+    {
+	// URXVT 1015 mouse reporting mode:
+	// Almost identical to xterm mouse mode, except the values
+	// are decimal instead of bytes.
+	//
+	// \033[%d;%d;%dM
+	//	       ^-- row
+	//	    ^----- column
+	//	 ^-------- code
+	//
+	// SGR 1006 mouse reporting mode:
+	// Almost identical to xterm mouse mode, except the values
+	// are decimal instead of bytes.
+	//
+	// \033[<%d;%d;%dM
+	//	       ^-- row
+	//	    ^----- column
+	//	 ^-------- code
+	//
+	// \033[<%d;%d;%dm        : mouse release event
+	//	       ^-- row
+	//	    ^----- column
+	//	 ^-------- code
+	p = modifiers_start;
+	if (p == NULL)
+	    return -1;
+
+	mouse_code = getdigits(&p);
+	if (*p++ != ';')
+	    return -1;
+
+	// when mouse reporting is SGR, add 32 to mouse code
+	if (key_name[0] == KS_SGR_MOUSE
+		|| key_name[0] == KS_SGR_MOUSE_RELEASE)
+	    mouse_code += 32;
+
+	if (key_name[0] == KS_SGR_MOUSE_RELEASE)
+	    mouse_code |= MOUSE_RELEASE;
+
+	mouse_col = getdigits(&p) - 1;
+	if (*p++ != ';')
+	    return -1;
+
+	mouse_row = getdigits(&p) - 1;
+
+	// The modifiers were the mouse coordinates, not the
+	// modifier keys (alt/shift/ctrl/meta) state.
+	*modifiers = 0;
+    }
+
+    if (key_name[0] == KS_MOUSE
+#  ifdef FEAT_MOUSE_GPM
+	    || key_name[0] == KS_GPM_MOUSE
+#  endif
+#  ifdef FEAT_MOUSE_URXVT
+	    || key_name[0] == KS_URXVT_MOUSE
+#  endif
+	    || key_name[0] == KS_SGR_MOUSE
+	    || key_name[0] == KS_SGR_MOUSE_RELEASE)
+    {
+#  if !defined(MSWIN)
+	/*
+	 * Handle mouse events.
+	 * Recognize the xterm mouse wheel, but not in the GUI, the
+	 * Linux console with GPM and the MS-DOS or Win32 console
+	 * (multi-clicks use >= 0x60).
+	 */
+	if (mouse_code >= MOUSEWHEEL_LOW
+#   ifdef FEAT_GUI
+		&& !gui.in_use
+#   endif
+#   ifdef FEAT_MOUSE_GPM
+		&& key_name[0] != KS_GPM_MOUSE
+#   endif
+	   )
+	{
+#   if defined(UNIX) && defined(FEAT_MOUSE_TTY)
+	    if (use_xterm_mouse() > 1 && mouse_code >= 0x80)
+		// mouse-move event, using MOUSE_DRAG works
+		mouse_code = MOUSE_DRAG;
+	    else
+#   endif
+		// Keep the mouse_code before it's changed, so that we
+		// remember that it was a mouse wheel click.
+		wheel_code = mouse_code;
+	}
+#   ifdef FEAT_MOUSE_XTERM
+	else if (held_button == MOUSE_RELEASE
+#    ifdef FEAT_GUI
+		&& !gui.in_use
+#    endif
+		&& (mouse_code == 0x23 || mouse_code == 0x24
+		    || mouse_code == 0x40 || mouse_code == 0x41))
+	{
+	    // Apparently 0x23 and 0x24 are used by rxvt scroll wheel.
+	    // And 0x40 and 0x41 are used by some xterm emulator.
+	    wheel_code = mouse_code - (mouse_code >= 0x40 ? 0x40 : 0x23)
+		+ MOUSEWHEEL_LOW;
+	}
+#   endif
+
+#   if defined(UNIX) && defined(FEAT_MOUSE_TTY)
+	else if (use_xterm_mouse() > 1)
+	{
+	    if (mouse_code & MOUSE_DRAG_XTERM)
+		mouse_code |= MOUSE_DRAG;
+	}
+#   endif
+#   ifdef FEAT_XCLIPBOARD
+	else if (!(mouse_code & MOUSE_DRAG & ~MOUSE_CLICK_MASK))
+	{
+	    if ((mouse_code & MOUSE_RELEASE) == MOUSE_RELEASE)
+		stop_xterm_trace();
+	    else
+		start_xterm_trace(mouse_code);
+	}
+#   endif
+#  endif
+    }
+# endif // !UNIX || FEAT_MOUSE_XTERM
+# ifdef FEAT_MOUSE_NET
+    if (key_name[0] == KS_NETTERM_MOUSE)
+    {
+	int mc, mr;
+
+	// expect a rather limited sequence like: balancing {
+	// \033}6,45\r
+	// '6' is the row, 45 is the column
+	p = tp + *slen;
+	mr = getdigits(&p);
+	if (*p++ != ',')
+	    return -1;
+	mc = getdigits(&p);
+	if (*p++ != '\r')
+	    return -1;
+
+	mouse_col = mc - 1;
+	mouse_row = mr - 1;
+	mouse_code = MOUSE_LEFT;
+	*slen += (int)(p - (tp + *slen));
+    }
+# endif	// FEAT_MOUSE_NET
+# ifdef FEAT_MOUSE_JSB
+    if (key_name[0] == KS_JSBTERM_MOUSE)
+    {
+	int mult, val, iter, button, status;
+
+	/*
+	 * JSBTERM Input Model
+	 * \033[0~zw uniq escape sequence
+	 * (L-x)  Left button pressed - not pressed x not reporting
+	 * (M-x)  Middle button pressed - not pressed x not reporting
+	 * (R-x)  Right button pressed - not pressed x not reporting
+	 * (SDmdu)  Single , Double click, m mouse move d button down
+	 *						   u button up
+	 *  ###   X cursor position padded to 3 digits
+	 *  ###   Y cursor position padded to 3 digits
+	 * (s-x)  SHIFT key pressed - not pressed x not reporting
+	 * (c-x)  CTRL key pressed - not pressed x not reporting
+	 * \033\\ terminating sequence
+	 */
+	p = tp + *slen;
+	button = mouse_code = 0;
+	switch (*p++)
+	{
+	    case 'L': button = 1; break;
+	    case '-': break;
+	    case 'x': break; // ignore sequence
+	    default:  return -1; // Unknown Result
+	}
+	switch (*p++)
+	{
+	    case 'M': button |= 2; break;
+	    case '-': break;
+	    case 'x': break; // ignore sequence
+	    default:  return -1; // Unknown Result
+	}
+	switch (*p++)
+	{
+	    case 'R': button |= 4; break;
+	    case '-': break;
+	    case 'x': break; // ignore sequence
+	    default:  return -1; // Unknown Result
+	}
+	status = *p++;
+	for (val = 0, mult = 100, iter = 0; iter < 3; iter++,
+		mult /= 10, p++)
+	    if (*p >= '0' && *p <= '9')
+		val += (*p - '0') * mult;
+	    else
+		return -1;
+	mouse_col = val;
+	for (val = 0, mult = 100, iter = 0; iter < 3; iter++,
+		mult /= 10, p++)
+	    if (*p >= '0' && *p <= '9')
+		val += (*p - '0') * mult;
+	    else
+		return -1;
+	mouse_row = val;
+	switch (*p++)
+	{
+	    case 's': button |= 8; break;  // SHIFT key Pressed
+	    case '-': break;  // Not Pressed
+	    case 'x': break;  // Not Reporting
+	    default:  return -1; // Unknown Result
+	}
+	switch (*p++)
+	{
+	    case 'c': button |= 16; break;  // CTRL key Pressed
+	    case '-': break;  // Not Pressed
+	    case 'x': break;  // Not Reporting
+	    default:  return -1; // Unknown Result
+	}
+	if (*p++ != '\033')
+	    return -1;
+	if (*p++ != '\\')
+	    return -1;
+	switch (status)
+	{
+	    case 'D': // Double Click
+	    case 'S': // Single Click
+		if (button & 1) mouse_code |= MOUSE_LEFT;
+		if (button & 2) mouse_code |= MOUSE_MIDDLE;
+		if (button & 4) mouse_code |= MOUSE_RIGHT;
+		if (button & 8) mouse_code |= MOUSE_SHIFT;
+		if (button & 16) mouse_code |= MOUSE_CTRL;
+		break;
+	    case 'm': // Mouse move
+		if (button & 1) mouse_code |= MOUSE_LEFT;
+		if (button & 2) mouse_code |= MOUSE_MIDDLE;
+		if (button & 4) mouse_code |= MOUSE_RIGHT;
+		if (button & 8) mouse_code |= MOUSE_SHIFT;
+		if (button & 16) mouse_code |= MOUSE_CTRL;
+		if ((button & 7) != 0)
+		{
+		    held_button = mouse_code;
+		    mouse_code |= MOUSE_DRAG;
+		}
+		is_drag = TRUE;
+		showmode();
+		break;
+	    case 'd': // Button Down
+		if (button & 1) mouse_code |= MOUSE_LEFT;
+		if (button & 2) mouse_code |= MOUSE_MIDDLE;
+		if (button & 4) mouse_code |= MOUSE_RIGHT;
+		if (button & 8) mouse_code |= MOUSE_SHIFT;
+		if (button & 16) mouse_code |= MOUSE_CTRL;
+		break;
+	    case 'u': // Button Up
+		if (button & 1)
+		    mouse_code |= MOUSE_LEFT | MOUSE_RELEASE;
+		if (button & 2)
+		    mouse_code |= MOUSE_MIDDLE | MOUSE_RELEASE;
+		if (button & 4)
+		    mouse_code |= MOUSE_RIGHT | MOUSE_RELEASE;
+		if (button & 8)
+		    mouse_code |= MOUSE_SHIFT;
+		if (button & 16)
+		    mouse_code |= MOUSE_CTRL;
+		break;
+	    default: return -1; // Unknown Result
+	}
+
+	*slen += (p - (tp + *slen));
+    }
+# endif // FEAT_MOUSE_JSB
+# ifdef FEAT_MOUSE_DEC
+    if (key_name[0] == KS_DEC_MOUSE)
+    {
+	/*
+	 * The DEC Locator Input Model
+	 * Netterm delivers the code sequence:
+	 *  \033[2;4;24;80&w  (left button down)
+	 *  \033[3;0;24;80&w  (left button up)
+	 *  \033[6;1;24;80&w  (right button down)
+	 *  \033[7;0;24;80&w  (right button up)
+	 * CSI Pe ; Pb ; Pr ; Pc ; Pp & w
+	 * Pe is the event code
+	 * Pb is the button code
+	 * Pr is the row coordinate
+	 * Pc is the column coordinate
+	 * Pp is the third coordinate (page number)
+	 * Pe, the event code indicates what event caused this report
+	 *    The following event codes are defined:
+	 *    0 - request, the terminal received an explicit request
+	 *	 for a locator report, but the locator is unavailable
+	 *    1 - request, the terminal received an explicit request
+	 *	 for a locator report
+	 *    2 - left button down
+	 *    3 - left button up
+	 *    4 - middle button down
+	 *    5 - middle button up
+	 *    6 - right button down
+	 *    7 - right button up
+	 *    8 - fourth button down
+	 *    9 - fourth button up
+	 *    10 - locator outside filter rectangle
+	 * Pb, the button code, ASCII decimal 0-15 indicating which
+	 *   buttons are down if any. The state of the four buttons
+	 *   on the locator correspond to the low four bits of the
+	 *   decimal value,
+	 *   "1" means button depressed
+	 *   0 - no buttons down,
+	 *   1 - right,
+	 *   2 - middle,
+	 *   4 - left,
+	 *   8 - fourth
+	 * Pr is the row coordinate of the locator position in the page,
+	 *   encoded as an ASCII decimal value.
+	 *   If Pr is omitted, the locator position is undefined
+	 *   (outside the terminal window for example).
+	 * Pc is the column coordinate of the locator position in the
+	 *   page, encoded as an ASCII decimal value.
+	 *   If Pc is omitted, the locator position is undefined
+	 *   (outside the terminal window for example).
+	 * Pp is the page coordinate of the locator position
+	 *   encoded as an ASCII decimal value.
+	 *   The page coordinate may be omitted if the locator is on
+	 *   page one (the default).  We ignore it anyway.
+	 */
+	int Pe, Pb, Pr, Pc;
+
+	p = tp + *slen;
+
+	// get event status
+	Pe = getdigits(&p);
+	if (*p++ != ';')
+	    return -1;
+
+	// get button status
+	Pb = getdigits(&p);
+	if (*p++ != ';')
+	    return -1;
+
+	// get row status
+	Pr = getdigits(&p);
+	if (*p++ != ';')
+	    return -1;
+
+	// get column status
+	Pc = getdigits(&p);
+
+	// the page parameter is optional
+	if (*p == ';')
+	{
+	    p++;
+	    (void)getdigits(&p);
+	}
+	if (*p++ != '&')
+	    return -1;
+	if (*p++ != 'w')
+	    return -1;
+
+	mouse_code = 0;
+	switch (Pe)
+	{
+	    case  0: return -1; // position request while unavailable
+	    case  1: // a response to a locator position request includes
+		     //	the status of all buttons
+		     Pb &= 7;   // mask off and ignore fourth button
+		     if (Pb & 4)
+			 mouse_code  = MOUSE_LEFT;
+		     if (Pb & 2)
+			 mouse_code  = MOUSE_MIDDLE;
+		     if (Pb & 1)
+			 mouse_code  = MOUSE_RIGHT;
+		     if (Pb)
+		     {
+			 held_button = mouse_code;
+			 mouse_code |= MOUSE_DRAG;
+			 WantQueryMouse = TRUE;
+		     }
+		     is_drag = TRUE;
+		     showmode();
+		     break;
+	    case  2: mouse_code = MOUSE_LEFT;
+		     WantQueryMouse = TRUE;
+		     break;
+	    case  3: mouse_code = MOUSE_RELEASE | MOUSE_LEFT;
+		     break;
+	    case  4: mouse_code = MOUSE_MIDDLE;
+		     WantQueryMouse = TRUE;
+		     break;
+	    case  5: mouse_code = MOUSE_RELEASE | MOUSE_MIDDLE;
+		     break;
+	    case  6: mouse_code = MOUSE_RIGHT;
+		     WantQueryMouse = TRUE;
+		     break;
+	    case  7: mouse_code = MOUSE_RELEASE | MOUSE_RIGHT;
+		     break;
+	    case  8: return -1; // fourth button down
+	    case  9: return -1; // fourth button up
+	    case 10: return -1; // mouse outside of filter rectangle
+	    default: return -1; // should never occur
+	}
+
+	mouse_col = Pc - 1;
+	mouse_row = Pr - 1;
+
+	*slen += (int)(p - (tp + *slen));
+    }
+# endif // FEAT_MOUSE_DEC
+# ifdef FEAT_MOUSE_PTERM
+    if (key_name[0] == KS_PTERM_MOUSE)
+    {
+	int button, num_clicks, action;
+
+	p = tp + *slen;
+
+	action = getdigits(&p);
+	if (*p++ != ';')
+	    return -1;
+
+	mouse_row = getdigits(&p);
+	if (*p++ != ';')
+	    return -1;
+	mouse_col = getdigits(&p);
+	if (*p++ != ';')
+	    return -1;
+
+	button = getdigits(&p);
+	mouse_code = 0;
+
+	switch (button)
+	{
+	    case 4: mouse_code = MOUSE_LEFT; break;
+	    case 1: mouse_code = MOUSE_RIGHT; break;
+	    case 2: mouse_code = MOUSE_MIDDLE; break;
+	    default: return -1;
+	}
+
+	switch (action)
+	{
+	    case 31: // Initial press
+		if (*p++ != ';')
+		    return -1;
+
+		num_clicks = getdigits(&p); // Not used
+		break;
+
+	    case 32: // Release
+		mouse_code |= MOUSE_RELEASE;
+		break;
+
+	    case 33: // Drag
+		held_button = mouse_code;
+		mouse_code |= MOUSE_DRAG;
+		break;
+
+	    default:
+		return -1;
+	}
+
+	if (*p++ != 't')
+	    return -1;
+
+	*slen += (p - (tp + *slen));
+    }
+# endif // FEAT_MOUSE_PTERM
+
+    // Interpret the mouse code
+    current_button = (mouse_code & MOUSE_CLICK_MASK);
+    if (current_button == MOUSE_RELEASE
+# ifdef FEAT_MOUSE_XTERM
+	    && wheel_code == 0
+# endif
+       )
+    {
+	/*
+	 * If we get a mouse drag or release event when
+	 * there is no mouse button held down (held_button ==
+	 * MOUSE_RELEASE), produce a K_IGNORE below.
+	 * (can happen when you hold down two buttons
+	 * and then let them go, or click in the menu bar, but not
+	 * on a menu, and drag into the text).
+	 */
+	if ((mouse_code & MOUSE_DRAG) == MOUSE_DRAG)
+	    is_drag = TRUE;
+	current_button = held_button;
+    }
+    else if (wheel_code == 0)
+    {
+# ifdef CHECK_DOUBLE_CLICK
+#  ifdef FEAT_MOUSE_GPM
+	/*
+	 * Only for Unix, when GUI not active, we handle
+	 * multi-clicks here, but not for GPM mouse events.
+	 */
+#   ifdef FEAT_GUI
+	if (key_name[0] != KS_GPM_MOUSE && !gui.in_use)
+#   else
+	    if (key_name[0] != KS_GPM_MOUSE)
+#   endif
+#  else
+#   ifdef FEAT_GUI
+		if (!gui.in_use)
+#   endif
+#  endif
+		{
+		    /*
+		     * Compute the time elapsed since the previous mouse click.
+		     */
+		    gettimeofday(&mouse_time, NULL);
+		    if (orig_mouse_time.tv_sec == 0)
+		    {
+			/*
+			 * Avoid computing the difference between mouse_time
+			 * and orig_mouse_time for the first click, as the
+			 * difference would be huge and would cause
+			 * multiplication overflow.
+			 */
+			timediff = p_mouset;
+		    }
+		    else
+		    {
+			timediff = (mouse_time.tv_usec
+				- orig_mouse_time.tv_usec) / 1000;
+			if (timediff < 0)
+			    --orig_mouse_time.tv_sec;
+			timediff += (mouse_time.tv_sec
+				- orig_mouse_time.tv_sec) * 1000;
+		    }
+		    orig_mouse_time = mouse_time;
+		    if (mouse_code == orig_mouse_code
+			    && timediff < p_mouset
+			    && orig_num_clicks != 4
+			    && orig_mouse_col == mouse_col
+			    && orig_mouse_row == mouse_row
+			    && (is_mouse_topline(curwin)
+				// Double click in tab pages line also works
+				// when window contents changes.
+				|| (mouse_row == 0 && firstwin->w_winrow > 0))
+		       )
+			++orig_num_clicks;
+		    else
+			orig_num_clicks = 1;
+		    orig_mouse_col = mouse_col;
+		    orig_mouse_row = mouse_row;
+		    set_mouse_topline(curwin);
+		}
+#  if defined(FEAT_GUI) || defined(FEAT_MOUSE_GPM)
+		else
+		    orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code);
+#  endif
+# else
+	orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code);
+# endif
+	is_click = TRUE;
+	orig_mouse_code = mouse_code;
+    }
+    if (!is_drag)
+	held_button = mouse_code & MOUSE_CLICK_MASK;
+
+    /*
+     * Translate the actual mouse event into a pseudo mouse event.
+     * First work out what modifiers are to be used.
+     */
+    if (orig_mouse_code & MOUSE_SHIFT)
+	*modifiers |= MOD_MASK_SHIFT;
+    if (orig_mouse_code & MOUSE_CTRL)
+	*modifiers |= MOD_MASK_CTRL;
+    if (orig_mouse_code & MOUSE_ALT)
+	*modifiers |= MOD_MASK_ALT;
+    if (orig_num_clicks == 2)
+	*modifiers |= MOD_MASK_2CLICK;
+    else if (orig_num_clicks == 3)
+	*modifiers |= MOD_MASK_3CLICK;
+    else if (orig_num_clicks == 4)
+	*modifiers |= MOD_MASK_4CLICK;
+
+    // Work out our pseudo mouse event. Note that MOUSE_RELEASE gets
+    // added, then it's not mouse up/down.
+    key_name[0] = KS_EXTRA;
+    if (wheel_code != 0
+	    && (wheel_code & MOUSE_RELEASE) != MOUSE_RELEASE)
+    {
+	if (wheel_code & MOUSE_CTRL)
+	    *modifiers |= MOD_MASK_CTRL;
+	if (wheel_code & MOUSE_ALT)
+	    *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,
+		is_click, is_drag);
+
+    // Make sure the mouse position is valid.  Some terminals may
+    // return weird values.
+    if (mouse_col >= Columns)
+	mouse_col = Columns - 1;
+    if (mouse_row >= Rows)
+	mouse_row = Rows - 1;
+
+    return 0;
+}
 #endif // FEAT_MOUSE
 
 // Functions also used for popup windows.
--- a/src/proto/mouse.pro
+++ b/src/proto/mouse.pro
@@ -13,6 +13,7 @@ int mouse_model_popup(void);
 int jump_to_mouse(int flags, int *inclusive, int which_button);
 void nv_mousescroll(cmdarg_T *cap);
 void nv_mouse(cmdarg_T *cap);
+int check_termcode_mouse(char_u *tp, int *slen, char_u *key_name, char_u *modifiers_start, int idx, int *modifiers);
 int mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump, int *plines_cache);
 win_T *mouse_find_win(int *rowp, int *colp, mouse_find_T popup);
 int vcol2col(win_T *wp, linenr_T lnum, int vcol);
--- a/src/proto/term.pro
+++ b/src/proto/term.pro
@@ -32,6 +32,7 @@ void term_push_title(int which);
 void term_pop_title(int which);
 void ttest(int pairs);
 void add_long_to_buf(long_u val, char_u *dst);
+int get_bytes_from_buf(char_u *buf, char_u *bytes, int num_bytes);
 void check_shellsize(void);
 void limit_screen_size(void);
 void win_new_shellsize(void);
@@ -59,8 +60,10 @@ void clear_termcodes(void);
 void add_termcode(char_u *name, char_u *string, int flags);
 char_u *find_termcode(char_u *name);
 char_u *get_termcode(int i);
+int get_termcode_len(int idx);
 void del_termcode(char_u *name);
 void set_mouse_topline(win_T *wp);
+int is_mouse_topline(win_T *wp);
 int check_termcode(int max_offset, char_u *buf, int bufsize, int *buflen);
 void term_get_fg_color(char_u *r, char_u *g, char_u *b);
 void term_get_bg_color(char_u *r, char_u *g, char_u *b);
--- a/src/term.c
+++ b/src/term.c
@@ -82,11 +82,6 @@ static void req_more_codes_from_term(voi
 static void got_code_from_term(char_u *code, int len);
 static void check_for_codes_from_term(void);
 #endif
-#if defined(FEAT_GUI) \
-    || (defined(FEAT_MOUSE) && (!defined(UNIX) || defined(FEAT_MOUSE_XTERM) \
-		|| defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE)))
-static int get_bytes_from_buf(char_u *, char_u *, int);
-#endif
 static void del_termcode_idx(int idx);
 static int find_term_bykeys(char_u *src);
 static int term_is_builtin(char_u *name);
@@ -3160,7 +3155,7 @@ get_long_from_buf(char_u *buf, long_u *v
  * from buf (between num_bytes and num_bytes*2), or -1 if not enough bytes were
  * available.
  */
-    static int
+    int
 get_bytes_from_buf(char_u *buf, char_u *bytes, int num_bytes)
 {
     int	    len = 0;
@@ -4094,6 +4089,15 @@ get_termcode(int i)
     return &termcodes[i].name[0];
 }
 
+/*
+ * Returns the length of the terminal code at index 'idx'.
+ */
+    int
+get_termcode_len(int idx)
+{
+    return termcodes[idx].len;
+}
+
     void
 del_termcode(char_u *name)
 {
@@ -4178,6 +4182,20 @@ set_mouse_topline(win_T *wp)
     orig_topfill = wp->w_topfill;
 # endif
 }
+
+/*
+ * Returns TRUE if the top line and top fill of window 'wp' matches the saved
+ * topline and topfill.
+ */
+    int
+is_mouse_topline(win_T *wp)
+{
+    return orig_topline == wp->w_topline
+#ifdef FEAT_DIFF
+	&& orig_topfill == wp->w_topfill
+#endif
+	;
+}
 #endif
 
 /*
@@ -4216,28 +4234,6 @@ check_termcode(
     char_u	string[MAX_KEY_CODE_LEN + 1];
     int		i, j;
     int		idx = 0;
-#ifdef FEAT_MOUSE
-# if !defined(UNIX) || defined(FEAT_MOUSE_XTERM) || defined(FEAT_GUI) \
-    || defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE)
-    char_u	bytes[6];
-    int		num_bytes;
-# endif
-    int		mouse_code = 0;	    /* init for GCC */
-    int		is_click, is_drag;
-    int		wheel_code = 0;
-    int		current_button;
-    static int	held_button = MOUSE_RELEASE;
-    static int	orig_num_clicks = 1;
-    static int	orig_mouse_code = 0x0;
-# ifdef CHECK_DOUBLE_CLICK
-    static int	orig_mouse_col = 0;
-    static int	orig_mouse_row = 0;
-    static struct timeval  orig_mouse_time = {0, 0};
-					/* time of previous mouse click */
-    struct timeval  mouse_time;		/* time of current mouse click */
-    long	timediff;		/* elapsed time in msec */
-# endif
-#endif
     int		cpo_koffset;
 
     cpo_koffset = (vim_strchr(p_cpo, CPO_KOFFSET) != NULL);
@@ -4958,8 +4954,10 @@ check_termcode(
 		    || key_name[1] == (int)KE_MOUSEDOWN
 		    || key_name[1] == (int)KE_MOUSEUP))
 	{
-	    num_bytes = get_bytes_from_buf(tp + slen, bytes, 4);
-	    if (num_bytes == -1)	/* not enough coordinates */
+	    char_u	bytes[6];
+	    int		num_bytes = get_bytes_from_buf(tp + slen, bytes, 4);
+
+	    if (num_bytes == -1)	// not enough coordinates
 		return -1;
 	    mouse_col = 128 * (bytes[0] - ' ' - 1) + bytes[1] - ' ' - 1;
 	    mouse_row = 128 * (bytes[2] - ' ' - 1) + bytes[3] - ' ' - 1;
@@ -4992,695 +4990,9 @@ check_termcode(
 		|| key_name[0] == KS_SGR_MOUSE
 		|| key_name[0] == KS_SGR_MOUSE_RELEASE)
 	{
-	    is_click = is_drag = FALSE;
-
-# if !defined(UNIX) || defined(FEAT_MOUSE_XTERM) || defined(FEAT_GUI) \
-	    || defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE)
-	    if (key_name[0] == KS_MOUSE
-#  ifdef FEAT_MOUSE_GPM
-		    || key_name[0] == KS_GPM_MOUSE
-#  endif
-	       )
-	    {
-		/*
-		 * For xterm we get "<t_mouse>scr", where
-		 *  s == encoded button state:
-		 *	   0x20 = left button down
-		 *	   0x21 = middle button down
-		 *	   0x22 = right button down
-		 *	   0x23 = any button release
-		 *	   0x60 = button 4 down (scroll wheel down)
-		 *	   0x61 = button 5 down (scroll wheel up)
-		 *	add 0x04 for SHIFT
-		 *	add 0x08 for ALT
-		 *	add 0x10 for CTRL
-		 *	add 0x20 for mouse drag (0x40 is drag with left button)
-		 *	add 0x40 for mouse move (0x80 is move, 0x81 too)
-		 *		 0x43 (drag + release) is also move
-		 *  c == column + ' ' + 1 == column + 33
-		 *  r == row + ' ' + 1 == row + 33
-		 *
-		 * The coordinates are passed on through global variables.
-		 * Ugly, but this avoids trouble with mouse clicks at an
-		 * unexpected moment and allows for mapping them.
-		 */
-		for (;;)
-		{
-#  ifdef FEAT_GUI
-		    if (gui.in_use)
-		    {
-			/* GUI uses more bits for columns > 223 */
-			num_bytes = get_bytes_from_buf(tp + slen, bytes, 5);
-			if (num_bytes == -1)	/* not enough coordinates */
-			    return -1;
-			mouse_code = bytes[0];
-			mouse_col = 128 * (bytes[1] - ' ' - 1)
-							 + bytes[2] - ' ' - 1;
-			mouse_row = 128 * (bytes[3] - ' ' - 1)
-							 + bytes[4] - ' ' - 1;
-		    }
-		    else
-#  endif
-		    {
-			num_bytes = get_bytes_from_buf(tp + slen, bytes, 3);
-			if (num_bytes == -1)	/* not enough coordinates */
-			    return -1;
-			mouse_code = bytes[0];
-			mouse_col = bytes[1] - ' ' - 1;
-			mouse_row = bytes[2] - ' ' - 1;
-		    }
-		    slen += num_bytes;
-
-		    /* If the following bytes is also a mouse code and it has
-		     * the same code, dump this one and get the next.  This
-		     * makes dragging a whole lot faster. */
-#  ifdef FEAT_GUI
-		    if (gui.in_use)
-			j = 3;
-		    else
-#  endif
-			j = termcodes[idx].len;
-		    if (STRNCMP(tp, tp + slen, (size_t)j) == 0
-			    && tp[slen + j] == mouse_code
-			    && tp[slen + j + 1] != NUL
-			    && tp[slen + j + 2] != NUL
-#  ifdef FEAT_GUI
-			    && (!gui.in_use
-				|| (tp[slen + j + 3] != NUL
-					&& tp[slen + j + 4] != NUL))
-#  endif
-			    )
-			slen += j;
-		    else
-			break;
-		}
-	    }
-
-	    if (key_name[0] == KS_URXVT_MOUSE
-		|| key_name[0] == KS_SGR_MOUSE
-		|| key_name[0] == KS_SGR_MOUSE_RELEASE)
-	    {
-		/* URXVT 1015 mouse reporting mode:
-		 * Almost identical to xterm mouse mode, except the values
-		 * are decimal instead of bytes.
-		 *
-		 * \033[%d;%d;%dM
-		 *	       ^-- row
-		 *	    ^----- column
-		 *	 ^-------- code
-		 *
-		 * SGR 1006 mouse reporting mode:
-		 * Almost identical to xterm mouse mode, except the values
-		 * are decimal instead of bytes.
-		 *
-		 * \033[<%d;%d;%dM
-		 *	       ^-- row
-		 *	    ^----- column
-		 *	 ^-------- code
-		 *
-		 * \033[<%d;%d;%dm        : mouse release event
-		 *	       ^-- row
-		 *	    ^----- column
-		 *	 ^-------- code
-		 */
-		p = modifiers_start;
-		if (p == NULL)
-		    return -1;
-
-		mouse_code = getdigits(&p);
-		if (*p++ != ';')
-		    return -1;
-
-		/* when mouse reporting is SGR, add 32 to mouse code */
-		if (key_name[0] == KS_SGR_MOUSE
-				    || key_name[0] == KS_SGR_MOUSE_RELEASE)
-		    mouse_code += 32;
-
-		if (key_name[0] == KS_SGR_MOUSE_RELEASE)
-		    mouse_code |= MOUSE_RELEASE;
-
-		mouse_col = getdigits(&p) - 1;
-		if (*p++ != ';')
-		    return -1;
-
-		mouse_row = getdigits(&p) - 1;
-
-		/* The modifiers were the mouse coordinates, not the
-		 * modifier keys (alt/shift/ctrl/meta) state. */
-		modifiers = 0;
-	    }
-
-	if (key_name[0] == KS_MOUSE
-#  ifdef FEAT_MOUSE_GPM
-	    || key_name[0] == KS_GPM_MOUSE
-#  endif
-#  ifdef FEAT_MOUSE_URXVT
-	    || key_name[0] == KS_URXVT_MOUSE
-#  endif
-	    || key_name[0] == KS_SGR_MOUSE
-	    || key_name[0] == KS_SGR_MOUSE_RELEASE)
-	{
-#  if !defined(MSWIN)
-		/*
-		 * Handle mouse events.
-		 * Recognize the xterm mouse wheel, but not in the GUI, the
-		 * Linux console with GPM and the MS-DOS or Win32 console
-		 * (multi-clicks use >= 0x60).
-		 */
-		if (mouse_code >= MOUSEWHEEL_LOW
-#   ifdef FEAT_GUI
-			&& !gui.in_use
-#   endif
-#   ifdef FEAT_MOUSE_GPM
-			&& key_name[0] != KS_GPM_MOUSE
-#   endif
-			)
-		{
-#   if defined(UNIX) && defined(FEAT_MOUSE_TTY)
-		    if (use_xterm_mouse() > 1 && mouse_code >= 0x80)
-			/* mouse-move event, using MOUSE_DRAG works */
-			mouse_code = MOUSE_DRAG;
-		    else
-#   endif
-			/* Keep the mouse_code before it's changed, so that we
-			 * remember that it was a mouse wheel click. */
-			wheel_code = mouse_code;
-		}
-#   ifdef FEAT_MOUSE_XTERM
-		else if (held_button == MOUSE_RELEASE
-#    ifdef FEAT_GUI
-			&& !gui.in_use
-#    endif
-			&& (mouse_code == 0x23 || mouse_code == 0x24
-			    || mouse_code == 0x40 || mouse_code == 0x41))
-		{
-		    /* Apparently 0x23 and 0x24 are used by rxvt scroll wheel.
-		     * And 0x40 and 0x41 are used by some xterm emulator. */
-		    wheel_code = mouse_code - (mouse_code >= 0x40 ? 0x40 : 0x23)
-							      + MOUSEWHEEL_LOW;
-		}
-#   endif
-
-#   if defined(UNIX) && defined(FEAT_MOUSE_TTY)
-		else if (use_xterm_mouse() > 1)
-		{
-		    if (mouse_code & MOUSE_DRAG_XTERM)
-			mouse_code |= MOUSE_DRAG;
-		}
-#   endif
-#   ifdef FEAT_XCLIPBOARD
-		else if (!(mouse_code & MOUSE_DRAG & ~MOUSE_CLICK_MASK))
-		{
-		    if ((mouse_code & MOUSE_RELEASE) == MOUSE_RELEASE)
-			stop_xterm_trace();
-		    else
-			start_xterm_trace(mouse_code);
-		}
-#   endif
-#  endif
-	    }
-# endif /* !UNIX || FEAT_MOUSE_XTERM */
-# ifdef FEAT_MOUSE_NET
-	    if (key_name[0] == KS_NETTERM_MOUSE)
-	    {
-		int mc, mr;
-
-		/* expect a rather limited sequence like: balancing {
-		 * \033}6,45\r
-		 * '6' is the row, 45 is the column
-		 */
-		p = tp + slen;
-		mr = getdigits(&p);
-		if (*p++ != ',')
-		    return -1;
-		mc = getdigits(&p);
-		if (*p++ != '\r')
-		    return -1;
-
-		mouse_col = mc - 1;
-		mouse_row = mr - 1;
-		mouse_code = MOUSE_LEFT;
-		slen += (int)(p - (tp + slen));
-	    }
-# endif	/* FEAT_MOUSE_NET */
-# ifdef FEAT_MOUSE_JSB
-	    if (key_name[0] == KS_JSBTERM_MOUSE)
-	    {
-		int mult, val, iter, button, status;
-
-		/* JSBTERM Input Model
-		 * \033[0~zw uniq escape sequence
-		 * (L-x)  Left button pressed - not pressed x not reporting
-		 * (M-x)  Middle button pressed - not pressed x not reporting
-		 * (R-x)  Right button pressed - not pressed x not reporting
-		 * (SDmdu)  Single , Double click, m mouse move d button down
-		 *						   u button up
-		 *  ###   X cursor position padded to 3 digits
-		 *  ###   Y cursor position padded to 3 digits
-		 * (s-x)  SHIFT key pressed - not pressed x not reporting
-		 * (c-x)  CTRL key pressed - not pressed x not reporting
-		 * \033\\ terminating sequence
-		 */
-
-		p = tp + slen;
-		button = mouse_code = 0;
-		switch (*p++)
-		{
-		    case 'L': button = 1; break;
-		    case '-': break;
-		    case 'x': break; /* ignore sequence */
-		    default:  return -1; /* Unknown Result */
-		}
-		switch (*p++)
-		{
-		    case 'M': button |= 2; break;
-		    case '-': break;
-		    case 'x': break; /* ignore sequence */
-		    default:  return -1; /* Unknown Result */
-		}
-		switch (*p++)
-		{
-		    case 'R': button |= 4; break;
-		    case '-': break;
-		    case 'x': break; /* ignore sequence */
-		    default:  return -1; /* Unknown Result */
-		}
-		status = *p++;
-		for (val = 0, mult = 100, iter = 0; iter < 3; iter++,
-							      mult /= 10, p++)
-		    if (*p >= '0' && *p <= '9')
-			val += (*p - '0') * mult;
-		    else
-			return -1;
-		mouse_col = val;
-		for (val = 0, mult = 100, iter = 0; iter < 3; iter++,
-							      mult /= 10, p++)
-		    if (*p >= '0' && *p <= '9')
-			val += (*p - '0') * mult;
-		    else
-			return -1;
-		mouse_row = val;
-		switch (*p++)
-		{
-		    case 's': button |= 8; break;  /* SHIFT key Pressed */
-		    case '-': break;  /* Not Pressed */
-		    case 'x': break;  /* Not Reporting */
-		    default:  return -1; /* Unknown Result */
-		}
-		switch (*p++)
-		{
-		    case 'c': button |= 16; break;  /* CTRL key Pressed */
-		    case '-': break;  /* Not Pressed */
-		    case 'x': break;  /* Not Reporting */
-		    default:  return -1; /* Unknown Result */
-		}
-		if (*p++ != '\033')
-		    return -1;
-		if (*p++ != '\\')
-		    return -1;
-		switch (status)
-		{
-		    case 'D': /* Double Click */
-		    case 'S': /* Single Click */
-			if (button & 1) mouse_code |= MOUSE_LEFT;
-			if (button & 2) mouse_code |= MOUSE_MIDDLE;
-			if (button & 4) mouse_code |= MOUSE_RIGHT;
-			if (button & 8) mouse_code |= MOUSE_SHIFT;
-			if (button & 16) mouse_code |= MOUSE_CTRL;
-			break;
-		    case 'm': /* Mouse move */
-			if (button & 1) mouse_code |= MOUSE_LEFT;
-			if (button & 2) mouse_code |= MOUSE_MIDDLE;
-			if (button & 4) mouse_code |= MOUSE_RIGHT;
-			if (button & 8) mouse_code |= MOUSE_SHIFT;
-			if (button & 16) mouse_code |= MOUSE_CTRL;
-			if ((button & 7) != 0)
-			{
-			    held_button = mouse_code;
-			    mouse_code |= MOUSE_DRAG;
-			}
-			is_drag = TRUE;
-			showmode();
-			break;
-		    case 'd': /* Button Down */
-			if (button & 1) mouse_code |= MOUSE_LEFT;
-			if (button & 2) mouse_code |= MOUSE_MIDDLE;
-			if (button & 4) mouse_code |= MOUSE_RIGHT;
-			if (button & 8) mouse_code |= MOUSE_SHIFT;
-			if (button & 16) mouse_code |= MOUSE_CTRL;
-			break;
-		    case 'u': /* Button Up */
-			if (button & 1)
-			    mouse_code |= MOUSE_LEFT | MOUSE_RELEASE;
-			if (button & 2)
-			    mouse_code |= MOUSE_MIDDLE | MOUSE_RELEASE;
-			if (button & 4)
-			    mouse_code |= MOUSE_RIGHT | MOUSE_RELEASE;
-			if (button & 8)
-			    mouse_code |= MOUSE_SHIFT;
-			if (button & 16)
-			    mouse_code |= MOUSE_CTRL;
-			break;
-		    default: return -1; /* Unknown Result */
-		}
-
-		slen += (p - (tp + slen));
-	    }
-# endif /* FEAT_MOUSE_JSB */
-# ifdef FEAT_MOUSE_DEC
-	    if (key_name[0] == KS_DEC_MOUSE)
-	    {
-	       /* The DEC Locator Input Model
-		* Netterm delivers the code sequence:
-		*  \033[2;4;24;80&w  (left button down)
-		*  \033[3;0;24;80&w  (left button up)
-		*  \033[6;1;24;80&w  (right button down)
-		*  \033[7;0;24;80&w  (right button up)
-		* CSI Pe ; Pb ; Pr ; Pc ; Pp & w
-		* Pe is the event code
-		* Pb is the button code
-		* Pr is the row coordinate
-		* Pc is the column coordinate
-		* Pp is the third coordinate (page number)
-		* Pe, the event code indicates what event caused this report
-		*    The following event codes are defined:
-		*    0 - request, the terminal received an explicit request
-		*	 for a locator report, but the locator is unavailable
-		*    1 - request, the terminal received an explicit request
-		*	 for a locator report
-		*    2 - left button down
-		*    3 - left button up
-		*    4 - middle button down
-		*    5 - middle button up
-		*    6 - right button down
-		*    7 - right button up
-		*    8 - fourth button down
-		*    9 - fourth button up
-		*    10 - locator outside filter rectangle
-		* Pb, the button code, ASCII decimal 0-15 indicating which
-		*   buttons are down if any. The state of the four buttons
-		*   on the locator correspond to the low four bits of the
-		*   decimal value,
-		*   "1" means button depressed
-		*   0 - no buttons down,
-		*   1 - right,
-		*   2 - middle,
-		*   4 - left,
-		*   8 - fourth
-		* Pr is the row coordinate of the locator position in the page,
-		*   encoded as an ASCII decimal value.
-		*   If Pr is omitted, the locator position is undefined
-		*   (outside the terminal window for example).
-		* Pc is the column coordinate of the locator position in the
-		*   page, encoded as an ASCII decimal value.
-		*   If Pc is omitted, the locator position is undefined
-		*   (outside the terminal window for example).
-		* Pp is the page coordinate of the locator position
-		*   encoded as an ASCII decimal value.
-		*   The page coordinate may be omitted if the locator is on
-		*   page one (the default).  We ignore it anyway.
-		*/
-		int Pe, Pb, Pr, Pc;
-
-		p = tp + slen;
-
-		/* get event status */
-		Pe = getdigits(&p);
-		if (*p++ != ';')
-		    return -1;
-
-		/* get button status */
-		Pb = getdigits(&p);
-		if (*p++ != ';')
-		    return -1;
-
-		/* get row status */
-		Pr = getdigits(&p);
-		if (*p++ != ';')
-		    return -1;
-
-		/* get column status */
-		Pc = getdigits(&p);
-
-		/* the page parameter is optional */
-		if (*p == ';')
-		{
-		    p++;
-		    (void)getdigits(&p);
-		}
-		if (*p++ != '&')
-		    return -1;
-		if (*p++ != 'w')
-		    return -1;
-
-		mouse_code = 0;
-		switch (Pe)
-		{
-		case  0: return -1; /* position request while unavailable */
-		case  1: /* a response to a locator position request includes
-			    the status of all buttons */
-			 Pb &= 7;   /* mask off and ignore fourth button */
-			 if (Pb & 4)
-			     mouse_code  = MOUSE_LEFT;
-			 if (Pb & 2)
-			     mouse_code  = MOUSE_MIDDLE;
-			 if (Pb & 1)
-			     mouse_code  = MOUSE_RIGHT;
-			 if (Pb)
-			 {
-			     held_button = mouse_code;
-			     mouse_code |= MOUSE_DRAG;
-			     WantQueryMouse = TRUE;
-			 }
-			 is_drag = TRUE;
-			 showmode();
-			 break;
-		case  2: mouse_code = MOUSE_LEFT;
-			 WantQueryMouse = TRUE;
-			 break;
-		case  3: mouse_code = MOUSE_RELEASE | MOUSE_LEFT;
-			 break;
-		case  4: mouse_code = MOUSE_MIDDLE;
-			 WantQueryMouse = TRUE;
-			 break;
-		case  5: mouse_code = MOUSE_RELEASE | MOUSE_MIDDLE;
-			 break;
-		case  6: mouse_code = MOUSE_RIGHT;
-			 WantQueryMouse = TRUE;
-			 break;
-		case  7: mouse_code = MOUSE_RELEASE | MOUSE_RIGHT;
-			 break;
-		case  8: return -1; /* fourth button down */
-		case  9: return -1; /* fourth button up */
-		case 10: return -1; /* mouse outside of filter rectangle */
-		default: return -1; /* should never occur */
-		}
-
-		mouse_col = Pc - 1;
-		mouse_row = Pr - 1;
-
-		slen += (int)(p - (tp + slen));
-	    }
-# endif /* FEAT_MOUSE_DEC */
-# ifdef FEAT_MOUSE_PTERM
-	    if (key_name[0] == KS_PTERM_MOUSE)
-	    {
-		int button, num_clicks, action;
-
-		p = tp + slen;
-
-		action = getdigits(&p);
-		if (*p++ != ';')
-		    return -1;
-
-		mouse_row = getdigits(&p);
-		if (*p++ != ';')
-		    return -1;
-		mouse_col = getdigits(&p);
-		if (*p++ != ';')
-		    return -1;
-
-		button = getdigits(&p);
-		mouse_code = 0;
-
-		switch (button)
-		{
-		    case 4: mouse_code = MOUSE_LEFT; break;
-		    case 1: mouse_code = MOUSE_RIGHT; break;
-		    case 2: mouse_code = MOUSE_MIDDLE; break;
-		    default: return -1;
-		}
-
-		switch (action)
-		{
-		    case 31: /* Initial press */
-			if (*p++ != ';')
-			    return -1;
-
-			num_clicks = getdigits(&p); /* Not used */
-			break;
-
-		    case 32: /* Release */
-			mouse_code |= MOUSE_RELEASE;
-			break;
-
-		    case 33: /* Drag */
-			held_button = mouse_code;
-			mouse_code |= MOUSE_DRAG;
-			break;
-
-		    default:
-			return -1;
-		}
-
-		if (*p++ != 't')
-		    return -1;
-
-		slen += (p - (tp + slen));
-	    }
-# endif /* FEAT_MOUSE_PTERM */
-
-	    /* Interpret the mouse code */
-	    current_button = (mouse_code & MOUSE_CLICK_MASK);
-	    if (current_button == MOUSE_RELEASE
-# ifdef FEAT_MOUSE_XTERM
-		    && wheel_code == 0
-# endif
-		    )
-	    {
-		/*
-		 * If we get a mouse drag or release event when
-		 * there is no mouse button held down (held_button ==
-		 * MOUSE_RELEASE), produce a K_IGNORE below.
-		 * (can happen when you hold down two buttons
-		 * and then let them go, or click in the menu bar, but not
-		 * on a menu, and drag into the text).
-		 */
-		if ((mouse_code & MOUSE_DRAG) == MOUSE_DRAG)
-		    is_drag = TRUE;
-		current_button = held_button;
-	    }
-	    else if (wheel_code == 0)
-	    {
-# ifdef CHECK_DOUBLE_CLICK
-#  ifdef FEAT_MOUSE_GPM
-		/*
-		 * Only for Unix, when GUI not active, we handle
-		 * multi-clicks here, but not for GPM mouse events.
-		 */
-#   ifdef FEAT_GUI
-		if (key_name[0] != KS_GPM_MOUSE && !gui.in_use)
-#   else
-		if (key_name[0] != KS_GPM_MOUSE)
-#   endif
-#  else
-#   ifdef FEAT_GUI
-		if (!gui.in_use)
-#   endif
-#  endif
-		{
-		    /*
-		     * Compute the time elapsed since the previous mouse click.
-		     */
-		    gettimeofday(&mouse_time, NULL);
-		    if (orig_mouse_time.tv_sec == 0)
-		    {
-			/*
-			 * Avoid computing the difference between mouse_time
-			 * and orig_mouse_time for the first click, as the
-			 * difference would be huge and would cause
-			 * multiplication overflow.
-			 */
-			timediff = p_mouset;
-		    }
-		    else
-		    {
-			timediff = (mouse_time.tv_usec
-					     - orig_mouse_time.tv_usec) / 1000;
-			if (timediff < 0)
-			    --orig_mouse_time.tv_sec;
-			timediff += (mouse_time.tv_sec
-					      - orig_mouse_time.tv_sec) * 1000;
-		    }
-		    orig_mouse_time = mouse_time;
-		    if (mouse_code == orig_mouse_code
-			    && timediff < p_mouset
-			    && orig_num_clicks != 4
-			    && orig_mouse_col == mouse_col
-			    && orig_mouse_row == mouse_row
-			    && ((orig_topline == curwin->w_topline
-#ifdef FEAT_DIFF
-				    && orig_topfill == curwin->w_topfill
-#endif
-				)
-				/* Double click in tab pages line also works
-				 * when window contents changes. */
-				|| (mouse_row == 0 && firstwin->w_winrow > 0))
-			    )
-			++orig_num_clicks;
-		    else
-			orig_num_clicks = 1;
-		    orig_mouse_col = mouse_col;
-		    orig_mouse_row = mouse_row;
-		    orig_topline = curwin->w_topline;
-#ifdef FEAT_DIFF
-		    orig_topfill = curwin->w_topfill;
-#endif
-		}
-#  if defined(FEAT_GUI) || defined(FEAT_MOUSE_GPM)
-		else
-		    orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code);
-#  endif
-# else
-		orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code);
-# endif
-		is_click = TRUE;
-		orig_mouse_code = mouse_code;
-	    }
-	    if (!is_drag)
-		held_button = mouse_code & MOUSE_CLICK_MASK;
-
-	    /*
-	     * Translate the actual mouse event into a pseudo mouse event.
-	     * First work out what modifiers are to be used.
-	     */
-	    if (orig_mouse_code & MOUSE_SHIFT)
-		modifiers |= MOD_MASK_SHIFT;
-	    if (orig_mouse_code & MOUSE_CTRL)
-		modifiers |= MOD_MASK_CTRL;
-	    if (orig_mouse_code & MOUSE_ALT)
-		modifiers |= MOD_MASK_ALT;
-	    if (orig_num_clicks == 2)
-		modifiers |= MOD_MASK_2CLICK;
-	    else if (orig_num_clicks == 3)
-		modifiers |= MOD_MASK_3CLICK;
-	    else if (orig_num_clicks == 4)
-		modifiers |= MOD_MASK_4CLICK;
-
-	    /* Work out our pseudo mouse event. Note that MOUSE_RELEASE gets
-	     * added, then it's not mouse up/down. */
-	    key_name[0] = KS_EXTRA;
-	    if (wheel_code != 0
-			      && (wheel_code & MOUSE_RELEASE) != MOUSE_RELEASE)
-	    {
-		if (wheel_code & MOUSE_CTRL)
-		    modifiers |= MOD_MASK_CTRL;
-		if (wheel_code & MOUSE_ALT)
-		    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,
-							   is_click, is_drag);
-
-	    /* Make sure the mouse position is valid.  Some terminals may
-	     * return weird values. */
-	    if (mouse_col >= Columns)
-		mouse_col = Columns - 1;
-	    if (mouse_row >= Rows)
-		mouse_row = Rows - 1;
+	    if (check_termcode_mouse(tp, &slen, key_name, modifiers_start, idx,
+							     &modifiers) == -1)
+		return -1;
 	}
 #endif /* FEAT_MOUSE */
 
@@ -5707,8 +5019,8 @@ check_termcode(
 	else if (key_name[0] == (int)KS_MENU)
 	{
 	    long_u	val;
-
-	    num_bytes = get_long_from_buf(tp + slen, &val);
+	    int		num_bytes = get_long_from_buf(tp + slen, &val);
+
 	    if (num_bytes == -1)
 		return -1;
 	    current_menu = (vimmenu_T *)val;
@@ -5726,8 +5038,10 @@ check_termcode(
 # ifdef FEAT_GUI_TABLINE
 	else if (key_name[0] == (int)KS_TABLINE)
 	{
-	    /* Selecting tabline tab or using its menu. */
-	    num_bytes = get_bytes_from_buf(tp + slen, bytes, 1);
+	    // Selecting tabline tab or using its menu.
+	    char_u	bytes[6];
+	    int		num_bytes = get_bytes_from_buf(tp + slen, bytes, 1);
+
 	    if (num_bytes == -1)
 		return -1;
 	    current_tab = (int)bytes[0];
@@ -5737,8 +5051,10 @@ check_termcode(
 	}
 	else if (key_name[0] == (int)KS_TABMENU)
 	{
-	    /* Selecting tabline tab or using its menu. */
-	    num_bytes = get_bytes_from_buf(tp + slen, bytes, 2);
+	    // Selecting tabline tab or using its menu.
+	    char_u	bytes[6];
+	    int		num_bytes = get_bytes_from_buf(tp + slen, bytes, 2);
+
 	    if (num_bytes == -1)
 		return -1;
 	    current_tab = (int)bytes[0];
@@ -5750,6 +5066,8 @@ check_termcode(
 	else if (key_name[0] == (int)KS_VER_SCROLLBAR)
 	{
 	    long_u	val;
+	    char_u	bytes[6];
+	    int		num_bytes;
 
 	    /* Get the last scrollbar event in the queue of the same type */
 	    j = 0;
@@ -5778,6 +5096,7 @@ check_termcode(
 	else if (key_name[0] == (int)KS_HOR_SCROLLBAR)
 	{
 	    long_u	val;
+	    int		num_bytes;
 
 	    /* Get the last horiz. scrollbar event in the queue */
 	    j = 0;
--- a/src/version.c
+++ b/src/version.c
@@ -758,6 +758,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    2070,
+/**/
     2069,
 /**/
     2068,