patch 8.1.2304: cannot get the mouse position when getting a mouse click

Problem:    Cannot get the mouse position when getting a mouse click.
Solution:   Add getmousepos().
diff --git a/src/evalfunc.c b/src/evalfunc.c
index e5be361..945c5f2 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -465,6 +465,7 @@
     {"getline",		1, 2, FEARG_1,	  f_getline},
     {"getloclist",	1, 2, 0,	  f_getloclist},
     {"getmatches",	0, 1, 0,	  f_getmatches},
+    {"getmousepos",	0, 0, 0,	  f_getmousepos},
     {"getpid",		0, 0, 0,	  f_getpid},
     {"getpos",		1, 1, FEARG_1,	  f_getpos},
     {"getqflist",	0, 1, 0,	  f_getqflist},
diff --git a/src/mouse.c b/src/mouse.c
index 3e1741f..f215d3d 100644
--- a/src/mouse.c
+++ b/src/mouse.c
@@ -2822,6 +2822,7 @@
     int		retval = FALSE;
     int		off;
     int		count;
+    char_u	*p;
 
 #ifdef FEAT_RIGHTLEFT
     if (win->w_p_rl)
@@ -2881,6 +2882,11 @@
 	col += row * (win->w_width - off);
 	// add skip column (for long wrapping line)
 	col += win->w_skipcol;
+	// limit to text length plus one
+	p = ml_get_buf(win->w_buffer, lnum, FALSE);
+	count = STRLEN(p);
+	if (col > count)
+	    col = count;
     }
 
     if (!win->w_p_wrap)
@@ -3001,3 +3007,61 @@
     return (int)(ptr - line);
 }
 #endif
+
+#if defined(FEAT_EVAL) || defined(PROTO)
+    void
+f_getmousepos(typval_T *argvars UNUSED, typval_T *rettv)
+{
+    dict_T	*d;
+    win_T	*wp;
+    int		row = mouse_row;
+    int		col = mouse_col;
+    varnumber_T winid = 0;
+    varnumber_T winrow = 0;
+    varnumber_T wincol = 0;
+    varnumber_T line = 0;
+    varnumber_T column = 0;
+
+    if (rettv_dict_alloc(rettv) != OK)
+	return;
+    d = rettv->vval.v_dict;
+
+    dict_add_number(d, "screenrow", (varnumber_T)mouse_row + 1);
+    dict_add_number(d, "screencol", (varnumber_T)mouse_col + 1);
+
+    wp = mouse_find_win(&row, &col, FIND_POPUP);
+    if (wp != NULL)
+    {
+	int	top_off = 0;
+	int	left_off = 0;
+	int	height = wp->w_height + wp->w_status_height;
+
+#ifdef FEAT_TEXT_PROP
+	if (WIN_IS_POPUP(wp))
+	{
+	    top_off = popup_top_extra(wp);
+	    left_off = popup_left_extra(wp);
+	    height = popup_height(wp);
+	}
+#endif
+	if (row < height)
+	{
+	    winid = wp->w_id;
+	    winrow = row + 1;
+	    wincol = col + 1;
+	    row -= top_off;
+	    col -= left_off;
+	    if (row >= 0 && row < wp->w_height && col >= 0 && col < wp->w_width)
+	    {
+		mouse_comp_pos(wp, &row, &col, &line, NULL);
+		column = col + 1;
+	    }
+	}
+    }
+    dict_add_number(d, "winid", winid);
+    dict_add_number(d, "winrow", winrow);
+    dict_add_number(d, "wincol", wincol);
+    dict_add_number(d, "line", line);
+    dict_add_number(d, "column", column);
+}
+#endif
diff --git a/src/popupwin.c b/src/popupwin.c
index ed04645..48c017f 100644
--- a/src/popupwin.c
+++ b/src/popupwin.c
@@ -1047,6 +1047,15 @@
 }
 
 /*
+ * Get the padding plus border at the left.
+ */
+    int
+popup_left_extra(win_T *wp)
+{
+    return wp->w_popup_border[3] + wp->w_popup_padding[3];
+}
+
+/*
  * Return the height of popup window "wp", including border and padding.
  */
     int
@@ -2908,33 +2917,12 @@
 
     argv[2].v_type = VAR_UNKNOWN;
 
-    if (is_mouse_key(c))
-    {
-	int		row = mouse_row - wp->w_winrow;
-	int		col = mouse_col - wp->w_wincol;
-	linenr_T	lnum;
-
-	if (row >= 0 && col >= 0)
-	{
-	    (void)mouse_comp_pos(wp, &row, &col, &lnum, NULL);
-	    set_vim_var_nr(VV_MOUSE_LNUM, lnum);
-	    set_vim_var_nr(VV_MOUSE_COL, col + 1);
-	    set_vim_var_nr(VV_MOUSE_WINID, wp->w_id);
-	}
-    }
-
     // NOTE: The callback might close the popup and make "wp" invalid.
     call_callback(&wp->w_filter_cb, -1, &rettv, 2, argv);
     if (win_valid_popup(wp) && old_lnum != wp->w_cursor.lnum)
 	popup_highlight_curline(wp);
     res = tv_get_number(&rettv);
 
-    if (is_mouse_key(c))
-    {
-	set_vim_var_nr(VV_MOUSE_LNUM, 0);
-	set_vim_var_nr(VV_MOUSE_COL, 0);
-	set_vim_var_nr(VV_MOUSE_WINID, wp->w_id);
-    }
     vim_free(argv[1].vval.v_string);
     clear_tv(&rettv);
     return res;
diff --git a/src/proto/mouse.pro b/src/proto/mouse.pro
index 3faf056..f2a8c38 100644
--- a/src/proto/mouse.pro
+++ b/src/proto/mouse.pro
@@ -17,4 +17,5 @@
 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);
+void f_getmousepos(typval_T *argvars, typval_T *rettv);
 /* vim: set ft=c : */
diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim
index 4f39d87..e7b81da 100644
--- a/src/testdir/test_functions.vim
+++ b/src/testdir/test_functions.vim
@@ -1331,6 +1331,7 @@
   call feedkeys('a', '')
   call assert_equal(char2nr('a'), getchar())
 
+  call setline(1, 'xxxx')
   call test_setmouse(1, 3)
   let v:mouse_win = 9
   let v:mouse_winid = 9
@@ -1342,6 +1343,7 @@
   call assert_equal(win_getid(1), v:mouse_winid)
   call assert_equal(1, v:mouse_lnum)
   call assert_equal(3, v:mouse_col)
+  enew!
 endfunc
 
 func Test_libcall_libcallnr()
diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim
index 44e1819..041d834 100644
--- a/src/testdir/test_popupwin.vim
+++ b/src/testdir/test_popupwin.vim
@@ -2205,42 +2205,106 @@
 
 func Test_popupwin_filter_mouse()
   func MyPopupFilter(winid, c)
-    let g:got_mouse_col = v:mouse_col
-    let g:got_mouse_lnum = v:mouse_lnum
-    let g:got_mouse_winid = v:mouse_winid
+    let g:got_mousepos = getmousepos()
     return 0
   endfunc
 
-  let winid = popup_create(['short', 'long line that will wrap', 'short'], #{
-	\ line: 4,
-	\ col: 8,
+  call setline(1, ['.'->repeat(25)]->repeat(10))
+  let winid = popup_create(['short', 'long line that will wrap', 'other'], #{
+	\ line: 2,
+	\ col: 4,
 	\ maxwidth: 12,
+	\ padding: [],
+	\ border: [],
 	\ filter: 'MyPopupFilter',
 	\ })
   redraw
-  call test_setmouse(4, 8)
-  call feedkeys("\<LeftMouse>", 'xt')
-  call assert_equal(1, g:got_mouse_col)
-  call assert_equal(1, g:got_mouse_lnum)
-  call assert_equal(winid, g:got_mouse_winid)
+  "    123456789012345678901
+  "  1 .....................
+  "  2 ...+--------------+..
+  "  3 ...|              |..
+  "  4 ...| short        |..
+  "  5 ...| long line th |..
+  "  6 ...| at will wrap |..
+  "  7 ...| other        |..
+  "  8 ...|              |..
+  "  9 ...+--------------+..
+  " 10 .....................
+  let tests = []
 
-  call test_setmouse(5, 8)
-  call feedkeys("\<LeftMouse>", 'xt')
-  call assert_equal(1, g:got_mouse_col)
-  call assert_equal(2, g:got_mouse_lnum)
+  func AddItemOutsidePopup(tests, row, col)
+    eval a:tests->add(#{clickrow: a:row, clickcol: a:col, result: #{
+	  \ screenrow: a:row, screencol: a:col,
+	  \ winid: win_getid(), winrow: a:row, wincol: a:col,
+	  \ line: a:row, column: a:col,
+	  \ }})
+  endfunc
+  func AddItemInPopupBorder(tests, winid, row, col)
+    eval a:tests->add(#{clickrow: a:row, clickcol: a:col, result: #{
+	  \ screenrow: a:row, screencol: a:col,
+	  \ winid: a:winid, winrow: a:row - 1, wincol: a:col - 3,
+	  \ line: 0, column: 0,
+	  \ }})
+  endfunc
+  func AddItemInPopupText(tests, winid, row, col, textline, textcol)
+    eval a:tests->add(#{clickrow: a:row, clickcol: a:col, result: #{
+	  \ screenrow: a:row, screencol: a:col,
+	  \ winid: a:winid, winrow: a:row - 1, wincol: a:col - 3,
+	  \ line: a:textline, column: a:textcol,
+	  \ }})
+  endfunc
 
-  call test_setmouse(6, 8)
-  call feedkeys("\<LeftMouse>", 'xt')
-  call assert_equal(13, g:got_mouse_col)
-  call assert_equal(2, g:got_mouse_lnum)
+  " above and below popup
+  for c in range(1, 21)
+    call AddItemOutsidePopup(tests, 1, c)
+    call AddItemOutsidePopup(tests, 10, c)
+  endfor
+  " left and right of popup
+  for r in range(1, 10)
+    call AddItemOutsidePopup(tests, r, 3)
+    call AddItemOutsidePopup(tests, r, 20)
+  endfor
+  " top and bottom in popup
+  for c in range(4, 19)
+    call AddItemInPopupBorder(tests, winid, 2, c)
+    call AddItemInPopupBorder(tests, winid, 3, c)
+    call AddItemInPopupBorder(tests, winid, 8, c)
+    call AddItemInPopupBorder(tests, winid, 9, c)
+  endfor
+  " left and right margin in popup
+  for r in range(2, 9)
+    call AddItemInPopupBorder(tests, winid, r, 4)
+    call AddItemInPopupBorder(tests, winid, r, 5)
+    call AddItemInPopupBorder(tests, winid, r, 18)
+    call AddItemInPopupBorder(tests, winid, r, 19)
+  endfor
+  " text "short"
+  call AddItemInPopupText(tests, winid, 4, 6, 1, 1)
+  call AddItemInPopupText(tests, winid, 4, 10, 1, 5)
+  call AddItemInPopupText(tests, winid, 4, 11, 1, 6)
+  call AddItemInPopupText(tests, winid, 4, 17, 1, 6)
+  " text "long line th"
+  call AddItemInPopupText(tests, winid, 5, 6, 2, 1)
+  call AddItemInPopupText(tests, winid, 5, 10, 2, 5)
+  call AddItemInPopupText(tests, winid, 5, 17, 2, 12)
+  " text "at will wrap"
+  call AddItemInPopupText(tests, winid, 6, 6, 2, 13)
+  call AddItemInPopupText(tests, winid, 6, 10, 2, 17)
+  call AddItemInPopupText(tests, winid, 6, 17, 2, 24)
+  " text "other"
+  call AddItemInPopupText(tests, winid, 7, 6, 3, 1)
+  call AddItemInPopupText(tests, winid, 7, 10, 3, 5)
+  call AddItemInPopupText(tests, winid, 7, 11, 3, 6)
+  call AddItemInPopupText(tests, winid, 7, 17, 3, 6)
 
-  call test_setmouse(7, 20)
-  call feedkeys("\<LeftMouse>", 'xt')
-  call assert_equal(13, g:got_mouse_col)
-  call assert_equal(3, g:got_mouse_lnum)
-  call assert_equal(winid, g:got_mouse_winid)
+  for item in tests
+    call test_setmouse(item.clickrow, item.clickcol)
+    call feedkeys("\<LeftMouse>", 'xt')
+    call assert_equal(item.result, g:got_mousepos)
+  endfor
 
   call popup_close(winid)
+  enew!
   delfunc MyPopupFilter
 endfunc
 
diff --git a/src/version.c b/src/version.c
index 5d97575..b08106c 100644
--- a/src/version.c
+++ b/src/version.c
@@ -742,6 +742,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    2304,
+/**/
     2303,
 /**/
     2302,