diff options
author | Björn Linse <bjorn.linse@gmail.com> | 2019-01-01 12:46:47 +0100 |
---|---|---|
committer | Björn Linse <bjorn.linse@gmail.com> | 2019-01-20 10:32:05 +0100 |
commit | a2be9c7218d7fb431191cd6146fba61bcd1e193e (patch) | |
tree | 6a18ff845b028572d6aa1f35900b571821a2082d /src | |
parent | 8a5c68f6959f8d315adb126ff49fd1970e7e75be (diff) | |
download | rneovim-a2be9c7218d7fb431191cd6146fba61bcd1e193e.tar.gz rneovim-a2be9c7218d7fb431191cd6146fba61bcd1e193e.tar.bz2 rneovim-a2be9c7218d7fb431191cd6146fba61bcd1e193e.zip |
ui: multigrid mouse support
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/api/vim.c | 96 | ||||
-rw-r--r-- | src/nvim/edit.c | 5 | ||||
-rw-r--r-- | src/nvim/eval.c | 3 | ||||
-rw-r--r-- | src/nvim/getchar.c | 11 | ||||
-rw-r--r-- | src/nvim/globals.h | 5 | ||||
-rw-r--r-- | src/nvim/mouse.c | 71 | ||||
-rw-r--r-- | src/nvim/normal.c | 5 | ||||
-rw-r--r-- | src/nvim/os/input.c | 109 | ||||
-rw-r--r-- | src/nvim/terminal.c | 4 | ||||
-rw-r--r-- | src/nvim/ui.c | 1 | ||||
-rw-r--r-- | src/nvim/window.c | 18 |
11 files changed, 253 insertions, 75 deletions
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index ce7ef681ef..7f62333d88 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -198,6 +198,10 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_csi) /// @note |keycodes| like <CR> are translated, so "<" is special. /// To input a literal "<", send <LT>. /// +/// For mouse events use |nvim_input_mouse()|. For back-compat reasons +/// this method supports mouse input as "<LeftMouse><col,row>". This +/// usage is deprecated since API level 6, use the dedicated method instead. +/// /// @param keys to be typed /// @return Number of bytes actually written (can be fewer than /// requested if the buffer becomes full). @@ -207,6 +211,98 @@ Integer nvim_input(String keys) return (Integer)input_enqueue(keys); } +/// Send mouse event from GUI +/// +/// The call is non-blocking. It doesn't wait on any resulting action, but +/// queues the event to be processed soon by the event loop. +/// +/// @note Currently this doesn't support "scripting" multiple mouse events +/// by calling it multiple times in a loop: the intermediate mouse +/// positions will be ignored. It should be used to implement real-time +/// mouse input in a GUI. The deprecated pseudokey form +/// ("<LeftMouse><col,row>") in |nvim_input()| has the same limitiation. +/// +/// @param button Which mouse button, one of "left", "right", "middle" or +/// "wheel". +/// @param action For ordinary buttons, one of "press", "drag" and "release" +/// For the wheel, use "up", "down", "left" and "right". +/// @param modifier String of modifiers each represented by a single char. +/// 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 For a client using |ui-multigrid| pass in the grid number. +/// Other clients should pass in 0 (not 1). +/// @param row row position of mouse (zero-based, like in redraw events) +/// @param col column position of mouse (zero-based, like in redraw events) +void nvim_input_mouse(String button, String action, String modifier, + Integer grid, Integer row, Integer col, Error *err) + FUNC_API_SINCE(6) FUNC_API_ASYNC +{ + if (button.data == NULL || action.data == NULL) { + goto error; + } + + int code = 0; + + if (strequal(button.data, "left")) { + code = KE_LEFTMOUSE; + } else if (strequal(button.data, "middle")) { + code = KE_MIDDLEMOUSE; + } else if (strequal(button.data, "right")) { + code = KE_RIGHTMOUSE; + } else if (strequal(button.data, "wheel")) { + code = KE_MOUSEDOWN; + } else { + goto error; + } + + if (code == KE_MOUSEDOWN) { + if (strequal(action.data, "down")) { + code = KE_MOUSEUP; + } else if (strequal(action.data, "up")) { + code = KE_MOUSEDOWN; + } else if (strequal(action.data, "left")) { + code = KE_MOUSERIGHT; + } else if (strequal(action.data, "right")) { + code = KE_MOUSELEFT; + } else { + goto error; + } + } else { + if (strequal(action.data, "press")) { + // pass + } else if (strequal(action.data, "drag")) { + code += KE_LEFTDRAG - KE_LEFTMOUSE; + } else if (strequal(action.data, "release")) { + code += KE_LEFTRELEASE - KE_LEFTMOUSE; + } else { + goto error; + } + } + + int modmask = 0; + for (size_t i = 0; i < modifier.size; i++) { + char byte = modifier.data[i]; + if (byte == '-') { + continue; + } + int mod = name_to_mod_mask(byte); + if (mod == 0) { + api_set_error(err, kErrorTypeValidation, + "invalid modifier %c", byte); + return; + } + modmask |= mod; + } + + input_enqueue_mouse(code, (uint8_t)modmask, (int)grid, (int)row, (int)col); + return; + +error: + api_set_error(err, kErrorTypeValidation, + "invalid button or action"); +} + /// Replaces terminal codes and |keycodes| (<CR>, <Esc>, ...) in a string with /// the internal representation. /// diff --git a/src/nvim/edit.c b/src/nvim/edit.c index bb3c0ec196..90507cd0a5 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -7943,11 +7943,10 @@ static void ins_mousescroll(int dir) pos_T tpos = curwin->w_cursor; if (mouse_row >= 0 && mouse_col >= 0) { - int row = mouse_row; - int col = mouse_col; + int row = mouse_row, col = mouse_col, grid = mouse_grid; // find the window at the pointer coordinates - win_T *const wp = mouse_find_win(&row, &col); + win_T *wp = mouse_find_win(&grid, &row, &col); if (wp == NULL) { return; } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 85b838e443..596eff5b79 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -9521,6 +9521,7 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (is_mouse_key(n)) { int row = mouse_row; int col = mouse_col; + int grid = mouse_grid; win_T *win; linenr_T lnum; win_T *wp; @@ -9529,7 +9530,7 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (row >= 0 && col >= 0) { /* Find the window at the mouse coordinates and compute the * text position. */ - win = mouse_find_win(&row, &col); + win = mouse_find_win(&grid, &row, &col); if (win == NULL) { return; } diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 53e9846c2d..be3e7bb676 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1187,10 +1187,11 @@ void save_typebuf(void) alloc_typebuf(); } -static int old_char = -1; /* character put back by vungetc() */ -static int old_mod_mask; /* mod_mask for ungotten character */ -static int old_mouse_row; /* mouse_row related to old_char */ -static int old_mouse_col; /* mouse_col related to old_char */ +static int old_char = -1; // character put back by vungetc() +static int old_mod_mask; // mod_mask for ungotten character +static int old_mouse_grid; // mouse_grid related to old_char +static int old_mouse_row; // mouse_row related to old_char +static int old_mouse_col; // mouse_col related to old_char /* @@ -1391,6 +1392,7 @@ int vgetc(void) c = old_char; old_char = -1; mod_mask = old_mod_mask; + mouse_grid = old_mouse_grid; mouse_row = old_mouse_row; mouse_col = old_mouse_col; } else { @@ -1585,6 +1587,7 @@ vungetc ( /* unget one character (can only be done once!) */ { old_char = c; old_mod_mask = mod_mask; + old_mouse_grid = mouse_grid; old_mouse_row = mouse_row; old_mouse_col = mouse_col; } diff --git a/src/nvim/globals.h b/src/nvim/globals.h index cc39e2c112..8a76ca2bee 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -394,9 +394,8 @@ EXTERN bufref_T au_new_curbuf INIT(= { NULL, 0, 0 }); EXTERN buf_T *au_pending_free_buf INIT(= NULL); EXTERN win_T *au_pending_free_win INIT(= NULL); -/* - * Mouse coordinates, set by check_termcode() - */ +// Mouse coordinates, set by handle_mouse_event() +EXTERN int mouse_grid; EXTERN int mouse_row; EXTERN int mouse_col; EXTERN bool mouse_past_bottom INIT(= false); /* mouse below last line */ diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index 887cbde921..22f5497f1e 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -70,6 +70,7 @@ int jump_to_mouse(int flags, bool first; int row = mouse_row; int col = mouse_col; + int grid = mouse_grid; int mouse_char; mouse_past_bottom = false; @@ -125,20 +126,22 @@ retnomove: return IN_UNKNOWN; // find the window where the row is in - wp = mouse_find_win(&row, &col); + wp = mouse_find_win(&grid, &row, &col); if (wp == NULL) { return IN_UNKNOWN; } dragwin = NULL; // winpos and height may change in win_enter()! - if (row >= wp->w_height) { // In (or below) status line + if (grid == DEFAULT_GRID_HANDLE && row >= wp->w_height) { + // In (or below) status line on_status_line = row - wp->w_height + 1; dragwin = wp; } else { on_status_line = 0; } - if (col >= wp->w_width) { // In separator line + if (grid == DEFAULT_GRID_HANDLE && col >= wp->w_width) { + // In separator line on_sep_line = col - wp->w_width + 1; dragwin = wp; } else { @@ -160,12 +163,10 @@ retnomove: && (wp->w_buffer != curwin->w_buffer || (!on_status_line && !on_sep_line - && ( - wp->w_p_rl ? col < wp->w_width - wp->w_p_fdc : - col >= wp->w_p_fdc - + (cmdwin_type == 0 && wp == - curwin ? 0 : 1) - ) + && (wp->w_p_rl + ? col < wp->w_grid.Columns - wp->w_p_fdc + : col >= wp->w_p_fdc + (cmdwin_type == 0 && wp == curwin + ? 0 : 1)) && (flags & MOUSE_MAY_STOP_VIS)))) { end_visual_mode(); redraw_curbuf_later(INVERTED); // delete the inversion @@ -257,7 +258,7 @@ retnomove: ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP); redraw_later(VALID); row = 0; - } else if (row >= curwin->w_height) { + } else if (row >= curwin->w_grid.Rows) { count = 0; for (first = true; curwin->w_topline < curbuf->b_ml.ml_line_count; ) { if (curwin->w_topfill > 0) { @@ -266,7 +267,7 @@ retnomove: count += plines(curwin->w_topline); } - if (!first && count > row - curwin->w_height + 1) { + if (!first && count > row - curwin->w_grid.Rows + 1) { break; } first = false; @@ -288,7 +289,7 @@ retnomove: redraw_later(VALID); curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP); - row = curwin->w_height - 1; + row = curwin->w_grid.Rows - 1; } else if (row == 0) { // When dragging the mouse, while the text has been scrolled up as // far as it goes, moving the mouse in the top line should scroll @@ -303,7 +304,7 @@ retnomove: } // Check for position outside of the fold column. - if (curwin->w_p_rl ? col < curwin->w_width - curwin->w_p_fdc : + if (curwin->w_p_rl ? col < curwin->w_grid.Columns - curwin->w_p_fdc : col >= curwin->w_p_fdc + (cmdwin_type == 0 ? 0 : 1)) { mouse_char = ' '; } @@ -369,8 +370,9 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump) int off; int count; - if (win->w_p_rl) - col = win->w_width - 1 - col; + if (win->w_p_rl) { + col = win->w_grid.Columns - 1 - col; + } lnum = win->w_topline; @@ -407,7 +409,7 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump) off = win_col_off(win) - win_col_off2(win); if (col < off) col = off; - col += row * (win->w_width - off); + col += row * (win->w_grid.Columns - off); // add skip column (for long wrapping line) col += win->w_skipcol; } @@ -428,11 +430,23 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump) return retval; } -// Find the window at screen position "*rowp" and "*colp". The positions are -// updated to become relative to the top-left of the window. -// Returns NULL when something is wrong. -win_T *mouse_find_win(int *rowp, int *colp) +/// 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(int *gridp, int *rowp, int *colp) { + win_T *wp_grid = mouse_find_grid_win(gridp, rowp, colp); + if (wp_grid) { + return wp_grid; + } + + // TODO(bfredl): grid zero will have floats displayed on it, and will + // be adjusted to float grids. + if (*gridp == 0) { + *gridp = DEFAULT_GRID_HANDLE; + } + frame_T *fp; fp = topframe; @@ -464,6 +478,19 @@ win_T *mouse_find_win(int *rowp, int *colp) return NULL; } +static win_T *mouse_find_grid_win(int *grid, int *rowp, int *colp) +{ + if (*grid > 1) { + win_T *wp = get_win_by_grid_handle(*grid); + if (wp && wp->w_grid.chars) { + *rowp = MIN(*rowp, wp->w_grid.Rows-1); + *colp = MIN(*colp, wp->w_grid.Columns-1); + return wp; + } + } + return NULL; +} + /* * setmouse() - switch mouse on/off depending on current mode and 'mouse' */ @@ -595,7 +622,7 @@ bool mouse_scroll_horiz(int dir) int step = 6; if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) { - step = curwin->w_width; + step = curwin->w_grid.Columns; } int leftcol = curwin->w_leftcol + (dir == MSCR_RIGHT ? -step : +step); @@ -647,7 +674,7 @@ static int mouse_adjust_click(win_T *wp, int row, int col) // Find the offset where scanning should begin. int offset = wp->w_leftcol; if (row > 0) { - offset += row * (wp->w_width - win_col_off(wp) - win_col_off2(wp) - + offset += row * (wp->w_grid.Columns - win_col_off(wp) - win_col_off2(wp) - wp->w_leftcol + wp->w_skipcol); } diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 0dc5f3175d..844e748681 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -3997,13 +3997,14 @@ static void nv_mousescroll(cmdarg_T *cap) win_T *old_curwin = curwin; if (mouse_row >= 0 && mouse_col >= 0) { - int row, col; + int grid, row, col; + grid = mouse_grid; row = mouse_row; col = mouse_col; // find the window at the pointer coordinates - win_T *const wp = mouse_find_win(&row, &col); + win_T *wp = mouse_find_win(&grid, &row, &col); if (wp == NULL) { return; } diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index f62253cbce..5e2c9ecb36 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -236,6 +236,53 @@ size_t input_enqueue(String keys) return rv; } +static uint8_t check_multiclick(int code, int grid, int row, int col) +{ + static int orig_num_clicks = 0; + static int orig_mouse_code = 0; + static int orig_mouse_grid = 0; + static int orig_mouse_col = 0; + static int orig_mouse_row = 0; + static uint64_t orig_mouse_time = 0; // time of previous mouse click + + if (code == KE_LEFTRELEASE || code == KE_RIGHTRELEASE + || code == KE_MIDDLERELEASE) { + return 0; + } + uint64_t mouse_time = os_hrtime(); // time of current mouse click (ns) + + // compute the time elapsed since the previous mouse click and + // convert p_mouse from ms to ns + uint64_t timediff = mouse_time - orig_mouse_time; + uint64_t mouset = (uint64_t)p_mouset * 1000000; + if (code == orig_mouse_code + && timediff < mouset + && orig_num_clicks != 4 + && orig_mouse_grid == grid + && orig_mouse_col == col + && orig_mouse_row == row) { + orig_num_clicks++; + } else { + orig_num_clicks = 1; + } + orig_mouse_code = code; + orig_mouse_grid = grid; + orig_mouse_col = col; + orig_mouse_row = row; + orig_mouse_time = mouse_time; + + uint8_t modifiers = 0; + 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; + } + return modifiers; +} + + // Mouse event handling code(Extract row/col if available and detect multiple // clicks) static unsigned int handle_mouse_event(char **ptr, uint8_t *buf, @@ -274,48 +321,16 @@ static unsigned int handle_mouse_event(char **ptr, uint8_t *buf, if (row >= Rows) { row = (int)Rows - 1; } + mouse_grid = 0; mouse_row = row; mouse_col = col; } *ptr += advance; } - static int orig_num_clicks = 0; - if (mouse_code != KE_LEFTRELEASE && mouse_code != KE_RIGHTRELEASE - && mouse_code != KE_MIDDLERELEASE) { - static int orig_mouse_code = 0; - static int orig_mouse_col = 0; - static int orig_mouse_row = 0; - static uint64_t orig_mouse_time = 0; // time of previous mouse click - uint64_t mouse_time = os_hrtime(); // time of current mouse click (ns) - - // compute the time elapsed since the previous mouse click and - // convert p_mouse from ms to ns - uint64_t timediff = mouse_time - orig_mouse_time; - uint64_t mouset = (uint64_t)p_mouset * 1000000; - if (mouse_code == orig_mouse_code - && timediff < mouset - && orig_num_clicks != 4 - && orig_mouse_col == mouse_col - && orig_mouse_row == mouse_row) { - orig_num_clicks++; - } else { - orig_num_clicks = 1; - } - orig_mouse_code = mouse_code; - orig_mouse_col = mouse_col; - orig_mouse_row = mouse_row; - orig_mouse_time = mouse_time; - } + uint8_t modifiers = check_multiclick(mouse_code, mouse_grid, + mouse_row, mouse_col); - uint8_t modifiers = 0; - 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; - } if (modifiers) { if (buf[1] != KS_MODIFIER) { @@ -334,6 +349,30 @@ static unsigned int handle_mouse_event(char **ptr, uint8_t *buf, return bufsize; } +size_t input_enqueue_mouse(int code, uint8_t modifier, + int grid, int row, int col) +{ + modifier |= check_multiclick(code, grid, row, col); + uint8_t buf[7], *p = buf; + if (modifier) { + p[0] = K_SPECIAL; + p[1] = KS_MODIFIER; + p[2] = modifier; + p += 3; + } + p[0] = K_SPECIAL; + p[1] = KS_EXTRA; + p[2] = (uint8_t)code; + + mouse_grid = grid; + mouse_row = row; + mouse_col = col; + + size_t written = 3 + (size_t)(p-buf); + rbuffer_write(input_buffer, (char *)buf, written); + return written; +} + /// @return true if the main loop is blocked and waiting for input. bool input_blocking(void) { diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 51bf22b31c..eae0f7ca13 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -981,8 +981,8 @@ static void mouse_action(Terminal *term, int button, int row, int col, // terminal should lose focus static bool send_mouse_event(Terminal *term, int c) { - int row = mouse_row, col = mouse_col; - win_T *mouse_win = mouse_find_win(&row, &col); + int row = mouse_row, col = mouse_col, grid = mouse_grid; + win_T *mouse_win = mouse_find_win(&grid, &row, &col); if (term->forward_mouse && mouse_win->w_buffer->terminal == term) { // event in the terminal window and mouse events was enabled by the diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 96232ab223..73d45d8192 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -454,5 +454,6 @@ void ui_grid_resize(handle_T grid_handle, int width, int height, Error *error) wp->w_grid.requested_rows = (int)height; wp->w_grid.requested_cols = (int)width; + win_inner_width_changed(wp); redraw_win_later(wp, SOME_VALID); } diff --git a/src/nvim/window.c b/src/nvim/window.c index 3cadfe612a..a2e3825274 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -5031,10 +5031,8 @@ void scroll_to_fraction(win_T *wp, int prev_height) } } -/// Set the width of a window. -void win_new_width(win_T *wp, int width) +void win_inner_width_changed(win_T *wp) { - wp->w_width = width; wp->w_lines_valid = 0; changed_line_abv_curs_win(wp); invalidate_botline_win(wp); @@ -5042,6 +5040,20 @@ void win_new_width(win_T *wp, int width) update_topline(); curs_columns(TRUE); /* validate w_wrow */ } +} + +/// Set the width of a window. +void win_new_width(win_T *wp, int width) +{ + wp->w_width = width; + // TODO(bfredl): refactor this. There should be some variable + // wp->w_inner_width which always contains the final actual width. + // Alternatively use wp->w_width for this and introduce wp->w_outer_width + // Then use this to fix terminal_resize. + if (!ui_is_external(kUIMultigrid) || wp->w_grid.requested_cols == 0) { + win_inner_width_changed(wp); + } + redraw_win_later(wp, NOT_VALID); wp->w_redr_status = TRUE; |