blob: 40c2159c04ff5780e60a414b9886ead9e6369f73 [file] [log] [blame]
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * mouse.c: mouse handling functions
12 */
13
14#include "vim.h"
15
LemonBoyc27747e2022-05-07 12:25:40 +010016/*
17 * Horiziontal and vertical steps used when scrolling.
18 * When negative scroll by a whole page.
19 */
20static long mouse_hor_step = 6;
21static long mouse_vert_step = 3;
22
23 void
24mouse_set_vert_scroll_step(long step)
25{
26 mouse_vert_step = step;
27}
28
29 void
30mouse_set_hor_scroll_step(long step)
31{
32 mouse_hor_step = step;
33}
34
Bram Moolenaar85c35022019-11-22 22:21:59 +010035#ifdef CHECK_DOUBLE_CLICK
36/*
37 * Return the duration from t1 to t2 in milliseconds.
38 */
39 static long
40time_diff_ms(struct timeval *t1, struct timeval *t2)
41{
42 // This handles wrapping of tv_usec correctly without any special case.
43 // Example of 2 pairs (tv_sec, tv_usec) with a duration of 5 ms:
44 // t1 = (1, 998000) t2 = (2, 3000) gives:
45 // (2 - 1) * 1000 + (3000 - 998000) / 1000 -> 5 ms.
46 return (t2->tv_sec - t1->tv_sec) * 1000
47 + (t2->tv_usec - t1->tv_usec) / 1000;
48}
49#endif
50
Bram Moolenaarb20b9e12019-09-21 20:48:04 +020051/*
52 * Get class of a character for selection: same class means same word.
53 * 0: blank
54 * 1: punctuation groups
55 * 2: normal word character
56 * >2: multi-byte word character.
57 */
58 static int
59get_mouse_class(char_u *p)
60{
61 int c;
62
63 if (has_mbyte && MB_BYTE2LEN(p[0]) > 1)
64 return mb_get_class(p);
65
66 c = *p;
67 if (c == ' ' || c == '\t')
68 return 0;
69
70 if (vim_iswordc(c))
71 return 2;
72
73 // There are a few special cases where we want certain combinations of
74 // characters to be considered as a single word. These are things like
75 // "->", "/ *", "*=", "+=", "&=", "<=", ">=", "!=" etc. Otherwise, each
76 // character is in its own class.
77 if (c != NUL && vim_strchr((char_u *)"-+*/%<>&|^!=", c) != NULL)
78 return 1;
79 return c;
80}
81
82/*
83 * Move "pos" back to the start of the word it's in.
84 */
85 static void
86find_start_of_word(pos_T *pos)
87{
88 char_u *line;
89 int cclass;
90 int col;
91
92 line = ml_get(pos->lnum);
93 cclass = get_mouse_class(line + pos->col);
94
95 while (pos->col > 0)
96 {
97 col = pos->col - 1;
98 col -= (*mb_head_off)(line, line + col);
99 if (get_mouse_class(line + col) != cclass)
100 break;
101 pos->col = col;
102 }
103}
104
105/*
106 * Move "pos" forward to the end of the word it's in.
107 * When 'selection' is "exclusive", the position is just after the word.
108 */
109 static void
110find_end_of_word(pos_T *pos)
111{
112 char_u *line;
113 int cclass;
114 int col;
115
116 line = ml_get(pos->lnum);
117 if (*p_sel == 'e' && pos->col > 0)
118 {
119 --pos->col;
120 pos->col -= (*mb_head_off)(line, line + pos->col);
121 }
122 cclass = get_mouse_class(line + pos->col);
123 while (line[pos->col] != NUL)
124 {
125 col = pos->col + (*mb_ptr2len)(line + pos->col);
126 if (get_mouse_class(line + col) != cclass)
127 {
128 if (*p_sel == 'e')
129 pos->col = col;
130 break;
131 }
132 pos->col = col;
133 }
134}
135
Bram Moolenaar910c3782019-09-22 14:11:50 +0200136#if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_GTK) \
Bram Moolenaar0b962e52022-04-03 18:02:37 +0100137 || defined(FEAT_GUI_MSWIN) \
Bram Moolenaar097148e2020-08-11 21:58:20 +0200138 || defined(FEAT_GUI_PHOTON) \
Bram Moolenaar910c3782019-09-22 14:11:50 +0200139 || defined(FEAT_TERM_POPUP_MENU)
140# define USE_POPUP_SETPOS
141# define NEED_VCOL2COL
142
143/*
Yee Cheng Chin17822c52022-10-13 13:17:40 +0100144 * Translate window coordinates to buffer position without any side effects.
145 * Returns IN_BUFFER and sets "mpos->col" to the column when in buffer text.
146 * The column is one for the first column.
Bram Moolenaar910c3782019-09-22 14:11:50 +0200147 */
148 static int
149get_fpos_of_mouse(pos_T *mpos)
150{
151 win_T *wp;
152 int row = mouse_row;
153 int col = mouse_col;
154
155 if (row < 0 || col < 0) // check if it makes sense
156 return IN_UNKNOWN;
157
158 // find the window where the row is in
159 wp = mouse_find_win(&row, &col, FAIL_POPUP);
160 if (wp == NULL)
161 return IN_UNKNOWN;
162 // winpos and height may change in win_enter()!
163 if (row >= wp->w_height) // In (or below) status line
164 return IN_STATUS_LINE;
165 if (col >= wp->w_width) // In vertical separator line
166 return IN_SEP_LINE;
167
168 if (wp != curwin)
169 return IN_UNKNOWN;
170
171 // compute the position in the buffer line from the posn on the screen
172 if (mouse_comp_pos(curwin, &row, &col, &mpos->lnum, NULL))
173 return IN_STATUS_LINE; // past bottom
174
175 mpos->col = vcol2col(wp, mpos->lnum, col);
176
Bram Moolenaar910c3782019-09-22 14:11:50 +0200177 mpos->coladd = 0;
178 return IN_BUFFER;
179}
180#endif
181
Bram Moolenaar8ab9ca92022-10-31 13:06:26 +0000182static int mouse_got_click = FALSE; // got a click some time back
183
184/*
185 * Reset the flag that a mouse click was seen. To be called when switching tab
186 * page.
187 */
188 void
189reset_mouse_got_click(void)
190{
191 mouse_got_click = FALSE;
192}
193
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200194/*
195 * Do the appropriate action for the current mouse click in the current mode.
196 * Not used for Command-line mode.
197 *
198 * Normal and Visual Mode:
199 * event modi- position visual change action
200 * fier cursor window
201 * left press - yes end yes
202 * left press C yes end yes "^]" (2)
203 * left press S yes end (popup: extend) yes "*" (2)
204 * left drag - yes start if moved no
205 * left relse - yes start if moved no
206 * middle press - yes if not active no put register
207 * middle press - yes if active no yank and put
208 * right press - yes start or extend yes
209 * right press S yes no change yes "#" (2)
210 * right drag - yes extend no
211 * right relse - yes extend no
212 *
213 * Insert or Replace Mode:
214 * event modi- position visual change action
215 * fier cursor window
216 * left press - yes (cannot be active) yes
217 * left press C yes (cannot be active) yes "CTRL-O^]" (2)
218 * left press S yes (cannot be active) yes "CTRL-O*" (2)
219 * left drag - yes start or extend (1) no CTRL-O (1)
220 * left relse - yes start or extend (1) no CTRL-O (1)
221 * middle press - no (cannot be active) no put register
222 * right press - yes start or extend yes CTRL-O
223 * right press S yes (cannot be active) yes "CTRL-O#" (2)
224 *
225 * (1) only if mouse pointer moved since press
226 * (2) only if click is in same buffer
227 *
228 * Return TRUE if start_arrow() should be called for edit mode.
229 */
230 int
231do_mouse(
232 oparg_T *oap, // operator argument, can be NULL
233 int c, // K_LEFTMOUSE, etc
234 int dir, // Direction to 'put' if necessary
235 long count,
236 int fixindent) // PUT_FIXINDENT if fixing indent necessary
237{
238 static int do_always = FALSE; // ignore 'mouse' setting next time
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200239
240 int which_button; // MOUSE_LEFT, _MIDDLE or _RIGHT
241 int is_click = FALSE; // If FALSE it's a drag or release event
242 int is_drag = FALSE; // If TRUE it's a drag event
243 int jump_flags = 0; // flags for jump_to_mouse()
244 pos_T start_visual;
245 int moved; // Has cursor moved?
246 int in_status_line; // mouse in status line
247 static int in_tab_line = FALSE; // mouse clicked in tab line
248 int in_sep_line; // mouse in vertical separator line
249 int c1, c2;
250#if defined(FEAT_FOLDING)
251 pos_T save_cursor;
252#endif
253 win_T *old_curwin = curwin;
254 static pos_T orig_cursor;
255 colnr_T leftcol, rightcol;
256 pos_T end_visual;
257 int diff;
258 int old_active = VIsual_active;
259 int old_mode = VIsual_mode;
260 int regname;
261
262#if defined(FEAT_FOLDING)
263 save_cursor = curwin->w_cursor;
264#endif
265
266 // When GUI is active, always recognize mouse events, otherwise:
267 // - Ignore mouse event in normal mode if 'mouse' doesn't include 'n'.
268 // - Ignore mouse event in visual mode if 'mouse' doesn't include 'v'.
269 // - For command line and insert mode 'mouse' is checked before calling
270 // do_mouse().
271 if (do_always)
272 do_always = FALSE;
273 else
274#ifdef FEAT_GUI
275 if (!gui.in_use)
276#endif
277 {
278 if (VIsual_active)
279 {
280 if (!mouse_has(MOUSE_VISUAL))
281 return FALSE;
282 }
Bram Moolenaar24959102022-05-07 20:01:16 +0100283 else if (State == MODE_NORMAL && !mouse_has(MOUSE_NORMAL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200284 return FALSE;
285 }
286
287 for (;;)
288 {
289 which_button = get_mouse_button(KEY2TERMCAP1(c), &is_click, &is_drag);
290 if (is_drag)
291 {
292 // If the next character is the same mouse event then use that
293 // one. Speeds up dragging the status line.
zeertzjq0f68e6c2022-04-05 13:17:01 +0100294 // Note: Since characters added to the stuff buffer in the code
295 // below need to come before the next character, do not do this
296 // when the current character was stuffed.
297 if (!KeyStuffed && vpeekc() != NUL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200298 {
299 int nc;
300 int save_mouse_row = mouse_row;
301 int save_mouse_col = mouse_col;
302
303 // Need to get the character, peeking doesn't get the actual
304 // one.
305 nc = safe_vgetc();
306 if (c == nc)
307 continue;
308 vungetc(nc);
309 mouse_row = save_mouse_row;
310 mouse_col = save_mouse_col;
311 }
312 }
313 break;
314 }
315
316 if (c == K_MOUSEMOVE)
317 {
318 // Mouse moved without a button pressed.
319#ifdef FEAT_BEVAL_TERM
320 ui_may_remove_balloon();
321 if (p_bevalterm)
322 {
323 profile_setlimit(p_bdlay, &bevalexpr_due);
324 bevalexpr_due_set = TRUE;
325 }
326#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100327#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200328 popup_handle_mouse_moved();
329#endif
330 return FALSE;
331 }
332
333#ifdef FEAT_MOUSESHAPE
334 // May have stopped dragging the status or separator line. The pointer is
335 // most likely still on the status or separator line.
336 if (!is_drag && drag_status_line)
337 {
338 drag_status_line = FALSE;
339 update_mouseshape(SHAPE_IDX_STATUS);
340 }
341 if (!is_drag && drag_sep_line)
342 {
343 drag_sep_line = FALSE;
344 update_mouseshape(SHAPE_IDX_VSEP);
345 }
346#endif
347
348 // Ignore drag and release events if we didn't get a click.
349 if (is_click)
Bram Moolenaar8ab9ca92022-10-31 13:06:26 +0000350 mouse_got_click = TRUE;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200351 else
352 {
Bram Moolenaar8ab9ca92022-10-31 13:06:26 +0000353 if (!mouse_got_click) // didn't get click, ignore
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200354 return FALSE;
Bram Moolenaar8ab9ca92022-10-31 13:06:26 +0000355 if (!is_drag) // release, reset mouse_got_click
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200356 {
Bram Moolenaar8ab9ca92022-10-31 13:06:26 +0000357 mouse_got_click = FALSE;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200358 if (in_tab_line)
359 {
360 in_tab_line = FALSE;
361 return FALSE;
362 }
363 }
364 }
365
366 // CTRL right mouse button does CTRL-T
367 if (is_click && (mod_mask & MOD_MASK_CTRL) && which_button == MOUSE_RIGHT)
368 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100369 if (State & MODE_INSERT)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200370 stuffcharReadbuff(Ctrl_O);
371 if (count > 1)
372 stuffnumReadbuff(count);
373 stuffcharReadbuff(Ctrl_T);
Bram Moolenaar8ab9ca92022-10-31 13:06:26 +0000374 mouse_got_click = FALSE; // ignore drag&release now
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200375 return FALSE;
376 }
377
378 // CTRL only works with left mouse button
379 if ((mod_mask & MOD_MASK_CTRL) && which_button != MOUSE_LEFT)
380 return FALSE;
381
382 // When a modifier is down, ignore drag and release events, as well as
383 // multiple clicks and the middle mouse button.
384 // Accept shift-leftmouse drags when 'mousemodel' is "popup.*".
385 if ((mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT
386 | MOD_MASK_META))
387 && (!is_click
388 || (mod_mask & MOD_MASK_MULTI_CLICK)
389 || which_button == MOUSE_MIDDLE)
390 && !((mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT))
391 && mouse_model_popup()
392 && which_button == MOUSE_LEFT)
393 && !((mod_mask & MOD_MASK_ALT)
394 && !mouse_model_popup()
395 && which_button == MOUSE_RIGHT)
396 )
397 return FALSE;
398
399 // If the button press was used as the movement command for an operator
400 // (eg "d<MOUSE>"), or it is the middle button that is held down, ignore
401 // drag/release events.
402 if (!is_click && which_button == MOUSE_MIDDLE)
403 return FALSE;
404
405 if (oap != NULL)
406 regname = oap->regname;
407 else
408 regname = 0;
409
410 // Middle mouse button does a 'put' of the selected text
411 if (which_button == MOUSE_MIDDLE)
412 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100413 if (State == MODE_NORMAL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200414 {
415 // If an operator was pending, we don't know what the user wanted
416 // to do. Go back to normal mode: Clear the operator and beep().
417 if (oap != NULL && oap->op_type != OP_NOP)
418 {
419 clearopbeep(oap);
420 return FALSE;
421 }
422
423 // If visual was active, yank the highlighted text and put it
424 // before the mouse pointer position.
425 // In Select mode replace the highlighted text with the clipboard.
426 if (VIsual_active)
427 {
428 if (VIsual_select)
429 {
430 stuffcharReadbuff(Ctrl_G);
431 stuffReadbuff((char_u *)"\"+p");
432 }
433 else
434 {
435 stuffcharReadbuff('y');
436 stuffcharReadbuff(K_MIDDLEMOUSE);
437 }
438 do_always = TRUE; // ignore 'mouse' setting next time
439 return FALSE;
440 }
441 // The rest is below jump_to_mouse()
442 }
443
Bram Moolenaar24959102022-05-07 20:01:16 +0100444 else if ((State & MODE_INSERT) == 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200445 return FALSE;
446
447 // Middle click in insert mode doesn't move the mouse, just insert the
448 // contents of a register. '.' register is special, can't insert that
449 // with do_put().
450 // Also paste at the cursor if the current mode isn't in 'mouse' (only
451 // happens for the GUI).
Bram Moolenaar24959102022-05-07 20:01:16 +0100452 if ((State & MODE_INSERT) || !mouse_has(MOUSE_NORMAL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200453 {
454 if (regname == '.')
455 insert_reg(regname, TRUE);
456 else
457 {
458#ifdef FEAT_CLIPBOARD
459 if (clip_star.available && regname == 0)
460 regname = '*';
461#endif
462 if ((State & REPLACE_FLAG) && !yank_register_mline(regname))
463 insert_reg(regname, TRUE);
464 else
465 {
Bram Moolenaarc3516f72020-09-08 22:45:35 +0200466 do_put(regname, NULL, BACKWARD, 1L,
467 fixindent | PUT_CURSEND);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200468
469 // Repeat it with CTRL-R CTRL-O r or CTRL-R CTRL-P r
470 AppendCharToRedobuff(Ctrl_R);
471 AppendCharToRedobuff(fixindent ? Ctrl_P : Ctrl_O);
472 AppendCharToRedobuff(regname == 0 ? '"' : regname);
473 }
474 }
475 return FALSE;
476 }
477 }
478
479 // When dragging or button-up stay in the same window.
480 if (!is_click)
481 jump_flags |= MOUSE_FOCUS | MOUSE_DID_MOVE;
482
483 start_visual.lnum = 0;
484
Bram Moolenaar80525752022-08-24 19:27:45 +0100485 if (TabPageIdxs != NULL) // only when initialized
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200486 {
Bram Moolenaar80525752022-08-24 19:27:45 +0100487 // Check for clicking in the tab page line.
488 if (mouse_row == 0 && firstwin->w_winrow > 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200489 {
Bram Moolenaar80525752022-08-24 19:27:45 +0100490 if (is_drag)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200491 {
Bram Moolenaar80525752022-08-24 19:27:45 +0100492 if (in_tab_line)
493 {
494 c1 = TabPageIdxs[mouse_col];
495 tabpage_move(c1 <= 0 ? 9999 : c1 < tabpage_index(curtab)
Martin Tournoij7904fa42022-10-04 16:28:45 +0100496 ? c1 - 1 : c1);
Bram Moolenaar80525752022-08-24 19:27:45 +0100497 }
498 return FALSE;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200499 }
Bram Moolenaar80525752022-08-24 19:27:45 +0100500
501 // click in a tab selects that tab page
Martin Tournoij7904fa42022-10-04 16:28:45 +0100502 if (is_click && cmdwin_type == 0 && mouse_col < Columns)
Bram Moolenaar80525752022-08-24 19:27:45 +0100503 {
504 in_tab_line = TRUE;
505 c1 = TabPageIdxs[mouse_col];
506 if (c1 >= 0)
507 {
508 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
509 {
510 // double click opens new page
511 end_visual_mode_keep_button();
512 tabpage_new();
513 tabpage_move(c1 == 0 ? 9999 : c1 - 1);
514 }
515 else
516 {
517 // Go to specified tab page, or next one if not clicking
518 // on a label.
519 goto_tabpage(c1);
520
521 // It's like clicking on the status line of a window.
522 if (curwin != old_curwin)
523 end_visual_mode_keep_button();
524 }
525 }
526 else
527 {
528 tabpage_T *tp;
529
530 // Close the current or specified tab page.
531 if (c1 == -999)
532 tp = curtab;
533 else
534 tp = find_tabpage(-c1);
535 if (tp == curtab)
536 {
537 if (first_tabpage->tp_next != NULL)
538 tabpage_close(FALSE);
539 }
540 else if (tp != NULL)
541 tabpage_close_other(tp, FALSE);
542 }
543 }
544 return TRUE;
545 }
546 else if (is_drag && in_tab_line)
547 {
548 c1 = TabPageIdxs[mouse_col];
549 tabpage_move(c1 <= 0 ? 9999 : c1 - 1);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200550 return FALSE;
551 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200552 }
553
554 // When 'mousemodel' is "popup" or "popup_setpos", translate mouse events:
555 // right button up -> pop-up menu
556 // shift-left button -> right button
557 // alt-left button -> alt-right button
558 if (mouse_model_popup())
559 {
560 if (which_button == MOUSE_RIGHT
561 && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)))
562 {
Bram Moolenaar910c3782019-09-22 14:11:50 +0200563#ifdef USE_POPUP_SETPOS
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200564# ifdef FEAT_GUI
565 if (gui.in_use)
566 {
567# if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_GTK) \
Bram Moolenaar097148e2020-08-11 21:58:20 +0200568 || defined(FEAT_GUI_PHOTON)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200569 if (!is_click)
570 // Ignore right button release events, only shows the popup
571 // menu on the button down event.
572 return FALSE;
573# endif
Bram Moolenaar0b962e52022-04-03 18:02:37 +0100574# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_HAIKU)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200575 if (is_click || is_drag)
576 // Ignore right button down and drag mouse events. Windows
577 // only shows the popup menu on the button up event.
578 return FALSE;
579# endif
580 }
581# endif
582# if defined(FEAT_GUI) && defined(FEAT_TERM_POPUP_MENU)
583 else
584# endif
585# if defined(FEAT_TERM_POPUP_MENU)
586 if (!is_click)
587 // Ignore right button release events, only shows the popup
588 // menu on the button down event.
589 return FALSE;
590#endif
591
592 jump_flags = 0;
593 if (STRCMP(p_mousem, "popup_setpos") == 0)
594 {
595 // First set the cursor position before showing the popup
596 // menu.
597 if (VIsual_active)
598 {
599 pos_T m_pos;
600
601 // set MOUSE_MAY_STOP_VIS if we are outside the
602 // selection or the current window (might have false
603 // negative here)
604 if (mouse_row < curwin->w_winrow
605 || mouse_row
606 > (curwin->w_winrow + curwin->w_height))
607 jump_flags = MOUSE_MAY_STOP_VIS;
608 else if (get_fpos_of_mouse(&m_pos) != IN_BUFFER)
609 jump_flags = MOUSE_MAY_STOP_VIS;
610 else
611 {
Yee Cheng Chin17822c52022-10-13 13:17:40 +0100612 if (VIsual_mode == 'V')
613 {
614 if ((curwin->w_cursor.lnum <= VIsual.lnum
615 && (m_pos.lnum < curwin->w_cursor.lnum
616 || VIsual.lnum < m_pos.lnum))
617 || (VIsual.lnum < curwin->w_cursor.lnum
618 && (m_pos.lnum < VIsual.lnum
619 || curwin->w_cursor.lnum < m_pos.lnum)))
620 {
621 jump_flags = MOUSE_MAY_STOP_VIS;
622 }
623 }
624 else if ((LTOREQ_POS(curwin->w_cursor, VIsual)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200625 && (LT_POS(m_pos, curwin->w_cursor)
626 || LT_POS(VIsual, m_pos)))
627 || (LT_POS(VIsual, curwin->w_cursor)
628 && (LT_POS(m_pos, VIsual)
629 || LT_POS(curwin->w_cursor, m_pos))))
630 {
631 jump_flags = MOUSE_MAY_STOP_VIS;
632 }
633 else if (VIsual_mode == Ctrl_V)
634 {
635 getvcols(curwin, &curwin->w_cursor, &VIsual,
636 &leftcol, &rightcol);
637 getvcol(curwin, &m_pos, NULL, &m_pos.col, NULL);
638 if (m_pos.col < leftcol || m_pos.col > rightcol)
639 jump_flags = MOUSE_MAY_STOP_VIS;
640 }
641 }
642 }
643 else
644 jump_flags = MOUSE_MAY_STOP_VIS;
645 }
646 if (jump_flags)
647 {
648 jump_flags = jump_to_mouse(jump_flags, NULL, which_button);
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100649 update_curbuf(VIsual_active ? UPD_INVERTED : UPD_VALID);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200650 setcursor();
651 out_flush(); // Update before showing popup menu
652 }
653# ifdef FEAT_MENU
654 show_popupmenu();
Bram Moolenaar8ab9ca92022-10-31 13:06:26 +0000655 mouse_got_click = FALSE; // ignore release events
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200656# endif
657 return (jump_flags & CURSOR_MOVED) != 0;
658#else
659 return FALSE;
660#endif
661 }
662 if (which_button == MOUSE_LEFT
663 && (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT)))
664 {
665 which_button = MOUSE_RIGHT;
666 mod_mask &= ~MOD_MASK_SHIFT;
667 }
668 }
669
Bram Moolenaar24959102022-05-07 20:01:16 +0100670 if ((State & (MODE_NORMAL | MODE_INSERT))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200671 && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)))
672 {
673 if (which_button == MOUSE_LEFT)
674 {
675 if (is_click)
676 {
677 // stop Visual mode for a left click in a window, but not when
678 // on a status line
679 if (VIsual_active)
680 jump_flags |= MOUSE_MAY_STOP_VIS;
681 }
682 else if (mouse_has(MOUSE_VISUAL))
683 jump_flags |= MOUSE_MAY_VIS;
684 }
685 else if (which_button == MOUSE_RIGHT)
686 {
687 if (is_click && VIsual_active)
688 {
689 // Remember the start and end of visual before moving the
690 // cursor.
691 if (LT_POS(curwin->w_cursor, VIsual))
692 {
693 start_visual = curwin->w_cursor;
694 end_visual = VIsual;
695 }
696 else
697 {
698 start_visual = VIsual;
699 end_visual = curwin->w_cursor;
700 }
701 }
702 jump_flags |= MOUSE_FOCUS;
703 if (mouse_has(MOUSE_VISUAL))
704 jump_flags |= MOUSE_MAY_VIS;
705 }
706 }
707
708 // If an operator is pending, ignore all drags and releases until the
709 // next mouse click.
710 if (!is_drag && oap != NULL && oap->op_type != OP_NOP)
711 {
Bram Moolenaar8ab9ca92022-10-31 13:06:26 +0000712 mouse_got_click = FALSE;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200713 oap->motion_type = MCHAR;
714 }
715
716 // When releasing the button let jump_to_mouse() know.
717 if (!is_click && !is_drag)
718 jump_flags |= MOUSE_RELEASED;
719
720 // JUMP!
721 jump_flags = jump_to_mouse(jump_flags,
722 oap == NULL ? NULL : &(oap->inclusive), which_button);
723
724#ifdef FEAT_MENU
725 // A click in the window toolbar has no side effects.
726 if (jump_flags & MOUSE_WINBAR)
727 return FALSE;
728#endif
729 moved = (jump_flags & CURSOR_MOVED);
730 in_status_line = (jump_flags & IN_STATUS_LINE);
731 in_sep_line = (jump_flags & IN_SEP_LINE);
732
733#ifdef FEAT_NETBEANS_INTG
734 if (isNetbeansBuffer(curbuf)
735 && !(jump_flags & (IN_STATUS_LINE | IN_SEP_LINE)))
736 {
737 int key = KEY2TERMCAP1(c);
738
739 if (key == (int)KE_LEFTRELEASE || key == (int)KE_MIDDLERELEASE
740 || key == (int)KE_RIGHTRELEASE)
741 netbeans_button_release(which_button);
742 }
743#endif
744
745 // When jumping to another window, clear a pending operator. That's a bit
746 // friendlier than beeping and not jumping to that window.
747 if (curwin != old_curwin && oap != NULL && oap->op_type != OP_NOP)
748 clearop(oap);
749
750#ifdef FEAT_FOLDING
751 if (mod_mask == 0
752 && !is_drag
753 && (jump_flags & (MOUSE_FOLD_CLOSE | MOUSE_FOLD_OPEN))
754 && which_button == MOUSE_LEFT)
755 {
756 // open or close a fold at this line
757 if (jump_flags & MOUSE_FOLD_OPEN)
758 openFold(curwin->w_cursor.lnum, 1L);
759 else
760 closeFold(curwin->w_cursor.lnum, 1L);
761 // don't move the cursor if still in the same window
762 if (curwin == old_curwin)
763 curwin->w_cursor = save_cursor;
764 }
765#endif
766
Martin Tournoij7904fa42022-10-04 16:28:45 +0100767#if defined(FEAT_CLIPBOARD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200768 if ((jump_flags & IN_OTHER_WIN) && !VIsual_active && clip_star.available)
769 {
770 clip_modeless(which_button, is_click, is_drag);
771 return FALSE;
772 }
773#endif
774
775 // Set global flag that we are extending the Visual area with mouse
776 // dragging; temporarily minimize 'scrolloff'.
777 if (VIsual_active && is_drag && get_scrolloff_value())
778 {
779 // In the very first line, allow scrolling one line
780 if (mouse_row == 0)
781 mouse_dragging = 2;
782 else
783 mouse_dragging = 1;
784 }
785
786 // When dragging the mouse above the window, scroll down.
787 if (is_drag && mouse_row < 0 && !in_status_line)
788 {
789 scroll_redraw(FALSE, 1L);
790 mouse_row = 0;
791 }
792
793 if (start_visual.lnum) // right click in visual mode
794 {
795 // When ALT is pressed make Visual mode blockwise.
796 if (mod_mask & MOD_MASK_ALT)
797 VIsual_mode = Ctrl_V;
798
799 // In Visual-block mode, divide the area in four, pick up the corner
800 // that is in the quarter that the cursor is in.
801 if (VIsual_mode == Ctrl_V)
802 {
803 getvcols(curwin, &start_visual, &end_visual, &leftcol, &rightcol);
804 if (curwin->w_curswant > (leftcol + rightcol) / 2)
805 end_visual.col = leftcol;
806 else
807 end_visual.col = rightcol;
808 if (curwin->w_cursor.lnum >=
809 (start_visual.lnum + end_visual.lnum) / 2)
810 end_visual.lnum = start_visual.lnum;
811
812 // move VIsual to the right column
813 start_visual = curwin->w_cursor; // save the cursor pos
814 curwin->w_cursor = end_visual;
815 coladvance(end_visual.col);
816 VIsual = curwin->w_cursor;
817 curwin->w_cursor = start_visual; // restore the cursor
818 }
819 else
820 {
821 // If the click is before the start of visual, change the start.
822 // If the click is after the end of visual, change the end. If
823 // the click is inside the visual, change the closest side.
824 if (LT_POS(curwin->w_cursor, start_visual))
825 VIsual = end_visual;
826 else if (LT_POS(end_visual, curwin->w_cursor))
827 VIsual = start_visual;
828 else
829 {
830 // In the same line, compare column number
831 if (end_visual.lnum == start_visual.lnum)
832 {
833 if (curwin->w_cursor.col - start_visual.col >
834 end_visual.col - curwin->w_cursor.col)
835 VIsual = start_visual;
836 else
837 VIsual = end_visual;
838 }
839
840 // In different lines, compare line number
841 else
842 {
843 diff = (curwin->w_cursor.lnum - start_visual.lnum) -
844 (end_visual.lnum - curwin->w_cursor.lnum);
845
846 if (diff > 0) // closest to end
847 VIsual = start_visual;
848 else if (diff < 0) // closest to start
849 VIsual = end_visual;
850 else // in the middle line
851 {
852 if (curwin->w_cursor.col <
853 (start_visual.col + end_visual.col) / 2)
854 VIsual = end_visual;
855 else
856 VIsual = start_visual;
857 }
858 }
859 }
860 }
861 }
862 // If Visual mode started in insert mode, execute "CTRL-O"
Bram Moolenaar24959102022-05-07 20:01:16 +0100863 else if ((State & MODE_INSERT) && VIsual_active)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200864 stuffcharReadbuff(Ctrl_O);
865
866 // Middle mouse click: Put text before cursor.
867 if (which_button == MOUSE_MIDDLE)
868 {
869#ifdef FEAT_CLIPBOARD
870 if (clip_star.available && regname == 0)
871 regname = '*';
872#endif
873 if (yank_register_mline(regname))
874 {
875 if (mouse_past_bottom)
876 dir = FORWARD;
877 }
878 else if (mouse_past_eol)
879 dir = FORWARD;
880
881 if (fixindent)
882 {
883 c1 = (dir == BACKWARD) ? '[' : ']';
884 c2 = 'p';
885 }
886 else
887 {
888 c1 = (dir == FORWARD) ? 'p' : 'P';
889 c2 = NUL;
890 }
891 prep_redo(regname, count, NUL, c1, NUL, c2, NUL);
892
893 // Remember where the paste started, so in edit() Insstart can be set
894 // to this position
895 if (restart_edit != 0)
896 where_paste_started = curwin->w_cursor;
Bram Moolenaarc3516f72020-09-08 22:45:35 +0200897 do_put(regname, NULL, dir, count, fixindent | PUT_CURSEND);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200898 }
899
900#if defined(FEAT_QUICKFIX)
901 // Ctrl-Mouse click or double click in a quickfix window jumps to the
902 // error under the mouse pointer.
903 else if (((mod_mask & MOD_MASK_CTRL)
904 || (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
905 && bt_quickfix(curbuf))
906 {
907 if (curwin->w_llist_ref == NULL) // quickfix window
908 do_cmdline_cmd((char_u *)".cc");
909 else // location list window
910 do_cmdline_cmd((char_u *)".ll");
Bram Moolenaar8ab9ca92022-10-31 13:06:26 +0000911 mouse_got_click = FALSE; // ignore drag&release now
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200912 }
913#endif
914
915 // Ctrl-Mouse click (or double click in a help window) jumps to the tag
916 // under the mouse pointer.
917 else if ((mod_mask & MOD_MASK_CTRL) || (curbuf->b_help
918 && (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK))
919 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100920 if (State & MODE_INSERT)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200921 stuffcharReadbuff(Ctrl_O);
922 stuffcharReadbuff(Ctrl_RSB);
Bram Moolenaar8ab9ca92022-10-31 13:06:26 +0000923 mouse_got_click = FALSE; // ignore drag&release now
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200924 }
925
926 // Shift-Mouse click searches for the next occurrence of the word under
927 // the mouse pointer
928 else if ((mod_mask & MOD_MASK_SHIFT))
929 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100930 if ((State & MODE_INSERT) || (VIsual_active && VIsual_select))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200931 stuffcharReadbuff(Ctrl_O);
932 if (which_button == MOUSE_LEFT)
933 stuffcharReadbuff('*');
934 else // MOUSE_RIGHT
935 stuffcharReadbuff('#');
936 }
937
938 // Handle double clicks, unless on status line
939 else if (in_status_line)
940 {
941#ifdef FEAT_MOUSESHAPE
942 if ((is_drag || is_click) && !drag_status_line)
943 {
944 drag_status_line = TRUE;
945 update_mouseshape(-1);
946 }
947#endif
948 }
949 else if (in_sep_line)
950 {
951#ifdef FEAT_MOUSESHAPE
952 if ((is_drag || is_click) && !drag_sep_line)
953 {
954 drag_sep_line = TRUE;
955 update_mouseshape(-1);
956 }
957#endif
958 }
Bram Moolenaar24959102022-05-07 20:01:16 +0100959 else if ((mod_mask & MOD_MASK_MULTI_CLICK)
960 && (State & (MODE_NORMAL | MODE_INSERT))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +0200961 && mouse_has(MOUSE_VISUAL))
962 {
963 if (is_click || !VIsual_active)
964 {
965 if (VIsual_active)
966 orig_cursor = VIsual;
967 else
968 {
969 check_visual_highlight();
970 VIsual = curwin->w_cursor;
971 orig_cursor = VIsual;
972 VIsual_active = TRUE;
973 VIsual_reselect = TRUE;
974 // start Select mode if 'selectmode' contains "mouse"
975 may_start_select('o');
976 setmouse();
977 }
978 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
979 {
980 // Double click with ALT pressed makes it blockwise.
981 if (mod_mask & MOD_MASK_ALT)
982 VIsual_mode = Ctrl_V;
983 else
984 VIsual_mode = 'v';
985 }
986 else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_3CLICK)
987 VIsual_mode = 'V';
988 else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_4CLICK)
989 VIsual_mode = Ctrl_V;
990#ifdef FEAT_CLIPBOARD
991 // Make sure the clipboard gets updated. Needed because start and
992 // end may still be the same, and the selection needs to be owned
993 clip_star.vmode = NUL;
994#endif
995 }
996 // A double click selects a word or a block.
997 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
998 {
999 pos_T *pos = NULL;
1000 int gc;
1001
1002 if (is_click)
1003 {
1004 // If the character under the cursor (skipping white space) is
1005 // not a word character, try finding a match and select a (),
1006 // {}, [], #if/#endif, etc. block.
1007 end_visual = curwin->w_cursor;
1008 while (gc = gchar_pos(&end_visual), VIM_ISWHITE(gc))
1009 inc(&end_visual);
1010 if (oap != NULL)
1011 oap->motion_type = MCHAR;
1012 if (oap != NULL
1013 && VIsual_mode == 'v'
1014 && !vim_iswordc(gchar_pos(&end_visual))
1015 && EQUAL_POS(curwin->w_cursor, VIsual)
1016 && (pos = findmatch(oap, NUL)) != NULL)
1017 {
1018 curwin->w_cursor = *pos;
1019 if (oap->motion_type == MLINE)
1020 VIsual_mode = 'V';
1021 else if (*p_sel == 'e')
1022 {
1023 if (LT_POS(curwin->w_cursor, VIsual))
1024 ++VIsual.col;
1025 else
1026 ++curwin->w_cursor.col;
1027 }
1028 }
1029 }
1030
1031 if (pos == NULL && (is_click || is_drag))
1032 {
1033 // When not found a match or when dragging: extend to include
1034 // a word.
1035 if (LT_POS(curwin->w_cursor, orig_cursor))
1036 {
1037 find_start_of_word(&curwin->w_cursor);
1038 find_end_of_word(&VIsual);
1039 }
1040 else
1041 {
1042 find_start_of_word(&VIsual);
1043 if (*p_sel == 'e' && *ml_get_cursor() != NUL)
1044 curwin->w_cursor.col +=
1045 (*mb_ptr2len)(ml_get_cursor());
1046 find_end_of_word(&curwin->w_cursor);
1047 }
1048 }
1049 curwin->w_set_curswant = TRUE;
1050 }
1051 if (is_click)
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001052 redraw_curbuf_later(UPD_INVERTED); // update the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001053 }
1054 else if (VIsual_active && !old_active)
1055 {
1056 if (mod_mask & MOD_MASK_ALT)
1057 VIsual_mode = Ctrl_V;
1058 else
1059 VIsual_mode = 'v';
1060 }
1061
1062 // If Visual mode changed show it later.
1063 if ((!VIsual_active && old_active && mode_displayed)
1064 || (VIsual_active && p_smd && msg_silent == 0
1065 && (!old_active || VIsual_mode != old_mode)))
1066 redraw_cmdline = TRUE;
1067
1068 return moved;
1069}
1070
1071 void
1072ins_mouse(int c)
1073{
1074 pos_T tpos;
1075 win_T *old_curwin = curwin;
1076
1077# ifdef FEAT_GUI
1078 // When GUI is active, also move/paste when 'mouse' is empty
1079 if (!gui.in_use)
1080# endif
1081 if (!mouse_has(MOUSE_INSERT))
1082 return;
1083
1084 undisplay_dollar();
1085 tpos = curwin->w_cursor;
1086 if (do_mouse(NULL, c, BACKWARD, 1L, 0))
1087 {
1088 win_T *new_curwin = curwin;
1089
1090 if (curwin != old_curwin && win_valid(old_curwin))
1091 {
1092 // Mouse took us to another window. We need to go back to the
1093 // previous one to stop insert there properly.
1094 curwin = old_curwin;
1095 curbuf = curwin->w_buffer;
1096#ifdef FEAT_JOB_CHANNEL
1097 if (bt_prompt(curbuf))
1098 // Restart Insert mode when re-entering the prompt buffer.
1099 curbuf->b_prompt_insert = 'A';
1100#endif
1101 }
1102 start_arrow(curwin == old_curwin ? &tpos : NULL);
1103 if (curwin != new_curwin && win_valid(new_curwin))
1104 {
1105 curwin = new_curwin;
1106 curbuf = curwin->w_buffer;
1107 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001108 set_can_cindent(TRUE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001109 }
1110
1111 // redraw status lines (in case another window became active)
1112 redraw_statuslines();
1113}
1114
1115 void
1116ins_mousescroll(int dir)
1117{
1118 pos_T tpos;
1119 win_T *old_curwin = curwin, *wp;
1120 int did_scroll = FALSE;
1121
1122 tpos = curwin->w_cursor;
1123
1124 if (mouse_row >= 0 && mouse_col >= 0)
1125 {
1126 int row, col;
1127
1128 row = mouse_row;
1129 col = mouse_col;
1130
1131 // find the window at the pointer coordinates
1132 wp = mouse_find_win(&row, &col, FIND_POPUP);
1133 if (wp == NULL)
1134 return;
1135 curwin = wp;
1136 curbuf = curwin->w_buffer;
1137 }
1138 if (curwin == old_curwin)
1139 undisplay_dollar();
1140
1141 // Don't scroll the window in which completion is being done.
1142 if (!pum_visible() || curwin != old_curwin)
1143 {
LemonBoyc27747e2022-05-07 12:25:40 +01001144 long step;
1145
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001146 if (dir == MSCR_DOWN || dir == MSCR_UP)
1147 {
LemonBoyc27747e2022-05-07 12:25:40 +01001148 if (mouse_vert_step < 0
1149 || mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
1150 step = (long)(curwin->w_botline - curwin->w_topline);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001151 else
LemonBoyc27747e2022-05-07 12:25:40 +01001152 step = mouse_vert_step;
1153 scroll_redraw(dir, step);
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001154# ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001155 if (WIN_IS_POPUP(curwin))
1156 popup_set_firstline(curwin);
1157# endif
1158 }
1159#ifdef FEAT_GUI
1160 else
1161 {
LemonBoyc27747e2022-05-07 12:25:40 +01001162 int val;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001163
LemonBoyc27747e2022-05-07 12:25:40 +01001164 if (mouse_hor_step < 0
1165 || mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001166 step = curwin->w_width;
LemonBoyc27747e2022-05-07 12:25:40 +01001167 else
1168 step = mouse_hor_step;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001169 val = curwin->w_leftcol + (dir == MSCR_RIGHT ? -step : step);
1170 if (val < 0)
1171 val = 0;
1172 gui_do_horiz_scroll(val, TRUE);
1173 }
1174#endif
1175 did_scroll = TRUE;
LemonBoy66e13ae2022-04-21 22:52:11 +01001176 may_trigger_winscrolled();
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001177 }
1178
1179 curwin->w_redr_status = TRUE;
1180
1181 curwin = old_curwin;
1182 curbuf = curwin->w_buffer;
1183
1184 // The popup menu may overlay the window, need to redraw it.
1185 // TODO: Would be more efficient to only redraw the windows that are
1186 // overlapped by the popup menu.
1187 if (pum_visible() && did_scroll)
1188 {
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001189 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001190 ins_compl_show_pum();
1191 }
1192
1193 if (!EQUAL_POS(curwin->w_cursor, tpos))
1194 {
1195 start_arrow(&tpos);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001196 set_can_cindent(TRUE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001197 }
1198}
1199
1200/*
1201 * Return TRUE if "c" is a mouse key.
1202 */
1203 int
1204is_mouse_key(int c)
1205{
1206 return c == K_LEFTMOUSE
1207 || c == K_LEFTMOUSE_NM
1208 || c == K_LEFTDRAG
1209 || c == K_LEFTRELEASE
1210 || c == K_LEFTRELEASE_NM
1211 || c == K_MOUSEMOVE
1212 || c == K_MIDDLEMOUSE
1213 || c == K_MIDDLEDRAG
1214 || c == K_MIDDLERELEASE
1215 || c == K_RIGHTMOUSE
1216 || c == K_RIGHTDRAG
1217 || c == K_RIGHTRELEASE
1218 || c == K_MOUSEDOWN
1219 || c == K_MOUSEUP
1220 || c == K_MOUSELEFT
1221 || c == K_MOUSERIGHT
1222 || c == K_X1MOUSE
1223 || c == K_X1DRAG
1224 || c == K_X1RELEASE
1225 || c == K_X2MOUSE
1226 || c == K_X2DRAG
1227 || c == K_X2RELEASE;
1228}
1229
1230static struct mousetable
1231{
1232 int pseudo_code; // Code for pseudo mouse event
1233 int button; // Which mouse button is it?
1234 int is_click; // Is it a mouse button click event?
1235 int is_drag; // Is it a mouse drag event?
1236} mouse_table[] =
1237{
1238 {(int)KE_LEFTMOUSE, MOUSE_LEFT, TRUE, FALSE},
1239#ifdef FEAT_GUI
1240 {(int)KE_LEFTMOUSE_NM, MOUSE_LEFT, TRUE, FALSE},
1241#endif
1242 {(int)KE_LEFTDRAG, MOUSE_LEFT, FALSE, TRUE},
1243 {(int)KE_LEFTRELEASE, MOUSE_LEFT, FALSE, FALSE},
1244#ifdef FEAT_GUI
1245 {(int)KE_LEFTRELEASE_NM, MOUSE_LEFT, FALSE, FALSE},
1246#endif
1247 {(int)KE_MIDDLEMOUSE, MOUSE_MIDDLE, TRUE, FALSE},
1248 {(int)KE_MIDDLEDRAG, MOUSE_MIDDLE, FALSE, TRUE},
1249 {(int)KE_MIDDLERELEASE, MOUSE_MIDDLE, FALSE, FALSE},
1250 {(int)KE_RIGHTMOUSE, MOUSE_RIGHT, TRUE, FALSE},
1251 {(int)KE_RIGHTDRAG, MOUSE_RIGHT, FALSE, TRUE},
1252 {(int)KE_RIGHTRELEASE, MOUSE_RIGHT, FALSE, FALSE},
1253 {(int)KE_X1MOUSE, MOUSE_X1, TRUE, FALSE},
1254 {(int)KE_X1DRAG, MOUSE_X1, FALSE, TRUE},
1255 {(int)KE_X1RELEASE, MOUSE_X1, FALSE, FALSE},
1256 {(int)KE_X2MOUSE, MOUSE_X2, TRUE, FALSE},
1257 {(int)KE_X2DRAG, MOUSE_X2, FALSE, TRUE},
1258 {(int)KE_X2RELEASE, MOUSE_X2, FALSE, FALSE},
1259 // DRAG without CLICK
1260 {(int)KE_MOUSEMOVE, MOUSE_RELEASE, FALSE, TRUE},
1261 // RELEASE without CLICK
1262 {(int)KE_IGNORE, MOUSE_RELEASE, FALSE, FALSE},
1263 {0, 0, 0, 0},
1264};
1265
1266/*
1267 * Look up the given mouse code to return the relevant information in the other
1268 * arguments. Return which button is down or was released.
1269 */
1270 int
1271get_mouse_button(int code, int *is_click, int *is_drag)
1272{
1273 int i;
1274
1275 for (i = 0; mouse_table[i].pseudo_code; i++)
1276 if (code == mouse_table[i].pseudo_code)
1277 {
1278 *is_click = mouse_table[i].is_click;
1279 *is_drag = mouse_table[i].is_drag;
1280 return mouse_table[i].button;
1281 }
1282 return 0; // Shouldn't get here
1283}
1284
1285/*
1286 * Return the appropriate pseudo mouse event token (KE_LEFTMOUSE etc) based on
1287 * the given information about which mouse button is down, and whether the
1288 * mouse was clicked, dragged or released.
1289 */
1290 int
1291get_pseudo_mouse_code(
1292 int button, // eg MOUSE_LEFT
1293 int is_click,
1294 int is_drag)
1295{
1296 int i;
1297
1298 for (i = 0; mouse_table[i].pseudo_code; i++)
1299 if (button == mouse_table[i].button
1300 && is_click == mouse_table[i].is_click
1301 && is_drag == mouse_table[i].is_drag)
1302 {
1303#ifdef FEAT_GUI
1304 // Trick: a non mappable left click and release has mouse_col -1
1305 // or added MOUSE_COLOFF. Used for 'mousefocus' in
1306 // gui_mouse_moved()
1307 if (mouse_col < 0 || mouse_col > MOUSE_COLOFF)
1308 {
1309 if (mouse_col < 0)
1310 mouse_col = 0;
1311 else
1312 mouse_col -= MOUSE_COLOFF;
1313 if (mouse_table[i].pseudo_code == (int)KE_LEFTMOUSE)
1314 return (int)KE_LEFTMOUSE_NM;
1315 if (mouse_table[i].pseudo_code == (int)KE_LEFTRELEASE)
1316 return (int)KE_LEFTRELEASE_NM;
1317 }
1318#endif
1319 return mouse_table[i].pseudo_code;
1320 }
1321 return (int)KE_IGNORE; // not recognized, ignore it
1322}
1323
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001324# define HMT_NORMAL 1
1325# define HMT_NETTERM 2
1326# define HMT_DEC 4
1327# define HMT_JSBTERM 8
1328# define HMT_PTERM 16
1329# define HMT_URXVT 32
1330# define HMT_GPM 64
1331# define HMT_SGR 128
1332# define HMT_SGR_REL 256
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001333static int has_mouse_termcode = 0;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001334
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001335 void
1336set_mouse_termcode(
1337 int n, // KS_MOUSE, KS_NETTERM_MOUSE or KS_DEC_MOUSE
1338 char_u *s)
1339{
1340 char_u name[2];
1341
1342 name[0] = n;
1343 name[1] = KE_FILLER;
1344 add_termcode(name, s, FALSE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001345# ifdef FEAT_MOUSE_JSB
1346 if (n == KS_JSBTERM_MOUSE)
1347 has_mouse_termcode |= HMT_JSBTERM;
1348 else
1349# endif
1350# ifdef FEAT_MOUSE_NET
1351 if (n == KS_NETTERM_MOUSE)
1352 has_mouse_termcode |= HMT_NETTERM;
1353 else
1354# endif
1355# ifdef FEAT_MOUSE_DEC
1356 if (n == KS_DEC_MOUSE)
1357 has_mouse_termcode |= HMT_DEC;
1358 else
1359# endif
1360# ifdef FEAT_MOUSE_PTERM
1361 if (n == KS_PTERM_MOUSE)
1362 has_mouse_termcode |= HMT_PTERM;
1363 else
1364# endif
1365# ifdef FEAT_MOUSE_URXVT
1366 if (n == KS_URXVT_MOUSE)
1367 has_mouse_termcode |= HMT_URXVT;
1368 else
1369# endif
1370# ifdef FEAT_MOUSE_GPM
1371 if (n == KS_GPM_MOUSE)
1372 has_mouse_termcode |= HMT_GPM;
1373 else
1374# endif
1375 if (n == KS_SGR_MOUSE)
1376 has_mouse_termcode |= HMT_SGR;
1377 else if (n == KS_SGR_MOUSE_RELEASE)
1378 has_mouse_termcode |= HMT_SGR_REL;
1379 else
1380 has_mouse_termcode |= HMT_NORMAL;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001381}
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001382
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001383# if defined(UNIX) || defined(VMS) || defined(PROTO)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001384 void
1385del_mouse_termcode(
1386 int n) // KS_MOUSE, KS_NETTERM_MOUSE or KS_DEC_MOUSE
1387{
1388 char_u name[2];
1389
1390 name[0] = n;
1391 name[1] = KE_FILLER;
1392 del_termcode(name);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001393# ifdef FEAT_MOUSE_JSB
1394 if (n == KS_JSBTERM_MOUSE)
1395 has_mouse_termcode &= ~HMT_JSBTERM;
1396 else
1397# endif
1398# ifdef FEAT_MOUSE_NET
1399 if (n == KS_NETTERM_MOUSE)
1400 has_mouse_termcode &= ~HMT_NETTERM;
1401 else
1402# endif
1403# ifdef FEAT_MOUSE_DEC
1404 if (n == KS_DEC_MOUSE)
1405 has_mouse_termcode &= ~HMT_DEC;
1406 else
1407# endif
1408# ifdef FEAT_MOUSE_PTERM
1409 if (n == KS_PTERM_MOUSE)
1410 has_mouse_termcode &= ~HMT_PTERM;
1411 else
1412# endif
1413# ifdef FEAT_MOUSE_URXVT
1414 if (n == KS_URXVT_MOUSE)
1415 has_mouse_termcode &= ~HMT_URXVT;
1416 else
1417# endif
1418# ifdef FEAT_MOUSE_GPM
1419 if (n == KS_GPM_MOUSE)
1420 has_mouse_termcode &= ~HMT_GPM;
1421 else
1422# endif
1423 if (n == KS_SGR_MOUSE)
1424 has_mouse_termcode &= ~HMT_SGR;
1425 else if (n == KS_SGR_MOUSE_RELEASE)
1426 has_mouse_termcode &= ~HMT_SGR_REL;
1427 else
1428 has_mouse_termcode &= ~HMT_NORMAL;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001429}
1430# endif
1431
1432/*
1433 * setmouse() - switch mouse on/off depending on current mode and 'mouse'
1434 */
1435 void
1436setmouse(void)
1437{
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001438 int checkfor;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001439
1440# ifdef FEAT_MOUSESHAPE
1441 update_mouseshape(-1);
1442# endif
1443
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001444 // Should be outside proc, but may break MOUSESHAPE
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001445# ifdef FEAT_GUI
1446 // In the GUI the mouse is always enabled.
1447 if (gui.in_use)
1448 return;
1449# endif
1450 // be quick when mouse is off
1451 if (*p_mouse == NUL || has_mouse_termcode == 0)
1452 return;
1453
1454 // don't switch mouse on when not in raw mode (Ex mode)
1455 if (cur_tmode != TMODE_RAW)
1456 {
1457 mch_setmouse(FALSE);
1458 return;
1459 }
1460
1461 if (VIsual_active)
1462 checkfor = MOUSE_VISUAL;
Bram Moolenaar24959102022-05-07 20:01:16 +01001463 else if (State == MODE_HITRETURN || State == MODE_ASKMORE
1464 || State == MODE_SETWSIZE)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001465 checkfor = MOUSE_RETURN;
Bram Moolenaar24959102022-05-07 20:01:16 +01001466 else if (State & MODE_INSERT)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001467 checkfor = MOUSE_INSERT;
Bram Moolenaar24959102022-05-07 20:01:16 +01001468 else if (State & MODE_CMDLINE)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001469 checkfor = MOUSE_COMMAND;
Bram Moolenaar24959102022-05-07 20:01:16 +01001470 else if (State == MODE_CONFIRM || State == MODE_EXTERNCMD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001471 checkfor = ' '; // don't use mouse for ":confirm" or ":!cmd"
1472 else
1473 checkfor = MOUSE_NORMAL; // assume normal mode
1474
1475 if (mouse_has(checkfor))
1476 mch_setmouse(TRUE);
1477 else
1478 mch_setmouse(FALSE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001479}
1480
1481/*
1482 * Return TRUE if
1483 * - "c" is in 'mouse', or
1484 * - 'a' is in 'mouse' and "c" is in MOUSE_A, or
1485 * - the current buffer is a help file and 'h' is in 'mouse' and we are in a
1486 * normal editing mode (not at hit-return message).
1487 */
1488 int
1489mouse_has(int c)
1490{
1491 char_u *p;
1492
1493 for (p = p_mouse; *p; ++p)
1494 switch (*p)
1495 {
1496 case 'a': if (vim_strchr((char_u *)MOUSE_A, c) != NULL)
1497 return TRUE;
1498 break;
1499 case MOUSE_HELP: if (c != MOUSE_RETURN && curbuf->b_help)
1500 return TRUE;
1501 break;
1502 default: if (c == *p) return TRUE; break;
1503 }
1504 return FALSE;
1505}
1506
1507/*
1508 * Return TRUE when 'mousemodel' is set to "popup" or "popup_setpos".
1509 */
1510 int
1511mouse_model_popup(void)
1512{
1513 return (p_mousem[0] == 'p');
1514}
1515
1516/*
1517 * Move the cursor to the specified row and column on the screen.
1518 * Change current window if necessary. Returns an integer with the
1519 * CURSOR_MOVED bit set if the cursor has moved or unset otherwise.
1520 *
1521 * The MOUSE_FOLD_CLOSE bit is set when clicked on the '-' in a fold column.
1522 * The MOUSE_FOLD_OPEN bit is set when clicked on the '+' in a fold column.
1523 *
1524 * If flags has MOUSE_FOCUS, then the current window will not be changed, and
1525 * if the mouse is outside the window then the text will scroll, or if the
1526 * mouse was previously on a status line, then the status line may be dragged.
1527 *
1528 * If flags has MOUSE_MAY_VIS, then VIsual mode will be started before the
1529 * cursor is moved unless the cursor was on a status line.
1530 * This function returns one of IN_UNKNOWN, IN_BUFFER, IN_STATUS_LINE or
1531 * IN_SEP_LINE depending on where the cursor was clicked.
1532 *
1533 * If flags has MOUSE_MAY_STOP_VIS, then Visual mode will be stopped, unless
1534 * the mouse is on the status line of the same window.
1535 *
1536 * If flags has MOUSE_DID_MOVE, nothing is done if the mouse didn't move since
1537 * the last call.
1538 *
1539 * If flags has MOUSE_SETPOS, nothing is done, only the current position is
1540 * remembered.
1541 */
1542 int
1543jump_to_mouse(
1544 int flags,
1545 int *inclusive, // used for inclusive operator, can be NULL
1546 int which_button) // MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE
1547{
1548 static int on_status_line = 0; // #lines below bottom of window
1549 static int on_sep_line = 0; // on separator right of window
1550#ifdef FEAT_MENU
1551 static int in_winbar = FALSE;
1552#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001553#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001554 static int in_popup_win = FALSE;
1555 static win_T *click_in_popup_win = NULL;
1556#endif
1557 static int prev_row = -1;
1558 static int prev_col = -1;
1559 static win_T *dragwin = NULL; // window being dragged
1560 static int did_drag = FALSE; // drag was noticed
1561
1562 win_T *wp, *old_curwin;
1563 pos_T old_cursor;
1564 int count;
1565 int first;
1566 int row = mouse_row;
1567 int col = mouse_col;
Bram Moolenaarb9081882022-07-09 04:56:24 +01001568 colnr_T col_from_screen = -1;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001569#ifdef FEAT_FOLDING
Bram Moolenaarb9081882022-07-09 04:56:24 +01001570 int mouse_char = ' ';
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001571#endif
1572
1573 mouse_past_bottom = FALSE;
1574 mouse_past_eol = FALSE;
1575
1576 if (flags & MOUSE_RELEASED)
1577 {
1578 // On button release we may change window focus if positioned on a
1579 // status line and no dragging happened.
1580 if (dragwin != NULL && !did_drag)
1581 flags &= ~(MOUSE_FOCUS | MOUSE_DID_MOVE);
1582 dragwin = NULL;
1583 did_drag = FALSE;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001584#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001585 if (click_in_popup_win != NULL && popup_dragwin == NULL)
1586 popup_close_for_mouse_click(click_in_popup_win);
1587
1588 popup_dragwin = NULL;
1589 click_in_popup_win = NULL;
1590#endif
1591 }
1592
1593 if ((flags & MOUSE_DID_MOVE)
1594 && prev_row == mouse_row
1595 && prev_col == mouse_col)
1596 {
1597retnomove:
1598 // before moving the cursor for a left click which is NOT in a status
1599 // line, stop Visual mode
1600 if (on_status_line)
1601 return IN_STATUS_LINE;
1602 if (on_sep_line)
1603 return IN_SEP_LINE;
1604#ifdef FEAT_MENU
1605 if (in_winbar)
1606 {
1607 // A quick second click may arrive as a double-click, but we use it
1608 // as a second click in the WinBar.
1609 if ((mod_mask & MOD_MASK_MULTI_CLICK) && !(flags & MOUSE_RELEASED))
1610 {
1611 wp = mouse_find_win(&row, &col, FAIL_POPUP);
1612 if (wp == NULL)
1613 return IN_UNKNOWN;
1614 winbar_click(wp, col);
1615 }
1616 return IN_OTHER_WIN | MOUSE_WINBAR;
1617 }
1618#endif
1619 if (flags & MOUSE_MAY_STOP_VIS)
1620 {
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +02001621 end_visual_mode_keep_button();
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001622 redraw_curbuf_later(UPD_INVERTED); // delete the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001623 }
Martin Tournoij7904fa42022-10-04 16:28:45 +01001624#if defined(FEAT_CLIPBOARD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001625 // Continue a modeless selection in another window.
1626 if (cmdwin_type != 0 && row < curwin->w_winrow)
1627 return IN_OTHER_WIN;
1628#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001629#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001630 // Continue a modeless selection in a popup window or dragging it.
1631 if (in_popup_win)
1632 {
1633 click_in_popup_win = NULL; // don't close it on release
1634 if (popup_dragwin != NULL)
1635 {
1636 // dragging a popup window
1637 popup_drag(popup_dragwin);
1638 return IN_UNKNOWN;
1639 }
1640 return IN_OTHER_WIN;
1641 }
1642#endif
1643 return IN_BUFFER;
1644 }
1645
1646 prev_row = mouse_row;
1647 prev_col = mouse_col;
1648
1649 if (flags & MOUSE_SETPOS)
1650 goto retnomove; // ugly goto...
1651
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001652 old_curwin = curwin;
1653 old_cursor = curwin->w_cursor;
1654
1655 if (!(flags & MOUSE_FOCUS))
1656 {
1657 if (row < 0 || col < 0) // check if it makes sense
1658 return IN_UNKNOWN;
1659
1660 // find the window where the row is in and adjust "row" and "col" to be
1661 // relative to top-left of the window
1662 wp = mouse_find_win(&row, &col, FIND_POPUP);
1663 if (wp == NULL)
1664 return IN_UNKNOWN;
1665 dragwin = NULL;
1666
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001667#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001668 // Click in a popup window may start dragging or modeless selection,
1669 // but not much else.
1670 if (WIN_IS_POPUP(wp))
1671 {
1672 on_sep_line = 0;
Bram Moolenaarbfc57862021-11-26 15:57:40 +00001673 on_status_line = 0;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001674 in_popup_win = TRUE;
1675 if (which_button == MOUSE_LEFT && popup_close_if_on_X(wp, row, col))
1676 {
1677 return IN_UNKNOWN;
1678 }
Bram Moolenaar0b74d002021-11-29 17:38:02 +00001679 else if (((wp->w_popup_flags & (POPF_DRAG | POPF_RESIZE))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001680 && popup_on_border(wp, row, col))
Bram Moolenaar0b74d002021-11-29 17:38:02 +00001681 || (wp->w_popup_flags & POPF_DRAGALL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001682 {
1683 popup_dragwin = wp;
1684 popup_start_drag(wp, row, col);
1685 return IN_UNKNOWN;
1686 }
1687 // Only close on release, otherwise it's not possible to drag or do
1688 // modeless selection.
1689 else if (wp->w_popup_close == POPCLOSE_CLICK
1690 && which_button == MOUSE_LEFT)
1691 {
1692 click_in_popup_win = wp;
1693 }
1694 else if (which_button == MOUSE_LEFT)
1695 // If the click is in the scrollbar, may scroll up/down.
1696 popup_handle_scrollbar_click(wp, row, col);
1697# ifdef FEAT_CLIPBOARD
1698 return IN_OTHER_WIN;
1699# else
1700 return IN_UNKNOWN;
1701# endif
1702 }
1703 in_popup_win = FALSE;
1704 popup_dragwin = NULL;
1705#endif
1706#ifdef FEAT_MENU
1707 if (row == -1)
1708 {
1709 // A click in the window toolbar does not enter another window or
1710 // change Visual highlighting.
1711 winbar_click(wp, col);
1712 in_winbar = TRUE;
1713 return IN_OTHER_WIN | MOUSE_WINBAR;
1714 }
1715 in_winbar = FALSE;
1716#endif
1717
1718 // winpos and height may change in win_enter()!
1719 if (row >= wp->w_height) // In (or below) status line
1720 {
1721 on_status_line = row - wp->w_height + 1;
1722 dragwin = wp;
1723 }
1724 else
1725 on_status_line = 0;
1726 if (col >= wp->w_width) // In separator line
1727 {
1728 on_sep_line = col - wp->w_width + 1;
1729 dragwin = wp;
1730 }
1731 else
1732 on_sep_line = 0;
1733
1734 // The rightmost character of the status line might be a vertical
1735 // separator character if there is no connecting window to the right.
1736 if (on_status_line && on_sep_line)
1737 {
1738 if (stl_connected(wp))
1739 on_sep_line = 0;
1740 else
1741 on_status_line = 0;
1742 }
1743
1744 // Before jumping to another buffer, or moving the cursor for a left
1745 // click, stop Visual mode.
1746 if (VIsual_active
1747 && (wp->w_buffer != curwin->w_buffer
1748 || (!on_status_line && !on_sep_line
1749#ifdef FEAT_FOLDING
1750 && (
1751# ifdef FEAT_RIGHTLEFT
1752 wp->w_p_rl ? col < wp->w_width - wp->w_p_fdc :
1753# endif
Martin Tournoij7904fa42022-10-04 16:28:45 +01001754 col >= wp->w_p_fdc + (cmdwin_type == 0 && wp == curwin ? 0 : 1)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001755 )
1756#endif
1757 && (flags & MOUSE_MAY_STOP_VIS))))
1758 {
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +02001759 end_visual_mode_keep_button();
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001760 redraw_curbuf_later(UPD_INVERTED); // delete the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001761 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001762 if (cmdwin_type != 0 && wp != curwin)
1763 {
1764 // A click outside the command-line window: Use modeless
1765 // selection if possible. Allow dragging the status lines.
1766 on_sep_line = 0;
1767# ifdef FEAT_CLIPBOARD
1768 if (on_status_line)
1769 return IN_STATUS_LINE;
1770 return IN_OTHER_WIN;
1771# else
1772 row = 0;
1773 col += wp->w_wincol;
1774 wp = curwin;
1775# endif
1776 }
Bram Moolenaar219c7d02020-02-01 21:57:29 +01001777#if defined(FEAT_PROP_POPUP) && defined(FEAT_TERMINAL)
1778 if (popup_is_popup(curwin) && curbuf->b_term != NULL)
1779 // terminal in popup window: don't jump to another window
1780 return IN_OTHER_WIN;
1781#endif
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001782 // Only change window focus when not clicking on or dragging the
1783 // status line. Do change focus when releasing the mouse button
1784 // (MOUSE_FOCUS was set above if we dragged first).
1785 if (dragwin == NULL || (flags & MOUSE_RELEASED))
1786 win_enter(wp, TRUE); // can make wp invalid!
1787
1788 if (curwin != old_curwin)
1789 {
1790#ifdef CHECK_DOUBLE_CLICK
1791 // set topline, to be able to check for double click ourselves
1792 set_mouse_topline(curwin);
1793#endif
1794#ifdef FEAT_TERMINAL
1795 // when entering a terminal window may change state
1796 term_win_entered();
1797#endif
1798 }
1799 if (on_status_line) // In (or below) status line
1800 {
1801 // Don't use start_arrow() if we're in the same window
1802 if (curwin == old_curwin)
1803 return IN_STATUS_LINE;
1804 else
1805 return IN_STATUS_LINE | CURSOR_MOVED;
1806 }
1807 if (on_sep_line) // In (or below) status line
1808 {
1809 // Don't use start_arrow() if we're in the same window
1810 if (curwin == old_curwin)
1811 return IN_SEP_LINE;
1812 else
1813 return IN_SEP_LINE | CURSOR_MOVED;
1814 }
1815
1816 curwin->w_cursor.lnum = curwin->w_topline;
1817#ifdef FEAT_GUI
1818 // remember topline, needed for double click
1819 gui_prev_topline = curwin->w_topline;
1820# ifdef FEAT_DIFF
1821 gui_prev_topfill = curwin->w_topfill;
1822# endif
1823#endif
1824 }
1825 else if (on_status_line && which_button == MOUSE_LEFT)
1826 {
1827 if (dragwin != NULL)
1828 {
1829 // Drag the status line
zeertzjq6dab00a2022-05-20 13:45:59 +01001830 count = row - W_WINROW(dragwin) - dragwin->w_height + 1
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001831 - on_status_line;
1832 win_drag_status_line(dragwin, count);
1833 did_drag |= count;
1834 }
1835 return IN_STATUS_LINE; // Cursor didn't move
1836 }
1837 else if (on_sep_line && which_button == MOUSE_LEFT)
1838 {
1839 if (dragwin != NULL)
1840 {
1841 // Drag the separator column
1842 count = col - dragwin->w_wincol - dragwin->w_width + 1
1843 - on_sep_line;
1844 win_drag_vsep_line(dragwin, count);
1845 did_drag |= count;
1846 }
1847 return IN_SEP_LINE; // Cursor didn't move
1848 }
1849#ifdef FEAT_MENU
1850 else if (in_winbar)
1851 {
1852 // After a click on the window toolbar don't start Visual mode.
1853 return IN_OTHER_WIN | MOUSE_WINBAR;
1854 }
1855#endif
1856 else // keep_window_focus must be TRUE
1857 {
1858 // before moving the cursor for a left click, stop Visual mode
1859 if (flags & MOUSE_MAY_STOP_VIS)
1860 {
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +02001861 end_visual_mode_keep_button();
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001862 redraw_curbuf_later(UPD_INVERTED); // delete the inversion
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001863 }
1864
Martin Tournoij7904fa42022-10-04 16:28:45 +01001865#if defined(FEAT_CLIPBOARD)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001866 // Continue a modeless selection in another window.
1867 if (cmdwin_type != 0 && row < curwin->w_winrow)
1868 return IN_OTHER_WIN;
1869#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001870#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001871 if (in_popup_win)
1872 {
1873 if (popup_dragwin != NULL)
1874 {
1875 // dragging a popup window
1876 popup_drag(popup_dragwin);
1877 return IN_UNKNOWN;
1878 }
1879 // continue a modeless selection in a popup window
1880 click_in_popup_win = NULL;
1881 return IN_OTHER_WIN;
1882 }
1883#endif
1884
1885 row -= W_WINROW(curwin);
1886 col -= curwin->w_wincol;
1887
1888 // When clicking beyond the end of the window, scroll the screen.
1889 // Scroll by however many rows outside the window we are.
1890 if (row < 0)
1891 {
1892 count = 0;
1893 for (first = TRUE; curwin->w_topline > 1; )
1894 {
1895#ifdef FEAT_DIFF
1896 if (curwin->w_topfill < diff_check(curwin, curwin->w_topline))
1897 ++count;
1898 else
1899#endif
1900 count += plines(curwin->w_topline - 1);
1901 if (!first && count > -row)
1902 break;
1903 first = FALSE;
1904#ifdef FEAT_FOLDING
1905 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
1906#endif
1907#ifdef FEAT_DIFF
1908 if (curwin->w_topfill < diff_check(curwin, curwin->w_topline))
1909 ++curwin->w_topfill;
1910 else
1911#endif
1912 {
1913 --curwin->w_topline;
1914#ifdef FEAT_DIFF
1915 curwin->w_topfill = 0;
1916#endif
1917 }
1918 }
1919#ifdef FEAT_DIFF
1920 check_topfill(curwin, FALSE);
1921#endif
1922 curwin->w_valid &=
1923 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001924 redraw_later(UPD_VALID);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001925 row = 0;
1926 }
1927 else if (row >= curwin->w_height)
1928 {
1929 count = 0;
1930 for (first = TRUE; curwin->w_topline < curbuf->b_ml.ml_line_count; )
1931 {
1932#ifdef FEAT_DIFF
1933 if (curwin->w_topfill > 0)
1934 ++count;
1935 else
1936#endif
1937 count += plines(curwin->w_topline);
1938 if (!first && count > row - curwin->w_height + 1)
1939 break;
1940 first = FALSE;
1941#ifdef FEAT_FOLDING
1942 if (hasFolding(curwin->w_topline, NULL, &curwin->w_topline)
1943 && curwin->w_topline == curbuf->b_ml.ml_line_count)
1944 break;
1945#endif
1946#ifdef FEAT_DIFF
1947 if (curwin->w_topfill > 0)
1948 --curwin->w_topfill;
1949 else
1950#endif
1951 {
1952 ++curwin->w_topline;
1953#ifdef FEAT_DIFF
1954 curwin->w_topfill =
1955 diff_check_fill(curwin, curwin->w_topline);
1956#endif
1957 }
1958 }
1959#ifdef FEAT_DIFF
1960 check_topfill(curwin, FALSE);
1961#endif
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001962 redraw_later(UPD_VALID);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001963 curwin->w_valid &=
1964 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
1965 row = curwin->w_height - 1;
1966 }
1967 else if (row == 0)
1968 {
1969 // When dragging the mouse, while the text has been scrolled up as
1970 // far as it goes, moving the mouse in the top line should scroll
1971 // the text down (done later when recomputing w_topline).
1972 if (mouse_dragging > 0
1973 && curwin->w_cursor.lnum
1974 == curwin->w_buffer->b_ml.ml_line_count
1975 && curwin->w_cursor.lnum == curwin->w_topline)
1976 curwin->w_valid &= ~(VALID_TOPLINE);
1977 }
1978 }
1979
Bram Moolenaarb9081882022-07-09 04:56:24 +01001980 if (prev_row >= 0 && prev_row < Rows && prev_col >= 0 && prev_col <= Columns
1981 && ScreenLines != NULL)
1982 {
1983 int off = LineOffset[prev_row] + prev_col;
1984
1985 // Only use ScreenCols[] after the window was redrawn. Mainly matters
1986 // for tests, a user would not click before redrawing.
Bram Moolenaar8f49e692022-08-09 14:19:40 +01001987 // Do not use when 'virtualedit' is active.
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001988 if (curwin->w_redr_type <= UPD_VALID_NO_UPDATE && !virtual_active())
Bram Moolenaarb9081882022-07-09 04:56:24 +01001989 col_from_screen = ScreenCols[off];
1990#ifdef FEAT_FOLDING
1991 // Remember the character under the mouse, it might be a '-' or '+' in
1992 // the fold column.
1993 mouse_char = ScreenLines[off];
1994#endif
1995 }
1996
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001997#ifdef FEAT_FOLDING
1998 // Check for position outside of the fold column.
1999 if (
2000# ifdef FEAT_RIGHTLEFT
2001 curwin->w_p_rl ? col < curwin->w_width - curwin->w_p_fdc :
2002# endif
Martin Tournoij7904fa42022-10-04 16:28:45 +01002003 col >= curwin->w_p_fdc + (cmdwin_type == 0 ? 0 : 1)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002004 )
2005 mouse_char = ' ';
2006#endif
2007
2008 // compute the position in the buffer line from the posn on the screen
2009 if (mouse_comp_pos(curwin, &row, &col, &curwin->w_cursor.lnum, NULL))
2010 mouse_past_bottom = TRUE;
2011
2012 // Start Visual mode before coladvance(), for when 'sel' != "old"
2013 if ((flags & MOUSE_MAY_VIS) && !VIsual_active)
2014 {
2015 check_visual_highlight();
2016 VIsual = old_cursor;
2017 VIsual_active = TRUE;
2018 VIsual_reselect = TRUE;
2019 // if 'selectmode' contains "mouse", start Select mode
2020 may_start_select('o');
2021 setmouse();
2022 if (p_smd && msg_silent == 0)
2023 redraw_cmdline = TRUE; // show visual mode later
2024 }
2025
Bram Moolenaarb9081882022-07-09 04:56:24 +01002026 if (col_from_screen >= 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002027 {
Bram Moolenaarb9081882022-07-09 04:56:24 +01002028 // Use the column from ScreenCols[], it is accurate also after
2029 // concealed characters.
2030 curwin->w_cursor.col = col_from_screen;
2031 if (col_from_screen == MAXCOL)
2032 {
2033 curwin->w_curswant = col_from_screen;
2034 curwin->w_set_curswant = FALSE; // May still have been TRUE
2035 mouse_past_eol = TRUE;
2036 if (inclusive != NULL)
2037 *inclusive = TRUE;
2038 }
2039 else
2040 {
2041 curwin->w_set_curswant = TRUE;
2042 if (inclusive != NULL)
2043 *inclusive = FALSE;
2044 }
2045 check_cursor_col();
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002046 }
Bram Moolenaarb9081882022-07-09 04:56:24 +01002047 else
2048 {
2049 curwin->w_curswant = col;
2050 curwin->w_set_curswant = FALSE; // May still have been TRUE
2051 if (coladvance(col) == FAIL) // Mouse click beyond end of line
2052 {
2053 if (inclusive != NULL)
2054 *inclusive = TRUE;
2055 mouse_past_eol = TRUE;
2056 }
2057 else if (inclusive != NULL)
2058 *inclusive = FALSE;
2059 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002060
2061 count = IN_BUFFER;
2062 if (curwin != old_curwin || curwin->w_cursor.lnum != old_cursor.lnum
2063 || curwin->w_cursor.col != old_cursor.col)
2064 count |= CURSOR_MOVED; // Cursor has moved
2065
2066# ifdef FEAT_FOLDING
Bram Moolenaar96ba25a2022-07-04 17:34:33 +01002067 if (mouse_char == curwin->w_fill_chars.foldclosed)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002068 count |= MOUSE_FOLD_OPEN;
2069 else if (mouse_char != ' ')
2070 count |= MOUSE_FOLD_CLOSE;
2071# endif
2072
2073 return count;
2074}
2075
2076/*
LemonBoyc27747e2022-05-07 12:25:40 +01002077 * Mouse scroll wheel: Default action is to scroll mouse_vert_step lines (or
2078 * mouse_hor_step, depending on the scroll direction), or one page when Shift or
2079 * Ctrl is used.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002080 * K_MOUSEUP (cap->arg == 1) or K_MOUSEDOWN (cap->arg == 0) or
2081 * K_MOUSELEFT (cap->arg == -1) or K_MOUSERIGHT (cap->arg == -2)
2082 */
2083 void
2084nv_mousescroll(cmdarg_T *cap)
2085{
2086 win_T *old_curwin = curwin, *wp;
2087
2088 if (mouse_row >= 0 && mouse_col >= 0)
2089 {
2090 int row, col;
2091
2092 row = mouse_row;
2093 col = mouse_col;
2094
2095 // find the window at the pointer coordinates
2096 wp = mouse_find_win(&row, &col, FIND_POPUP);
2097 if (wp == NULL)
2098 return;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002099#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002100 if (WIN_IS_POPUP(wp) && !wp->w_has_scrollbar)
2101 return;
2102#endif
2103 curwin = wp;
2104 curbuf = curwin->w_buffer;
2105 }
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002106 if (cap->arg == MSCR_UP || cap->arg == MSCR_DOWN)
2107 {
2108# ifdef FEAT_TERMINAL
2109 if (term_use_loop())
2110 // This window is a terminal window, send the mouse event there.
2111 // Set "typed" to FALSE to avoid an endless loop.
Bram Moolenaar1e814bc2019-11-03 21:19:41 +01002112 send_keys_to_term(curbuf->b_term, cap->cmdchar, mod_mask, FALSE);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002113 else
2114# endif
LemonBoyc27747e2022-05-07 12:25:40 +01002115 if (mouse_vert_step < 0 || mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002116 {
2117 (void)onepage(cap->arg ? FORWARD : BACKWARD, 1L);
2118 }
2119 else
2120 {
2121 // Don't scroll more than half the window height.
LemonBoyc27747e2022-05-07 12:25:40 +01002122 if (curwin->w_height < mouse_vert_step * 2)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002123 {
2124 cap->count1 = curwin->w_height / 2;
2125 if (cap->count1 == 0)
2126 cap->count1 = 1;
2127 }
2128 else
LemonBoyc27747e2022-05-07 12:25:40 +01002129 cap->count1 = mouse_vert_step;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002130 cap->count0 = cap->count1;
2131 nv_scroll_line(cap);
2132 }
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002133#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002134 if (WIN_IS_POPUP(curwin))
2135 popup_set_firstline(curwin);
2136#endif
2137 }
2138# ifdef FEAT_GUI
2139 else
2140 {
2141 // Horizontal scroll - only allowed when 'wrap' is disabled
2142 if (!curwin->w_p_wrap)
2143 {
LemonBoyc27747e2022-05-07 12:25:40 +01002144 int val, step;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002145
LemonBoyc27747e2022-05-07 12:25:40 +01002146 if (mouse_hor_step < 0
2147 || mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002148 step = curwin->w_width;
LemonBoyc27747e2022-05-07 12:25:40 +01002149 else
2150 step = mouse_hor_step;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002151 val = curwin->w_leftcol + (cap->arg == MSCR_RIGHT ? -step : +step);
2152 if (val < 0)
2153 val = 0;
2154
2155 gui_do_horiz_scroll(val, TRUE);
2156 }
2157 }
2158# endif
2159# ifdef FEAT_SYN_HL
2160 if (curwin != old_curwin && curwin->w_p_cul)
2161 redraw_for_cursorline(curwin);
2162# endif
LemonBoy66e13ae2022-04-21 22:52:11 +01002163 may_trigger_winscrolled();
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002164
2165 curwin->w_redr_status = TRUE;
2166
2167 curwin = old_curwin;
2168 curbuf = curwin->w_buffer;
2169}
2170
2171/*
2172 * Mouse clicks and drags.
2173 */
2174 void
2175nv_mouse(cmdarg_T *cap)
2176{
2177 (void)do_mouse(cap->oap, cap->cmdchar, BACKWARD, cap->count1, 0);
2178}
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002179
Bram Moolenaar85eee5b2021-06-03 20:34:57 +02002180static int held_button = MOUSE_RELEASE;
2181
2182 void
2183reset_held_button()
2184{
2185 held_button = MOUSE_RELEASE;
2186}
2187
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002188/*
2189 * Check if typebuf 'tp' contains a terminal mouse code and returns the
2190 * modifiers found in typebuf in 'modifiers'.
2191 */
2192 int
2193check_termcode_mouse(
2194 char_u *tp,
2195 int *slen,
2196 char_u *key_name,
2197 char_u *modifiers_start,
2198 int idx,
2199 int *modifiers)
2200{
2201 int j;
2202 char_u *p;
2203# if !defined(UNIX) || defined(FEAT_MOUSE_XTERM) || defined(FEAT_GUI) \
2204 || defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE)
2205 char_u bytes[6];
2206 int num_bytes;
2207# endif
2208 int mouse_code = 0; // init for GCC
2209 int is_click, is_drag;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002210 int is_release, release_is_ambiguous;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002211 int wheel_code = 0;
2212 int current_button;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002213 static int orig_num_clicks = 1;
2214 static int orig_mouse_code = 0x0;
2215# ifdef CHECK_DOUBLE_CLICK
2216 static int orig_mouse_col = 0;
2217 static int orig_mouse_row = 0;
2218 static struct timeval orig_mouse_time = {0, 0};
2219 // time of previous mouse click
2220 struct timeval mouse_time; // time of current mouse click
2221 long timediff; // elapsed time in msec
2222# endif
2223
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002224 is_click = is_drag = is_release = release_is_ambiguous = FALSE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002225
2226# if !defined(UNIX) || defined(FEAT_MOUSE_XTERM) || defined(FEAT_GUI) \
2227 || defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE)
2228 if (key_name[0] == KS_MOUSE
2229# ifdef FEAT_MOUSE_GPM
2230 || key_name[0] == KS_GPM_MOUSE
2231# endif
2232 )
2233 {
2234 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002235 * For xterm we get "<t_mouse>scr", where s == encoded button state:
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002236 * 0x20 = left button down
2237 * 0x21 = middle button down
2238 * 0x22 = right button down
2239 * 0x23 = any button release
2240 * 0x60 = button 4 down (scroll wheel down)
2241 * 0x61 = button 5 down (scroll wheel up)
2242 * add 0x04 for SHIFT
2243 * add 0x08 for ALT
2244 * add 0x10 for CTRL
2245 * add 0x20 for mouse drag (0x40 is drag with left button)
2246 * add 0x40 for mouse move (0x80 is move, 0x81 too)
2247 * 0x43 (drag + release) is also move
2248 * c == column + ' ' + 1 == column + 33
2249 * r == row + ' ' + 1 == row + 33
2250 *
Bram Moolenaar13c04632020-07-12 13:47:42 +02002251 * The coordinates are passed on through global variables. Ugly, but
2252 * this avoids trouble with mouse clicks at an unexpected moment and
2253 * allows for mapping them.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002254 */
2255 for (;;)
2256 {
2257# ifdef FEAT_GUI
2258 if (gui.in_use)
2259 {
2260 // GUI uses more bits for columns > 223
2261 num_bytes = get_bytes_from_buf(tp + *slen, bytes, 5);
2262 if (num_bytes == -1) // not enough coordinates
2263 return -1;
2264 mouse_code = bytes[0];
2265 mouse_col = 128 * (bytes[1] - ' ' - 1)
2266 + bytes[2] - ' ' - 1;
2267 mouse_row = 128 * (bytes[3] - ' ' - 1)
2268 + bytes[4] - ' ' - 1;
2269 }
2270 else
2271# endif
2272 {
2273 num_bytes = get_bytes_from_buf(tp + *slen, bytes, 3);
2274 if (num_bytes == -1) // not enough coordinates
2275 return -1;
2276 mouse_code = bytes[0];
2277 mouse_col = bytes[1] - ' ' - 1;
2278 mouse_row = bytes[2] - ' ' - 1;
2279 }
2280 *slen += num_bytes;
2281
Bram Moolenaar13c04632020-07-12 13:47:42 +02002282 // If the following bytes is also a mouse code and it has the same
2283 // code, dump this one and get the next. This makes dragging a
2284 // whole lot faster.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002285# ifdef FEAT_GUI
2286 if (gui.in_use)
2287 j = 3;
2288 else
2289# endif
2290 j = get_termcode_len(idx);
2291 if (STRNCMP(tp, tp + *slen, (size_t)j) == 0
2292 && tp[*slen + j] == mouse_code
2293 && tp[*slen + j + 1] != NUL
2294 && tp[*slen + j + 2] != NUL
2295# ifdef FEAT_GUI
2296 && (!gui.in_use
2297 || (tp[*slen + j + 3] != NUL
2298 && tp[*slen + j + 4] != NUL))
2299# endif
2300 )
2301 *slen += j;
2302 else
2303 break;
2304 }
2305 }
2306
2307 if (key_name[0] == KS_URXVT_MOUSE
2308 || key_name[0] == KS_SGR_MOUSE
2309 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2310 {
2311 // URXVT 1015 mouse reporting mode:
Bram Moolenaar13c04632020-07-12 13:47:42 +02002312 // Almost identical to xterm mouse mode, except the values are decimal
2313 // instead of bytes.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002314 //
2315 // \033[%d;%d;%dM
2316 // ^-- row
2317 // ^----- column
2318 // ^-------- code
2319 //
2320 // SGR 1006 mouse reporting mode:
Bram Moolenaar13c04632020-07-12 13:47:42 +02002321 // Almost identical to xterm mouse mode, except the values are decimal
2322 // instead of bytes.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002323 //
2324 // \033[<%d;%d;%dM
2325 // ^-- row
2326 // ^----- column
2327 // ^-------- code
2328 //
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002329 // \033[<%d;%d;%dm : mouse release event
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002330 // ^-- row
2331 // ^----- column
2332 // ^-------- code
2333 p = modifiers_start;
2334 if (p == NULL)
2335 return -1;
2336
2337 mouse_code = getdigits(&p);
2338 if (*p++ != ';')
2339 return -1;
2340
2341 // when mouse reporting is SGR, add 32 to mouse code
2342 if (key_name[0] == KS_SGR_MOUSE
2343 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2344 mouse_code += 32;
2345
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002346 mouse_col = getdigits(&p) - 1;
2347 if (*p++ != ';')
2348 return -1;
2349
2350 mouse_row = getdigits(&p) - 1;
2351
Bram Moolenaar13c04632020-07-12 13:47:42 +02002352 // The modifiers were the mouse coordinates, not the modifier keys
2353 // (alt/shift/ctrl/meta) state.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002354 *modifiers = 0;
2355 }
2356
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002357 if (key_name[0] == KS_SGR_MOUSE
2358 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2359 {
2360 if (key_name[0] == KS_SGR_MOUSE_RELEASE)
Bram Moolenaar13c04632020-07-12 13:47:42 +02002361 {
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002362 is_release = TRUE;
Bram Moolenaar13c04632020-07-12 13:47:42 +02002363 // This is used below to set held_button.
2364 mouse_code |= MOUSE_RELEASE;
2365 }
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002366 }
2367 else
2368 {
2369 release_is_ambiguous = TRUE;
2370 if ((mouse_code & MOUSE_RELEASE) == MOUSE_RELEASE)
2371 is_release = TRUE;
2372 }
2373
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002374 if (key_name[0] == KS_MOUSE
2375# ifdef FEAT_MOUSE_GPM
2376 || key_name[0] == KS_GPM_MOUSE
2377# endif
2378# ifdef FEAT_MOUSE_URXVT
2379 || key_name[0] == KS_URXVT_MOUSE
2380# endif
2381 || key_name[0] == KS_SGR_MOUSE
2382 || key_name[0] == KS_SGR_MOUSE_RELEASE)
2383 {
2384# if !defined(MSWIN)
2385 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002386 * Handle old style mouse events.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002387 * Recognize the xterm mouse wheel, but not in the GUI, the
2388 * Linux console with GPM and the MS-DOS or Win32 console
2389 * (multi-clicks use >= 0x60).
2390 */
2391 if (mouse_code >= MOUSEWHEEL_LOW
2392# ifdef FEAT_GUI
2393 && !gui.in_use
2394# endif
2395# ifdef FEAT_MOUSE_GPM
2396 && key_name[0] != KS_GPM_MOUSE
2397# endif
2398 )
2399 {
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02002400# if defined(UNIX)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002401 if (use_xterm_mouse() > 1 && mouse_code >= 0x80)
2402 // mouse-move event, using MOUSE_DRAG works
2403 mouse_code = MOUSE_DRAG;
2404 else
2405# endif
2406 // Keep the mouse_code before it's changed, so that we
2407 // remember that it was a mouse wheel click.
2408 wheel_code = mouse_code;
2409 }
2410# ifdef FEAT_MOUSE_XTERM
2411 else if (held_button == MOUSE_RELEASE
2412# ifdef FEAT_GUI
2413 && !gui.in_use
2414# endif
2415 && (mouse_code == 0x23 || mouse_code == 0x24
2416 || mouse_code == 0x40 || mouse_code == 0x41))
2417 {
2418 // Apparently 0x23 and 0x24 are used by rxvt scroll wheel.
2419 // And 0x40 and 0x41 are used by some xterm emulator.
2420 wheel_code = mouse_code - (mouse_code >= 0x40 ? 0x40 : 0x23)
Bram Moolenaard6212b82022-08-03 15:48:33 +01002421 + MOUSEWHEEL_LOW;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002422 }
2423# endif
2424
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02002425# if defined(UNIX)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002426 else if (use_xterm_mouse() > 1)
2427 {
2428 if (mouse_code & MOUSE_DRAG_XTERM)
2429 mouse_code |= MOUSE_DRAG;
2430 }
2431# endif
2432# ifdef FEAT_XCLIPBOARD
2433 else if (!(mouse_code & MOUSE_DRAG & ~MOUSE_CLICK_MASK))
2434 {
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002435 if (is_release)
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002436 stop_xterm_trace();
2437 else
2438 start_xterm_trace(mouse_code);
2439 }
2440# endif
2441# endif
2442 }
2443# endif // !UNIX || FEAT_MOUSE_XTERM
2444# ifdef FEAT_MOUSE_NET
2445 if (key_name[0] == KS_NETTERM_MOUSE)
2446 {
2447 int mc, mr;
2448
2449 // expect a rather limited sequence like: balancing {
2450 // \033}6,45\r
2451 // '6' is the row, 45 is the column
2452 p = tp + *slen;
2453 mr = getdigits(&p);
2454 if (*p++ != ',')
2455 return -1;
2456 mc = getdigits(&p);
2457 if (*p++ != '\r')
2458 return -1;
2459
2460 mouse_col = mc - 1;
2461 mouse_row = mr - 1;
2462 mouse_code = MOUSE_LEFT;
2463 *slen += (int)(p - (tp + *slen));
2464 }
2465# endif // FEAT_MOUSE_NET
2466# ifdef FEAT_MOUSE_JSB
2467 if (key_name[0] == KS_JSBTERM_MOUSE)
2468 {
2469 int mult, val, iter, button, status;
2470
2471 /*
2472 * JSBTERM Input Model
2473 * \033[0~zw uniq escape sequence
2474 * (L-x) Left button pressed - not pressed x not reporting
2475 * (M-x) Middle button pressed - not pressed x not reporting
2476 * (R-x) Right button pressed - not pressed x not reporting
Bram Moolenaar13c04632020-07-12 13:47:42 +02002477 * (SDmdu) Single , Double click, m: mouse move, d: button down,
2478 * u: button up
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002479 * ### X cursor position padded to 3 digits
2480 * ### Y cursor position padded to 3 digits
2481 * (s-x) SHIFT key pressed - not pressed x not reporting
2482 * (c-x) CTRL key pressed - not pressed x not reporting
2483 * \033\\ terminating sequence
2484 */
2485 p = tp + *slen;
2486 button = mouse_code = 0;
2487 switch (*p++)
2488 {
2489 case 'L': button = 1; break;
2490 case '-': break;
2491 case 'x': break; // ignore sequence
2492 default: return -1; // Unknown Result
2493 }
2494 switch (*p++)
2495 {
2496 case 'M': button |= 2; break;
2497 case '-': break;
2498 case 'x': break; // ignore sequence
2499 default: return -1; // Unknown Result
2500 }
2501 switch (*p++)
2502 {
2503 case 'R': button |= 4; break;
2504 case '-': break;
2505 case 'x': break; // ignore sequence
2506 default: return -1; // Unknown Result
2507 }
2508 status = *p++;
2509 for (val = 0, mult = 100, iter = 0; iter < 3; iter++,
2510 mult /= 10, p++)
2511 if (*p >= '0' && *p <= '9')
2512 val += (*p - '0') * mult;
2513 else
2514 return -1;
2515 mouse_col = val;
2516 for (val = 0, mult = 100, iter = 0; iter < 3; iter++,
2517 mult /= 10, p++)
2518 if (*p >= '0' && *p <= '9')
2519 val += (*p - '0') * mult;
2520 else
2521 return -1;
2522 mouse_row = val;
2523 switch (*p++)
2524 {
2525 case 's': button |= 8; break; // SHIFT key Pressed
2526 case '-': break; // Not Pressed
2527 case 'x': break; // Not Reporting
2528 default: return -1; // Unknown Result
2529 }
2530 switch (*p++)
2531 {
2532 case 'c': button |= 16; break; // CTRL key Pressed
2533 case '-': break; // Not Pressed
2534 case 'x': break; // Not Reporting
2535 default: return -1; // Unknown Result
2536 }
2537 if (*p++ != '\033')
2538 return -1;
2539 if (*p++ != '\\')
2540 return -1;
2541 switch (status)
2542 {
2543 case 'D': // Double Click
2544 case 'S': // Single Click
2545 if (button & 1) mouse_code |= MOUSE_LEFT;
2546 if (button & 2) mouse_code |= MOUSE_MIDDLE;
2547 if (button & 4) mouse_code |= MOUSE_RIGHT;
2548 if (button & 8) mouse_code |= MOUSE_SHIFT;
2549 if (button & 16) mouse_code |= MOUSE_CTRL;
2550 break;
2551 case 'm': // Mouse move
2552 if (button & 1) mouse_code |= MOUSE_LEFT;
2553 if (button & 2) mouse_code |= MOUSE_MIDDLE;
2554 if (button & 4) mouse_code |= MOUSE_RIGHT;
2555 if (button & 8) mouse_code |= MOUSE_SHIFT;
2556 if (button & 16) mouse_code |= MOUSE_CTRL;
2557 if ((button & 7) != 0)
2558 {
2559 held_button = mouse_code;
2560 mouse_code |= MOUSE_DRAG;
2561 }
2562 is_drag = TRUE;
2563 showmode();
2564 break;
2565 case 'd': // Button Down
2566 if (button & 1) mouse_code |= MOUSE_LEFT;
2567 if (button & 2) mouse_code |= MOUSE_MIDDLE;
2568 if (button & 4) mouse_code |= MOUSE_RIGHT;
2569 if (button & 8) mouse_code |= MOUSE_SHIFT;
2570 if (button & 16) mouse_code |= MOUSE_CTRL;
2571 break;
2572 case 'u': // Button Up
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002573 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002574 if (button & 1)
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002575 mouse_code |= MOUSE_LEFT;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002576 if (button & 2)
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002577 mouse_code |= MOUSE_MIDDLE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002578 if (button & 4)
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002579 mouse_code |= MOUSE_RIGHT;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002580 if (button & 8)
2581 mouse_code |= MOUSE_SHIFT;
2582 if (button & 16)
2583 mouse_code |= MOUSE_CTRL;
2584 break;
2585 default: return -1; // Unknown Result
2586 }
2587
2588 *slen += (p - (tp + *slen));
2589 }
2590# endif // FEAT_MOUSE_JSB
2591# ifdef FEAT_MOUSE_DEC
2592 if (key_name[0] == KS_DEC_MOUSE)
2593 {
2594 /*
2595 * The DEC Locator Input Model
2596 * Netterm delivers the code sequence:
2597 * \033[2;4;24;80&w (left button down)
2598 * \033[3;0;24;80&w (left button up)
2599 * \033[6;1;24;80&w (right button down)
2600 * \033[7;0;24;80&w (right button up)
2601 * CSI Pe ; Pb ; Pr ; Pc ; Pp & w
2602 * Pe is the event code
2603 * Pb is the button code
2604 * Pr is the row coordinate
2605 * Pc is the column coordinate
2606 * Pp is the third coordinate (page number)
2607 * Pe, the event code indicates what event caused this report
2608 * The following event codes are defined:
Bram Moolenaar13c04632020-07-12 13:47:42 +02002609 * 0 - request, the terminal received an explicit request for a
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002610 * locator report, but the locator is unavailable
Bram Moolenaar13c04632020-07-12 13:47:42 +02002611 * 1 - request, the terminal received an explicit request for a
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002612 * locator report
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002613 * 2 - left button down
2614 * 3 - left button up
2615 * 4 - middle button down
2616 * 5 - middle button up
2617 * 6 - right button down
2618 * 7 - right button up
2619 * 8 - fourth button down
2620 * 9 - fourth button up
2621 * 10 - locator outside filter rectangle
Bram Moolenaar13c04632020-07-12 13:47:42 +02002622 * Pb, the button code, ASCII decimal 0-15 indicating which buttons are
2623 * down if any. The state of the four buttons on the locator
2624 * correspond to the low four bits of the decimal value, "1" means
2625 * button depressed
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002626 * 0 - no buttons down,
2627 * 1 - right,
2628 * 2 - middle,
2629 * 4 - left,
2630 * 8 - fourth
2631 * Pr is the row coordinate of the locator position in the page,
Bram Moolenaar13c04632020-07-12 13:47:42 +02002632 * encoded as an ASCII decimal value. If Pr is omitted, the locator
2633 * position is undefined (outside the terminal window for example).
2634 * Pc is the column coordinate of the locator position in the page,
2635 * encoded as an ASCII decimal value. If Pc is omitted, the locator
2636 * position is undefined (outside the terminal window for example).
2637 * Pp is the page coordinate of the locator position encoded as an
2638 * ASCII decimal value. The page coordinate may be omitted if the
2639 * locator is on page one (the default). We ignore it anyway.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002640 */
2641 int Pe, Pb, Pr, Pc;
2642
2643 p = tp + *slen;
2644
2645 // get event status
2646 Pe = getdigits(&p);
2647 if (*p++ != ';')
2648 return -1;
2649
2650 // get button status
2651 Pb = getdigits(&p);
2652 if (*p++ != ';')
2653 return -1;
2654
2655 // get row status
2656 Pr = getdigits(&p);
2657 if (*p++ != ';')
2658 return -1;
2659
2660 // get column status
2661 Pc = getdigits(&p);
2662
2663 // the page parameter is optional
2664 if (*p == ';')
2665 {
2666 p++;
2667 (void)getdigits(&p);
2668 }
2669 if (*p++ != '&')
2670 return -1;
2671 if (*p++ != 'w')
2672 return -1;
2673
2674 mouse_code = 0;
2675 switch (Pe)
2676 {
2677 case 0: return -1; // position request while unavailable
2678 case 1: // a response to a locator position request includes
2679 // the status of all buttons
2680 Pb &= 7; // mask off and ignore fourth button
2681 if (Pb & 4)
2682 mouse_code = MOUSE_LEFT;
2683 if (Pb & 2)
2684 mouse_code = MOUSE_MIDDLE;
2685 if (Pb & 1)
2686 mouse_code = MOUSE_RIGHT;
2687 if (Pb)
2688 {
2689 held_button = mouse_code;
2690 mouse_code |= MOUSE_DRAG;
2691 WantQueryMouse = TRUE;
2692 }
2693 is_drag = TRUE;
2694 showmode();
2695 break;
2696 case 2: mouse_code = MOUSE_LEFT;
2697 WantQueryMouse = TRUE;
2698 break;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002699 case 3: mouse_code = MOUSE_LEFT;
2700 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002701 break;
2702 case 4: mouse_code = MOUSE_MIDDLE;
2703 WantQueryMouse = TRUE;
2704 break;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002705 case 5: mouse_code = MOUSE_MIDDLE;
2706 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002707 break;
2708 case 6: mouse_code = MOUSE_RIGHT;
2709 WantQueryMouse = TRUE;
2710 break;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002711 case 7: mouse_code = MOUSE_RIGHT;
2712 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002713 break;
2714 case 8: return -1; // fourth button down
2715 case 9: return -1; // fourth button up
2716 case 10: return -1; // mouse outside of filter rectangle
2717 default: return -1; // should never occur
2718 }
2719
2720 mouse_col = Pc - 1;
2721 mouse_row = Pr - 1;
2722
2723 *slen += (int)(p - (tp + *slen));
2724 }
2725# endif // FEAT_MOUSE_DEC
2726# ifdef FEAT_MOUSE_PTERM
2727 if (key_name[0] == KS_PTERM_MOUSE)
2728 {
2729 int button, num_clicks, action;
2730
2731 p = tp + *slen;
2732
2733 action = getdigits(&p);
2734 if (*p++ != ';')
2735 return -1;
2736
2737 mouse_row = getdigits(&p);
2738 if (*p++ != ';')
2739 return -1;
2740 mouse_col = getdigits(&p);
2741 if (*p++ != ';')
2742 return -1;
2743
2744 button = getdigits(&p);
2745 mouse_code = 0;
2746
2747 switch (button)
2748 {
2749 case 4: mouse_code = MOUSE_LEFT; break;
2750 case 1: mouse_code = MOUSE_RIGHT; break;
2751 case 2: mouse_code = MOUSE_MIDDLE; break;
2752 default: return -1;
2753 }
2754
2755 switch (action)
2756 {
2757 case 31: // Initial press
2758 if (*p++ != ';')
2759 return -1;
2760
2761 num_clicks = getdigits(&p); // Not used
2762 break;
2763
2764 case 32: // Release
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002765 is_release = TRUE;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002766 break;
2767
2768 case 33: // Drag
2769 held_button = mouse_code;
2770 mouse_code |= MOUSE_DRAG;
2771 break;
2772
2773 default:
2774 return -1;
2775 }
2776
2777 if (*p++ != 't')
2778 return -1;
2779
2780 *slen += (p - (tp + *slen));
2781 }
2782# endif // FEAT_MOUSE_PTERM
2783
2784 // Interpret the mouse code
2785 current_button = (mouse_code & MOUSE_CLICK_MASK);
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002786 if (is_release)
2787 current_button |= MOUSE_RELEASE;
2788
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002789 if (current_button == MOUSE_RELEASE
2790# ifdef FEAT_MOUSE_XTERM
2791 && wheel_code == 0
2792# endif
2793 )
2794 {
2795 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002796 * If we get a mouse drag or release event when there is no mouse
2797 * button held down (held_button == MOUSE_RELEASE), produce a K_IGNORE
2798 * below.
2799 * (can happen when you hold down two buttons and then let them go, or
2800 * click in the menu bar, but not on a menu, and drag into the text).
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002801 */
2802 if ((mouse_code & MOUSE_DRAG) == MOUSE_DRAG)
2803 is_drag = TRUE;
2804 current_button = held_button;
2805 }
Bram Moolenaard6212b82022-08-03 15:48:33 +01002806 else
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002807 {
Bram Moolenaard6212b82022-08-03 15:48:33 +01002808 if (wheel_code == 0)
2809 {
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002810# ifdef CHECK_DOUBLE_CLICK
2811# ifdef FEAT_MOUSE_GPM
2812 /*
Bram Moolenaar13c04632020-07-12 13:47:42 +02002813 * Only for Unix, when GUI not active, we handle multi-clicks here, but
2814 * not for GPM mouse events.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002815 */
2816# ifdef FEAT_GUI
2817 if (key_name[0] != KS_GPM_MOUSE && !gui.in_use)
2818# else
2819 if (key_name[0] != KS_GPM_MOUSE)
2820# endif
2821# else
2822# ifdef FEAT_GUI
2823 if (!gui.in_use)
2824# endif
2825# endif
2826 {
2827 /*
2828 * Compute the time elapsed since the previous mouse click.
2829 */
2830 gettimeofday(&mouse_time, NULL);
2831 if (orig_mouse_time.tv_sec == 0)
2832 {
2833 /*
2834 * Avoid computing the difference between mouse_time
2835 * and orig_mouse_time for the first click, as the
2836 * difference would be huge and would cause
2837 * multiplication overflow.
2838 */
2839 timediff = p_mouset;
2840 }
2841 else
Bram Moolenaar85c35022019-11-22 22:21:59 +01002842 timediff = time_diff_ms(&orig_mouse_time, &mouse_time);
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002843 orig_mouse_time = mouse_time;
2844 if (mouse_code == orig_mouse_code
2845 && timediff < p_mouset
2846 && orig_num_clicks != 4
2847 && orig_mouse_col == mouse_col
2848 && orig_mouse_row == mouse_row
2849 && (is_mouse_topline(curwin)
2850 // Double click in tab pages line also works
2851 // when window contents changes.
2852 || (mouse_row == 0 && firstwin->w_winrow > 0))
2853 )
2854 ++orig_num_clicks;
2855 else
2856 orig_num_clicks = 1;
2857 orig_mouse_col = mouse_col;
2858 orig_mouse_row = mouse_row;
2859 set_mouse_topline(curwin);
2860 }
2861# if defined(FEAT_GUI) || defined(FEAT_MOUSE_GPM)
2862 else
2863 orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code);
2864# endif
2865# else
2866 orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code);
2867# endif
2868 is_click = TRUE;
Bram Moolenaard6212b82022-08-03 15:48:33 +01002869 }
2870 orig_mouse_code = mouse_code;
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002871 }
2872 if (!is_drag)
2873 held_button = mouse_code & MOUSE_CLICK_MASK;
2874
2875 /*
2876 * Translate the actual mouse event into a pseudo mouse event.
2877 * First work out what modifiers are to be used.
2878 */
2879 if (orig_mouse_code & MOUSE_SHIFT)
2880 *modifiers |= MOD_MASK_SHIFT;
2881 if (orig_mouse_code & MOUSE_CTRL)
2882 *modifiers |= MOD_MASK_CTRL;
2883 if (orig_mouse_code & MOUSE_ALT)
2884 *modifiers |= MOD_MASK_ALT;
2885 if (orig_num_clicks == 2)
2886 *modifiers |= MOD_MASK_2CLICK;
2887 else if (orig_num_clicks == 3)
2888 *modifiers |= MOD_MASK_3CLICK;
2889 else if (orig_num_clicks == 4)
2890 *modifiers |= MOD_MASK_4CLICK;
2891
Bram Moolenaar13c04632020-07-12 13:47:42 +02002892 // Work out our pseudo mouse event. Note that MOUSE_RELEASE gets added,
2893 // then it's not mouse up/down.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002894 key_name[0] = KS_EXTRA;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002895 if (wheel_code != 0 && (!is_release || release_is_ambiguous))
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002896 {
2897 if (wheel_code & MOUSE_CTRL)
2898 *modifiers |= MOD_MASK_CTRL;
2899 if (wheel_code & MOUSE_ALT)
2900 *modifiers |= MOD_MASK_ALT;
Bram Moolenaard58d4f92020-07-01 15:49:29 +02002901
2902 if (wheel_code & 1 && wheel_code & 2)
2903 key_name[1] = (int)KE_MOUSELEFT;
2904 else if (wheel_code & 2)
2905 key_name[1] = (int)KE_MOUSERIGHT;
2906 else if (wheel_code & 1)
2907 key_name[1] = (int)KE_MOUSEUP;
2908 else
2909 key_name[1] = (int)KE_MOUSEDOWN;
2910
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002911 held_button = MOUSE_RELEASE;
2912 }
2913 else
Bram Moolenaar13c04632020-07-12 13:47:42 +02002914 key_name[1] = get_pseudo_mouse_code(current_button, is_click, is_drag);
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002915
Bram Moolenaar13c04632020-07-12 13:47:42 +02002916
2917 // Make sure the mouse position is valid. Some terminals may return weird
2918 // values.
Bram Moolenaarb8ff5c22019-09-23 21:16:54 +02002919 if (mouse_col >= Columns)
2920 mouse_col = Columns - 1;
2921 if (mouse_row >= Rows)
2922 mouse_row = Rows - 1;
2923
2924 return 0;
2925}
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002926
2927// Functions also used for popup windows.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002928
2929/*
2930 * Compute the buffer line position from the screen position "rowp" / "colp" in
2931 * window "win".
Bram Moolenaar452143c2020-07-15 17:38:21 +02002932 * "plines_cache" can be NULL (no cache) or an array with "Rows" entries that
2933 * caches the plines_win() result from a previous call. Entry is zero if not
2934 * computed yet. There must be no text or setting changes since the entry is
2935 * put in the cache.
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002936 * Returns TRUE if the position is below the last line.
2937 */
2938 int
2939mouse_comp_pos(
2940 win_T *win,
2941 int *rowp,
2942 int *colp,
2943 linenr_T *lnump,
2944 int *plines_cache)
2945{
2946 int col = *colp;
2947 int row = *rowp;
2948 linenr_T lnum;
2949 int retval = FALSE;
2950 int off;
2951 int count;
2952
2953#ifdef FEAT_RIGHTLEFT
2954 if (win->w_p_rl)
2955 col = win->w_width - 1 - col;
2956#endif
2957
2958 lnum = win->w_topline;
2959
2960 while (row > 0)
2961 {
2962 int cache_idx = lnum - win->w_topline;
2963
Bram Moolenaar452143c2020-07-15 17:38:21 +02002964 // Only "Rows" lines are cached, with folding we'll run out of entries
2965 // and use the slow way.
2966 if (plines_cache != NULL && cache_idx < Rows
2967 && plines_cache[cache_idx] > 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002968 count = plines_cache[cache_idx];
2969 else
2970 {
2971#ifdef FEAT_DIFF
2972 // Don't include filler lines in "count"
2973 if (win->w_p_diff
2974# ifdef FEAT_FOLDING
2975 && !hasFoldingWin(win, lnum, NULL, NULL, TRUE, NULL)
2976# endif
2977 )
2978 {
2979 if (lnum == win->w_topline)
2980 row -= win->w_topfill;
2981 else
2982 row -= diff_check_fill(win, lnum);
2983 count = plines_win_nofill(win, lnum, TRUE);
2984 }
2985 else
2986#endif
2987 count = plines_win(win, lnum, TRUE);
Bram Moolenaar452143c2020-07-15 17:38:21 +02002988 if (plines_cache != NULL && cache_idx < Rows)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002989 plines_cache[cache_idx] = count;
2990 }
2991 if (count > row)
2992 break; // Position is in this buffer line.
2993#ifdef FEAT_FOLDING
2994 (void)hasFoldingWin(win, lnum, NULL, &lnum, TRUE, NULL);
2995#endif
2996 if (lnum == win->w_buffer->b_ml.ml_line_count)
2997 {
2998 retval = TRUE;
2999 break; // past end of file
3000 }
3001 row -= count;
3002 ++lnum;
3003 }
3004
3005 if (!retval)
3006 {
3007 // Compute the column without wrapping.
3008 off = win_col_off(win) - win_col_off2(win);
3009 if (col < off)
3010 col = off;
3011 col += row * (win->w_width - off);
3012 // add skip column (for long wrapping line)
3013 col += win->w_skipcol;
3014 }
3015
3016 if (!win->w_p_wrap)
3017 col += win->w_leftcol;
3018
3019 // skip line number and fold column in front of the line
3020 col -= win_col_off(win);
Bram Moolenaardbfa7952020-11-02 20:04:22 +01003021 if (col <= 0)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003022 {
3023#ifdef FEAT_NETBEANS_INTG
Bram Moolenaardbfa7952020-11-02 20:04:22 +01003024 // if mouse is clicked on the gutter, then inform the netbeans server
3025 if (*colp < win_col_off(win))
3026 netbeans_gutter_click(lnum);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003027#endif
3028 col = 0;
3029 }
3030
3031 *colp = col;
3032 *rowp = row;
3033 *lnump = lnum;
3034 return retval;
3035}
3036
3037/*
3038 * Find the window at screen position "*rowp" and "*colp". The positions are
3039 * updated to become relative to the top-left of the window.
3040 * When "popup" is FAIL_POPUP and the position is in a popup window then NULL
3041 * is returned. When "popup" is IGNORE_POPUP then do not even check popup
3042 * windows.
3043 * Returns NULL when something is wrong.
3044 */
3045 win_T *
3046mouse_find_win(int *rowp, int *colp, mouse_find_T popup UNUSED)
3047{
3048 frame_T *fp;
3049 win_T *wp;
3050
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003051#ifdef FEAT_PROP_POPUP
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003052 win_T *pwp = NULL;
3053
3054 if (popup != IGNORE_POPUP)
3055 {
Bram Moolenaarafe45b62019-11-13 22:35:19 +01003056 popup_reset_handled(POPUP_HANDLED_1);
3057 while ((wp = find_next_popup(TRUE, POPUP_HANDLED_1)) != NULL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003058 {
3059 if (*rowp >= wp->w_winrow && *rowp < wp->w_winrow + popup_height(wp)
3060 && *colp >= wp->w_wincol
3061 && *colp < wp->w_wincol + popup_width(wp))
3062 pwp = wp;
3063 }
3064 if (pwp != NULL)
3065 {
3066 if (popup == FAIL_POPUP)
3067 return NULL;
3068 *rowp -= pwp->w_winrow;
3069 *colp -= pwp->w_wincol;
3070 return pwp;
3071 }
3072 }
3073#endif
3074
3075 fp = topframe;
3076 *rowp -= firstwin->w_winrow;
3077 for (;;)
3078 {
3079 if (fp->fr_layout == FR_LEAF)
3080 break;
3081 if (fp->fr_layout == FR_ROW)
3082 {
3083 for (fp = fp->fr_child; fp->fr_next != NULL; fp = fp->fr_next)
3084 {
3085 if (*colp < fp->fr_width)
3086 break;
3087 *colp -= fp->fr_width;
3088 }
3089 }
3090 else // fr_layout == FR_COL
3091 {
3092 for (fp = fp->fr_child; fp->fr_next != NULL; fp = fp->fr_next)
3093 {
3094 if (*rowp < fp->fr_height)
3095 break;
3096 *rowp -= fp->fr_height;
3097 }
3098 }
3099 }
3100 // When using a timer that closes a window the window might not actually
3101 // exist.
3102 FOR_ALL_WINDOWS(wp)
3103 if (wp == fp->fr_win)
3104 {
3105#ifdef FEAT_MENU
3106 *rowp -= wp->w_winbar_height;
3107#endif
3108 return wp;
3109 }
3110 return NULL;
3111}
3112
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003113#if defined(NEED_VCOL2COL) || defined(FEAT_BEVAL) || defined(FEAT_PROP_POPUP) \
Bram Moolenaar424da7a2022-03-13 19:08:48 +00003114 || defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003115/*
3116 * Convert a virtual (screen) column to a character column.
3117 * The first column is one.
3118 */
3119 int
3120vcol2col(win_T *wp, linenr_T lnum, int vcol)
3121{
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003122 char_u *line;
3123 chartabsize_T cts;
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003124
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003125 // try to advance to the specified column
3126 line = ml_get_buf(wp->w_buffer, lnum, FALSE);
3127 init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
3128 while (cts.cts_vcol < vcol && *cts.cts_ptr != NUL)
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003129 {
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003130 cts.cts_vcol += win_lbr_chartabsize(&cts, NULL);
3131 MB_PTR_ADV(cts.cts_ptr);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003132 }
Bram Moolenaar7f9969c2022-07-25 18:13:54 +01003133 clear_chartabsize_arg(&cts);
3134
3135 return (int)(cts.cts_ptr - line);
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02003136}
3137#endif
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003138
3139#if defined(FEAT_EVAL) || defined(PROTO)
3140 void
3141f_getmousepos(typval_T *argvars UNUSED, typval_T *rettv)
3142{
3143 dict_T *d;
3144 win_T *wp;
3145 int row = mouse_row;
3146 int col = mouse_col;
3147 varnumber_T winid = 0;
3148 varnumber_T winrow = 0;
3149 varnumber_T wincol = 0;
Bram Moolenaar533870a2022-03-13 15:52:44 +00003150 linenr_T lnum = 0;
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003151 varnumber_T column = 0;
3152
Bram Moolenaar93a10962022-06-16 11:42:09 +01003153 if (rettv_dict_alloc(rettv) == FAIL)
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003154 return;
3155 d = rettv->vval.v_dict;
3156
3157 dict_add_number(d, "screenrow", (varnumber_T)mouse_row + 1);
3158 dict_add_number(d, "screencol", (varnumber_T)mouse_col + 1);
3159
3160 wp = mouse_find_win(&row, &col, FIND_POPUP);
3161 if (wp != NULL)
3162 {
3163 int top_off = 0;
3164 int left_off = 0;
3165 int height = wp->w_height + wp->w_status_height;
3166
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003167#ifdef FEAT_PROP_POPUP
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003168 if (WIN_IS_POPUP(wp))
3169 {
3170 top_off = popup_top_extra(wp);
3171 left_off = popup_left_extra(wp);
3172 height = popup_height(wp);
3173 }
3174#endif
3175 if (row < height)
3176 {
3177 winid = wp->w_id;
3178 winrow = row + 1;
3179 wincol = col + 1;
3180 row -= top_off;
3181 col -= left_off;
3182 if (row >= 0 && row < wp->w_height && col >= 0 && col < wp->w_width)
3183 {
Sean Dewar10792fe2022-03-15 09:46:54 +00003184 (void)mouse_comp_pos(wp, &row, &col, &lnum, NULL);
3185 col = vcol2col(wp, lnum, col);
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003186 column = col + 1;
3187 }
3188 }
3189 }
3190 dict_add_number(d, "winid", winid);
3191 dict_add_number(d, "winrow", winrow);
3192 dict_add_number(d, "wincol", wincol);
Bram Moolenaar533870a2022-03-13 15:52:44 +00003193 dict_add_number(d, "line", (varnumber_T)lnum);
Bram Moolenaardb3a2052019-11-16 18:22:41 +01003194 dict_add_number(d, "column", column);
3195}
3196#endif