blob: 72148760d8855cd13000d0ac5d3017cbff2a5273 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
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 * ui.c: functions that handle the user interface.
12 * 1. Keyboard input stuff, and a bit of windowing stuff. These are called
13 * before the machine specific stuff (mch_*) so that we can call the GUI
14 * stuff instead if the GUI is running.
15 * 2. Clipboard stuff.
16 * 3. Input buffer stuff.
17 */
18
19#include "vim.h"
20
Bram Moolenaar2c6f3dc2013-07-05 20:09:16 +020021#ifdef FEAT_CYGWIN_WIN32_CLIPBOARD
22# define WIN32_LEAN_AND_MEAN
23# include <windows.h>
24# include "winclip.pro"
25#endif
26
Bram Moolenaar071d4272004-06-13 20:20:40 +000027 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010028ui_write(char_u *s, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +000029{
30#ifdef FEAT_GUI
31 if (gui.in_use && !gui.dying && !gui.starting)
32 {
33 gui_write(s, len);
34 if (p_wd)
Bram Moolenaarc9e649a2017-12-18 18:14:47 +010035 gui_wait_for_chars(p_wd, typebuf.tb_change_cnt);
Bram Moolenaar071d4272004-06-13 20:20:40 +000036 return;
37 }
38#endif
39#ifndef NO_CONSOLE
40 /* Don't output anything in silent mode ("ex -s") unless 'verbose' set */
41 if (!(silent_mode && p_verbose == 0))
42 {
Bram Moolenaar4f974752019-02-17 17:44:42 +010043#if !defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +000044 char_u *tofree = NULL;
45
46 if (output_conv.vc_type != CONV_NONE)
47 {
48 /* Convert characters from 'encoding' to 'termencoding'. */
49 tofree = string_convert(&output_conv, s, &len);
50 if (tofree != NULL)
51 s = tofree;
52 }
53#endif
54
55 mch_write(s, len);
56
Bram Moolenaar4f974752019-02-17 17:44:42 +010057# if !defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +000058 if (output_conv.vc_type != CONV_NONE)
59 vim_free(tofree);
Bram Moolenaar264b74f2019-01-24 17:18:42 +010060# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000061 }
62#endif
63}
64
Bram Moolenaar4f974752019-02-17 17:44:42 +010065#if defined(UNIX) || defined(VMS) || defined(PROTO) || defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +000066/*
67 * When executing an external program, there may be some typed characters that
68 * are not consumed by it. Give them back to ui_inchar() and they are stored
69 * here for the next call.
70 */
71static char_u *ta_str = NULL;
72static int ta_off; /* offset for next char to use when ta_str != NULL */
73static int ta_len; /* length of ta_str when it's not NULL*/
74
75 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010076ui_inchar_undo(char_u *s, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +000077{
78 char_u *new;
79 int newlen;
80
81 newlen = len;
82 if (ta_str != NULL)
83 newlen += ta_len - ta_off;
84 new = alloc(newlen);
85 if (new != NULL)
86 {
87 if (ta_str != NULL)
88 {
89 mch_memmove(new, ta_str + ta_off, (size_t)(ta_len - ta_off));
90 mch_memmove(new + ta_len - ta_off, s, (size_t)len);
91 vim_free(ta_str);
92 }
93 else
94 mch_memmove(new, s, (size_t)len);
95 ta_str = new;
96 ta_len = newlen;
97 ta_off = 0;
98 }
99}
100#endif
101
102/*
Bram Moolenaarb6101cf2012-10-21 00:58:39 +0200103 * ui_inchar(): low level input function.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000104 * Get characters from the keyboard.
105 * Return the number of characters that are available.
106 * If "wtime" == 0 do not wait for characters.
107 * If "wtime" == -1 wait forever for characters.
108 * If "wtime" > 0 wait "wtime" milliseconds for a character.
109 *
110 * "tb_change_cnt" is the value of typebuf.tb_change_cnt if "buf" points into
111 * it. When typebuf.tb_change_cnt changes (e.g., when a message is received
112 * from a remote client) "buf" can no longer be used. "tb_change_cnt" is NULL
113 * otherwise.
114 */
115 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100116ui_inchar(
117 char_u *buf,
118 int maxlen,
119 long wtime, /* don't use "time", MIPS cannot handle it */
120 int tb_change_cnt)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000121{
122 int retval = 0;
123
124#if defined(FEAT_GUI) && (defined(UNIX) || defined(VMS))
125 /*
126 * Use the typeahead if there is any.
127 */
128 if (ta_str != NULL)
129 {
130 if (maxlen >= ta_len - ta_off)
131 {
132 mch_memmove(buf, ta_str + ta_off, (size_t)ta_len);
Bram Moolenaard23a8232018-02-10 18:45:26 +0100133 VIM_CLEAR(ta_str);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000134 return ta_len;
135 }
136 mch_memmove(buf, ta_str + ta_off, (size_t)maxlen);
137 ta_off += maxlen;
138 return maxlen;
139 }
140#endif
141
Bram Moolenaar05159a02005-02-26 23:04:13 +0000142#ifdef FEAT_PROFILE
Bram Moolenaarb3656ed2006-03-20 21:59:49 +0000143 if (do_profiling == PROF_YES && wtime != 0)
Bram Moolenaar05159a02005-02-26 23:04:13 +0000144 prof_inchar_enter();
145#endif
146
Bram Moolenaar071d4272004-06-13 20:20:40 +0000147#ifdef NO_CONSOLE_INPUT
148 /* Don't wait for character input when the window hasn't been opened yet.
149 * Do try reading, this works when redirecting stdin from a file.
150 * Must return something, otherwise we'll loop forever. If we run into
151 * this very often we probably got stuck, exit Vim. */
152 if (no_console_input())
153 {
154 static int count = 0;
155
156# ifndef NO_CONSOLE
Bram Moolenaar13410242018-11-26 21:19:11 +0100157 retval = mch_inchar(buf, maxlen, wtime, tb_change_cnt);
Bram Moolenaar43b604c2005-03-22 23:06:55 +0000158 if (retval > 0 || typebuf_changed(tb_change_cnt) || wtime >= 0)
Bram Moolenaar05159a02005-02-26 23:04:13 +0000159 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000160# endif
161 if (wtime == -1 && ++count == 1000)
162 read_error_exit();
163 buf[0] = CAR;
Bram Moolenaar05159a02005-02-26 23:04:13 +0000164 retval = 1;
165 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000166 }
167#endif
168
Bram Moolenaarc8da3112007-03-08 12:36:46 +0000169 /* If we are going to wait for some time or block... */
170 if (wtime == -1 || wtime > 100L)
171 {
172 /* ... allow signals to kill us. */
173 (void)vim_handle_signal(SIGNAL_UNBLOCK);
174
175 /* ... there is no need for CTRL-C to interrupt something, don't let
176 * it set got_int when it was mapped. */
Bram Moolenaar50008692015-01-14 16:08:32 +0100177 if ((mapped_ctrl_c | curbuf->b_mapped_ctrl_c) & get_real_state())
Bram Moolenaarc8da3112007-03-08 12:36:46 +0000178 ctrl_c_interrupts = FALSE;
179 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000180
Bram Moolenaare40b9d42019-01-27 16:55:47 +0100181 /*
182 * Here we call gui_inchar() or mch_inchar(), the GUI or machine-dependent
183 * input function. The functionality they implement is like this:
184 *
185 * while (not timed out)
186 * {
187 * handle-resize;
188 * parse-queued-messages;
189 * if (waited for 'updatetime')
190 * trigger-cursorhold;
191 * ui_wait_for_chars_or_timer()
192 * if (character available)
193 * break;
194 * }
195 *
196 * ui_wait_for_chars_or_timer() does:
197 *
198 * while (not timed out)
199 * {
200 * if (any-timer-triggered)
201 * invoke-timer-callback;
202 * wait-for-character();
203 * if (character available)
204 * break;
205 * }
206 *
207 * wait-for-character() does:
208 * while (not timed out)
209 * {
210 * Wait for event;
211 * if (something on channel)
212 * read/write channel;
213 * else if (resized)
214 * handle_resize();
215 * else if (system event)
216 * deal-with-system-event;
217 * else if (character available)
218 * break;
219 * }
220 *
221 */
222
Bram Moolenaar071d4272004-06-13 20:20:40 +0000223#ifdef FEAT_GUI
224 if (gui.in_use)
Bram Moolenaarc9e649a2017-12-18 18:14:47 +0100225 retval = gui_inchar(buf, maxlen, wtime, tb_change_cnt);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000226#endif
227#ifndef NO_CONSOLE
228# ifdef FEAT_GUI
229 else
230# endif
231 retval = mch_inchar(buf, maxlen, wtime, tb_change_cnt);
232#endif
233
Bram Moolenaarc8da3112007-03-08 12:36:46 +0000234 if (wtime == -1 || wtime > 100L)
235 /* block SIGHUP et al. */
236 (void)vim_handle_signal(SIGNAL_BLOCK);
237
Bram Moolenaar071d4272004-06-13 20:20:40 +0000238 ctrl_c_interrupts = TRUE;
239
Bram Moolenaar05159a02005-02-26 23:04:13 +0000240#ifdef NO_CONSOLE_INPUT
241theend:
242#endif
243#ifdef FEAT_PROFILE
Bram Moolenaarb3656ed2006-03-20 21:59:49 +0000244 if (do_profiling == PROF_YES && wtime != 0)
Bram Moolenaar05159a02005-02-26 23:04:13 +0000245 prof_inchar_exit();
246#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000247 return retval;
248}
249
Bram Moolenaare40b9d42019-01-27 16:55:47 +0100250#if defined(UNIX) || defined(FEAT_GUI) || defined(PROTO)
251/*
252 * Common code for mch_inchar() and gui_inchar(): Wait for a while or
253 * indefinitely until characters are available, dealing with timers and
254 * messages on channels.
255 *
256 * "buf" may be NULL if the available characters are not to be returned, only
257 * check if they are available.
258 *
259 * Return the number of characters that are available.
260 * If "wtime" == 0 do not wait for characters.
261 * If "wtime" == n wait a short time for characters.
262 * If "wtime" == -1 wait forever for characters.
263 */
264 int
265inchar_loop(
266 char_u *buf,
267 int maxlen,
268 long wtime, // don't use "time", MIPS cannot handle it
269 int tb_change_cnt,
270 int (*wait_func)(long wtime, int *interrupted, int ignore_input),
271 int (*resize_func)(int check_only))
272{
273 int len;
274 int interrupted = FALSE;
Bram Moolenaar12dfc9e2019-01-28 22:32:58 +0100275 int did_call_wait_func = FALSE;
Bram Moolenaare40b9d42019-01-27 16:55:47 +0100276 int did_start_blocking = FALSE;
277 long wait_time;
278 long elapsed_time = 0;
279#ifdef ELAPSED_FUNC
280 elapsed_T start_tv;
281
282 ELAPSED_INIT(start_tv);
283#endif
284
285 /* repeat until we got a character or waited long enough */
286 for (;;)
287 {
288 /* Check if window changed size while we were busy, perhaps the ":set
289 * columns=99" command was used. */
290 if (resize_func != NULL)
291 resize_func(FALSE);
292
293#ifdef MESSAGE_QUEUE
294 // Only process messages when waiting.
295 if (wtime != 0)
296 {
297 parse_queued_messages();
298 // If input was put directly in typeahead buffer bail out here.
299 if (typebuf_changed(tb_change_cnt))
300 return 0;
301 }
302#endif
303 if (wtime < 0 && did_start_blocking)
304 // blocking and already waited for p_ut
305 wait_time = -1;
306 else
307 {
308 if (wtime >= 0)
309 wait_time = wtime;
310 else
311 // going to block after p_ut
312 wait_time = p_ut;
313#ifdef ELAPSED_FUNC
314 elapsed_time = ELAPSED_FUNC(start_tv);
315#endif
316 wait_time -= elapsed_time;
Bram Moolenaar12dfc9e2019-01-28 22:32:58 +0100317
318 // If the waiting time is now zero or less, we timed out. However,
319 // loop at least once to check for characters and events. Matters
320 // when "wtime" is zero.
321 if (wait_time <= 0 && did_call_wait_func)
Bram Moolenaare40b9d42019-01-27 16:55:47 +0100322 {
323 if (wtime >= 0)
324 // no character available within "wtime"
325 return 0;
326
327 // No character available within 'updatetime'.
328 did_start_blocking = TRUE;
329 if (trigger_cursorhold() && maxlen >= 3
330 && !typebuf_changed(tb_change_cnt))
331 {
332 // Put K_CURSORHOLD in the input buffer or return it.
333 if (buf == NULL)
334 {
335 char_u ibuf[3];
336
337 ibuf[0] = CSI;
338 ibuf[1] = KS_EXTRA;
339 ibuf[2] = (int)KE_CURSORHOLD;
340 add_to_input_buf(ibuf, 3);
341 }
342 else
343 {
344 buf[0] = K_SPECIAL;
345 buf[1] = KS_EXTRA;
346 buf[2] = (int)KE_CURSORHOLD;
347 }
348 return 3;
349 }
350
351 // There is no character available within 'updatetime' seconds:
352 // flush all the swap files to disk. Also done when
353 // interrupted by SIGWINCH.
354 before_blocking();
355 continue;
356 }
357 }
358
359#ifdef FEAT_JOB_CHANNEL
360 if (wait_time < 0 || wait_time > 100L)
361 {
362 // Checking if a job ended requires polling. Do this at least
363 // every 100 msec.
364 if (has_pending_job())
365 wait_time = 100L;
366
367 // If there is readahead then parse_queued_messages() timed out and
368 // we should call it again soon.
369 if (channel_any_readahead())
370 wait_time = 10L;
371 }
372#endif
373#ifdef FEAT_BEVAL_GUI
374 if (p_beval && wait_time > 100L)
375 // The 'balloonexpr' may indirectly invoke a callback while waiting
376 // for a character, need to check often.
377 wait_time = 100L;
378#endif
379
380 // Wait for a character to be typed or another event, such as the winch
381 // signal or an event on the monitored file descriptors.
Bram Moolenaar12dfc9e2019-01-28 22:32:58 +0100382 did_call_wait_func = TRUE;
Bram Moolenaare40b9d42019-01-27 16:55:47 +0100383 if (wait_func(wait_time, &interrupted, FALSE))
384 {
385 // If input was put directly in typeahead buffer bail out here.
386 if (typebuf_changed(tb_change_cnt))
387 return 0;
388
389 // We might have something to return now.
390 if (buf == NULL)
391 // "buf" is NULL, we were just waiting, not actually getting
392 // input.
393 return input_available();
394
395 len = read_from_input_buf(buf, (long)maxlen);
396 if (len > 0)
397 return len;
398 continue;
399 }
400 // Timed out or interrupted with no character available.
401
402#ifndef ELAPSED_FUNC
403 // estimate the elapsed time
404 elapsed_time += wait_time;
405#endif
406
407 if ((resize_func != NULL && resize_func(TRUE))
Bram Moolenaar3e9d4d82019-01-27 17:08:40 +0100408#if defined(FEAT_CLIENTSERVER) && defined(UNIX)
Bram Moolenaare40b9d42019-01-27 16:55:47 +0100409 || server_waiting()
410#endif
411#ifdef MESSAGE_QUEUE
412 || interrupted
413#endif
414 || wait_time > 0
415 || (wtime < 0 && !did_start_blocking))
416 // no character available, but something to be done, keep going
417 continue;
418
419 // no character available or interrupted, return zero
420 break;
421 }
422 return 0;
423}
424#endif
425
Bram Moolenaarc46af532019-01-09 22:24:49 +0100426#if defined(FEAT_TIMERS) || defined(PROTO)
Bram Moolenaarc9e649a2017-12-18 18:14:47 +0100427/*
428 * Wait for a timer to fire or "wait_func" to return non-zero.
429 * Returns OK when something was read.
430 * Returns FAIL when it timed out or was interrupted.
431 */
432 int
433ui_wait_for_chars_or_timer(
434 long wtime,
435 int (*wait_func)(long wtime, int *interrupted, int ignore_input),
436 int *interrupted,
437 int ignore_input)
438{
439 int due_time;
440 long remaining = wtime;
441 int tb_change_cnt = typebuf.tb_change_cnt;
Bram Moolenaarc46af532019-01-09 22:24:49 +0100442# ifdef FEAT_JOB_CHANNEL
Bram Moolenaare299bbd2019-01-17 14:12:02 +0100443 int brief_wait = FALSE;
Bram Moolenaarc46af532019-01-09 22:24:49 +0100444# endif
Bram Moolenaarc9e649a2017-12-18 18:14:47 +0100445
Bram Moolenaarc46af532019-01-09 22:24:49 +0100446 // When waiting very briefly don't trigger timers.
Bram Moolenaarc9e649a2017-12-18 18:14:47 +0100447 if (wtime >= 0 && wtime < 10L)
448 return wait_func(wtime, NULL, ignore_input);
449
450 while (wtime < 0 || remaining > 0)
451 {
Bram Moolenaarc46af532019-01-09 22:24:49 +0100452 // Trigger timers and then get the time in wtime until the next one is
453 // due. Wait up to that time.
Bram Moolenaarc9e649a2017-12-18 18:14:47 +0100454 due_time = check_due_timer();
455 if (typebuf.tb_change_cnt != tb_change_cnt)
456 {
457 /* timer may have used feedkeys() */
458 return FAIL;
459 }
460 if (due_time <= 0 || (wtime > 0 && due_time > remaining))
461 due_time = remaining;
Bram Moolenaarc46af532019-01-09 22:24:49 +0100462# ifdef FEAT_JOB_CHANNEL
463 if ((due_time < 0 || due_time > 10L)
464# ifdef FEAT_GUI
465 && !gui.in_use
466# endif
467 && (has_pending_job() || channel_any_readahead()))
468 {
469 // There is a pending job or channel, should return soon in order
470 // to handle them ASAP. Do check for input briefly.
471 due_time = 10L;
472 brief_wait = TRUE;
473 }
474# endif
Bram Moolenaarc9e649a2017-12-18 18:14:47 +0100475 if (wait_func(due_time, interrupted, ignore_input))
476 return OK;
Bram Moolenaarc46af532019-01-09 22:24:49 +0100477 if ((interrupted != NULL && *interrupted)
478# ifdef FEAT_JOB_CHANNEL
479 || brief_wait
480# endif
481 )
482 // Nothing available, but need to return so that side effects get
483 // handled, such as handling a message on a channel.
Bram Moolenaara338adc2018-01-31 20:51:47 +0100484 return FAIL;
Bram Moolenaarc9e649a2017-12-18 18:14:47 +0100485 if (wtime > 0)
486 remaining -= due_time;
487 }
488 return FAIL;
489}
490#endif
491
Bram Moolenaar071d4272004-06-13 20:20:40 +0000492/*
Bram Moolenaarc46af532019-01-09 22:24:49 +0100493 * Return non-zero if a character is available.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000494 */
495 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100496ui_char_avail(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000497{
498#ifdef FEAT_GUI
499 if (gui.in_use)
500 {
501 gui_mch_update();
502 return input_available();
503 }
504#endif
505#ifndef NO_CONSOLE
506# ifdef NO_CONSOLE_INPUT
507 if (no_console_input())
508 return 0;
509# endif
510 return mch_char_avail();
511#else
512 return 0;
513#endif
514}
515
516/*
517 * Delay for the given number of milliseconds. If ignoreinput is FALSE then we
518 * cancel the delay if a key is hit.
519 */
520 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100521ui_delay(long msec, int ignoreinput)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000522{
523#ifdef FEAT_GUI
524 if (gui.in_use && !ignoreinput)
Bram Moolenaarc9e649a2017-12-18 18:14:47 +0100525 gui_wait_for_chars(msec, typebuf.tb_change_cnt);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000526 else
527#endif
528 mch_delay(msec, ignoreinput);
529}
530
531/*
532 * If the machine has job control, use it to suspend the program,
533 * otherwise fake it by starting a new shell.
534 * When running the GUI iconify the window.
535 */
536 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100537ui_suspend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000538{
539#ifdef FEAT_GUI
540 if (gui.in_use)
541 {
542 gui_mch_iconify();
543 return;
544 }
545#endif
546 mch_suspend();
547}
548
549#if !defined(UNIX) || !defined(SIGTSTP) || defined(PROTO) || defined(__BEOS__)
550/*
551 * When the OS can't really suspend, call this function to start a shell.
552 * This is never called in the GUI.
553 */
554 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100555suspend_shell(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000556{
557 if (*p_sh == NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100558 emsg(_(e_shellempty));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000559 else
560 {
Bram Moolenaar32526b32019-01-19 17:43:09 +0100561 msg_puts(_("new shell started\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000562 do_shell(NULL, 0);
563 }
564}
565#endif
566
567/*
568 * Try to get the current Vim shell size. Put the result in Rows and Columns.
569 * Use the new sizes as defaults for 'columns' and 'lines'.
570 * Return OK when size could be determined, FAIL otherwise.
571 */
572 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100573ui_get_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000574{
575 int retval;
576
577#ifdef FEAT_GUI
578 if (gui.in_use)
579 retval = gui_get_shellsize();
580 else
581#endif
582 retval = mch_get_shellsize();
583
584 check_shellsize();
585
586 /* adjust the default for 'lines' and 'columns' */
587 if (retval == OK)
588 {
589 set_number_default("lines", Rows);
590 set_number_default("columns", Columns);
591 }
592 return retval;
593}
594
595/*
596 * Set the size of the Vim shell according to Rows and Columns, if possible.
597 * The gui_set_shellsize() or mch_set_shellsize() function will try to set the
598 * new size. If this is not possible, it will adjust Rows and Columns.
599 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000600 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100601ui_set_shellsize(
602 int mustset UNUSED) /* set by the user */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000603{
604#ifdef FEAT_GUI
605 if (gui.in_use)
Bram Moolenaar8968a312013-07-03 16:58:44 +0200606 gui_set_shellsize(mustset, TRUE, RESIZE_BOTH);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000607 else
608#endif
609 mch_set_shellsize();
610}
611
612/*
613 * Called when Rows and/or Columns changed. Adjust scroll region and mouse
614 * region.
615 */
616 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100617ui_new_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000618{
619 if (full_screen && !exiting)
620 {
621#ifdef FEAT_GUI
622 if (gui.in_use)
623 gui_new_shellsize();
624 else
625#endif
626 mch_new_shellsize();
627 }
628}
629
Bram Moolenaar6bc93052019-04-06 20:00:19 +0200630#if ((defined(FEAT_EVAL) || defined(FEAT_TERMINAL)) \
Bram Moolenaar3d3f2172019-04-06 17:56:05 +0200631 && (defined(FEAT_GUI) \
632 || (defined(HAVE_TGETENT) && defined(FEAT_TERMRESPONSE)))) \
Bram Moolenaarfa1e90c2019-04-06 17:47:40 +0200633 || defined(PROTO)
634/*
635 * Get the window position in pixels, if possible.
636 * Return FAIL when not possible.
637 */
638 int
639ui_get_winpos(int *x, int *y, varnumber_T timeout)
640{
641# ifdef FEAT_GUI
642 if (gui.in_use)
643 return gui_mch_get_winpos(x, y);
644# endif
645# if defined(HAVE_TGETENT) && defined(FEAT_TERMRESPONSE)
646 return term_get_winpos(x, y, timeout);
Bram Moolenaar6bc93052019-04-06 20:00:19 +0200647# else
648 return FAIL;
Bram Moolenaarfa1e90c2019-04-06 17:47:40 +0200649# endif
650}
651#endif
652
Bram Moolenaar071d4272004-06-13 20:20:40 +0000653 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100654ui_breakcheck(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000655{
Bram Moolenaarb9c31e72016-09-29 15:18:57 +0200656 ui_breakcheck_force(FALSE);
657}
658
659/*
660 * When "force" is true also check when the terminal is not in raw mode.
661 * This is useful to read input on channels.
662 */
663 void
664ui_breakcheck_force(int force)
665{
Bram Moolenaar48d23bb2018-11-20 02:42:43 +0100666 static int recursive = FALSE;
667 int save_updating_screen = updating_screen;
Bram Moolenaare3caa112017-01-31 22:07:42 +0100668
Bram Moolenaar48d23bb2018-11-20 02:42:43 +0100669 // We could be called recursively if stderr is redirected, calling
670 // fill_input_buf() calls settmode() when stdin isn't a tty. settmode()
671 // calls vgetorpeek() which calls ui_breakcheck() again.
672 if (recursive)
673 return;
674 recursive = TRUE;
675
676 // We do not want gui_resize_shell() to redraw the screen here.
Bram Moolenaare3caa112017-01-31 22:07:42 +0100677 ++updating_screen;
678
Bram Moolenaar071d4272004-06-13 20:20:40 +0000679#ifdef FEAT_GUI
680 if (gui.in_use)
681 gui_mch_update();
682 else
683#endif
Bram Moolenaarb9c31e72016-09-29 15:18:57 +0200684 mch_breakcheck(force);
Bram Moolenaare3caa112017-01-31 22:07:42 +0100685
Bram Moolenaar42335f52018-09-13 15:33:43 +0200686 if (save_updating_screen)
687 updating_screen = TRUE;
Bram Moolenaar0cb8ac72018-05-11 22:01:51 +0200688 else
689 reset_updating_screen(FALSE);
Bram Moolenaar48d23bb2018-11-20 02:42:43 +0100690
691 recursive = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000692}
693
694/*****************************************************************************
695 * Functions for copying and pasting text between applications.
696 * This is always included in a GUI version, but may also be included when the
697 * clipboard and mouse is available to a terminal version such as xterm.
698 * Note: there are some more functions in ops.c that handle selection stuff.
699 *
700 * Also note that the majority of functions here deal with the X 'primary'
701 * (visible - for Visual mode use) selection, and only that. There are no
702 * versions of these for the 'clipboard' selection, as Visual mode has no use
703 * for them.
704 */
705
706#if defined(FEAT_CLIPBOARD) || defined(PROTO)
707
708/*
709 * Selection stuff using Visual mode, for cutting and pasting text to other
710 * windows.
711 */
712
713/*
714 * Call this to initialise the clipboard. Pass it FALSE if the clipboard code
715 * is included, but the clipboard can not be used, or TRUE if the clipboard can
716 * be used. Eg unix may call this with FALSE, then call it again with TRUE if
717 * the GUI starts.
718 */
719 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100720clip_init(int can_use)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000721{
722 VimClipboard *cb;
723
724 cb = &clip_star;
725 for (;;)
726 {
727 cb->available = can_use;
728 cb->owned = FALSE;
729 cb->start.lnum = 0;
730 cb->start.col = 0;
731 cb->end.lnum = 0;
732 cb->end.col = 0;
733 cb->state = SELECT_CLEARED;
734
735 if (cb == &clip_plus)
736 break;
737 cb = &clip_plus;
738 }
739}
740
741/*
742 * Check whether the VIsual area has changed, and if so try to become the owner
743 * of the selection, and free any old converted selection we may still have
744 * lying around. If the VIsual mode has ended, make a copy of what was
745 * selected so we can still give it to others. Will probably have to make sure
746 * this is called whenever VIsual mode is ended.
747 */
748 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100749clip_update_selection(VimClipboard *clip)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000750{
Bram Moolenaarc0885aa2012-07-10 16:49:23 +0200751 pos_T start, end;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000752
753 /* If visual mode is only due to a redo command ("."), then ignore it */
754 if (!redo_VIsual_busy && VIsual_active && (State & NORMAL))
755 {
Bram Moolenaarb5aedf32017-03-12 18:23:53 +0100756 if (LT_POS(VIsual, curwin->w_cursor))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000757 {
758 start = VIsual;
759 end = curwin->w_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000760 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +0000761 end.col += (*mb_ptr2len)(ml_get_cursor()) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000762 }
763 else
764 {
765 start = curwin->w_cursor;
766 end = VIsual;
767 }
Bram Moolenaarb5aedf32017-03-12 18:23:53 +0100768 if (!EQUAL_POS(clip->start, start)
769 || !EQUAL_POS(clip->end, end)
Bram Moolenaarc0885aa2012-07-10 16:49:23 +0200770 || clip->vmode != VIsual_mode)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000771 {
Bram Moolenaarc0885aa2012-07-10 16:49:23 +0200772 clip_clear_selection(clip);
773 clip->start = start;
774 clip->end = end;
775 clip->vmode = VIsual_mode;
776 clip_free_selection(clip);
777 clip_own_selection(clip);
778 clip_gen_set_selection(clip);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000779 }
780 }
781}
782
783 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100784clip_own_selection(VimClipboard *cbd)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000785{
786 /*
787 * Also want to check somehow that we are reading from the keyboard rather
788 * than a mapping etc.
789 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000790#ifdef FEAT_X11
Bram Moolenaar7cfea752010-06-22 06:07:12 +0200791 /* Always own the selection, we might have lost it without being
Bram Moolenaar62b42182010-09-21 22:09:37 +0200792 * notified, e.g. during a ":sh" command. */
Bram Moolenaar7cfea752010-06-22 06:07:12 +0200793 if (cbd->available)
794 {
795 int was_owned = cbd->owned;
796
797 cbd->owned = (clip_gen_own_selection(cbd) == OK);
Bram Moolenaarc0885aa2012-07-10 16:49:23 +0200798 if (!was_owned && (cbd == &clip_star || cbd == &clip_plus))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000799 {
Bram Moolenaarebefac62005-12-28 22:39:57 +0000800 /* May have to show a different kind of highlighting for the
801 * selected area. There is no specific redraw command for this,
802 * just redraw all windows on the current buffer. */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000803 if (cbd->owned
Bram Moolenaarb3656ed2006-03-20 21:59:49 +0000804 && (get_real_state() == VISUAL
805 || get_real_state() == SELECTMODE)
Bram Moolenaarc0885aa2012-07-10 16:49:23 +0200806 && (cbd == &clip_star ? clip_isautosel_star()
807 : clip_isautosel_plus())
Bram Moolenaar8820b482017-03-16 17:23:31 +0100808 && HL_ATTR(HLF_V) != HL_ATTR(HLF_VNC))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000809 redraw_curbuf_later(INVERTED_ALL);
810 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000811 }
Bram Moolenaar7cfea752010-06-22 06:07:12 +0200812#else
Bram Moolenaarb6101cf2012-10-21 00:58:39 +0200813 /* Only own the clipboard when we didn't own it yet. */
Bram Moolenaar7cfea752010-06-22 06:07:12 +0200814 if (!cbd->owned && cbd->available)
815 cbd->owned = (clip_gen_own_selection(cbd) == OK);
816#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000817}
818
819 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100820clip_lose_selection(VimClipboard *cbd)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000821{
822#ifdef FEAT_X11
823 int was_owned = cbd->owned;
824#endif
Bram Moolenaarc0885aa2012-07-10 16:49:23 +0200825 int visual_selection = FALSE;
826
827 if (cbd == &clip_star || cbd == &clip_plus)
828 visual_selection = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000829
830 clip_free_selection(cbd);
831 cbd->owned = FALSE;
832 if (visual_selection)
Bram Moolenaarc0885aa2012-07-10 16:49:23 +0200833 clip_clear_selection(cbd);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000834 clip_gen_lose_selection(cbd);
835#ifdef FEAT_X11
836 if (visual_selection)
837 {
838 /* May have to show a different kind of highlighting for the selected
839 * area. There is no specific redraw command for this, just redraw all
840 * windows on the current buffer. */
841 if (was_owned
Bram Moolenaarb3656ed2006-03-20 21:59:49 +0000842 && (get_real_state() == VISUAL
843 || get_real_state() == SELECTMODE)
Bram Moolenaarc0885aa2012-07-10 16:49:23 +0200844 && (cbd == &clip_star ?
845 clip_isautosel_star() : clip_isautosel_plus())
Bram Moolenaar8820b482017-03-16 17:23:31 +0100846 && HL_ATTR(HLF_V) != HL_ATTR(HLF_VNC))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000847 {
848 update_curbuf(INVERTED_ALL);
849 setcursor();
850 cursor_on();
Bram Moolenaara338adc2018-01-31 20:51:47 +0100851 out_flush_cursor(TRUE, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000852 }
853 }
854#endif
855}
856
Bram Moolenaarc0885aa2012-07-10 16:49:23 +0200857 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100858clip_copy_selection(VimClipboard *clip)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000859{
Bram Moolenaarc0885aa2012-07-10 16:49:23 +0200860 if (VIsual_active && (State & NORMAL) && clip->available)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000861 {
Bram Moolenaarc0885aa2012-07-10 16:49:23 +0200862 clip_update_selection(clip);
863 clip_free_selection(clip);
864 clip_own_selection(clip);
865 if (clip->owned)
866 clip_get_selection(clip);
867 clip_gen_set_selection(clip);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000868 }
869}
870
871/*
Bram Moolenaar6b1ee342014-08-06 18:17:11 +0200872 * Save and restore clip_unnamed before doing possibly many changes. This
873 * prevents accessing the clipboard very often which might slow down Vim
874 * considerably.
875 */
Bram Moolenaar73a156b2015-01-27 21:39:05 +0100876static int global_change_count = 0; /* if set, inside a start_global_changes */
Bram Moolenaar3fcfa352017-03-29 19:20:41 +0200877static int clipboard_needs_update = FALSE; /* clipboard needs to be updated */
878static int clip_did_set_selection = TRUE;
Bram Moolenaar6b1ee342014-08-06 18:17:11 +0200879
880/*
881 * Save clip_unnamed and reset it.
882 */
883 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100884start_global_changes(void)
Bram Moolenaar6b1ee342014-08-06 18:17:11 +0200885{
Bram Moolenaar5c27fd12015-01-27 14:09:37 +0100886 if (++global_change_count > 1)
887 return;
Bram Moolenaar6b1ee342014-08-06 18:17:11 +0200888 clip_unnamed_saved = clip_unnamed;
Bram Moolenaar5c27fd12015-01-27 14:09:37 +0100889 clipboard_needs_update = FALSE;
Bram Moolenaar6b1ee342014-08-06 18:17:11 +0200890
Bram Moolenaar5c27fd12015-01-27 14:09:37 +0100891 if (clip_did_set_selection)
Bram Moolenaar6b1ee342014-08-06 18:17:11 +0200892 {
893 clip_unnamed = FALSE;
894 clip_did_set_selection = FALSE;
895 }
896}
897
898/*
Bram Moolenaar3fcfa352017-03-29 19:20:41 +0200899 * Return TRUE if setting the clipboard was postponed, it already contains the
900 * right text.
901 */
902 int
903is_clipboard_needs_update()
904{
905 return clipboard_needs_update;
906}
907
908/*
Bram Moolenaar6b1ee342014-08-06 18:17:11 +0200909 * Restore clip_unnamed and set the selection when needed.
910 */
911 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100912end_global_changes(void)
Bram Moolenaar6b1ee342014-08-06 18:17:11 +0200913{
Bram Moolenaar5c27fd12015-01-27 14:09:37 +0100914 if (--global_change_count > 0)
915 /* recursive */
916 return;
917 if (!clip_did_set_selection)
Bram Moolenaar6b1ee342014-08-06 18:17:11 +0200918 {
919 clip_did_set_selection = TRUE;
920 clip_unnamed = clip_unnamed_saved;
Bram Moolenaar5c27fd12015-01-27 14:09:37 +0100921 clip_unnamed_saved = FALSE;
922 if (clipboard_needs_update)
Bram Moolenaar6b1ee342014-08-06 18:17:11 +0200923 {
Bram Moolenaar5c27fd12015-01-27 14:09:37 +0100924 /* only store something in the clipboard,
925 * if we have yanked anything to it */
926 if (clip_unnamed & CLIP_UNNAMED)
927 {
928 clip_own_selection(&clip_star);
929 clip_gen_set_selection(&clip_star);
930 }
931 if (clip_unnamed & CLIP_UNNAMED_PLUS)
932 {
933 clip_own_selection(&clip_plus);
934 clip_gen_set_selection(&clip_plus);
935 }
Bram Moolenaar6b1ee342014-08-06 18:17:11 +0200936 }
937 }
Bram Moolenaar3fcfa352017-03-29 19:20:41 +0200938 clipboard_needs_update = FALSE;
Bram Moolenaar6b1ee342014-08-06 18:17:11 +0200939}
940
941/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000942 * Called when Visual mode is ended: update the selection.
943 */
944 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100945clip_auto_select(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000946{
Bram Moolenaarc0885aa2012-07-10 16:49:23 +0200947 if (clip_isautosel_star())
948 clip_copy_selection(&clip_star);
949 if (clip_isautosel_plus())
950 clip_copy_selection(&clip_plus);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000951}
952
953/*
Bram Moolenaarc0885aa2012-07-10 16:49:23 +0200954 * Return TRUE if automatic selection of Visual area is desired for the *
955 * register.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000956 */
957 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100958clip_isautosel_star(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000959{
960 return (
961#ifdef FEAT_GUI
962 gui.in_use ? (vim_strchr(p_go, GO_ASEL) != NULL) :
963#endif
Bram Moolenaarc0885aa2012-07-10 16:49:23 +0200964 clip_autoselect_star);
965}
966
967/*
968 * Return TRUE if automatic selection of Visual area is desired for the +
969 * register.
970 */
971 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100972clip_isautosel_plus(void)
Bram Moolenaarc0885aa2012-07-10 16:49:23 +0200973{
974 return (
975#ifdef FEAT_GUI
976 gui.in_use ? (vim_strchr(p_go, GO_ASELPLUS) != NULL) :
977#endif
978 clip_autoselect_plus);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000979}
980
981
982/*
983 * Stuff for general mouse selection, without using Visual mode.
984 */
985
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100986static void clip_invert_area(int, int, int, int, int how);
987static void clip_invert_rectangle(int row, int col, int height, int width, int invert);
988static void clip_get_word_boundaries(VimClipboard *, int, int);
989static int clip_get_line_end(int);
990static void clip_update_modeless_selection(VimClipboard *, int, int,
991 int, int);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000992
993/* flags for clip_invert_area() */
994#define CLIP_CLEAR 1
995#define CLIP_SET 2
996#define CLIP_TOGGLE 3
997
998/*
999 * Start, continue or end a modeless selection. Used when editing the
1000 * command-line and in the cmdline window.
1001 */
1002 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001003clip_modeless(int button, int is_click, int is_drag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001004{
1005 int repeat;
1006
1007 repeat = ((clip_star.mode == SELECT_MODE_CHAR
1008 || clip_star.mode == SELECT_MODE_LINE)
1009 && (mod_mask & MOD_MASK_2CLICK))
1010 || (clip_star.mode == SELECT_MODE_WORD
1011 && (mod_mask & MOD_MASK_3CLICK));
1012 if (is_click && button == MOUSE_RIGHT)
1013 {
1014 /* Right mouse button: If there was no selection, start one.
1015 * Otherwise extend the existing selection. */
1016 if (clip_star.state == SELECT_CLEARED)
1017 clip_start_selection(mouse_col, mouse_row, FALSE);
1018 clip_process_selection(button, mouse_col, mouse_row, repeat);
1019 }
1020 else if (is_click)
1021 clip_start_selection(mouse_col, mouse_row, repeat);
1022 else if (is_drag)
1023 {
1024 /* Don't try extending a selection if there isn't one. Happens when
1025 * button-down is in the cmdline and them moving mouse upwards. */
1026 if (clip_star.state != SELECT_CLEARED)
1027 clip_process_selection(button, mouse_col, mouse_row, repeat);
1028 }
1029 else /* release */
1030 clip_process_selection(MOUSE_RELEASE, mouse_col, mouse_row, FALSE);
1031}
1032
1033/*
1034 * Compare two screen positions ala strcmp()
1035 */
1036 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001037clip_compare_pos(
1038 int row1,
1039 int col1,
1040 int row2,
1041 int col2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001042{
1043 if (row1 > row2) return(1);
1044 if (row1 < row2) return(-1);
1045 if (col1 > col2) return(1);
1046 if (col1 < col2) return(-1);
Bram Moolenaar9e341102016-02-23 23:04:36 +01001047 return(0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001048}
1049
1050/*
1051 * Start the selection
1052 */
1053 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001054clip_start_selection(int col, int row, int repeated_click)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001055{
1056 VimClipboard *cb = &clip_star;
1057
1058 if (cb->state == SELECT_DONE)
Bram Moolenaarc0885aa2012-07-10 16:49:23 +02001059 clip_clear_selection(cb);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001060
1061 row = check_row(row);
1062 col = check_col(col);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001063 col = mb_fix_col(col, row);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001064
1065 cb->start.lnum = row;
1066 cb->start.col = col;
1067 cb->end = cb->start;
1068 cb->origin_row = (short_u)cb->start.lnum;
1069 cb->state = SELECT_IN_PROGRESS;
1070
1071 if (repeated_click)
1072 {
1073 if (++cb->mode > SELECT_MODE_LINE)
1074 cb->mode = SELECT_MODE_CHAR;
1075 }
1076 else
1077 cb->mode = SELECT_MODE_CHAR;
1078
1079#ifdef FEAT_GUI
1080 /* clear the cursor until the selection is made */
1081 if (gui.in_use)
1082 gui_undraw_cursor();
1083#endif
1084
1085 switch (cb->mode)
1086 {
1087 case SELECT_MODE_CHAR:
1088 cb->origin_start_col = cb->start.col;
1089 cb->word_end_col = clip_get_line_end((int)cb->start.lnum);
1090 break;
1091
1092 case SELECT_MODE_WORD:
1093 clip_get_word_boundaries(cb, (int)cb->start.lnum, cb->start.col);
1094 cb->origin_start_col = cb->word_start_col;
1095 cb->origin_end_col = cb->word_end_col;
1096
1097 clip_invert_area((int)cb->start.lnum, cb->word_start_col,
1098 (int)cb->end.lnum, cb->word_end_col, CLIP_SET);
1099 cb->start.col = cb->word_start_col;
1100 cb->end.col = cb->word_end_col;
1101 break;
1102
1103 case SELECT_MODE_LINE:
1104 clip_invert_area((int)cb->start.lnum, 0, (int)cb->start.lnum,
1105 (int)Columns, CLIP_SET);
1106 cb->start.col = 0;
1107 cb->end.col = Columns;
1108 break;
1109 }
1110
1111 cb->prev = cb->start;
1112
1113#ifdef DEBUG_SELECTION
1114 printf("Selection started at (%u,%u)\n", cb->start.lnum, cb->start.col);
1115#endif
1116}
1117
1118/*
1119 * Continue processing the selection
1120 */
1121 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001122clip_process_selection(
1123 int button,
1124 int col,
1125 int row,
1126 int_u repeated_click)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001127{
1128 VimClipboard *cb = &clip_star;
1129 int diff;
1130 int slen = 1; /* cursor shape width */
1131
1132 if (button == MOUSE_RELEASE)
1133 {
1134 /* Check to make sure we have something selected */
1135 if (cb->start.lnum == cb->end.lnum && cb->start.col == cb->end.col)
1136 {
1137#ifdef FEAT_GUI
1138 if (gui.in_use)
1139 gui_update_cursor(FALSE, FALSE);
1140#endif
1141 cb->state = SELECT_CLEARED;
1142 return;
1143 }
1144
1145#ifdef DEBUG_SELECTION
1146 printf("Selection ended: (%u,%u) to (%u,%u)\n", cb->start.lnum,
1147 cb->start.col, cb->end.lnum, cb->end.col);
1148#endif
Bram Moolenaarc0885aa2012-07-10 16:49:23 +02001149 if (clip_isautosel_star()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001150 || (
1151#ifdef FEAT_GUI
1152 gui.in_use ? (vim_strchr(p_go, GO_ASELML) != NULL) :
1153#endif
1154 clip_autoselectml))
1155 clip_copy_modeless_selection(FALSE);
1156#ifdef FEAT_GUI
1157 if (gui.in_use)
1158 gui_update_cursor(FALSE, FALSE);
1159#endif
1160
1161 cb->state = SELECT_DONE;
1162 return;
1163 }
1164
1165 row = check_row(row);
1166 col = check_col(col);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001167 col = mb_fix_col(col, row);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001168
1169 if (col == (int)cb->prev.col && row == cb->prev.lnum && !repeated_click)
1170 return;
1171
1172 /*
1173 * When extending the selection with the right mouse button, swap the
1174 * start and end if the position is before half the selection
1175 */
1176 if (cb->state == SELECT_DONE && button == MOUSE_RIGHT)
1177 {
1178 /*
1179 * If the click is before the start, or the click is inside the
1180 * selection and the start is the closest side, set the origin to the
1181 * end of the selection.
1182 */
1183 if (clip_compare_pos(row, col, (int)cb->start.lnum, cb->start.col) < 0
1184 || (clip_compare_pos(row, col,
1185 (int)cb->end.lnum, cb->end.col) < 0
1186 && (((cb->start.lnum == cb->end.lnum
1187 && cb->end.col - col > col - cb->start.col))
1188 || ((diff = (cb->end.lnum - row) -
1189 (row - cb->start.lnum)) > 0
1190 || (diff == 0 && col < (int)(cb->start.col +
1191 cb->end.col) / 2)))))
1192 {
1193 cb->origin_row = (short_u)cb->end.lnum;
1194 cb->origin_start_col = cb->end.col - 1;
1195 cb->origin_end_col = cb->end.col;
1196 }
1197 else
1198 {
1199 cb->origin_row = (short_u)cb->start.lnum;
1200 cb->origin_start_col = cb->start.col;
1201 cb->origin_end_col = cb->start.col;
1202 }
1203 if (cb->mode == SELECT_MODE_WORD && !repeated_click)
1204 cb->mode = SELECT_MODE_CHAR;
1205 }
1206
1207 /* set state, for when using the right mouse button */
1208 cb->state = SELECT_IN_PROGRESS;
1209
1210#ifdef DEBUG_SELECTION
1211 printf("Selection extending to (%d,%d)\n", row, col);
1212#endif
1213
1214 if (repeated_click && ++cb->mode > SELECT_MODE_LINE)
1215 cb->mode = SELECT_MODE_CHAR;
1216
1217 switch (cb->mode)
1218 {
1219 case SELECT_MODE_CHAR:
1220 /* If we're on a different line, find where the line ends */
1221 if (row != cb->prev.lnum)
1222 cb->word_end_col = clip_get_line_end(row);
1223
1224 /* See if we are before or after the origin of the selection */
1225 if (clip_compare_pos(row, col, cb->origin_row,
1226 cb->origin_start_col) >= 0)
1227 {
1228 if (col >= (int)cb->word_end_col)
1229 clip_update_modeless_selection(cb, cb->origin_row,
1230 cb->origin_start_col, row, (int)Columns);
1231 else
1232 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001233 if (has_mbyte && mb_lefthalve(row, col))
1234 slen = 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001235 clip_update_modeless_selection(cb, cb->origin_row,
1236 cb->origin_start_col, row, col + slen);
1237 }
1238 }
1239 else
1240 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001241 if (has_mbyte
1242 && mb_lefthalve(cb->origin_row, cb->origin_start_col))
1243 slen = 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001244 if (col >= (int)cb->word_end_col)
1245 clip_update_modeless_selection(cb, row, cb->word_end_col,
1246 cb->origin_row, cb->origin_start_col + slen);
1247 else
1248 clip_update_modeless_selection(cb, row, col,
1249 cb->origin_row, cb->origin_start_col + slen);
1250 }
1251 break;
1252
1253 case SELECT_MODE_WORD:
1254 /* If we are still within the same word, do nothing */
1255 if (row == cb->prev.lnum && col >= (int)cb->word_start_col
1256 && col < (int)cb->word_end_col && !repeated_click)
1257 return;
1258
1259 /* Get new word boundaries */
1260 clip_get_word_boundaries(cb, row, col);
1261
1262 /* Handle being after the origin point of selection */
1263 if (clip_compare_pos(row, col, cb->origin_row,
1264 cb->origin_start_col) >= 0)
1265 clip_update_modeless_selection(cb, cb->origin_row,
1266 cb->origin_start_col, row, cb->word_end_col);
1267 else
1268 clip_update_modeless_selection(cb, row, cb->word_start_col,
1269 cb->origin_row, cb->origin_end_col);
1270 break;
1271
1272 case SELECT_MODE_LINE:
1273 if (row == cb->prev.lnum && !repeated_click)
1274 return;
1275
1276 if (clip_compare_pos(row, col, cb->origin_row,
1277 cb->origin_start_col) >= 0)
1278 clip_update_modeless_selection(cb, cb->origin_row, 0, row,
1279 (int)Columns);
1280 else
1281 clip_update_modeless_selection(cb, row, 0, cb->origin_row,
1282 (int)Columns);
1283 break;
1284 }
1285
1286 cb->prev.lnum = row;
1287 cb->prev.col = col;
1288
1289#ifdef DEBUG_SELECTION
1290 printf("Selection is: (%u,%u) to (%u,%u)\n", cb->start.lnum,
1291 cb->start.col, cb->end.lnum, cb->end.col);
1292#endif
1293}
1294
Bram Moolenaar071d4272004-06-13 20:20:40 +00001295# if defined(FEAT_GUI) || defined(PROTO)
1296/*
1297 * Redraw part of the selection if character at "row,col" is inside of it.
1298 * Only used for the GUI.
1299 */
1300 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001301clip_may_redraw_selection(int row, int col, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001302{
1303 int start = col;
1304 int end = col + len;
1305
1306 if (clip_star.state != SELECT_CLEARED
1307 && row >= clip_star.start.lnum
1308 && row <= clip_star.end.lnum)
1309 {
1310 if (row == clip_star.start.lnum && start < (int)clip_star.start.col)
1311 start = clip_star.start.col;
1312 if (row == clip_star.end.lnum && end > (int)clip_star.end.col)
1313 end = clip_star.end.col;
1314 if (end > start)
1315 clip_invert_area(row, start, row, end, 0);
1316 }
1317}
1318# endif
1319
1320/*
1321 * Called from outside to clear selected region from the display
1322 */
1323 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001324clip_clear_selection(VimClipboard *cbd)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001325{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001326
Bram Moolenaarc0885aa2012-07-10 16:49:23 +02001327 if (cbd->state == SELECT_CLEARED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001328 return;
1329
Bram Moolenaarc0885aa2012-07-10 16:49:23 +02001330 clip_invert_area((int)cbd->start.lnum, cbd->start.col, (int)cbd->end.lnum,
1331 cbd->end.col, CLIP_CLEAR);
1332 cbd->state = SELECT_CLEARED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001333}
1334
1335/*
1336 * Clear the selection if any lines from "row1" to "row2" are inside of it.
1337 */
1338 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001339clip_may_clear_selection(int row1, int row2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001340{
1341 if (clip_star.state == SELECT_DONE
1342 && row2 >= clip_star.start.lnum
1343 && row1 <= clip_star.end.lnum)
Bram Moolenaarc0885aa2012-07-10 16:49:23 +02001344 clip_clear_selection(&clip_star);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001345}
1346
1347/*
1348 * Called before the screen is scrolled up or down. Adjusts the line numbers
1349 * of the selection. Call with big number when clearing the screen.
1350 */
1351 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001352clip_scroll_selection(
1353 int rows) /* negative for scroll down */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001354{
1355 int lnum;
1356
1357 if (clip_star.state == SELECT_CLEARED)
1358 return;
1359
1360 lnum = clip_star.start.lnum - rows;
1361 if (lnum <= 0)
1362 clip_star.start.lnum = 0;
1363 else if (lnum >= screen_Rows) /* scrolled off of the screen */
1364 clip_star.state = SELECT_CLEARED;
1365 else
1366 clip_star.start.lnum = lnum;
1367
1368 lnum = clip_star.end.lnum - rows;
1369 if (lnum < 0) /* scrolled off of the screen */
1370 clip_star.state = SELECT_CLEARED;
1371 else if (lnum >= screen_Rows)
1372 clip_star.end.lnum = screen_Rows - 1;
1373 else
1374 clip_star.end.lnum = lnum;
1375}
1376
1377/*
1378 * Invert a region of the display between a starting and ending row and column
1379 * Values for "how":
1380 * CLIP_CLEAR: undo inversion
1381 * CLIP_SET: set inversion
1382 * CLIP_TOGGLE: set inversion if pos1 < pos2, undo inversion otherwise.
1383 * 0: invert (GUI only).
1384 */
1385 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001386clip_invert_area(
1387 int row1,
1388 int col1,
1389 int row2,
1390 int col2,
1391 int how)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001392{
1393 int invert = FALSE;
1394
1395 if (how == CLIP_SET)
1396 invert = TRUE;
1397
1398 /* Swap the from and to positions so the from is always before */
1399 if (clip_compare_pos(row1, col1, row2, col2) > 0)
1400 {
1401 int tmp_row, tmp_col;
1402
1403 tmp_row = row1;
1404 tmp_col = col1;
1405 row1 = row2;
1406 col1 = col2;
1407 row2 = tmp_row;
1408 col2 = tmp_col;
1409 }
1410 else if (how == CLIP_TOGGLE)
1411 invert = TRUE;
1412
1413 /* If all on the same line, do it the easy way */
1414 if (row1 == row2)
1415 {
1416 clip_invert_rectangle(row1, col1, 1, col2 - col1, invert);
1417 }
1418 else
1419 {
1420 /* Handle a piece of the first line */
1421 if (col1 > 0)
1422 {
1423 clip_invert_rectangle(row1, col1, 1, (int)Columns - col1, invert);
1424 row1++;
1425 }
1426
1427 /* Handle a piece of the last line */
1428 if (col2 < Columns - 1)
1429 {
1430 clip_invert_rectangle(row2, 0, 1, col2, invert);
1431 row2--;
1432 }
1433
1434 /* Handle the rectangle thats left */
1435 if (row2 >= row1)
1436 clip_invert_rectangle(row1, 0, row2 - row1 + 1, (int)Columns,
1437 invert);
1438 }
1439}
1440
1441/*
1442 * Invert or un-invert a rectangle of the screen.
1443 * "invert" is true if the result is inverted.
1444 */
1445 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001446clip_invert_rectangle(
1447 int row,
1448 int col,
1449 int height,
1450 int width,
1451 int invert)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001452{
1453#ifdef FEAT_GUI
1454 if (gui.in_use)
1455 gui_mch_invert_rectangle(row, col, height, width);
1456 else
1457#endif
1458 screen_draw_rectangle(row, col, height, width, invert);
1459}
1460
1461/*
1462 * Copy the currently selected area into the '*' register so it will be
1463 * available for pasting.
1464 * When "both" is TRUE also copy to the '+' register.
1465 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001466 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001467clip_copy_modeless_selection(int both UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001468{
1469 char_u *buffer;
1470 char_u *bufp;
1471 int row;
1472 int start_col;
1473 int end_col;
1474 int line_end_col;
1475 int add_newline_flag = FALSE;
1476 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001477 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001478 int row1 = clip_star.start.lnum;
1479 int col1 = clip_star.start.col;
1480 int row2 = clip_star.end.lnum;
1481 int col2 = clip_star.end.col;
1482
Bram Moolenaar482aaeb2005-09-29 18:26:07 +00001483 /* Can't use ScreenLines unless initialized */
1484 if (ScreenLines == NULL)
1485 return;
1486
Bram Moolenaar071d4272004-06-13 20:20:40 +00001487 /*
1488 * Make sure row1 <= row2, and if row1 == row2 that col1 <= col2.
1489 */
1490 if (row1 > row2)
1491 {
1492 row = row1; row1 = row2; row2 = row;
1493 row = col1; col1 = col2; col2 = row;
1494 }
1495 else if (row1 == row2 && col1 > col2)
1496 {
1497 row = col1; col1 = col2; col2 = row;
1498 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001499 /* correct starting point for being on right halve of double-wide char */
1500 p = ScreenLines + LineOffset[row1];
1501 if (enc_dbcs != 0)
1502 col1 -= (*mb_head_off)(p, p + col1);
1503 else if (enc_utf8 && p[col1] == 0)
1504 --col1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001505
1506 /* Create a temporary buffer for storing the text */
1507 len = (row2 - row1 + 1) * Columns + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001508 if (enc_dbcs != 0)
1509 len *= 2; /* max. 2 bytes per display cell */
1510 else if (enc_utf8)
Bram Moolenaar362e1a32006-03-06 23:29:24 +00001511 len *= MB_MAXBYTES;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001512 buffer = lalloc((long_u)len, TRUE);
1513 if (buffer == NULL) /* out of memory */
1514 return;
1515
1516 /* Process each row in the selection */
1517 for (bufp = buffer, row = row1; row <= row2; row++)
1518 {
1519 if (row == row1)
1520 start_col = col1;
1521 else
1522 start_col = 0;
1523
1524 if (row == row2)
1525 end_col = col2;
1526 else
1527 end_col = Columns;
1528
1529 line_end_col = clip_get_line_end(row);
1530
1531 /* See if we need to nuke some trailing whitespace */
1532 if (end_col >= Columns && (row < row2 || end_col > line_end_col))
1533 {
1534 /* Get rid of trailing whitespace */
1535 end_col = line_end_col;
1536 if (end_col < start_col)
1537 end_col = start_col;
1538
1539 /* If the last line extended to the end, add an extra newline */
1540 if (row == row2)
1541 add_newline_flag = TRUE;
1542 }
1543
1544 /* If after the first row, we need to always add a newline */
1545 if (row > row1 && !LineWraps[row - 1])
1546 *bufp++ = NL;
1547
1548 if (row < screen_Rows && end_col <= screen_Columns)
1549 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001550 if (enc_dbcs != 0)
1551 {
Bram Moolenaar89d40322006-08-29 15:30:07 +00001552 int i;
1553
Bram Moolenaar071d4272004-06-13 20:20:40 +00001554 p = ScreenLines + LineOffset[row];
1555 for (i = start_col; i < end_col; ++i)
1556 if (enc_dbcs == DBCS_JPNU && p[i] == 0x8e)
1557 {
1558 /* single-width double-byte char */
1559 *bufp++ = 0x8e;
1560 *bufp++ = ScreenLines2[LineOffset[row] + i];
1561 }
1562 else
1563 {
1564 *bufp++ = p[i];
1565 if (MB_BYTE2LEN(p[i]) == 2)
1566 *bufp++ = p[++i];
1567 }
1568 }
1569 else if (enc_utf8)
1570 {
1571 int off;
Bram Moolenaar362e1a32006-03-06 23:29:24 +00001572 int i;
Bram Moolenaar34e9e2f2006-03-14 23:07:19 +00001573 int ci;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001574
1575 off = LineOffset[row];
1576 for (i = start_col; i < end_col; ++i)
1577 {
1578 /* The base character is either in ScreenLinesUC[] or
1579 * ScreenLines[]. */
1580 if (ScreenLinesUC[off + i] == 0)
1581 *bufp++ = ScreenLines[off + i];
1582 else
1583 {
1584 bufp += utf_char2bytes(ScreenLinesUC[off + i], bufp);
Bram Moolenaar34e9e2f2006-03-14 23:07:19 +00001585 for (ci = 0; ci < Screen_mco; ++ci)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001586 {
Bram Moolenaar362e1a32006-03-06 23:29:24 +00001587 /* Add a composing character. */
Bram Moolenaar34e9e2f2006-03-14 23:07:19 +00001588 if (ScreenLinesC[ci][off + i] == 0)
Bram Moolenaar362e1a32006-03-06 23:29:24 +00001589 break;
Bram Moolenaar34e9e2f2006-03-14 23:07:19 +00001590 bufp += utf_char2bytes(ScreenLinesC[ci][off + i],
Bram Moolenaar071d4272004-06-13 20:20:40 +00001591 bufp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001592 }
1593 }
1594 /* Skip right halve of double-wide character. */
1595 if (ScreenLines[off + i + 1] == 0)
1596 ++i;
1597 }
1598 }
1599 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001600 {
1601 STRNCPY(bufp, ScreenLines + LineOffset[row] + start_col,
1602 end_col - start_col);
1603 bufp += end_col - start_col;
1604 }
1605 }
1606 }
1607
1608 /* Add a newline at the end if the selection ended there */
1609 if (add_newline_flag)
1610 *bufp++ = NL;
1611
1612 /* First cleanup any old selection and become the owner. */
1613 clip_free_selection(&clip_star);
1614 clip_own_selection(&clip_star);
1615
1616 /* Yank the text into the '*' register. */
1617 clip_yank_selection(MCHAR, buffer, (long)(bufp - buffer), &clip_star);
1618
1619 /* Make the register contents available to the outside world. */
1620 clip_gen_set_selection(&clip_star);
1621
1622#ifdef FEAT_X11
1623 if (both)
1624 {
1625 /* Do the same for the '+' register. */
1626 clip_free_selection(&clip_plus);
1627 clip_own_selection(&clip_plus);
1628 clip_yank_selection(MCHAR, buffer, (long)(bufp - buffer), &clip_plus);
1629 clip_gen_set_selection(&clip_plus);
1630 }
1631#endif
1632 vim_free(buffer);
1633}
1634
1635/*
1636 * Find the starting and ending positions of the word at the given row and
1637 * column. Only white-separated words are recognized here.
1638 */
1639#define CHAR_CLASS(c) (c <= ' ' ? ' ' : vim_iswordc(c))
1640
1641 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001642clip_get_word_boundaries(VimClipboard *cb, int row, int col)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001643{
1644 int start_class;
1645 int temp_col;
1646 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001647 int mboff;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001648
Bram Moolenaar482aaeb2005-09-29 18:26:07 +00001649 if (row >= screen_Rows || col >= screen_Columns || ScreenLines == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001650 return;
1651
1652 p = ScreenLines + LineOffset[row];
Bram Moolenaar071d4272004-06-13 20:20:40 +00001653 /* Correct for starting in the right halve of a double-wide char */
1654 if (enc_dbcs != 0)
1655 col -= dbcs_screen_head_off(p, p + col);
1656 else if (enc_utf8 && p[col] == 0)
1657 --col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001658 start_class = CHAR_CLASS(p[col]);
1659
1660 temp_col = col;
1661 for ( ; temp_col > 0; temp_col--)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001662 if (enc_dbcs != 0
1663 && (mboff = dbcs_screen_head_off(p, p + temp_col - 1)) > 0)
1664 temp_col -= mboff;
Bram Moolenaar264b74f2019-01-24 17:18:42 +01001665 else if (CHAR_CLASS(p[temp_col - 1]) != start_class
1666 && !(enc_utf8 && p[temp_col - 1] == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001667 break;
1668 cb->word_start_col = temp_col;
1669
1670 temp_col = col;
1671 for ( ; temp_col < screen_Columns; temp_col++)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001672 if (enc_dbcs != 0 && dbcs_ptr2cells(p + temp_col) == 2)
1673 ++temp_col;
Bram Moolenaar264b74f2019-01-24 17:18:42 +01001674 else if (CHAR_CLASS(p[temp_col]) != start_class
1675 && !(enc_utf8 && p[temp_col] == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001676 break;
1677 cb->word_end_col = temp_col;
1678}
1679
1680/*
1681 * Find the column position for the last non-whitespace character on the given
1682 * line.
1683 */
1684 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001685clip_get_line_end(int row)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001686{
1687 int i;
1688
Bram Moolenaar482aaeb2005-09-29 18:26:07 +00001689 if (row >= screen_Rows || ScreenLines == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001690 return 0;
1691 for (i = screen_Columns; i > 0; i--)
1692 if (ScreenLines[LineOffset[row] + i - 1] != ' ')
1693 break;
1694 return i;
1695}
1696
1697/*
1698 * Update the currently selected region by adding and/or subtracting from the
1699 * beginning or end and inverting the changed area(s).
1700 */
1701 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001702clip_update_modeless_selection(
1703 VimClipboard *cb,
1704 int row1,
1705 int col1,
1706 int row2,
1707 int col2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001708{
1709 /* See if we changed at the beginning of the selection */
1710 if (row1 != cb->start.lnum || col1 != (int)cb->start.col)
1711 {
1712 clip_invert_area(row1, col1, (int)cb->start.lnum, cb->start.col,
1713 CLIP_TOGGLE);
1714 cb->start.lnum = row1;
1715 cb->start.col = col1;
1716 }
1717
1718 /* See if we changed at the end of the selection */
1719 if (row2 != cb->end.lnum || col2 != (int)cb->end.col)
1720 {
1721 clip_invert_area((int)cb->end.lnum, cb->end.col, row2, col2,
1722 CLIP_TOGGLE);
1723 cb->end.lnum = row2;
1724 cb->end.col = col2;
1725 }
1726}
1727
1728 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001729clip_gen_own_selection(VimClipboard *cbd)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001730{
1731#ifdef FEAT_XCLIPBOARD
1732# ifdef FEAT_GUI
1733 if (gui.in_use)
1734 return clip_mch_own_selection(cbd);
1735 else
1736# endif
1737 return clip_xterm_own_selection(cbd);
1738#else
1739 return clip_mch_own_selection(cbd);
1740#endif
1741}
1742
1743 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001744clip_gen_lose_selection(VimClipboard *cbd)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001745{
1746#ifdef FEAT_XCLIPBOARD
1747# ifdef FEAT_GUI
1748 if (gui.in_use)
1749 clip_mch_lose_selection(cbd);
1750 else
1751# endif
1752 clip_xterm_lose_selection(cbd);
1753#else
1754 clip_mch_lose_selection(cbd);
1755#endif
1756}
1757
1758 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001759clip_gen_set_selection(VimClipboard *cbd)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001760{
Bram Moolenaar6b1ee342014-08-06 18:17:11 +02001761 if (!clip_did_set_selection)
1762 {
1763 /* Updating postponed, so that accessing the system clipboard won't
Bram Moolenaarbdace832019-03-02 10:13:42 +01001764 * hang Vim when accessing it many times (e.g. on a :g command). */
Bram Moolenaar5c27fd12015-01-27 14:09:37 +01001765 if ((cbd == &clip_plus && (clip_unnamed_saved & CLIP_UNNAMED_PLUS))
1766 || (cbd == &clip_star && (clip_unnamed_saved & CLIP_UNNAMED)))
1767 {
1768 clipboard_needs_update = TRUE;
Bram Moolenaar6b1ee342014-08-06 18:17:11 +02001769 return;
Bram Moolenaar5c27fd12015-01-27 14:09:37 +01001770 }
Bram Moolenaar6b1ee342014-08-06 18:17:11 +02001771 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001772#ifdef FEAT_XCLIPBOARD
1773# ifdef FEAT_GUI
1774 if (gui.in_use)
1775 clip_mch_set_selection(cbd);
1776 else
1777# endif
1778 clip_xterm_set_selection(cbd);
1779#else
1780 clip_mch_set_selection(cbd);
1781#endif
1782}
1783
1784 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001785clip_gen_request_selection(VimClipboard *cbd)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001786{
1787#ifdef FEAT_XCLIPBOARD
1788# ifdef FEAT_GUI
1789 if (gui.in_use)
1790 clip_mch_request_selection(cbd);
1791 else
1792# endif
1793 clip_xterm_request_selection(cbd);
1794#else
1795 clip_mch_request_selection(cbd);
1796#endif
1797}
1798
Bram Moolenaar113e1072019-01-20 15:30:40 +01001799#if (defined(FEAT_X11) && defined(USE_SYSTEM)) || defined(PROTO)
Bram Moolenaar1a0316c2013-03-13 17:50:25 +01001800 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001801clip_gen_owner_exists(VimClipboard *cbd UNUSED)
Bram Moolenaar1a0316c2013-03-13 17:50:25 +01001802{
1803#ifdef FEAT_XCLIPBOARD
1804# ifdef FEAT_GUI_GTK
1805 if (gui.in_use)
1806 return clip_gtk_owner_exists(cbd);
1807 else
1808# endif
1809 return clip_x11_owner_exists(cbd);
Bram Moolenaar690ae9c2013-07-13 20:58:11 +02001810#else
Bram Moolenaar1a0316c2013-03-13 17:50:25 +01001811 return TRUE;
Bram Moolenaar690ae9c2013-07-13 20:58:11 +02001812#endif
Bram Moolenaar1a0316c2013-03-13 17:50:25 +01001813}
Bram Moolenaar113e1072019-01-20 15:30:40 +01001814#endif
Bram Moolenaar1a0316c2013-03-13 17:50:25 +01001815
Bram Moolenaar071d4272004-06-13 20:20:40 +00001816#endif /* FEAT_CLIPBOARD */
1817
1818/*****************************************************************************
1819 * Functions that handle the input buffer.
1820 * This is used for any GUI version, and the unix terminal version.
1821 *
1822 * For Unix, the input characters are buffered to be able to check for a
1823 * CTRL-C. This should be done with signals, but I don't know how to do that
1824 * in a portable way for a tty in RAW mode.
1825 *
1826 * For the client-server code in the console the received keys are put in the
1827 * input buffer.
1828 */
1829
1830#if defined(USE_INPUT_BUF) || defined(PROTO)
1831
1832/*
1833 * Internal typeahead buffer. Includes extra space for long key code
1834 * descriptions which would otherwise overflow. The buffer is considered full
1835 * when only this extra space (or part of it) remains.
1836 */
Bram Moolenaarbb1969b2019-01-17 15:45:25 +01001837#if defined(FEAT_JOB_CHANNEL) || defined(FEAT_CLIENTSERVER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001838 /*
Bram Moolenaarbb1969b2019-01-17 15:45:25 +01001839 * NetBeans stuffs debugger commands into the input buffer.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001840 * This requires a larger buffer...
1841 * (Madsen) Go with this for remote input as well ...
1842 */
1843# define INBUFLEN 4096
1844#else
1845# define INBUFLEN 250
1846#endif
1847
1848static char_u inbuf[INBUFLEN + MAX_KEY_CODE_LEN];
1849static int inbufcount = 0; /* number of chars in inbuf[] */
1850
1851/*
1852 * vim_is_input_buf_full(), vim_is_input_buf_empty(), add_to_input_buf(), and
1853 * trash_input_buf() are functions for manipulating the input buffer. These
1854 * are used by the gui_* calls when a GUI is used to handle keyboard input.
1855 */
1856
1857 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001858vim_is_input_buf_full(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001859{
1860 return (inbufcount >= INBUFLEN);
1861}
1862
1863 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001864vim_is_input_buf_empty(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001865{
1866 return (inbufcount == 0);
1867}
1868
1869#if defined(FEAT_OLE) || defined(PROTO)
1870 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001871vim_free_in_input_buf(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001872{
1873 return (INBUFLEN - inbufcount);
1874}
1875#endif
1876
Bram Moolenaar241a8aa2005-12-06 20:04:44 +00001877#if defined(FEAT_GUI_GTK) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001878 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001879vim_used_in_input_buf(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001880{
1881 return inbufcount;
1882}
1883#endif
1884
Bram Moolenaar071d4272004-06-13 20:20:40 +00001885/*
1886 * Return the current contents of the input buffer and make it empty.
1887 * The returned pointer must be passed to set_input_buf() later.
1888 */
1889 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001890get_input_buf(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001891{
1892 garray_T *gap;
1893
1894 /* We use a growarray to store the data pointer and the length. */
1895 gap = (garray_T *)alloc((unsigned)sizeof(garray_T));
1896 if (gap != NULL)
1897 {
1898 /* Add one to avoid a zero size. */
1899 gap->ga_data = alloc((unsigned)inbufcount + 1);
1900 if (gap->ga_data != NULL)
1901 mch_memmove(gap->ga_data, inbuf, (size_t)inbufcount);
1902 gap->ga_len = inbufcount;
1903 }
1904 trash_input_buf();
1905 return (char_u *)gap;
1906}
1907
1908/*
1909 * Restore the input buffer with a pointer returned from get_input_buf().
1910 * The allocated memory is freed, this only works once!
1911 */
1912 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001913set_input_buf(char_u *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001914{
1915 garray_T *gap = (garray_T *)p;
1916
1917 if (gap != NULL)
1918 {
1919 if (gap->ga_data != NULL)
1920 {
1921 mch_memmove(inbuf, gap->ga_data, gap->ga_len);
1922 inbufcount = gap->ga_len;
1923 vim_free(gap->ga_data);
1924 }
1925 vim_free(gap);
1926 }
1927}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001928
Bram Moolenaar071d4272004-06-13 20:20:40 +00001929/*
1930 * Add the given bytes to the input buffer
1931 * Special keys start with CSI. A real CSI must have been translated to
1932 * CSI KS_EXTRA KE_CSI. K_SPECIAL doesn't require translation.
1933 */
1934 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001935add_to_input_buf(char_u *s, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001936{
1937 if (inbufcount + len > INBUFLEN + MAX_KEY_CODE_LEN)
1938 return; /* Shouldn't ever happen! */
1939
1940#ifdef FEAT_HANGULIN
1941 if ((State & (INSERT|CMDLINE)) && hangul_input_state_get())
1942 if ((len = hangul_input_process(s, len)) == 0)
1943 return;
1944#endif
1945
1946 while (len--)
1947 inbuf[inbufcount++] = *s++;
1948}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001949
Bram Moolenaar071d4272004-06-13 20:20:40 +00001950/*
1951 * Add "str[len]" to the input buffer while escaping CSI bytes.
1952 */
1953 void
1954add_to_input_buf_csi(char_u *str, int len)
1955{
1956 int i;
1957 char_u buf[2];
1958
1959 for (i = 0; i < len; ++i)
1960 {
1961 add_to_input_buf(str + i, 1);
1962 if (str[i] == CSI)
1963 {
1964 /* Turn CSI into K_CSI. */
1965 buf[0] = KS_EXTRA;
1966 buf[1] = (int)KE_CSI;
1967 add_to_input_buf(buf, 2);
1968 }
1969 }
1970}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001971
1972#if defined(FEAT_HANGULIN) || defined(PROTO)
1973 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001974push_raw_key(char_u *s, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001975{
Bram Moolenaar72f4cc42015-11-10 14:35:18 +01001976 char_u *tmpbuf;
Bram Moolenaar179f1b92016-03-04 22:52:34 +01001977 char_u *inp = s;
Bram Moolenaar72f4cc42015-11-10 14:35:18 +01001978
Bram Moolenaar179f1b92016-03-04 22:52:34 +01001979 /* use the conversion result if possible */
Bram Moolenaar72f4cc42015-11-10 14:35:18 +01001980 tmpbuf = hangul_string_convert(s, &len);
1981 if (tmpbuf != NULL)
Bram Moolenaar179f1b92016-03-04 22:52:34 +01001982 inp = tmpbuf;
Bram Moolenaar72f4cc42015-11-10 14:35:18 +01001983
Bram Moolenaar179f1b92016-03-04 22:52:34 +01001984 for (; len--; inp++)
1985 {
1986 inbuf[inbufcount++] = *inp;
1987 if (*inp == CSI)
Bram Moolenaar00ded432016-03-03 11:45:15 +01001988 {
Bram Moolenaar179f1b92016-03-04 22:52:34 +01001989 /* Turn CSI into K_CSI. */
1990 inbuf[inbufcount++] = KS_EXTRA;
1991 inbuf[inbufcount++] = (int)KE_CSI;
Bram Moolenaar00ded432016-03-03 11:45:15 +01001992 }
Bram Moolenaar00ded432016-03-03 11:45:15 +01001993 }
Bram Moolenaar179f1b92016-03-04 22:52:34 +01001994 vim_free(tmpbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001995}
1996#endif
1997
Bram Moolenaar071d4272004-06-13 20:20:40 +00001998/* Remove everything from the input buffer. Called when ^C is found */
1999 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002000trash_input_buf(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002001{
2002 inbufcount = 0;
2003}
Bram Moolenaar071d4272004-06-13 20:20:40 +00002004
2005/*
2006 * Read as much data from the input buffer as possible up to maxlen, and store
2007 * it in buf.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002008 */
2009 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002010read_from_input_buf(char_u *buf, long maxlen)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002011{
2012 if (inbufcount == 0) /* if the buffer is empty, fill it */
2013 fill_input_buf(TRUE);
2014 if (maxlen > inbufcount)
2015 maxlen = inbufcount;
2016 mch_memmove(buf, inbuf, (size_t)maxlen);
2017 inbufcount -= maxlen;
2018 if (inbufcount)
2019 mch_memmove(inbuf, inbuf + maxlen, (size_t)inbufcount);
2020 return (int)maxlen;
2021}
2022
2023 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002024fill_input_buf(int exit_on_error UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002025{
Bram Moolenaard0573012017-10-28 21:11:06 +02002026#if defined(UNIX) || defined(VMS) || defined(MACOS_X)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002027 int len;
2028 int try;
2029 static int did_read_something = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002030 static char_u *rest = NULL; /* unconverted rest of previous read */
2031 static int restlen = 0;
2032 int unconverted;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002033#endif
2034
2035#ifdef FEAT_GUI
Bram Moolenaar54ee7752005-05-31 22:22:17 +00002036 if (gui.in_use
2037# ifdef NO_CONSOLE_INPUT
2038 /* Don't use the GUI input when the window hasn't been opened yet.
2039 * We get here from ui_inchar() when we should try reading from stdin. */
2040 && !no_console_input()
2041# endif
2042 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002043 {
2044 gui_mch_update();
2045 return;
2046 }
2047#endif
Bram Moolenaard0573012017-10-28 21:11:06 +02002048#if defined(UNIX) || defined(VMS) || defined(MACOS_X)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002049 if (vim_is_input_buf_full())
2050 return;
2051 /*
2052 * Fill_input_buf() is only called when we really need a character.
2053 * If we can't get any, but there is some in the buffer, just return.
2054 * If we can't get any, and there isn't any in the buffer, we give up and
2055 * exit Vim.
2056 */
2057# ifdef __BEOS__
2058 /*
2059 * On the BeBox version (for now), all input is secretly performed within
2060 * beos_select() which is called from RealWaitForChar().
2061 */
2062 while (!vim_is_input_buf_full() && RealWaitForChar(read_cmd_fd, 0, NULL))
2063 ;
2064 len = inbufcount;
2065 inbufcount = 0;
2066# else
2067
Bram Moolenaar071d4272004-06-13 20:20:40 +00002068 if (rest != NULL)
2069 {
2070 /* Use remainder of previous call, starts with an invalid character
2071 * that may become valid when reading more. */
2072 if (restlen > INBUFLEN - inbufcount)
2073 unconverted = INBUFLEN - inbufcount;
2074 else
2075 unconverted = restlen;
2076 mch_memmove(inbuf + inbufcount, rest, unconverted);
2077 if (unconverted == restlen)
Bram Moolenaard23a8232018-02-10 18:45:26 +01002078 VIM_CLEAR(rest);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002079 else
2080 {
2081 restlen -= unconverted;
2082 mch_memmove(rest, rest + unconverted, restlen);
2083 }
2084 inbufcount += unconverted;
2085 }
2086 else
2087 unconverted = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002088
2089 len = 0; /* to avoid gcc warning */
2090 for (try = 0; try < 100; ++try)
2091 {
Bram Moolenaar4e601e32018-04-24 13:29:51 +02002092 size_t readlen = (size_t)((INBUFLEN - inbufcount)
Bram Moolenaar264b74f2019-01-24 17:18:42 +01002093 / input_conv.vc_factor);
Bram Moolenaar4e601e32018-04-24 13:29:51 +02002094# ifdef VMS
Bram Moolenaar49943732018-04-24 20:27:26 +02002095 len = vms_read((char *)inbuf + inbufcount, readlen);
Bram Moolenaar4e601e32018-04-24 13:29:51 +02002096# else
2097 len = read(read_cmd_fd, (char *)inbuf + inbufcount, readlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002098# endif
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00002099
Bram Moolenaar071d4272004-06-13 20:20:40 +00002100 if (len > 0 || got_int)
2101 break;
2102 /*
2103 * If reading stdin results in an error, continue reading stderr.
2104 * This helps when using "foo | xargs vim".
2105 */
2106 if (!did_read_something && !isatty(read_cmd_fd) && read_cmd_fd == 0)
2107 {
2108 int m = cur_tmode;
2109
2110 /* We probably set the wrong file descriptor to raw mode. Switch
2111 * back to cooked mode, use another descriptor and set the mode to
2112 * what it was. */
2113 settmode(TMODE_COOK);
2114#ifdef HAVE_DUP
2115 /* Use stderr for stdin, also works for shell commands. */
2116 close(0);
Bram Moolenaar42335f52018-09-13 15:33:43 +02002117 vim_ignored = dup(2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002118#else
2119 read_cmd_fd = 2; /* read from stderr instead of stdin */
2120#endif
2121 settmode(m);
2122 }
2123 if (!exit_on_error)
2124 return;
2125 }
2126# endif
2127 if (len <= 0 && !got_int)
2128 read_error_exit();
2129 if (len > 0)
2130 did_read_something = TRUE;
2131 if (got_int)
2132 {
2133 /* Interrupted, pretend a CTRL-C was typed. */
2134 inbuf[0] = 3;
2135 inbufcount = 1;
2136 }
2137 else
2138 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002139 /*
2140 * May perform conversion on the input characters.
2141 * Include the unconverted rest of the previous call.
2142 * If there is an incomplete char at the end it is kept for the next
2143 * time, reading more bytes should make conversion possible.
2144 * Don't do this in the unlikely event that the input buffer is too
2145 * small ("rest" still contains more bytes).
2146 */
2147 if (input_conv.vc_type != CONV_NONE)
2148 {
2149 inbufcount -= unconverted;
2150 len = convert_input_safe(inbuf + inbufcount,
2151 len + unconverted, INBUFLEN - inbufcount,
2152 rest == NULL ? &rest : NULL, &restlen);
2153 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002154 while (len-- > 0)
2155 {
2156 /*
2157 * if a CTRL-C was typed, remove it from the buffer and set got_int
2158 */
2159 if (inbuf[inbufcount] == 3 && ctrl_c_interrupts)
2160 {
2161 /* remove everything typed before the CTRL-C */
2162 mch_memmove(inbuf, inbuf + inbufcount, (size_t)(len + 1));
2163 inbufcount = 0;
2164 got_int = TRUE;
2165 }
2166 ++inbufcount;
2167 }
2168 }
Bram Moolenaare7fedb62015-12-31 19:07:19 +01002169#endif /* UNIX or VMS*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00002170}
Bram Moolenaare7fedb62015-12-31 19:07:19 +01002171#endif /* defined(UNIX) || defined(FEAT_GUI) || defined(VMS) */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002172
2173/*
2174 * Exit because of an input read error.
2175 */
2176 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002177read_error_exit(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002178{
2179 if (silent_mode) /* Normal way to exit for "ex -s" */
2180 getout(0);
2181 STRCPY(IObuff, _("Vim: Error reading input, exiting...\n"));
2182 preserve_exit();
2183}
2184
2185#if defined(CURSOR_SHAPE) || defined(PROTO)
2186/*
2187 * May update the shape of the cursor.
2188 */
2189 void
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +02002190ui_cursor_shape_forced(int forced)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002191{
2192# ifdef FEAT_GUI
2193 if (gui.in_use)
2194 gui_update_cursor_later();
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002195 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002196# endif
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +02002197 term_cursor_mode(forced);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002198
Bram Moolenaar071d4272004-06-13 20:20:40 +00002199# ifdef MCH_CURSOR_SHAPE
2200 mch_update_cursor();
2201# endif
Bram Moolenaarf5963f72010-07-23 22:10:27 +02002202
2203# ifdef FEAT_CONCEAL
Bram Moolenaarb9464822018-05-10 15:09:49 +02002204 conceal_check_cursor_line();
Bram Moolenaarf5963f72010-07-23 22:10:27 +02002205# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002206}
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +02002207
2208 void
2209ui_cursor_shape(void)
2210{
2211 ui_cursor_shape_forced(FALSE);
2212}
Bram Moolenaar071d4272004-06-13 20:20:40 +00002213#endif
2214
Bram Moolenaar071d4272004-06-13 20:20:40 +00002215/*
2216 * Check bounds for column number
2217 */
2218 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002219check_col(int col)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002220{
2221 if (col < 0)
2222 return 0;
2223 if (col >= (int)screen_Columns)
2224 return (int)screen_Columns - 1;
2225 return col;
2226}
2227
2228/*
2229 * Check bounds for row number
2230 */
2231 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002232check_row(int row)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002233{
2234 if (row < 0)
2235 return 0;
2236 if (row >= (int)screen_Rows)
2237 return (int)screen_Rows - 1;
2238 return row;
2239}
Bram Moolenaar071d4272004-06-13 20:20:40 +00002240
2241/*
2242 * Stuff for the X clipboard. Shared between VMS and Unix.
2243 */
2244
2245#if defined(FEAT_XCLIPBOARD) || defined(FEAT_GUI_X11) || defined(PROTO)
2246# include <X11/Xatom.h>
2247# include <X11/Intrinsic.h>
2248
2249/*
2250 * Open the application context (if it hasn't been opened yet).
2251 * Used for Motif and Athena GUI and the xterm clipboard.
2252 */
2253 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002254open_app_context(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002255{
2256 if (app_context == NULL)
2257 {
2258 XtToolkitInitialize();
2259 app_context = XtCreateApplicationContext();
2260 }
2261}
2262
2263static Atom vim_atom; /* Vim's own special selection format */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002264static Atom vimenc_atom; /* Vim's extended selection format */
Bram Moolenaarefcb54b2012-02-12 01:35:10 +01002265static Atom utf8_atom;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002266static Atom compound_text_atom;
2267static Atom text_atom;
2268static Atom targets_atom;
Bram Moolenaar7cfea752010-06-22 06:07:12 +02002269static Atom timestamp_atom; /* Used to get a timestamp */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002270
2271 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002272x11_setup_atoms(Display *dpy)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002273{
2274 vim_atom = XInternAtom(dpy, VIM_ATOM_NAME, False);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002275 vimenc_atom = XInternAtom(dpy, VIMENC_ATOM_NAME,False);
Bram Moolenaarefcb54b2012-02-12 01:35:10 +01002276 utf8_atom = XInternAtom(dpy, "UTF8_STRING", False);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002277 compound_text_atom = XInternAtom(dpy, "COMPOUND_TEXT", False);
2278 text_atom = XInternAtom(dpy, "TEXT", False);
Bram Moolenaar7cfea752010-06-22 06:07:12 +02002279 targets_atom = XInternAtom(dpy, "TARGETS", False);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002280 clip_star.sel_atom = XA_PRIMARY;
Bram Moolenaar7cfea752010-06-22 06:07:12 +02002281 clip_plus.sel_atom = XInternAtom(dpy, "CLIPBOARD", False);
2282 timestamp_atom = XInternAtom(dpy, "TIMESTAMP", False);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002283}
2284
2285/*
2286 * X Selection stuff, for cutting and pasting text to other windows.
2287 */
2288
Bram Moolenaarcdb7e1b2017-07-19 19:55:58 +02002289static Boolean clip_x11_convert_selection_cb(Widget w, Atom *sel_atom, Atom *target, Atom *type, XtPointer *value, long_u *length, int *format);
2290static void clip_x11_lose_ownership_cb(Widget w, Atom *sel_atom);
2291static void clip_x11_notify_cb(Widget w, Atom *sel_atom, Atom *target);
Bram Moolenaar7cfea752010-06-22 06:07:12 +02002292
2293/*
2294 * Property callback to get a timestamp for XtOwnSelection.
2295 */
2296 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002297clip_x11_timestamp_cb(
2298 Widget w,
2299 XtPointer n UNUSED,
2300 XEvent *event,
2301 Boolean *cont UNUSED)
Bram Moolenaar7cfea752010-06-22 06:07:12 +02002302{
2303 Atom actual_type;
2304 int format;
2305 unsigned long nitems, bytes_after;
2306 unsigned char *prop=NULL;
2307 XPropertyEvent *xproperty=&event->xproperty;
2308
2309 /* Must be a property notify, state can't be Delete (True), has to be
2310 * one of the supported selection types. */
2311 if (event->type != PropertyNotify || xproperty->state
2312 || (xproperty->atom != clip_star.sel_atom
2313 && xproperty->atom != clip_plus.sel_atom))
2314 return;
2315
2316 if (XGetWindowProperty(xproperty->display, xproperty->window,
2317 xproperty->atom, 0, 0, False, timestamp_atom, &actual_type, &format,
2318 &nitems, &bytes_after, &prop))
2319 return;
2320
2321 if (prop)
2322 XFree(prop);
2323
2324 /* Make sure the property type is "TIMESTAMP" and it's 32 bits. */
2325 if (actual_type != timestamp_atom || format != 32)
2326 return;
2327
2328 /* Get the selection, using the event timestamp. */
Bram Moolenaar62b42182010-09-21 22:09:37 +02002329 if (XtOwnSelection(w, xproperty->atom, xproperty->time,
2330 clip_x11_convert_selection_cb, clip_x11_lose_ownership_cb,
Bram Moolenaarcdb7e1b2017-07-19 19:55:58 +02002331 clip_x11_notify_cb) == OK)
Bram Moolenaar62b42182010-09-21 22:09:37 +02002332 {
2333 /* Set the "owned" flag now, there may have been a call to
2334 * lose_ownership_cb in between. */
2335 if (xproperty->atom == clip_plus.sel_atom)
2336 clip_plus.owned = TRUE;
2337 else
2338 clip_star.owned = TRUE;
2339 }
Bram Moolenaar7cfea752010-06-22 06:07:12 +02002340}
2341
2342 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002343x11_setup_selection(Widget w)
Bram Moolenaar7cfea752010-06-22 06:07:12 +02002344{
2345 XtAddEventHandler(w, PropertyChangeMask, False,
2346 /*(XtEventHandler)*/clip_x11_timestamp_cb, (XtPointer)NULL);
2347}
2348
Bram Moolenaar071d4272004-06-13 20:20:40 +00002349 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002350clip_x11_request_selection_cb(
2351 Widget w UNUSED,
2352 XtPointer success,
2353 Atom *sel_atom,
2354 Atom *type,
2355 XtPointer value,
2356 long_u *length,
2357 int *format)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002358{
Bram Moolenaard44347f2011-06-19 01:14:29 +02002359 int motion_type = MAUTO;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002360 long_u len;
2361 char_u *p;
2362 char **text_list = NULL;
2363 VimClipboard *cbd;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002364 char_u *tmpbuf = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002365
2366 if (*sel_atom == clip_plus.sel_atom)
2367 cbd = &clip_plus;
2368 else
2369 cbd = &clip_star;
2370
2371 if (value == NULL || *length == 0)
2372 {
Bram Moolenaar24d92ce2008-09-14 13:58:34 +00002373 clip_free_selection(cbd); /* nothing received, clear register */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002374 *(int *)success = FALSE;
2375 return;
2376 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002377 p = (char_u *)value;
2378 len = *length;
2379 if (*type == vim_atom)
2380 {
2381 motion_type = *p++;
2382 len--;
2383 }
2384
Bram Moolenaar071d4272004-06-13 20:20:40 +00002385 else if (*type == vimenc_atom)
2386 {
2387 char_u *enc;
2388 vimconv_T conv;
2389 int convlen;
2390
2391 motion_type = *p++;
2392 --len;
2393
2394 enc = p;
2395 p += STRLEN(p) + 1;
2396 len -= p - enc;
2397
2398 /* If the encoding of the text is different from 'encoding', attempt
2399 * converting it. */
2400 conv.vc_type = CONV_NONE;
2401 convert_setup(&conv, enc, p_enc);
2402 if (conv.vc_type != CONV_NONE)
2403 {
2404 convlen = len; /* Need to use an int here. */
2405 tmpbuf = string_convert(&conv, p, &convlen);
2406 len = convlen;
2407 if (tmpbuf != NULL)
2408 p = tmpbuf;
2409 convert_setup(&conv, NULL, NULL);
2410 }
2411 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002412
Bram Moolenaarefcb54b2012-02-12 01:35:10 +01002413 else if (*type == compound_text_atom
Bram Moolenaarefcb54b2012-02-12 01:35:10 +01002414 || *type == utf8_atom
Bram Moolenaar264b74f2019-01-24 17:18:42 +01002415 || (enc_dbcs != 0 && *type == text_atom))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002416 {
2417 XTextProperty text_prop;
2418 int n_text = 0;
2419 int status;
2420
2421 text_prop.value = (unsigned char *)value;
2422 text_prop.encoding = *type;
2423 text_prop.format = *format;
Bram Moolenaar24d92ce2008-09-14 13:58:34 +00002424 text_prop.nitems = len;
Bram Moolenaar264b74f2019-01-24 17:18:42 +01002425#if defined(X_HAVE_UTF8_STRING)
Bram Moolenaare2e663f2013-03-07 18:02:30 +01002426 if (*type == utf8_atom)
2427 status = Xutf8TextPropertyToTextList(X_DISPLAY, &text_prop,
2428 &text_list, &n_text);
2429 else
2430#endif
2431 status = XmbTextPropertyToTextList(X_DISPLAY, &text_prop,
Bram Moolenaar071d4272004-06-13 20:20:40 +00002432 &text_list, &n_text);
2433 if (status != Success || n_text < 1)
2434 {
2435 *(int *)success = FALSE;
2436 return;
2437 }
2438 p = (char_u *)text_list[0];
2439 len = STRLEN(p);
2440 }
2441 clip_yank_selection(motion_type, p, (long)len, cbd);
2442
2443 if (text_list != NULL)
2444 XFreeStringList(text_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002445 vim_free(tmpbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002446 XtFree((char *)value);
2447 *(int *)success = TRUE;
2448}
2449
2450 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002451clip_x11_request_selection(
2452 Widget myShell,
2453 Display *dpy,
2454 VimClipboard *cbd)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002455{
2456 XEvent event;
2457 Atom type;
2458 static int success;
2459 int i;
Bram Moolenaar89417b92008-09-07 19:48:53 +00002460 time_t start_time;
2461 int timed_out = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002462
Bram Moolenaar264b74f2019-01-24 17:18:42 +01002463 for (i = 0; i < 6; i++)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002464 {
2465 switch (i)
2466 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002467 case 0: type = vimenc_atom; break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002468 case 1: type = vim_atom; break;
Bram Moolenaarefcb54b2012-02-12 01:35:10 +01002469 case 2: type = utf8_atom; break;
Bram Moolenaarefcb54b2012-02-12 01:35:10 +01002470 case 3: type = compound_text_atom; break;
2471 case 4: type = text_atom; break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002472 default: type = XA_STRING;
2473 }
Bram Moolenaar81851112013-04-12 12:27:30 +02002474 if (type == utf8_atom
2475# if defined(X_HAVE_UTF8_STRING)
2476 && !enc_utf8
2477# endif
2478 )
2479 /* Only request utf-8 when 'encoding' is utf8 and
2480 * Xutf8TextPropertyToTextList is available. */
Bram Moolenaarefcb54b2012-02-12 01:35:10 +01002481 continue;
Bram Moolenaar24d92ce2008-09-14 13:58:34 +00002482 success = MAYBE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002483 XtGetSelectionValue(myShell, cbd->sel_atom, type,
2484 clip_x11_request_selection_cb, (XtPointer)&success, CurrentTime);
2485
2486 /* Make sure the request for the selection goes out before waiting for
2487 * a response. */
2488 XFlush(dpy);
2489
2490 /*
2491 * Wait for result of selection request, otherwise if we type more
2492 * characters, then they will appear before the one that requested the
2493 * paste! Don't worry, we will catch up with any other events later.
2494 */
Bram Moolenaar89417b92008-09-07 19:48:53 +00002495 start_time = time(NULL);
Bram Moolenaar24d92ce2008-09-14 13:58:34 +00002496 while (success == MAYBE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002497 {
Bram Moolenaarcdb7e1b2017-07-19 19:55:58 +02002498 if (XCheckTypedEvent(dpy, PropertyNotify, &event)
2499 || XCheckTypedEvent(dpy, SelectionNotify, &event)
2500 || XCheckTypedEvent(dpy, SelectionRequest, &event))
Bram Moolenaar89417b92008-09-07 19:48:53 +00002501 {
Bram Moolenaar24d92ce2008-09-14 13:58:34 +00002502 /* This is where clip_x11_request_selection_cb() should be
2503 * called. It may actually happen a bit later, so we loop
2504 * until "success" changes.
2505 * We may get a SelectionRequest here and if we don't handle
2506 * it we hang. KDE klipper does this, for example.
2507 * We need to handle a PropertyNotify for large selections. */
Bram Moolenaar89417b92008-09-07 19:48:53 +00002508 XtDispatchEvent(&event);
Bram Moolenaar24d92ce2008-09-14 13:58:34 +00002509 continue;
Bram Moolenaar89417b92008-09-07 19:48:53 +00002510 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002511
Bram Moolenaar89417b92008-09-07 19:48:53 +00002512 /* Time out after 2 to 3 seconds to avoid that we hang when the
2513 * other process doesn't respond. Note that the SelectionNotify
2514 * event may still come later when the selection owner comes back
Bram Moolenaar24d92ce2008-09-14 13:58:34 +00002515 * to life and the text gets inserted unexpectedly. Don't know
2516 * why that happens or how to avoid that :-(. */
Bram Moolenaar89417b92008-09-07 19:48:53 +00002517 if (time(NULL) > start_time + 2)
2518 {
2519 timed_out = TRUE;
2520 break;
2521 }
2522
Bram Moolenaar071d4272004-06-13 20:20:40 +00002523 /* Do we need this? Probably not. */
2524 XSync(dpy, False);
2525
Bram Moolenaar89417b92008-09-07 19:48:53 +00002526 /* Wait for 1 msec to avoid that we eat up all CPU time. */
2527 ui_delay(1L, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002528 }
2529
Bram Moolenaar24d92ce2008-09-14 13:58:34 +00002530 if (success == TRUE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002531 return;
Bram Moolenaar89417b92008-09-07 19:48:53 +00002532
2533 /* don't do a retry with another type after timing out, otherwise we
2534 * hang for 15 seconds. */
2535 if (timed_out)
2536 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002537 }
2538
2539 /* Final fallback position - use the X CUT_BUFFER0 store */
Bram Moolenaarbbc936b2009-07-01 16:04:58 +00002540 yank_cut_buffer0(dpy, cbd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002541}
2542
Bram Moolenaar071d4272004-06-13 20:20:40 +00002543 static Boolean
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002544clip_x11_convert_selection_cb(
2545 Widget w UNUSED,
2546 Atom *sel_atom,
2547 Atom *target,
2548 Atom *type,
2549 XtPointer *value,
2550 long_u *length,
2551 int *format)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002552{
Bram Moolenaarcdb7e1b2017-07-19 19:55:58 +02002553 static char_u *save_result = NULL;
2554 static long_u save_length = 0;
2555 char_u *string;
2556 int motion_type;
2557 VimClipboard *cbd;
2558 int i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002559
2560 if (*sel_atom == clip_plus.sel_atom)
2561 cbd = &clip_plus;
2562 else
2563 cbd = &clip_star;
2564
2565 if (!cbd->owned)
2566 return False; /* Shouldn't ever happen */
2567
2568 /* requestor wants to know what target types we support */
2569 if (*target == targets_atom)
2570 {
Bram Moolenaarcdb7e1b2017-07-19 19:55:58 +02002571 static Atom array[7];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002572
Bram Moolenaar071d4272004-06-13 20:20:40 +00002573 *value = (XtPointer)array;
2574 i = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002575 array[i++] = targets_atom;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002576 array[i++] = vimenc_atom;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002577 array[i++] = vim_atom;
Bram Moolenaarefcb54b2012-02-12 01:35:10 +01002578 if (enc_utf8)
2579 array[i++] = utf8_atom;
Bram Moolenaarefcb54b2012-02-12 01:35:10 +01002580 array[i++] = XA_STRING;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002581 array[i++] = text_atom;
2582 array[i++] = compound_text_atom;
Bram Moolenaarefcb54b2012-02-12 01:35:10 +01002583
Bram Moolenaar071d4272004-06-13 20:20:40 +00002584 *type = XA_ATOM;
2585 /* This used to be: *format = sizeof(Atom) * 8; but that caused
2586 * crashes on 64 bit machines. (Peter Derr) */
2587 *format = 32;
2588 *length = i;
2589 return True;
2590 }
2591
2592 if ( *target != XA_STRING
Bram Moolenaar071d4272004-06-13 20:20:40 +00002593 && *target != vimenc_atom
Bram Moolenaar980e58f2014-06-12 13:28:30 +02002594 && (*target != utf8_atom || !enc_utf8)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002595 && *target != vim_atom
2596 && *target != text_atom
2597 && *target != compound_text_atom)
2598 return False;
2599
2600 clip_get_selection(cbd);
2601 motion_type = clip_convert_selection(&string, length, cbd);
2602 if (motion_type < 0)
2603 return False;
2604
2605 /* For our own format, the first byte contains the motion type */
2606 if (*target == vim_atom)
2607 (*length)++;
2608
Bram Moolenaar071d4272004-06-13 20:20:40 +00002609 /* Our own format with encoding: motion 'encoding' NUL text */
2610 if (*target == vimenc_atom)
2611 *length += STRLEN(p_enc) + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002612
Bram Moolenaarcdb7e1b2017-07-19 19:55:58 +02002613 if (save_length < *length || save_length / 2 >= *length)
2614 *value = XtRealloc((char *)save_result, (Cardinal)*length + 1);
2615 else
2616 *value = save_result;
2617 if (*value == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002618 {
2619 vim_free(string);
2620 return False;
2621 }
Bram Moolenaarcdb7e1b2017-07-19 19:55:58 +02002622 save_result = (char_u *)*value;
2623 save_length = *length;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002624
Bram Moolenaar264b74f2019-01-24 17:18:42 +01002625 if (*target == XA_STRING || (*target == utf8_atom && enc_utf8))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002626 {
Bram Moolenaarcdb7e1b2017-07-19 19:55:58 +02002627 mch_memmove(save_result, string, (size_t)(*length));
Bram Moolenaarefcb54b2012-02-12 01:35:10 +01002628 *type = *target;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002629 }
Bram Moolenaarefcb54b2012-02-12 01:35:10 +01002630 else if (*target == compound_text_atom || *target == text_atom)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002631 {
2632 XTextProperty text_prop;
Bram Moolenaarcdb7e1b2017-07-19 19:55:58 +02002633 char *string_nt = (char *)save_result;
Bram Moolenaarfe17e762013-06-29 14:17:02 +02002634 int conv_result;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002635
2636 /* create NUL terminated string which XmbTextListToTextProperty wants */
2637 mch_memmove(string_nt, string, (size_t)*length);
2638 string_nt[*length] = NUL;
Bram Moolenaarfe17e762013-06-29 14:17:02 +02002639 conv_result = XmbTextListToTextProperty(X_DISPLAY, (char **)&string_nt,
2640 1, XCompoundTextStyle, &text_prop);
Bram Moolenaarfe17e762013-06-29 14:17:02 +02002641 if (conv_result != Success)
2642 {
2643 vim_free(string);
2644 return False;
2645 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002646 *value = (XtPointer)(text_prop.value); /* from plain text */
2647 *length = text_prop.nitems;
2648 *type = compound_text_atom;
Bram Moolenaarcdb7e1b2017-07-19 19:55:58 +02002649 XtFree((char *)save_result);
2650 save_result = (char_u *)*value;
2651 save_length = *length;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002652 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002653 else if (*target == vimenc_atom)
2654 {
2655 int l = STRLEN(p_enc);
2656
Bram Moolenaarcdb7e1b2017-07-19 19:55:58 +02002657 save_result[0] = motion_type;
2658 STRCPY(save_result + 1, p_enc);
2659 mch_memmove(save_result + l + 2, string, (size_t)(*length - l - 2));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002660 *type = vimenc_atom;
2661 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002662 else
2663 {
Bram Moolenaarcdb7e1b2017-07-19 19:55:58 +02002664 save_result[0] = motion_type;
2665 mch_memmove(save_result + 1, string, (size_t)(*length - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002666 *type = vim_atom;
2667 }
2668 *format = 8; /* 8 bits per char */
2669 vim_free(string);
2670 return True;
2671}
2672
Bram Moolenaar071d4272004-06-13 20:20:40 +00002673 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002674clip_x11_lose_ownership_cb(Widget w UNUSED, Atom *sel_atom)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002675{
2676 if (*sel_atom == clip_plus.sel_atom)
2677 clip_lose_selection(&clip_plus);
2678 else
2679 clip_lose_selection(&clip_star);
2680}
2681
2682 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002683clip_x11_lose_selection(Widget myShell, VimClipboard *cbd)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002684{
Bram Moolenaar1a0316c2013-03-13 17:50:25 +01002685 XtDisownSelection(myShell, cbd->sel_atom,
2686 XtLastTimestampProcessed(XtDisplay(myShell)));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002687}
2688
Bram Moolenaarcdb7e1b2017-07-19 19:55:58 +02002689 static void
2690clip_x11_notify_cb(Widget w UNUSED, Atom *sel_atom UNUSED, Atom *target UNUSED)
2691{
2692 /* To prevent automatically freeing the selection value. */
2693}
2694
Bram Moolenaar071d4272004-06-13 20:20:40 +00002695 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002696clip_x11_own_selection(Widget myShell, VimClipboard *cbd)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002697{
Bram Moolenaar62b42182010-09-21 22:09:37 +02002698 /* When using the GUI we have proper timestamps, use the one of the last
2699 * event. When in the console we don't get events (the terminal gets
2700 * them), Get the time by a zero-length append, clip_x11_timestamp_cb will
2701 * be called with the current timestamp. */
2702#ifdef FEAT_GUI
2703 if (gui.in_use)
2704 {
2705 if (XtOwnSelection(myShell, cbd->sel_atom,
2706 XtLastTimestampProcessed(XtDisplay(myShell)),
2707 clip_x11_convert_selection_cb, clip_x11_lose_ownership_cb,
Bram Moolenaarcdb7e1b2017-07-19 19:55:58 +02002708 clip_x11_notify_cb) == False)
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +01002709 return FAIL;
Bram Moolenaar62b42182010-09-21 22:09:37 +02002710 }
2711 else
2712#endif
2713 {
2714 if (!XChangeProperty(XtDisplay(myShell), XtWindow(myShell),
2715 cbd->sel_atom, timestamp_atom, 32, PropModeAppend, NULL, 0))
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +01002716 return FAIL;
Bram Moolenaar62b42182010-09-21 22:09:37 +02002717 }
Bram Moolenaar7cfea752010-06-22 06:07:12 +02002718 /* Flush is required in a terminal as nothing else is doing it. */
2719 XFlush(XtDisplay(myShell));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002720 return OK;
2721}
2722
2723/*
2724 * Send the current selection to the clipboard. Do nothing for X because we
2725 * will fill in the selection only when requested by another app.
2726 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002727 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002728clip_x11_set_selection(VimClipboard *cbd UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002729{
2730}
Bram Moolenaar1a0316c2013-03-13 17:50:25 +01002731
Bram Moolenaar113e1072019-01-20 15:30:40 +01002732#if (defined(FEAT_X11) && defined(FEAT_XCLIPBOARD) && defined(USE_SYSTEM)) \
2733 || defined(PROTO)
Bram Moolenaar1a0316c2013-03-13 17:50:25 +01002734 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002735clip_x11_owner_exists(VimClipboard *cbd)
Bram Moolenaar1a0316c2013-03-13 17:50:25 +01002736{
2737 return XGetSelectionOwner(X_DISPLAY, cbd->sel_atom) != None;
2738}
Bram Moolenaar071d4272004-06-13 20:20:40 +00002739#endif
Bram Moolenaar113e1072019-01-20 15:30:40 +01002740#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002741
Bram Moolenaarbbc936b2009-07-01 16:04:58 +00002742#if defined(FEAT_XCLIPBOARD) || defined(FEAT_GUI_X11) \
2743 || defined(FEAT_GUI_GTK) || defined(PROTO)
2744/*
2745 * Get the contents of the X CUT_BUFFER0 and put it in "cbd".
2746 */
2747 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002748yank_cut_buffer0(Display *dpy, VimClipboard *cbd)
Bram Moolenaarbbc936b2009-07-01 16:04:58 +00002749{
2750 int nbytes = 0;
2751 char_u *buffer = (char_u *)XFetchBuffer(dpy, &nbytes, 0);
2752
2753 if (nbytes > 0)
2754 {
Bram Moolenaarbbc936b2009-07-01 16:04:58 +00002755 int done = FALSE;
2756
2757 /* CUT_BUFFER0 is supposed to be always latin1. Convert to 'enc' when
2758 * using a multi-byte encoding. Conversion between two 8-bit
2759 * character sets usually fails and the text might actually be in
2760 * 'enc' anyway. */
2761 if (has_mbyte)
2762 {
Bram Moolenaar2660c0e2010-01-19 14:59:56 +01002763 char_u *conv_buf;
Bram Moolenaarbbc936b2009-07-01 16:04:58 +00002764 vimconv_T vc;
2765
2766 vc.vc_type = CONV_NONE;
2767 if (convert_setup(&vc, (char_u *)"latin1", p_enc) == OK)
2768 {
2769 conv_buf = string_convert(&vc, buffer, &nbytes);
2770 if (conv_buf != NULL)
2771 {
2772 clip_yank_selection(MCHAR, conv_buf, (long)nbytes, cbd);
2773 vim_free(conv_buf);
2774 done = TRUE;
2775 }
2776 convert_setup(&vc, NULL, NULL);
2777 }
2778 }
2779 if (!done) /* use the text without conversion */
Bram Moolenaarbbc936b2009-07-01 16:04:58 +00002780 clip_yank_selection(MCHAR, buffer, (long)nbytes, cbd);
2781 XFree((void *)buffer);
2782 if (p_verbose > 0)
2783 {
2784 verbose_enter();
Bram Moolenaar32526b32019-01-19 17:43:09 +01002785 verb_msg(_("Used CUT_BUFFER0 instead of empty selection"));
Bram Moolenaarbbc936b2009-07-01 16:04:58 +00002786 verbose_leave();
2787 }
2788 }
2789}
2790#endif
2791
Bram Moolenaar071d4272004-06-13 20:20:40 +00002792#if defined(FEAT_MOUSE) || defined(PROTO)
2793
2794/*
2795 * Move the cursor to the specified row and column on the screen.
Bram Moolenaar49325942007-05-10 19:19:59 +00002796 * Change current window if necessary. Returns an integer with the
Bram Moolenaar071d4272004-06-13 20:20:40 +00002797 * CURSOR_MOVED bit set if the cursor has moved or unset otherwise.
2798 *
2799 * The MOUSE_FOLD_CLOSE bit is set when clicked on the '-' in a fold column.
2800 * The MOUSE_FOLD_OPEN bit is set when clicked on the '+' in a fold column.
2801 *
2802 * If flags has MOUSE_FOCUS, then the current window will not be changed, and
2803 * if the mouse is outside the window then the text will scroll, or if the
2804 * mouse was previously on a status line, then the status line may be dragged.
2805 *
2806 * If flags has MOUSE_MAY_VIS, then VIsual mode will be started before the
2807 * cursor is moved unless the cursor was on a status line.
2808 * This function returns one of IN_UNKNOWN, IN_BUFFER, IN_STATUS_LINE or
2809 * IN_SEP_LINE depending on where the cursor was clicked.
2810 *
2811 * If flags has MOUSE_MAY_STOP_VIS, then Visual mode will be stopped, unless
2812 * the mouse is on the status line of the same window.
2813 *
2814 * If flags has MOUSE_DID_MOVE, nothing is done if the mouse didn't move since
2815 * the last call.
2816 *
2817 * If flags has MOUSE_SETPOS, nothing is done, only the current position is
2818 * remembered.
2819 */
2820 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002821jump_to_mouse(
2822 int flags,
2823 int *inclusive, /* used for inclusive operator, can be NULL */
2824 int which_button) /* MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002825{
2826 static int on_status_line = 0; /* #lines below bottom of window */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002827 static int on_sep_line = 0; /* on separator right of window */
Bram Moolenaareb163d72017-09-23 15:08:17 +02002828#ifdef FEAT_MENU
2829 static int in_winbar = FALSE;
2830#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002831 static int prev_row = -1;
2832 static int prev_col = -1;
2833 static win_T *dragwin = NULL; /* window being dragged */
2834 static int did_drag = FALSE; /* drag was noticed */
2835
2836 win_T *wp, *old_curwin;
2837 pos_T old_cursor;
2838 int count;
2839 int first;
2840 int row = mouse_row;
2841 int col = mouse_col;
2842#ifdef FEAT_FOLDING
2843 int mouse_char;
2844#endif
2845
2846 mouse_past_bottom = FALSE;
2847 mouse_past_eol = FALSE;
2848
2849 if (flags & MOUSE_RELEASED)
2850 {
2851 /* On button release we may change window focus if positioned on a
2852 * status line and no dragging happened. */
2853 if (dragwin != NULL && !did_drag)
2854 flags &= ~(MOUSE_FOCUS | MOUSE_DID_MOVE);
2855 dragwin = NULL;
2856 did_drag = FALSE;
2857 }
2858
2859 if ((flags & MOUSE_DID_MOVE)
2860 && prev_row == mouse_row
2861 && prev_col == mouse_col)
2862 {
2863retnomove:
Bram Moolenaar49325942007-05-10 19:19:59 +00002864 /* before moving the cursor for a left click which is NOT in a status
Bram Moolenaar071d4272004-06-13 20:20:40 +00002865 * line, stop Visual mode */
2866 if (on_status_line)
2867 return IN_STATUS_LINE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002868 if (on_sep_line)
2869 return IN_SEP_LINE;
Bram Moolenaard327b0c2017-11-12 16:56:12 +01002870#ifdef FEAT_MENU
2871 if (in_winbar)
2872 {
2873 /* A quick second click may arrive as a double-click, but we use it
2874 * as a second click in the WinBar. */
2875 if ((mod_mask & MOD_MASK_MULTI_CLICK) && !(flags & MOUSE_RELEASED))
2876 {
2877 wp = mouse_find_win(&row, &col);
2878 if (wp == NULL)
2879 return IN_UNKNOWN;
2880 winbar_click(wp, col);
2881 }
2882 return IN_OTHER_WIN | MOUSE_WINBAR;
2883 }
2884#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002885 if (flags & MOUSE_MAY_STOP_VIS)
2886 {
2887 end_visual_mode();
2888 redraw_curbuf_later(INVERTED); /* delete the inversion */
2889 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002890#if defined(FEAT_CMDWIN) && defined(FEAT_CLIPBOARD)
2891 /* Continue a modeless selection in another window. */
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02002892 if (cmdwin_type != 0 && row < curwin->w_winrow)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002893 return IN_OTHER_WIN;
2894#endif
2895 return IN_BUFFER;
2896 }
2897
2898 prev_row = mouse_row;
2899 prev_col = mouse_col;
2900
2901 if (flags & MOUSE_SETPOS)
2902 goto retnomove; /* ugly goto... */
2903
2904#ifdef FEAT_FOLDING
2905 /* Remember the character under the mouse, it might be a '-' or '+' in the
2906 * fold column. */
Bram Moolenaar482aaeb2005-09-29 18:26:07 +00002907 if (row >= 0 && row < Rows && col >= 0 && col <= Columns
2908 && ScreenLines != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002909 mouse_char = ScreenLines[LineOffset[row] + col];
2910 else
2911 mouse_char = ' ';
2912#endif
2913
2914 old_curwin = curwin;
2915 old_cursor = curwin->w_cursor;
2916
2917 if (!(flags & MOUSE_FOCUS))
2918 {
2919 if (row < 0 || col < 0) /* check if it makes sense */
2920 return IN_UNKNOWN;
2921
Bram Moolenaar071d4272004-06-13 20:20:40 +00002922 /* find the window where the row is in */
2923 wp = mouse_find_win(&row, &col);
Bram Moolenaar989a70c2017-08-16 22:46:01 +02002924 if (wp == NULL)
2925 return IN_UNKNOWN;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002926 dragwin = NULL;
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02002927
2928#ifdef FEAT_MENU
2929 if (row == -1)
2930 {
2931 /* A click in the window toolbar does not enter another window or
2932 * change Visual highlighting. */
2933 winbar_click(wp, col);
Bram Moolenaareb163d72017-09-23 15:08:17 +02002934 in_winbar = TRUE;
2935 return IN_OTHER_WIN | MOUSE_WINBAR;
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02002936 }
Bram Moolenaareb163d72017-09-23 15:08:17 +02002937 in_winbar = FALSE;
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02002938#endif
2939
Bram Moolenaar071d4272004-06-13 20:20:40 +00002940 /*
2941 * winpos and height may change in win_enter()!
2942 */
2943 if (row >= wp->w_height) /* In (or below) status line */
2944 {
2945 on_status_line = row - wp->w_height + 1;
2946 dragwin = wp;
2947 }
2948 else
2949 on_status_line = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002950 if (col >= wp->w_width) /* In separator line */
2951 {
2952 on_sep_line = col - wp->w_width + 1;
2953 dragwin = wp;
2954 }
2955 else
2956 on_sep_line = 0;
2957
2958 /* The rightmost character of the status line might be a vertical
2959 * separator character if there is no connecting window to the right. */
2960 if (on_status_line && on_sep_line)
2961 {
2962 if (stl_connected(wp))
2963 on_sep_line = 0;
2964 else
2965 on_status_line = 0;
2966 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002967
Bram Moolenaar071d4272004-06-13 20:20:40 +00002968 /* Before jumping to another buffer, or moving the cursor for a left
2969 * click, stop Visual mode. */
2970 if (VIsual_active
2971 && (wp->w_buffer != curwin->w_buffer
Bram Moolenaar4033c552017-09-16 20:54:51 +02002972 || (!on_status_line && !on_sep_line
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01002973#ifdef FEAT_FOLDING
Bram Moolenaar071d4272004-06-13 20:20:40 +00002974 && (
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01002975# ifdef FEAT_RIGHTLEFT
Bram Moolenaar02631462017-09-22 15:20:32 +02002976 wp->w_p_rl ? col < wp->w_width - wp->w_p_fdc :
Bram Moolenaar071d4272004-06-13 20:20:40 +00002977# endif
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01002978 col >= wp->w_p_fdc
2979# ifdef FEAT_CMDWIN
2980 + (cmdwin_type == 0 && wp == curwin ? 0 : 1)
2981# endif
2982 )
2983#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002984 && (flags & MOUSE_MAY_STOP_VIS))))
2985 {
2986 end_visual_mode();
2987 redraw_curbuf_later(INVERTED); /* delete the inversion */
2988 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002989#ifdef FEAT_CMDWIN
2990 if (cmdwin_type != 0 && wp != curwin)
2991 {
2992 /* A click outside the command-line window: Use modeless
Bram Moolenaarf679a432010-03-02 18:16:09 +01002993 * selection if possible. Allow dragging the status lines. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002994 on_sep_line = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002995# ifdef FEAT_CLIPBOARD
2996 if (on_status_line)
2997 return IN_STATUS_LINE;
2998 return IN_OTHER_WIN;
2999# else
3000 row = 0;
3001 col += wp->w_wincol;
3002 wp = curwin;
3003# endif
3004 }
3005#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003006 /* Only change window focus when not clicking on or dragging the
3007 * status line. Do change focus when releasing the mouse button
3008 * (MOUSE_FOCUS was set above if we dragged first). */
3009 if (dragwin == NULL || (flags & MOUSE_RELEASED))
3010 win_enter(wp, TRUE); /* can make wp invalid! */
Bram Moolenaarc48369c2018-03-11 19:30:45 +01003011
Bram Moolenaar071d4272004-06-13 20:20:40 +00003012 if (curwin != old_curwin)
Bram Moolenaarc48369c2018-03-11 19:30:45 +01003013 {
3014#ifdef CHECK_DOUBLE_CLICK
3015 /* set topline, to be able to check for double click ourselves */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003016 set_mouse_topline(curwin);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003017#endif
Bram Moolenaarc48369c2018-03-11 19:30:45 +01003018#ifdef FEAT_TERMINAL
3019 /* when entering a terminal window may change state */
3020 term_win_entered();
3021#endif
3022 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003023 if (on_status_line) /* In (or below) status line */
3024 {
3025 /* Don't use start_arrow() if we're in the same window */
3026 if (curwin == old_curwin)
3027 return IN_STATUS_LINE;
3028 else
3029 return IN_STATUS_LINE | CURSOR_MOVED;
3030 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003031 if (on_sep_line) /* In (or below) status line */
3032 {
3033 /* Don't use start_arrow() if we're in the same window */
3034 if (curwin == old_curwin)
3035 return IN_SEP_LINE;
3036 else
3037 return IN_SEP_LINE | CURSOR_MOVED;
3038 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003039
3040 curwin->w_cursor.lnum = curwin->w_topline;
3041#ifdef FEAT_GUI
3042 /* remember topline, needed for double click */
3043 gui_prev_topline = curwin->w_topline;
3044# ifdef FEAT_DIFF
3045 gui_prev_topfill = curwin->w_topfill;
3046# endif
3047#endif
3048 }
3049 else if (on_status_line && which_button == MOUSE_LEFT)
3050 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003051 if (dragwin != NULL)
3052 {
3053 /* Drag the status line */
3054 count = row - dragwin->w_winrow - dragwin->w_height + 1
3055 - on_status_line;
3056 win_drag_status_line(dragwin, count);
3057 did_drag |= count;
3058 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003059 return IN_STATUS_LINE; /* Cursor didn't move */
3060 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003061 else if (on_sep_line && which_button == MOUSE_LEFT)
3062 {
3063 if (dragwin != NULL)
3064 {
3065 /* Drag the separator column */
3066 count = col - dragwin->w_wincol - dragwin->w_width + 1
3067 - on_sep_line;
3068 win_drag_vsep_line(dragwin, count);
3069 did_drag |= count;
3070 }
3071 return IN_SEP_LINE; /* Cursor didn't move */
3072 }
Bram Moolenaareb163d72017-09-23 15:08:17 +02003073#ifdef FEAT_MENU
3074 else if (in_winbar)
3075 {
3076 /* After a click on the window toolbar don't start Visual mode. */
3077 return IN_OTHER_WIN | MOUSE_WINBAR;
3078 }
3079#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003080 else /* keep_window_focus must be TRUE */
3081 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003082 /* before moving the cursor for a left click, stop Visual mode */
3083 if (flags & MOUSE_MAY_STOP_VIS)
3084 {
3085 end_visual_mode();
3086 redraw_curbuf_later(INVERTED); /* delete the inversion */
3087 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003088
3089#if defined(FEAT_CMDWIN) && defined(FEAT_CLIPBOARD)
3090 /* Continue a modeless selection in another window. */
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02003091 if (cmdwin_type != 0 && row < curwin->w_winrow)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003092 return IN_OTHER_WIN;
3093#endif
3094
3095 row -= W_WINROW(curwin);
Bram Moolenaar53f81742017-09-22 14:35:51 +02003096 col -= curwin->w_wincol;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003097
3098 /*
3099 * When clicking beyond the end of the window, scroll the screen.
3100 * Scroll by however many rows outside the window we are.
3101 */
3102 if (row < 0)
3103 {
3104 count = 0;
3105 for (first = TRUE; curwin->w_topline > 1; )
3106 {
3107#ifdef FEAT_DIFF
3108 if (curwin->w_topfill < diff_check(curwin, curwin->w_topline))
3109 ++count;
3110 else
3111#endif
3112 count += plines(curwin->w_topline - 1);
3113 if (!first && count > -row)
3114 break;
3115 first = FALSE;
3116#ifdef FEAT_FOLDING
Bram Moolenaarcde88542015-08-11 19:14:00 +02003117 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003118#endif
3119#ifdef FEAT_DIFF
3120 if (curwin->w_topfill < diff_check(curwin, curwin->w_topline))
3121 ++curwin->w_topfill;
3122 else
3123#endif
3124 {
3125 --curwin->w_topline;
3126#ifdef FEAT_DIFF
3127 curwin->w_topfill = 0;
3128#endif
3129 }
3130 }
3131#ifdef FEAT_DIFF
3132 check_topfill(curwin, FALSE);
3133#endif
3134 curwin->w_valid &=
3135 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
3136 redraw_later(VALID);
3137 row = 0;
3138 }
3139 else if (row >= curwin->w_height)
3140 {
3141 count = 0;
3142 for (first = TRUE; curwin->w_topline < curbuf->b_ml.ml_line_count; )
3143 {
3144#ifdef FEAT_DIFF
3145 if (curwin->w_topfill > 0)
3146 ++count;
3147 else
3148#endif
3149 count += plines(curwin->w_topline);
3150 if (!first && count > row - curwin->w_height + 1)
3151 break;
3152 first = FALSE;
3153#ifdef FEAT_FOLDING
3154 if (hasFolding(curwin->w_topline, NULL, &curwin->w_topline)
3155 && curwin->w_topline == curbuf->b_ml.ml_line_count)
3156 break;
3157#endif
3158#ifdef FEAT_DIFF
3159 if (curwin->w_topfill > 0)
3160 --curwin->w_topfill;
3161 else
3162#endif
3163 {
3164 ++curwin->w_topline;
3165#ifdef FEAT_DIFF
3166 curwin->w_topfill =
3167 diff_check_fill(curwin, curwin->w_topline);
3168#endif
3169 }
3170 }
3171#ifdef FEAT_DIFF
3172 check_topfill(curwin, FALSE);
3173#endif
3174 redraw_later(VALID);
3175 curwin->w_valid &=
3176 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
3177 row = curwin->w_height - 1;
3178 }
3179 else if (row == 0)
3180 {
3181 /* When dragging the mouse, while the text has been scrolled up as
3182 * far as it goes, moving the mouse in the top line should scroll
3183 * the text down (done later when recomputing w_topline). */
Bram Moolenaar8cfdc0d2007-05-06 14:12:36 +00003184 if (mouse_dragging > 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00003185 && curwin->w_cursor.lnum
3186 == curwin->w_buffer->b_ml.ml_line_count
3187 && curwin->w_cursor.lnum == curwin->w_topline)
3188 curwin->w_valid &= ~(VALID_TOPLINE);
3189 }
3190 }
3191
3192#ifdef FEAT_FOLDING
3193 /* Check for position outside of the fold column. */
3194 if (
3195# ifdef FEAT_RIGHTLEFT
Bram Moolenaar02631462017-09-22 15:20:32 +02003196 curwin->w_p_rl ? col < curwin->w_width - curwin->w_p_fdc :
Bram Moolenaar071d4272004-06-13 20:20:40 +00003197# endif
3198 col >= curwin->w_p_fdc
3199# ifdef FEAT_CMDWIN
3200 + (cmdwin_type == 0 ? 0 : 1)
3201# endif
3202 )
3203 mouse_char = ' ';
3204#endif
3205
3206 /* compute the position in the buffer line from the posn on the screen */
3207 if (mouse_comp_pos(curwin, &row, &col, &curwin->w_cursor.lnum))
3208 mouse_past_bottom = TRUE;
3209
Bram Moolenaar071d4272004-06-13 20:20:40 +00003210 /* Start Visual mode before coladvance(), for when 'sel' != "old" */
3211 if ((flags & MOUSE_MAY_VIS) && !VIsual_active)
3212 {
3213 check_visual_highlight();
3214 VIsual = old_cursor;
3215 VIsual_active = TRUE;
3216 VIsual_reselect = TRUE;
3217 /* if 'selectmode' contains "mouse", start Select mode */
3218 may_start_select('o');
3219 setmouse();
Bram Moolenaar7df351e2006-01-23 22:30:28 +00003220 if (p_smd && msg_silent == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003221 redraw_cmdline = TRUE; /* show visual mode later */
3222 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003223
3224 curwin->w_curswant = col;
3225 curwin->w_set_curswant = FALSE; /* May still have been TRUE */
3226 if (coladvance(col) == FAIL) /* Mouse click beyond end of line */
3227 {
3228 if (inclusive != NULL)
3229 *inclusive = TRUE;
3230 mouse_past_eol = TRUE;
3231 }
3232 else if (inclusive != NULL)
3233 *inclusive = FALSE;
3234
3235 count = IN_BUFFER;
3236 if (curwin != old_curwin || curwin->w_cursor.lnum != old_cursor.lnum
3237 || curwin->w_cursor.col != old_cursor.col)
3238 count |= CURSOR_MOVED; /* Cursor has moved */
3239
3240#ifdef FEAT_FOLDING
3241 if (mouse_char == '+')
3242 count |= MOUSE_FOLD_OPEN;
3243 else if (mouse_char != ' ')
3244 count |= MOUSE_FOLD_CLOSE;
3245#endif
3246
3247 return count;
3248}
3249
3250/*
3251 * Compute the position in the buffer line from the posn on the screen in
3252 * window "win".
3253 * Returns TRUE if the position is below the last line.
3254 */
3255 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003256mouse_comp_pos(
3257 win_T *win,
3258 int *rowp,
3259 int *colp,
3260 linenr_T *lnump)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003261{
3262 int col = *colp;
3263 int row = *rowp;
3264 linenr_T lnum;
3265 int retval = FALSE;
3266 int off;
3267 int count;
3268
3269#ifdef FEAT_RIGHTLEFT
3270 if (win->w_p_rl)
Bram Moolenaar02631462017-09-22 15:20:32 +02003271 col = win->w_width - 1 - col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003272#endif
3273
3274 lnum = win->w_topline;
3275
3276 while (row > 0)
3277 {
3278#ifdef FEAT_DIFF
3279 /* Don't include filler lines in "count" */
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00003280 if (win->w_p_diff
3281# ifdef FEAT_FOLDING
3282 && !hasFoldingWin(win, lnum, NULL, NULL, TRUE, NULL)
3283# endif
3284 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003285 {
3286 if (lnum == win->w_topline)
3287 row -= win->w_topfill;
3288 else
3289 row -= diff_check_fill(win, lnum);
3290 count = plines_win_nofill(win, lnum, TRUE);
3291 }
3292 else
3293#endif
3294 count = plines_win(win, lnum, TRUE);
3295 if (count > row)
3296 break; /* Position is in this buffer line. */
3297#ifdef FEAT_FOLDING
3298 (void)hasFoldingWin(win, lnum, NULL, &lnum, TRUE, NULL);
3299#endif
3300 if (lnum == win->w_buffer->b_ml.ml_line_count)
3301 {
3302 retval = TRUE;
3303 break; /* past end of file */
3304 }
3305 row -= count;
3306 ++lnum;
3307 }
3308
3309 if (!retval)
3310 {
3311 /* Compute the column without wrapping. */
3312 off = win_col_off(win) - win_col_off2(win);
3313 if (col < off)
3314 col = off;
Bram Moolenaar02631462017-09-22 15:20:32 +02003315 col += row * (win->w_width - off);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003316 /* add skip column (for long wrapping line) */
3317 col += win->w_skipcol;
3318 }
3319
3320 if (!win->w_p_wrap)
3321 col += win->w_leftcol;
3322
3323 /* skip line number and fold column in front of the line */
3324 col -= win_col_off(win);
3325 if (col < 0)
3326 {
3327#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarcc448b32010-07-14 16:52:17 +02003328 netbeans_gutter_click(lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003329#endif
3330 col = 0;
3331 }
3332
3333 *colp = col;
3334 *rowp = row;
3335 *lnump = lnum;
3336 return retval;
3337}
3338
Bram Moolenaar071d4272004-06-13 20:20:40 +00003339/*
3340 * Find the window at screen position "*rowp" and "*colp". The positions are
3341 * updated to become relative to the top-left of the window.
Bram Moolenaar989a70c2017-08-16 22:46:01 +02003342 * Returns NULL when something is wrong.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003343 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003344 win_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003345mouse_find_win(int *rowp, int *colp UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003346{
3347 frame_T *fp;
Bram Moolenaar989a70c2017-08-16 22:46:01 +02003348 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003349
3350 fp = topframe;
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00003351 *rowp -= firstwin->w_winrow;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003352 for (;;)
3353 {
3354 if (fp->fr_layout == FR_LEAF)
3355 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003356 if (fp->fr_layout == FR_ROW)
3357 {
3358 for (fp = fp->fr_child; fp->fr_next != NULL; fp = fp->fr_next)
3359 {
3360 if (*colp < fp->fr_width)
3361 break;
3362 *colp -= fp->fr_width;
3363 }
3364 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003365 else /* fr_layout == FR_COL */
3366 {
3367 for (fp = fp->fr_child; fp->fr_next != NULL; fp = fp->fr_next)
3368 {
3369 if (*rowp < fp->fr_height)
3370 break;
3371 *rowp -= fp->fr_height;
3372 }
3373 }
3374 }
Bram Moolenaar989a70c2017-08-16 22:46:01 +02003375 /* When using a timer that closes a window the window might not actually
3376 * exist. */
3377 FOR_ALL_WINDOWS(wp)
3378 if (wp == fp->fr_win)
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02003379 {
3380#ifdef FEAT_MENU
3381 *rowp -= wp->w_winbar_height;
3382#endif
Bram Moolenaar989a70c2017-08-16 22:46:01 +02003383 return wp;
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02003384 }
Bram Moolenaar989a70c2017-08-16 22:46:01 +02003385 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003386}
Bram Moolenaar071d4272004-06-13 20:20:40 +00003387
Bram Moolenaar860cae12010-06-05 23:22:07 +02003388#if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_GTK) || defined(FEAT_GUI_MAC) \
Bram Moolenaar071d4272004-06-13 20:20:40 +00003389 || defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MSWIN) \
Bram Moolenaar658a1542018-03-03 19:29:43 +01003390 || defined(FEAT_GUI_PHOTON) || defined(FEAT_TERM_POPUP_MENU) \
3391 || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003392/*
3393 * Translate window coordinates to buffer position without any side effects
3394 */
3395 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003396get_fpos_of_mouse(pos_T *mpos)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003397{
3398 win_T *wp;
3399 int row = mouse_row;
3400 int col = mouse_col;
3401
3402 if (row < 0 || col < 0) /* check if it makes sense */
3403 return IN_UNKNOWN;
3404
Bram Moolenaar071d4272004-06-13 20:20:40 +00003405 /* find the window where the row is in */
3406 wp = mouse_find_win(&row, &col);
Bram Moolenaar989a70c2017-08-16 22:46:01 +02003407 if (wp == NULL)
3408 return IN_UNKNOWN;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003409 /*
3410 * winpos and height may change in win_enter()!
3411 */
3412 if (row >= wp->w_height) /* In (or below) status line */
3413 return IN_STATUS_LINE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003414 if (col >= wp->w_width) /* In vertical separator line */
3415 return IN_SEP_LINE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003416
3417 if (wp != curwin)
3418 return IN_UNKNOWN;
3419
3420 /* compute the position in the buffer line from the posn on the screen */
3421 if (mouse_comp_pos(curwin, &row, &col, &mpos->lnum))
3422 return IN_STATUS_LINE; /* past bottom */
3423
3424 mpos->col = vcol2col(wp, mpos->lnum, col);
3425
3426 if (mpos->col > 0)
3427 --mpos->col;
Bram Moolenaara9d52e32010-07-31 16:44:19 +02003428 mpos->coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003429 return IN_BUFFER;
3430}
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01003431#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003432
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01003433#if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_GTK) || defined(FEAT_GUI_MAC) \
3434 || defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MSWIN) \
Bram Moolenaar3767b612018-03-03 19:51:58 +01003435 || defined(FEAT_GUI_PHOTON) || defined(FEAT_BEVAL) \
3436 || defined(FEAT_TERM_POPUP_MENU) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003437/*
3438 * Convert a virtual (screen) column to a character column.
3439 * The first column is one.
3440 */
3441 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003442vcol2col(win_T *wp, linenr_T lnum, int vcol)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003443{
3444 /* try to advance to the specified column */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003445 int count = 0;
3446 char_u *ptr;
Bram Moolenaar597a4222014-06-25 14:39:50 +02003447 char_u *line;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003448
Bram Moolenaar597a4222014-06-25 14:39:50 +02003449 line = ptr = ml_get_buf(wp->w_buffer, lnum, FALSE);
Bram Moolenaarb6101cf2012-10-21 00:58:39 +02003450 while (count < vcol && *ptr != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003451 {
Bram Moolenaar597a4222014-06-25 14:39:50 +02003452 count += win_lbr_chartabsize(wp, line, ptr, count, NULL);
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003453 MB_PTR_ADV(ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003454 }
Bram Moolenaar597a4222014-06-25 14:39:50 +02003455 return (int)(ptr - line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003456}
3457#endif
3458
3459#endif /* FEAT_MOUSE */
3460
Bram Moolenaar4f974752019-02-17 17:44:42 +01003461#if defined(FEAT_GUI) || defined(MSWIN) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003462/*
3463 * Called when focus changed. Used for the GUI or for systems where this can
3464 * be done in the console (Win32).
3465 */
3466 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003467ui_focus_change(
3468 int in_focus) /* TRUE if focus gained. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003469{
3470 static time_t last_time = (time_t)0;
3471 int need_redraw = FALSE;
3472
3473 /* When activated: Check if any file was modified outside of Vim.
3474 * Only do this when not done within the last two seconds (could get
3475 * several events in a row). */
3476 if (in_focus && last_time + 2 < time(NULL))
3477 {
3478 need_redraw = check_timestamps(
3479# ifdef FEAT_GUI
3480 gui.in_use
3481# else
3482 FALSE
3483# endif
3484 );
3485 last_time = time(NULL);
3486 }
3487
Bram Moolenaar071d4272004-06-13 20:20:40 +00003488 /*
3489 * Fire the focus gained/lost autocommand.
3490 */
3491 need_redraw |= apply_autocmds(in_focus ? EVENT_FOCUSGAINED
3492 : EVENT_FOCUSLOST, NULL, NULL, FALSE, curbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003493
3494 if (need_redraw)
3495 {
3496 /* Something was executed, make sure the cursor is put back where it
3497 * belongs. */
3498 need_wait_return = FALSE;
3499
3500 if (State & CMDLINE)
3501 redrawcmdline();
3502 else if (State == HITRETURN || State == SETWSIZE || State == ASKMORE
3503 || State == EXTERNCMD || State == CONFIRM || exmode_active)
3504 repeat_message();
3505 else if ((State & NORMAL) || (State & INSERT))
3506 {
3507 if (must_redraw != 0)
3508 update_screen(0);
3509 setcursor();
3510 }
3511 cursor_on(); /* redrawing may have switched it off */
Bram Moolenaara338adc2018-01-31 20:51:47 +01003512 out_flush_cursor(FALSE, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003513# ifdef FEAT_GUI
3514 if (gui.in_use)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003515 gui_update_scrollbars(FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003516# endif
3517 }
3518#ifdef FEAT_TITLE
3519 /* File may have been changed from 'readonly' to 'noreadonly' */
3520 if (need_maketitle)
3521 maketitle();
3522#endif
3523}
3524#endif
3525
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01003526#if defined(HAVE_INPUT_METHOD) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003527/*
3528 * Save current Input Method status to specified place.
3529 */
3530 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003531im_save_status(long *psave)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003532{
3533 /* Don't save when 'imdisable' is set or "xic" is NULL, IM is always
3534 * disabled then (but might start later).
3535 * Also don't save when inside a mapping, vgetc_im_active has not been set
3536 * then.
3537 * And don't save when the keys were stuffed (e.g., for a "." command).
3538 * And don't save when the GUI is running but our window doesn't have
3539 * input focus (e.g., when a find dialog is open). */
3540 if (!p_imdisable && KeyTyped && !KeyStuffed
3541# ifdef FEAT_XIM
3542 && xic != NULL
3543# endif
3544# ifdef FEAT_GUI
3545 && (!gui.in_use || gui.in_focus)
3546# endif
3547 )
3548 {
3549 /* Do save when IM is on, or IM is off and saved status is on. */
3550 if (vgetc_im_active)
3551 *psave = B_IMODE_IM;
3552 else if (*psave == B_IMODE_IM)
3553 *psave = B_IMODE_NONE;
3554 }
3555}
3556#endif