feat(ui): support grid=0 in nvim_input_mouse #32535

Problem:
Multigrid UIs have to find out which window to send the input by using
the Nvim focus rules, which are not fully documented.

Furthermore,`getmousepos()` has several problems when multigrid is
enabled, with the main one being that screenrow and screencol are window
relative instead of screen relative, due to the fact that the UI don't
send any absolute coordinates.

Solution:
Allow passing 0 as grid to `nvim_input_mouse`, with absolute
coordinates, which lets nvim determine the actual window to send the
mouse input to. This works as long as nvim is in charge of the window
positioning. If the UI repositions or resizes the windows, it can still
pass the grid it determines like before.
This commit is contained in:
fredizzimo
2025-09-14 00:57:04 +03:00
committed by GitHub
parent c1648cf820
commit 8ae9a44d38
15 changed files with 2388 additions and 2162 deletions

View File

@@ -382,7 +382,8 @@ Integer nvim_input(uint64_t channel_id, String keys)
/// The same specifiers are used as for a key press, except
/// that the "-" separator is optional, so "C-A-", "c-a"
/// and "CA" can all be used to specify Ctrl+Alt+click.
/// @param grid Grid number if the client uses |ui-multigrid|, else 0.
/// @param grid Grid number (used by |ui-multigrid| client), or 0 to let Nvim decide positioning of
/// windows. For more information, see [dev-ui-multigrid]
/// @param row Mouse row-position (zero-based, like redraw events)
/// @param col Mouse column-position (zero-based, like redraw events)
/// @param[out] err Error details, if any

View File

@@ -2013,7 +2013,7 @@ static void getchar_common(typval_T *argvars, typval_T *rettv, bool allow_number
int winnr = 1;
// Find the window at the mouse coordinates and compute the
// text position.
win_T *const win = mouse_find_win(&grid, &row, &col);
win_T *const win = mouse_find_win_inner(&grid, &row, &col);
if (win == NULL) {
return;
}

View File

@@ -235,7 +235,7 @@ static int get_fpos_of_mouse(pos_T *mpos)
}
// find the window where the row is in
win_T *wp = mouse_find_win(&grid, &row, &col);
win_T *wp = mouse_find_win_inner(&grid, &row, &col);
if (wp == NULL) {
return IN_UNKNOWN;
}
@@ -663,7 +663,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, int count, bool fixindent)
int click_grid = mouse_grid;
int click_row = mouse_row;
int click_col = mouse_col;
win_T *wp = mouse_find_win(&click_grid, &click_row, &click_col);
win_T *wp = mouse_find_win_inner(&click_grid, &click_row, &click_col);
if (wp == NULL) {
return false;
}
@@ -1093,7 +1093,7 @@ void ins_mousescroll(int dir)
int grid = mouse_grid;
int row = mouse_row;
int col = mouse_col;
curwin = mouse_find_win(&grid, &row, &col);
curwin = mouse_find_win_inner(&grid, &row, &col);
if (curwin == NULL) {
curwin = old_curwin;
return;
@@ -1263,8 +1263,8 @@ retnomove:
}
// find the window where the row is in and adjust "row" and "col" to be
// relative to top-left of the window
win_T *wp = mouse_find_win(&grid, &row, &col);
// relative to top-left of the window inner area
win_T *wp = mouse_find_win_inner(&grid, &row, &col);
if (wp == NULL) {
return IN_UNKNOWN;
}
@@ -1581,7 +1581,7 @@ void nv_mousescroll(cmdarg_T *cap)
int grid = mouse_grid;
int row = mouse_row;
int col = mouse_col;
curwin = mouse_find_win(&grid, &row, &col);
curwin = mouse_find_win_inner(&grid, &row, &col);
if (curwin == NULL) {
curwin = old_curwin;
return;
@@ -1695,10 +1695,10 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump)
}
/// Find the window at "grid" position "*rowp" and "*colp". The positions are
/// updated to become relative to the top-left of the window.
/// updated to become relative to the top-left of the window inner area.
///
/// @return NULL when something is wrong.
win_T *mouse_find_win(int *gridp, int *rowp, int *colp)
win_T *mouse_find_win_inner(int *gridp, int *rowp, int *colp)
{
win_T *wp_grid = mouse_find_grid_win(gridp, rowp, colp);
if (wp_grid) {
@@ -1740,6 +1740,20 @@ win_T *mouse_find_win(int *gridp, int *rowp, int *colp)
return NULL;
}
/// Find the window at "grid" position "*rowp" and "*colp". The positions are
/// updated to become relative to the top-left of the window.
///
/// @return NULL when something is wrong.
win_T *mouse_find_win_outer(int *gridp, int *rowp, int *colp)
{
win_T *wp = mouse_find_win_inner(gridp, rowp, colp);
if (wp) {
*rowp += wp->w_winrow_off;
*colp += wp->w_wincol_off;
}
return wp;
}
static win_T *mouse_find_grid_win(int *gridp, int *rowp, int *colp)
{
if (*gridp == msg_grid.handle) {
@@ -1755,18 +1769,26 @@ static win_T *mouse_find_grid_win(int *gridp, int *rowp, int *colp)
}
} else if (*gridp == 0) {
ScreenGrid *grid = ui_comp_mouse_focus(*rowp, *colp);
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (&wp->w_grid_alloc != grid) {
continue;
}
if (grid == &pum_grid) {
*gridp = grid->handle;
*rowp -= grid->comp_row + wp->w_grid.row_offset;
*colp -= grid->comp_col + wp->w_grid.col_offset;
return wp;
*rowp -= grid->comp_row;
*colp -= grid->comp_col;
// The popup menu doesn't have a window, so return NULL
return NULL;
} else {
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (&wp->w_grid_alloc != grid) {
continue;
}
*gridp = grid->handle;
*rowp -= wp->w_winrow + wp->w_grid.row_offset;
*colp -= wp->w_wincol + wp->w_grid.col_offset;
return wp;
}
}
// no float found, click on the default grid
// TODO(bfredl): grid can be &pum_grid, allow select pum items by mouse?
// No grid found, return the default grid. With multigrid this happens for split separators for
// example.
*gridp = DEFAULT_GRID_HANDLE;
}
return NULL;
@@ -1877,7 +1899,7 @@ static void mouse_check_grid(colnr_T *vcolp, int *flagsp)
int click_col = mouse_col;
// XXX: this doesn't change click_grid if it is 1, even with multigrid
if (mouse_find_win(&click_grid, &click_row, &click_col) != curwin
if (mouse_find_win_inner(&click_grid, &click_row, &click_col) != curwin
// Only use vcols[] after the window was redrawn. Mainly matters
// for tests, a user would not click before redrawing.
|| curwin->w_redr_type != 0) {
@@ -1931,7 +1953,7 @@ void f_getmousepos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
tv_dict_add_nr(d, S_LEN("screenrow"), (varnumber_T)mouse_row + 1);
tv_dict_add_nr(d, S_LEN("screencol"), (varnumber_T)mouse_col + 1);
win_T *wp = mouse_find_win(&grid, &row, &col);
win_T *wp = mouse_find_win_inner(&grid, &row, &col);
if (wp != NULL) {
int height = wp->w_height + wp->w_hsep_height + wp->w_status_height;
// The height is adjusted by 1 when there is a bottom border. This is not

View File

@@ -37,6 +37,7 @@
#include "nvim/memory_defs.h"
#include "nvim/menu.h"
#include "nvim/message.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/option_defs.h"
@@ -1325,6 +1326,10 @@ static void pum_position_at_mouse(int min_width)
int col = mouse_col;
pum_win_row_offset = 0;
pum_win_col_offset = 0;
if (ui_has(kUIMultigrid) && grid == 0) {
mouse_find_win_outer(&grid, &row, &col);
}
if (grid > 1) {
win_T *wp = get_win_by_grid_handle(grid);
if (wp != NULL) {
@@ -1395,17 +1400,27 @@ static void pum_position_at_mouse(int min_width)
/// Select the pum entry at the mouse position.
static void pum_select_mouse_pos(void)
{
if (mouse_grid == pum_grid.handle) {
pum_selected = mouse_row;
int grid = mouse_grid;
int row = mouse_row;
int col = mouse_col;
if (grid == 0) {
mouse_find_win_outer(&grid, &row, &col);
}
if (grid == pum_grid.handle) {
pum_selected = row;
return;
} else if (mouse_grid != pum_anchor_grid
|| mouse_col < pum_left_col - pum_win_col_offset
|| mouse_col >= pum_right_col - pum_win_col_offset) {
}
if (grid != pum_anchor_grid
|| col < pum_left_col - pum_win_col_offset
|| col >= pum_right_col - pum_win_col_offset) {
pum_selected = -1;
return;
}
int idx = mouse_row - (pum_row - pum_win_row_offset);
int idx = row - (pum_row - pum_win_row_offset);
if (idx < 0 || idx >= pum_height) {
pum_selected = -1;

View File

@@ -1907,7 +1907,7 @@ static bool send_mouse_event(Terminal *term, int c)
int row = mouse_row;
int col = mouse_col;
int grid = mouse_grid;
win_T *mouse_win = mouse_find_win(&grid, &row, &col);
win_T *mouse_win = mouse_find_win_inner(&grid, &row, &col);
if (mouse_win == NULL) {
goto end;
}

View File

@@ -318,6 +318,15 @@ ScreenGrid *ui_comp_mouse_focus(int row, int col)
return grid;
}
}
if (ui_has(kUIMultigrid)) {
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
ScreenGrid *grid = &wp->w_grid_alloc;
if (grid->mouse_enabled && row >= wp->w_winrow && row < wp->w_winrow + grid->rows
&& col >= wp->w_wincol && col < wp->w_wincol + grid->cols) {
return grid;
}
}
}
return NULL;
}

View File

@@ -5187,6 +5187,8 @@ win_T *win_alloc(win_T *after, bool hidden)
new_wp->handle = ++last_win_id;
pmap_put(int)(&window_handles, new_wp->handle, new_wp);
new_wp->w_grid_alloc.mouse_enabled = true;
grid_assign_handle(&new_wp->w_grid_alloc);
// Init w: variables.

View File

@@ -192,7 +192,7 @@ void win_config_float(win_T *wp, WinConfig fconfig)
int row = mouse_row;
int col = mouse_col;
int grid = mouse_grid;
win_T *mouse_win = mouse_find_win(&grid, &row, &col);
win_T *mouse_win = mouse_find_win_inner(&grid, &row, &col);
if (mouse_win != NULL) {
fconfig.relative = kFloatRelativeWindow;
fconfig.row += row;