blob: 7552751225ec8d0738f0bc2e5e7e09cfd36516ae [file] [log] [blame]
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * clipboard.c: Functions to handle the clipboard
12 */
13
14#include "vim.h"
15
16#ifdef FEAT_CYGWIN_WIN32_CLIPBOARD
17# define WIN32_LEAN_AND_MEAN
18# include <windows.h>
19# include "winclip.pro"
20#endif
21
22// Functions for copying and pasting text between applications.
23// This is always included in a GUI version, but may also be included when the
24// clipboard and mouse is available to a terminal version such as xterm.
25// Note: there are some more functions in ops.c that handle selection stuff.
26//
27// Also note that the majority of functions here deal with the X 'primary'
28// (visible - for Visual mode use) selection, and only that. There are no
29// versions of these for the 'clipboard' selection, as Visual mode has no use
30// for them.
31
32#if defined(FEAT_CLIPBOARD) || defined(PROTO)
33
Foxe Chenb90c2392025-06-27 21:10:35 +020034#if defined(FEAT_WAYLAND_CLIPBOARD)
35// Mime types we support sending and receiving
36// Mimes with a lower index in the array are prioritized first when we are
37// receiving data.
38static const char *supported_mimes[] = {
39 VIMENC_ATOM_NAME,
40 VIM_ATOM_NAME,
41 "text/plain;charset=utf-8",
42 "text/plain",
43 "UTF8_STRING",
44 "STRING",
45 "TEXT"
46};
47
48static void clip_wl_receive_data(Clipboard_T *cbd,
49 const char *mime_type, int fd);
50static void clip_wl_send_data(const char *mime_type, int fd,
51 wayland_selection_T);
52static void clip_wl_selection_cancelled(wayland_selection_T selection);
53
54#if defined(USE_SYSTEM) && defined(PROTO)
55static int clip_wl_owner_exists(Clipboard_T *cbd);
56#endif
57
58#endif
59
Bram Moolenaar45fffdf2020-03-24 21:42:01 +010060/*
61 * Selection stuff using Visual mode, for cutting and pasting text to other
62 * windows.
63 */
64
65/*
66 * Call this to initialise the clipboard. Pass it FALSE if the clipboard code
67 * is included, but the clipboard can not be used, or TRUE if the clipboard can
68 * be used. Eg unix may call this with FALSE, then call it again with TRUE if
69 * the GUI starts.
70 */
71 void
72clip_init(int can_use)
73{
74 Clipboard_T *cb;
75
76 cb = &clip_star;
77 for (;;)
78 {
Foxe Chenb90c2392025-06-27 21:10:35 +020079 // No need to init again if cbd is already available
80 if (can_use && cb->available)
81 goto skip;
82
Bram Moolenaar45fffdf2020-03-24 21:42:01 +010083 cb->available = can_use;
84 cb->owned = FALSE;
85 cb->start.lnum = 0;
86 cb->start.col = 0;
87 cb->end.lnum = 0;
88 cb->end.col = 0;
89 cb->state = SELECT_CLEARED;
90
Foxe Chenb90c2392025-06-27 21:10:35 +020091skip:
Bram Moolenaar45fffdf2020-03-24 21:42:01 +010092 if (cb == &clip_plus)
93 break;
94 cb = &clip_plus;
95 }
96}
97
98/*
99 * Check whether the VIsual area has changed, and if so try to become the owner
100 * of the selection, and free any old converted selection we may still have
101 * lying around. If the VIsual mode has ended, make a copy of what was
102 * selected so we can still give it to others. Will probably have to make sure
103 * this is called whenever VIsual mode is ended.
104 */
105 void
106clip_update_selection(Clipboard_T *clip)
107{
108 pos_T start, end;
109
110 // If visual mode is only due to a redo command ("."), then ignore it
Bram Moolenaar24959102022-05-07 20:01:16 +0100111 if (!redo_VIsual_busy && VIsual_active && (State & MODE_NORMAL))
Bram Moolenaar45fffdf2020-03-24 21:42:01 +0100112 {
113 if (LT_POS(VIsual, curwin->w_cursor))
114 {
115 start = VIsual;
116 end = curwin->w_cursor;
117 if (has_mbyte)
118 end.col += (*mb_ptr2len)(ml_get_cursor()) - 1;
119 }
120 else
121 {
122 start = curwin->w_cursor;
123 end = VIsual;
124 }
125 if (!EQUAL_POS(clip->start, start)
126 || !EQUAL_POS(clip->end, end)
127 || clip->vmode != VIsual_mode)
128 {
129 clip_clear_selection(clip);
130 clip->start = start;
131 clip->end = end;
132 clip->vmode = VIsual_mode;
133 clip_free_selection(clip);
134 clip_own_selection(clip);
135 clip_gen_set_selection(clip);
136 }
137 }
138}
139
140 static int
141clip_gen_own_selection(Clipboard_T *cbd)
142{
Foxe Chenb90c2392025-06-27 21:10:35 +0200143#if defined(FEAT_XCLIPBOARD) || defined(FEAT_WAYLAND_CLIPBOARD)
Bram Moolenaar45fffdf2020-03-24 21:42:01 +0100144# ifdef FEAT_GUI
145 if (gui.in_use)
146 return clip_mch_own_selection(cbd);
147 else
148# endif
Foxe Chenb90c2392025-06-27 21:10:35 +0200149 {
150 if (clipmethod == CLIPMETHOD_WAYLAND)
151 {
152#ifdef FEAT_WAYLAND_CLIPBOARD
153 return clip_wl_own_selection(cbd);
154#endif
155 }
156 else if (clipmethod == CLIPMETHOD_X11)
157 {
158#ifdef FEAT_XCLIPBOARD
159 return clip_xterm_own_selection(cbd);
160#endif
161 }
162 }
163 return FAIL;
Bram Moolenaar45fffdf2020-03-24 21:42:01 +0100164#else
165 return clip_mch_own_selection(cbd);
166#endif
167}
168
169 void
170clip_own_selection(Clipboard_T *cbd)
171{
172 /*
173 * Also want to check somehow that we are reading from the keyboard rather
174 * than a mapping etc.
175 */
Foxe Chenb90c2392025-06-27 21:10:35 +0200176#if defined(FEAT_X11) || defined(FEAT_WAYLAND_CLIPBOARD)
Bram Moolenaar45fffdf2020-03-24 21:42:01 +0100177 // Always own the selection, we might have lost it without being
178 // notified, e.g. during a ":sh" command.
179 if (cbd->available)
180 {
181 int was_owned = cbd->owned;
182
183 cbd->owned = (clip_gen_own_selection(cbd) == OK);
184 if (!was_owned && (cbd == &clip_star || cbd == &clip_plus))
185 {
186 // May have to show a different kind of highlighting for the
187 // selected area. There is no specific redraw command for this,
188 // just redraw all windows on the current buffer.
189 if (cbd->owned
Bram Moolenaar24959102022-05-07 20:01:16 +0100190 && (get_real_state() == MODE_VISUAL
191 || get_real_state() == MODE_SELECT)
Bram Moolenaar45fffdf2020-03-24 21:42:01 +0100192 && (cbd == &clip_star ? clip_isautosel_star()
193 : clip_isautosel_plus())
194 && HL_ATTR(HLF_V) != HL_ATTR(HLF_VNC))
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100195 redraw_curbuf_later(UPD_INVERTED_ALL);
Bram Moolenaar45fffdf2020-03-24 21:42:01 +0100196 }
197 }
198#else
199 // Only own the clipboard when we didn't own it yet.
200 if (!cbd->owned && cbd->available)
201 cbd->owned = (clip_gen_own_selection(cbd) == OK);
202#endif
203}
204
205 static void
206clip_gen_lose_selection(Clipboard_T *cbd)
207{
Foxe Chenb90c2392025-06-27 21:10:35 +0200208#if defined(FEAT_XCLIPBOARD) || defined(FEAT_WAYLAND_CLIPBOARD)
Bram Moolenaar45fffdf2020-03-24 21:42:01 +0100209# ifdef FEAT_GUI
210 if (gui.in_use)
211 clip_mch_lose_selection(cbd);
212 else
213# endif
Foxe Chenb90c2392025-06-27 21:10:35 +0200214 {
215 if (clipmethod == CLIPMETHOD_WAYLAND)
216 {
217#ifdef FEAT_WAYLAND_CLIPBOARD
218 clip_wl_lose_selection(cbd);
219#endif
220 }
221 else if (clipmethod == CLIPMETHOD_X11)
222 {
223#ifdef FEAT_XCLIPBOARD
224 clip_xterm_lose_selection(cbd);
225#endif
226 }
227 }
Bram Moolenaar45fffdf2020-03-24 21:42:01 +0100228#else
229 clip_mch_lose_selection(cbd);
230#endif
231}
232
233 void
234clip_lose_selection(Clipboard_T *cbd)
235{
236#ifdef FEAT_X11
237 int was_owned = cbd->owned;
238#endif
239 int visual_selection = FALSE;
240
241 if (cbd == &clip_star || cbd == &clip_plus)
242 visual_selection = TRUE;
243
244 clip_free_selection(cbd);
245 cbd->owned = FALSE;
246 if (visual_selection)
247 clip_clear_selection(cbd);
248 clip_gen_lose_selection(cbd);
249#ifdef FEAT_X11
250 if (visual_selection)
251 {
252 // May have to show a different kind of highlighting for the selected
253 // area. There is no specific redraw command for this, just redraw all
254 // windows on the current buffer.
255 if (was_owned
Bram Moolenaar24959102022-05-07 20:01:16 +0100256 && (get_real_state() == MODE_VISUAL
Foxe Chenb90c2392025-06-27 21:10:35 +0200257 || get_real_state() == MODE_SELECT)
Bram Moolenaar45fffdf2020-03-24 21:42:01 +0100258 && (cbd == &clip_star ?
Foxe Chenb90c2392025-06-27 21:10:35 +0200259 clip_isautosel_star() : clip_isautosel_plus())
Bram Moolenaare275ba42021-10-06 13:41:07 +0100260 && HL_ATTR(HLF_V) != HL_ATTR(HLF_VNC)
261 && !exiting)
Bram Moolenaar45fffdf2020-03-24 21:42:01 +0100262 {
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100263 update_curbuf(UPD_INVERTED_ALL);
Bram Moolenaar45fffdf2020-03-24 21:42:01 +0100264 setcursor();
265 cursor_on();
266 out_flush_cursor(TRUE, FALSE);
267 }
268 }
269#endif
270}
271
272 static void
273clip_copy_selection(Clipboard_T *clip)
274{
Bram Moolenaar24959102022-05-07 20:01:16 +0100275 if (VIsual_active && (State & MODE_NORMAL) && clip->available)
Bram Moolenaar45fffdf2020-03-24 21:42:01 +0100276 {
277 clip_update_selection(clip);
278 clip_free_selection(clip);
279 clip_own_selection(clip);
280 if (clip->owned)
281 clip_get_selection(clip);
282 clip_gen_set_selection(clip);
283 }
284}
285
286/*
287 * Save and restore clip_unnamed before doing possibly many changes. This
288 * prevents accessing the clipboard very often which might slow down Vim
289 * considerably.
290 */
291static int global_change_count = 0; // if set, inside a start_global_changes
292static int clipboard_needs_update = FALSE; // clipboard needs to be updated
293static int clip_did_set_selection = TRUE;
294
295/*
296 * Save clip_unnamed and reset it.
297 */
298 void
299start_global_changes(void)
300{
301 if (++global_change_count > 1)
302 return;
303 clip_unnamed_saved = clip_unnamed;
304 clipboard_needs_update = FALSE;
305
306 if (clip_did_set_selection)
307 {
308 clip_unnamed = 0;
309 clip_did_set_selection = FALSE;
310 }
311}
312
313/*
314 * Return TRUE if setting the clipboard was postponed, it already contains the
315 * right text.
316 */
317 static int
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +0000318is_clipboard_needs_update(void)
Bram Moolenaar45fffdf2020-03-24 21:42:01 +0100319{
320 return clipboard_needs_update;
321}
322
323/*
324 * Restore clip_unnamed and set the selection when needed.
325 */
326 void
327end_global_changes(void)
328{
329 if (--global_change_count > 0)
330 // recursive
331 return;
332 if (!clip_did_set_selection)
333 {
334 clip_did_set_selection = TRUE;
335 clip_unnamed = clip_unnamed_saved;
336 clip_unnamed_saved = 0;
337 if (clipboard_needs_update)
338 {
339 // only store something in the clipboard,
340 // if we have yanked anything to it
341 if (clip_unnamed & CLIP_UNNAMED)
342 {
343 clip_own_selection(&clip_star);
344 clip_gen_set_selection(&clip_star);
345 }
346 if (clip_unnamed & CLIP_UNNAMED_PLUS)
347 {
348 clip_own_selection(&clip_plus);
349 clip_gen_set_selection(&clip_plus);
350 }
351 }
352 }
353 clipboard_needs_update = FALSE;
354}
355
356/*
357 * Called when Visual mode is ended: update the selection.
358 */
359 void
360clip_auto_select(void)
361{
362 if (clip_isautosel_star())
363 clip_copy_selection(&clip_star);
364 if (clip_isautosel_plus())
365 clip_copy_selection(&clip_plus);
366}
367
368/*
369 * Return TRUE if automatic selection of Visual area is desired for the *
370 * register.
371 */
372 int
373clip_isautosel_star(void)
374{
375 return (
376#ifdef FEAT_GUI
377 gui.in_use ? (vim_strchr(p_go, GO_ASEL) != NULL) :
378#endif
379 clip_autoselect_star);
380}
381
382/*
383 * Return TRUE if automatic selection of Visual area is desired for the +
384 * register.
385 */
386 int
387clip_isautosel_plus(void)
388{
389 return (
390#ifdef FEAT_GUI
391 gui.in_use ? (vim_strchr(p_go, GO_ASELPLUS) != NULL) :
392#endif
393 clip_autoselect_plus);
394}
395
396
397/*
398 * Stuff for general mouse selection, without using Visual mode.
399 */
400
401/*
402 * Compare two screen positions ala strcmp()
403 */
404 static int
405clip_compare_pos(
406 int row1,
407 int col1,
408 int row2,
409 int col2)
410{
411 if (row1 > row2) return(1);
412 if (row1 < row2) return(-1);
413 if (col1 > col2) return(1);
414 if (col1 < col2) return(-1);
415 return(0);
416}
417
418// "how" flags for clip_invert_area()
419#define CLIP_CLEAR 1
420#define CLIP_SET 2
421#define CLIP_TOGGLE 3
422
423/*
424 * Invert or un-invert a rectangle of the screen.
425 * "invert" is true if the result is inverted.
426 */
427 static void
428clip_invert_rectangle(
429 Clipboard_T *cbd UNUSED,
430 int row_arg,
431 int col_arg,
432 int height_arg,
433 int width_arg,
434 int invert)
435{
436 int row = row_arg;
437 int col = col_arg;
438 int height = height_arg;
439 int width = width_arg;
440
441#ifdef FEAT_PROP_POPUP
442 // this goes on top of all popup windows
443 screen_zindex = CLIP_ZINDEX;
444
445 if (col < cbd->min_col)
446 {
447 width -= cbd->min_col - col;
448 col = cbd->min_col;
449 }
450 if (width > cbd->max_col - col)
451 width = cbd->max_col - col;
452 if (row < cbd->min_row)
453 {
454 height -= cbd->min_row - row;
455 row = cbd->min_row;
456 }
457 if (height > cbd->max_row - row + 1)
458 height = cbd->max_row - row + 1;
459#endif
460#ifdef FEAT_GUI
461 if (gui.in_use)
462 gui_mch_invert_rectangle(row, col, height, width);
463 else
464#endif
Hirohito Higashi3b9b95d2025-06-01 20:22:55 +0200465 screen_draw_rectangle(row, col, height, width, invert);
Bram Moolenaar45fffdf2020-03-24 21:42:01 +0100466#ifdef FEAT_PROP_POPUP
467 screen_zindex = 0;
468#endif
469}
470
471/*
472 * Invert a region of the display between a starting and ending row and column
473 * Values for "how":
474 * CLIP_CLEAR: undo inversion
475 * CLIP_SET: set inversion
476 * CLIP_TOGGLE: set inversion if pos1 < pos2, undo inversion otherwise.
477 * 0: invert (GUI only).
478 */
479 static void
480clip_invert_area(
481 Clipboard_T *cbd,
482 int row1,
483 int col1,
484 int row2,
485 int col2,
486 int how)
487{
488 int invert = FALSE;
489 int max_col;
490
491#ifdef FEAT_PROP_POPUP
492 max_col = cbd->max_col - 1;
493#else
494 max_col = Columns - 1;
495#endif
496
497 if (how == CLIP_SET)
498 invert = TRUE;
499
500 // Swap the from and to positions so the from is always before
501 if (clip_compare_pos(row1, col1, row2, col2) > 0)
502 {
503 int tmp_row, tmp_col;
504
505 tmp_row = row1;
506 tmp_col = col1;
507 row1 = row2;
508 col1 = col2;
509 row2 = tmp_row;
510 col2 = tmp_col;
511 }
512 else if (how == CLIP_TOGGLE)
513 invert = TRUE;
514
515 // If all on the same line, do it the easy way
516 if (row1 == row2)
517 {
518 clip_invert_rectangle(cbd, row1, col1, 1, col2 - col1, invert);
519 }
520 else
521 {
522 // Handle a piece of the first line
523 if (col1 > 0)
524 {
525 clip_invert_rectangle(cbd, row1, col1, 1,
526 (int)Columns - col1, invert);
527 row1++;
528 }
529
530 // Handle a piece of the last line
531 if (col2 < max_col)
532 {
533 clip_invert_rectangle(cbd, row2, 0, 1, col2, invert);
534 row2--;
535 }
536
537 // Handle the rectangle that's left
538 if (row2 >= row1)
539 clip_invert_rectangle(cbd, row1, 0, row2 - row1 + 1,
540 (int)Columns, invert);
541 }
542}
543
544/*
545 * Start, continue or end a modeless selection. Used when editing the
546 * command-line, in the cmdline window and when the mouse is in a popup window.
547 */
548 void
549clip_modeless(int button, int is_click, int is_drag)
550{
551 int repeat;
552
553 repeat = ((clip_star.mode == SELECT_MODE_CHAR
554 || clip_star.mode == SELECT_MODE_LINE)
555 && (mod_mask & MOD_MASK_2CLICK))
556 || (clip_star.mode == SELECT_MODE_WORD
557 && (mod_mask & MOD_MASK_3CLICK));
558 if (is_click && button == MOUSE_RIGHT)
559 {
560 // Right mouse button: If there was no selection, start one.
561 // Otherwise extend the existing selection.
562 if (clip_star.state == SELECT_CLEARED)
563 clip_start_selection(mouse_col, mouse_row, FALSE);
564 clip_process_selection(button, mouse_col, mouse_row, repeat);
565 }
566 else if (is_click)
567 clip_start_selection(mouse_col, mouse_row, repeat);
568 else if (is_drag)
569 {
570 // Don't try extending a selection if there isn't one. Happens when
571 // button-down is in the cmdline and them moving mouse upwards.
572 if (clip_star.state != SELECT_CLEARED)
573 clip_process_selection(button, mouse_col, mouse_row, repeat);
574 }
575 else // release
576 clip_process_selection(MOUSE_RELEASE, mouse_col, mouse_row, FALSE);
577}
578
579/*
580 * Update the currently selected region by adding and/or subtracting from the
581 * beginning or end and inverting the changed area(s).
582 */
583 static void
584clip_update_modeless_selection(
585 Clipboard_T *cb,
586 int row1,
587 int col1,
588 int row2,
589 int col2)
590{
591 // See if we changed at the beginning of the selection
592 if (row1 != cb->start.lnum || col1 != (int)cb->start.col)
593 {
594 clip_invert_area(cb, row1, col1, (int)cb->start.lnum, cb->start.col,
595 CLIP_TOGGLE);
596 cb->start.lnum = row1;
597 cb->start.col = col1;
598 }
599
600 // See if we changed at the end of the selection
601 if (row2 != cb->end.lnum || col2 != (int)cb->end.col)
602 {
603 clip_invert_area(cb, (int)cb->end.lnum, cb->end.col, row2, col2,
604 CLIP_TOGGLE);
605 cb->end.lnum = row2;
606 cb->end.col = col2;
607 }
608}
609
610/*
611 * Find the starting and ending positions of the word at the given row and
612 * column. Only white-separated words are recognized here.
613 */
614#define CHAR_CLASS(c) (c <= ' ' ? ' ' : vim_iswordc(c))
615
616 static void
617clip_get_word_boundaries(Clipboard_T *cb, int row, int col)
618{
619 int start_class;
620 int temp_col;
621 char_u *p;
622 int mboff;
623
624 if (row >= screen_Rows || col >= screen_Columns || ScreenLines == NULL)
625 return;
626
627 p = ScreenLines + LineOffset[row];
Dominique Pelleaf4a61a2021-12-27 17:21:41 +0000628 // Correct for starting in the right half of a double-wide char
Bram Moolenaar45fffdf2020-03-24 21:42:01 +0100629 if (enc_dbcs != 0)
630 col -= dbcs_screen_head_off(p, p + col);
631 else if (enc_utf8 && p[col] == 0)
632 --col;
633 start_class = CHAR_CLASS(p[col]);
634
635 temp_col = col;
636 for ( ; temp_col > 0; temp_col--)
637 if (enc_dbcs != 0
638 && (mboff = dbcs_screen_head_off(p, p + temp_col - 1)) > 0)
639 temp_col -= mboff;
640 else if (CHAR_CLASS(p[temp_col - 1]) != start_class
641 && !(enc_utf8 && p[temp_col - 1] == 0))
642 break;
643 cb->word_start_col = temp_col;
644
645 temp_col = col;
646 for ( ; temp_col < screen_Columns; temp_col++)
647 if (enc_dbcs != 0 && dbcs_ptr2cells(p + temp_col) == 2)
648 ++temp_col;
649 else if (CHAR_CLASS(p[temp_col]) != start_class
650 && !(enc_utf8 && p[temp_col] == 0))
651 break;
652 cb->word_end_col = temp_col;
653}
654
655/*
656 * Find the column position for the last non-whitespace character on the given
657 * line at or before start_col.
658 */
659 static int
660clip_get_line_end(Clipboard_T *cbd UNUSED, int row)
661{
662 int i;
663
664 if (row >= screen_Rows || ScreenLines == NULL)
665 return 0;
666 for (i =
667#ifdef FEAT_PROP_POPUP
668 cbd->max_col;
669#else
670 screen_Columns;
671#endif
672 i > 0; i--)
673 if (ScreenLines[LineOffset[row] + i - 1] != ' ')
674 break;
675 return i;
676}
677
678/*
679 * Start the selection
680 */
681 void
682clip_start_selection(int col, int row, int repeated_click)
683{
684 Clipboard_T *cb = &clip_star;
685#ifdef FEAT_PROP_POPUP
686 win_T *wp;
687 int row_cp = row;
688 int col_cp = col;
689
690 wp = mouse_find_win(&row_cp, &col_cp, FIND_POPUP);
691 if (wp != NULL && WIN_IS_POPUP(wp)
692 && popup_is_in_scrollbar(wp, row_cp, col_cp))
693 // click or double click in scrollbar does not start a selection
694 return;
695#endif
696
697 if (cb->state == SELECT_DONE)
698 clip_clear_selection(cb);
699
700 row = check_row(row);
701 col = check_col(col);
702 col = mb_fix_col(col, row);
703
704 cb->start.lnum = row;
705 cb->start.col = col;
706 cb->end = cb->start;
707 cb->origin_row = (short_u)cb->start.lnum;
708 cb->state = SELECT_IN_PROGRESS;
709#ifdef FEAT_PROP_POPUP
710 if (wp != NULL && WIN_IS_POPUP(wp))
711 {
712 // Click in a popup window restricts selection to that window,
713 // excluding the border.
714 cb->min_col = wp->w_wincol + wp->w_popup_border[3];
715 cb->max_col = wp->w_wincol + popup_width(wp)
716 - wp->w_popup_border[1] - wp->w_has_scrollbar;
717 if (cb->max_col > screen_Columns)
718 cb->max_col = screen_Columns;
719 cb->min_row = wp->w_winrow + wp->w_popup_border[0];
720 cb->max_row = wp->w_winrow + popup_height(wp) - 1
721 - wp->w_popup_border[2];
722 }
723 else
724 {
725 cb->min_col = 0;
726 cb->max_col = screen_Columns;
727 cb->min_row = 0;
728 cb->max_row = screen_Rows;
729 }
730#endif
731
732 if (repeated_click)
733 {
734 if (++cb->mode > SELECT_MODE_LINE)
735 cb->mode = SELECT_MODE_CHAR;
736 }
737 else
738 cb->mode = SELECT_MODE_CHAR;
739
740#ifdef FEAT_GUI
741 // clear the cursor until the selection is made
742 if (gui.in_use)
743 gui_undraw_cursor();
744#endif
745
746 switch (cb->mode)
747 {
748 case SELECT_MODE_CHAR:
749 cb->origin_start_col = cb->start.col;
750 cb->word_end_col = clip_get_line_end(cb, (int)cb->start.lnum);
751 break;
752
753 case SELECT_MODE_WORD:
754 clip_get_word_boundaries(cb, (int)cb->start.lnum, cb->start.col);
755 cb->origin_start_col = cb->word_start_col;
756 cb->origin_end_col = cb->word_end_col;
757
758 clip_invert_area(cb, (int)cb->start.lnum, cb->word_start_col,
759 (int)cb->end.lnum, cb->word_end_col, CLIP_SET);
760 cb->start.col = cb->word_start_col;
761 cb->end.col = cb->word_end_col;
762 break;
763
764 case SELECT_MODE_LINE:
765 clip_invert_area(cb, (int)cb->start.lnum, 0, (int)cb->start.lnum,
766 (int)Columns, CLIP_SET);
767 cb->start.col = 0;
768 cb->end.col = Columns;
769 break;
770 }
771
772 cb->prev = cb->start;
773
774#ifdef DEBUG_SELECTION
775 printf("Selection started at (%ld,%d)\n", cb->start.lnum, cb->start.col);
776#endif
777}
778
779/*
780 * Continue processing the selection
781 */
782 void
783clip_process_selection(
784 int button,
785 int col,
786 int row,
787 int_u repeated_click)
788{
789 Clipboard_T *cb = &clip_star;
790 int diff;
791 int slen = 1; // cursor shape width
792
793 if (button == MOUSE_RELEASE)
794 {
795 if (cb->state != SELECT_IN_PROGRESS)
796 return;
797
798 // Check to make sure we have something selected
799 if (cb->start.lnum == cb->end.lnum && cb->start.col == cb->end.col)
800 {
801#ifdef FEAT_GUI
802 if (gui.in_use)
803 gui_update_cursor(FALSE, FALSE);
804#endif
805 cb->state = SELECT_CLEARED;
806 return;
807 }
808
809#ifdef DEBUG_SELECTION
810 printf("Selection ended: (%ld,%d) to (%ld,%d)\n", cb->start.lnum,
811 cb->start.col, cb->end.lnum, cb->end.col);
812#endif
813 if (clip_isautosel_star()
814 || (
815#ifdef FEAT_GUI
816 gui.in_use ? (vim_strchr(p_go, GO_ASELML) != NULL) :
817#endif
818 clip_autoselectml))
819 clip_copy_modeless_selection(FALSE);
820#ifdef FEAT_GUI
821 if (gui.in_use)
822 gui_update_cursor(FALSE, FALSE);
823#endif
824
825 cb->state = SELECT_DONE;
826 return;
827 }
828
829 row = check_row(row);
830 col = check_col(col);
831 col = mb_fix_col(col, row);
832
833 if (col == (int)cb->prev.col && row == cb->prev.lnum && !repeated_click)
834 return;
835
836 /*
837 * When extending the selection with the right mouse button, swap the
838 * start and end if the position is before half the selection
839 */
840 if (cb->state == SELECT_DONE && button == MOUSE_RIGHT)
841 {
842 /*
843 * If the click is before the start, or the click is inside the
844 * selection and the start is the closest side, set the origin to the
845 * end of the selection.
846 */
847 if (clip_compare_pos(row, col, (int)cb->start.lnum, cb->start.col) < 0
848 || (clip_compare_pos(row, col,
849 (int)cb->end.lnum, cb->end.col) < 0
850 && (((cb->start.lnum == cb->end.lnum
851 && cb->end.col - col > col - cb->start.col))
852 || ((diff = (cb->end.lnum - row) -
853 (row - cb->start.lnum)) > 0
854 || (diff == 0 && col < (int)(cb->start.col +
855 cb->end.col) / 2)))))
856 {
857 cb->origin_row = (short_u)cb->end.lnum;
858 cb->origin_start_col = cb->end.col - 1;
859 cb->origin_end_col = cb->end.col;
860 }
861 else
862 {
863 cb->origin_row = (short_u)cb->start.lnum;
864 cb->origin_start_col = cb->start.col;
865 cb->origin_end_col = cb->start.col;
866 }
867 if (cb->mode == SELECT_MODE_WORD && !repeated_click)
868 cb->mode = SELECT_MODE_CHAR;
869 }
870
871 // set state, for when using the right mouse button
872 cb->state = SELECT_IN_PROGRESS;
873
874#ifdef DEBUG_SELECTION
875 printf("Selection extending to (%d,%d)\n", row, col);
876#endif
877
878 if (repeated_click && ++cb->mode > SELECT_MODE_LINE)
879 cb->mode = SELECT_MODE_CHAR;
880
881 switch (cb->mode)
882 {
883 case SELECT_MODE_CHAR:
884 // If we're on a different line, find where the line ends
885 if (row != cb->prev.lnum)
886 cb->word_end_col = clip_get_line_end(cb, row);
887
888 // See if we are before or after the origin of the selection
889 if (clip_compare_pos(row, col, cb->origin_row,
890 cb->origin_start_col) >= 0)
891 {
892 if (col >= (int)cb->word_end_col)
893 clip_update_modeless_selection(cb, cb->origin_row,
894 cb->origin_start_col, row, (int)Columns);
895 else
896 {
897 if (has_mbyte && mb_lefthalve(row, col))
898 slen = 2;
899 clip_update_modeless_selection(cb, cb->origin_row,
900 cb->origin_start_col, row, col + slen);
901 }
902 }
903 else
904 {
905 if (has_mbyte
906 && mb_lefthalve(cb->origin_row, cb->origin_start_col))
907 slen = 2;
908 if (col >= (int)cb->word_end_col)
909 clip_update_modeless_selection(cb, row, cb->word_end_col,
910 cb->origin_row, cb->origin_start_col + slen);
911 else
912 clip_update_modeless_selection(cb, row, col,
913 cb->origin_row, cb->origin_start_col + slen);
914 }
915 break;
916
917 case SELECT_MODE_WORD:
918 // If we are still within the same word, do nothing
919 if (row == cb->prev.lnum && col >= (int)cb->word_start_col
920 && col < (int)cb->word_end_col && !repeated_click)
921 return;
922
923 // Get new word boundaries
924 clip_get_word_boundaries(cb, row, col);
925
926 // Handle being after the origin point of selection
927 if (clip_compare_pos(row, col, cb->origin_row,
928 cb->origin_start_col) >= 0)
929 clip_update_modeless_selection(cb, cb->origin_row,
930 cb->origin_start_col, row, cb->word_end_col);
931 else
932 clip_update_modeless_selection(cb, row, cb->word_start_col,
933 cb->origin_row, cb->origin_end_col);
934 break;
935
936 case SELECT_MODE_LINE:
937 if (row == cb->prev.lnum && !repeated_click)
938 return;
939
940 if (clip_compare_pos(row, col, cb->origin_row,
941 cb->origin_start_col) >= 0)
942 clip_update_modeless_selection(cb, cb->origin_row, 0, row,
943 (int)Columns);
944 else
945 clip_update_modeless_selection(cb, row, 0, cb->origin_row,
946 (int)Columns);
947 break;
948 }
949
950 cb->prev.lnum = row;
951 cb->prev.col = col;
952
953#ifdef DEBUG_SELECTION
954 printf("Selection is: (%ld,%d) to (%ld,%d)\n", cb->start.lnum,
955 cb->start.col, cb->end.lnum, cb->end.col);
956#endif
957}
958
959# if defined(FEAT_GUI) || defined(PROTO)
960/*
961 * Redraw part of the selection if character at "row,col" is inside of it.
962 * Only used for the GUI.
963 */
964 void
965clip_may_redraw_selection(int row, int col, int len)
966{
967 int start = col;
968 int end = col + len;
969
970 if (clip_star.state != SELECT_CLEARED
971 && row >= clip_star.start.lnum
972 && row <= clip_star.end.lnum)
973 {
974 if (row == clip_star.start.lnum && start < (int)clip_star.start.col)
975 start = clip_star.start.col;
976 if (row == clip_star.end.lnum && end > (int)clip_star.end.col)
977 end = clip_star.end.col;
978 if (end > start)
979 clip_invert_area(&clip_star, row, start, row, end, 0);
980 }
981}
982# endif
983
984/*
985 * Called from outside to clear selected region from the display
986 */
987 void
988clip_clear_selection(Clipboard_T *cbd)
989{
990
991 if (cbd->state == SELECT_CLEARED)
992 return;
993
994 clip_invert_area(cbd, (int)cbd->start.lnum, cbd->start.col,
995 (int)cbd->end.lnum, cbd->end.col, CLIP_CLEAR);
996 cbd->state = SELECT_CLEARED;
997}
998
999/*
1000 * Clear the selection if any lines from "row1" to "row2" are inside of it.
1001 */
1002 void
1003clip_may_clear_selection(int row1, int row2)
1004{
1005 if (clip_star.state == SELECT_DONE
1006 && row2 >= clip_star.start.lnum
1007 && row1 <= clip_star.end.lnum)
1008 clip_clear_selection(&clip_star);
1009}
1010
1011/*
1012 * Called before the screen is scrolled up or down. Adjusts the line numbers
1013 * of the selection. Call with big number when clearing the screen.
1014 */
1015 void
1016clip_scroll_selection(
1017 int rows) // negative for scroll down
1018{
1019 int lnum;
1020
1021 if (clip_star.state == SELECT_CLEARED)
1022 return;
1023
1024 lnum = clip_star.start.lnum - rows;
1025 if (lnum <= 0)
1026 clip_star.start.lnum = 0;
1027 else if (lnum >= screen_Rows) // scrolled off of the screen
1028 clip_star.state = SELECT_CLEARED;
1029 else
1030 clip_star.start.lnum = lnum;
1031
1032 lnum = clip_star.end.lnum - rows;
1033 if (lnum < 0) // scrolled off of the screen
1034 clip_star.state = SELECT_CLEARED;
1035 else if (lnum >= screen_Rows)
1036 clip_star.end.lnum = screen_Rows - 1;
1037 else
1038 clip_star.end.lnum = lnum;
1039}
1040
1041/*
1042 * Copy the currently selected area into the '*' register so it will be
1043 * available for pasting.
1044 * When "both" is TRUE also copy to the '+' register.
1045 */
1046 void
1047clip_copy_modeless_selection(int both UNUSED)
1048{
1049 char_u *buffer;
1050 char_u *bufp;
1051 int row;
1052 int start_col;
1053 int end_col;
1054 int line_end_col;
1055 int add_newline_flag = FALSE;
1056 int len;
1057 char_u *p;
1058 int row1 = clip_star.start.lnum;
1059 int col1 = clip_star.start.col;
1060 int row2 = clip_star.end.lnum;
1061 int col2 = clip_star.end.col;
1062
1063 // Can't use ScreenLines unless initialized
1064 if (ScreenLines == NULL)
1065 return;
1066
1067 /*
1068 * Make sure row1 <= row2, and if row1 == row2 that col1 <= col2.
1069 */
1070 if (row1 > row2)
1071 {
1072 row = row1; row1 = row2; row2 = row;
1073 row = col1; col1 = col2; col2 = row;
1074 }
1075 else if (row1 == row2 && col1 > col2)
1076 {
1077 row = col1; col1 = col2; col2 = row;
1078 }
1079#ifdef FEAT_PROP_POPUP
1080 if (col1 < clip_star.min_col)
1081 col1 = clip_star.min_col;
1082 if (col2 > clip_star.max_col)
1083 col2 = clip_star.max_col;
1084 if (row1 > clip_star.max_row || row2 < clip_star.min_row)
1085 return;
1086 if (row1 < clip_star.min_row)
1087 row1 = clip_star.min_row;
1088 if (row2 > clip_star.max_row)
1089 row2 = clip_star.max_row;
1090#endif
Dominique Pelleaf4a61a2021-12-27 17:21:41 +00001091 // correct starting point for being on right half of double-wide char
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01001092 p = ScreenLines + LineOffset[row1];
1093 if (enc_dbcs != 0)
1094 col1 -= (*mb_head_off)(p, p + col1);
1095 else if (enc_utf8 && p[col1] == 0)
1096 --col1;
1097
1098 // Create a temporary buffer for storing the text
1099 len = (row2 - row1 + 1) * Columns + 1;
1100 if (enc_dbcs != 0)
1101 len *= 2; // max. 2 bytes per display cell
1102 else if (enc_utf8)
1103 len *= MB_MAXBYTES;
1104 buffer = alloc(len);
1105 if (buffer == NULL) // out of memory
1106 return;
1107
1108 // Process each row in the selection
1109 for (bufp = buffer, row = row1; row <= row2; row++)
1110 {
1111 if (row == row1)
1112 start_col = col1;
1113 else
1114#ifdef FEAT_PROP_POPUP
1115 start_col = clip_star.min_col;
1116#else
1117 start_col = 0;
1118#endif
1119
1120 if (row == row2)
1121 end_col = col2;
1122 else
1123#ifdef FEAT_PROP_POPUP
1124 end_col = clip_star.max_col;
1125#else
1126 end_col = Columns;
1127#endif
1128
1129 line_end_col = clip_get_line_end(&clip_star, row);
1130
1131 // See if we need to nuke some trailing whitespace
1132 if (end_col >=
1133#ifdef FEAT_PROP_POPUP
1134 clip_star.max_col
1135#else
1136 Columns
1137#endif
1138 && (row < row2 || end_col > line_end_col))
1139 {
1140 // Get rid of trailing whitespace
1141 end_col = line_end_col;
1142 if (end_col < start_col)
1143 end_col = start_col;
1144
1145 // If the last line extended to the end, add an extra newline
1146 if (row == row2)
1147 add_newline_flag = TRUE;
1148 }
1149
1150 // If after the first row, we need to always add a newline
1151 if (row > row1 && !LineWraps[row - 1])
1152 *bufp++ = NL;
1153
1154 // Safetey check for in case resizing went wrong
1155 if (row < screen_Rows && end_col <= screen_Columns)
1156 {
1157 if (enc_dbcs != 0)
1158 {
1159 int i;
1160
1161 p = ScreenLines + LineOffset[row];
1162 for (i = start_col; i < end_col; ++i)
1163 if (enc_dbcs == DBCS_JPNU && p[i] == 0x8e)
1164 {
1165 // single-width double-byte char
1166 *bufp++ = 0x8e;
1167 *bufp++ = ScreenLines2[LineOffset[row] + i];
1168 }
1169 else
1170 {
1171 *bufp++ = p[i];
1172 if (MB_BYTE2LEN(p[i]) == 2)
1173 *bufp++ = p[++i];
1174 }
1175 }
1176 else if (enc_utf8)
1177 {
1178 int off;
1179 int i;
1180 int ci;
1181
1182 off = LineOffset[row];
1183 for (i = start_col; i < end_col; ++i)
1184 {
1185 // The base character is either in ScreenLinesUC[] or
1186 // ScreenLines[].
1187 if (ScreenLinesUC[off + i] == 0)
1188 *bufp++ = ScreenLines[off + i];
1189 else
1190 {
1191 bufp += utf_char2bytes(ScreenLinesUC[off + i], bufp);
1192 for (ci = 0; ci < Screen_mco; ++ci)
1193 {
1194 // Add a composing character.
1195 if (ScreenLinesC[ci][off + i] == 0)
1196 break;
1197 bufp += utf_char2bytes(ScreenLinesC[ci][off + i],
1198 bufp);
1199 }
1200 }
Dominique Pelleaf4a61a2021-12-27 17:21:41 +00001201 // Skip right half of double-wide character.
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01001202 if (ScreenLines[off + i + 1] == 0)
1203 ++i;
1204 }
1205 }
1206 else
1207 {
1208 STRNCPY(bufp, ScreenLines + LineOffset[row] + start_col,
1209 end_col - start_col);
1210 bufp += end_col - start_col;
1211 }
1212 }
1213 }
1214
1215 // Add a newline at the end if the selection ended there
1216 if (add_newline_flag)
1217 *bufp++ = NL;
1218
1219 // First cleanup any old selection and become the owner.
1220 clip_free_selection(&clip_star);
1221 clip_own_selection(&clip_star);
1222
1223 // Yank the text into the '*' register.
1224 clip_yank_selection(MCHAR, buffer, (long)(bufp - buffer), &clip_star);
1225
1226 // Make the register contents available to the outside world.
1227 clip_gen_set_selection(&clip_star);
1228
1229#ifdef FEAT_X11
1230 if (both)
1231 {
1232 // Do the same for the '+' register.
1233 clip_free_selection(&clip_plus);
1234 clip_own_selection(&clip_plus);
1235 clip_yank_selection(MCHAR, buffer, (long)(bufp - buffer), &clip_plus);
1236 clip_gen_set_selection(&clip_plus);
1237 }
1238#endif
1239 vim_free(buffer);
1240}
1241
1242 void
1243clip_gen_set_selection(Clipboard_T *cbd)
1244{
1245 if (!clip_did_set_selection)
1246 {
1247 // Updating postponed, so that accessing the system clipboard won't
1248 // hang Vim when accessing it many times (e.g. on a :g command).
1249 if ((cbd == &clip_plus && (clip_unnamed_saved & CLIP_UNNAMED_PLUS))
1250 || (cbd == &clip_star && (clip_unnamed_saved & CLIP_UNNAMED)))
1251 {
1252 clipboard_needs_update = TRUE;
1253 return;
1254 }
1255 }
Foxe Chenb90c2392025-06-27 21:10:35 +02001256#if defined(FEAT_XCLIPBOARD) || defined(FEAT_WAYLAND_CLIPBOARD)
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01001257# ifdef FEAT_GUI
1258 if (gui.in_use)
1259 clip_mch_set_selection(cbd);
1260 else
1261# endif
Foxe Chenb90c2392025-06-27 21:10:35 +02001262 {
1263 if (clipmethod == CLIPMETHOD_WAYLAND)
1264 {
1265#ifdef FEAT_WAYLAND_CLIPBOARD
1266 clip_wl_set_selection(cbd);
1267#endif
1268 }
1269 else if (clipmethod == CLIPMETHOD_X11)
1270 {
1271#ifdef FEAT_XCLIPBOARD
1272 clip_xterm_set_selection(cbd);
1273#endif
1274 }
1275 }
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01001276#else
1277 clip_mch_set_selection(cbd);
1278#endif
1279}
1280
1281 static void
1282clip_gen_request_selection(Clipboard_T *cbd)
1283{
Foxe Chenb90c2392025-06-27 21:10:35 +02001284#if defined(FEAT_XCLIPBOARD) || defined(FEAT_WAYLAND_CLIPBOARD)
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01001285# ifdef FEAT_GUI
1286 if (gui.in_use)
1287 clip_mch_request_selection(cbd);
1288 else
1289# endif
Foxe Chenb90c2392025-06-27 21:10:35 +02001290 {
1291 if (clipmethod == CLIPMETHOD_WAYLAND)
1292 {
1293#ifdef FEAT_WAYLAND_CLIPBOARD
1294 clip_wl_request_selection(cbd);
1295#endif
1296 }
1297 else if (clipmethod == CLIPMETHOD_X11)
1298 {
1299#ifdef FEAT_XCLIPBOARD
1300 clip_xterm_request_selection(cbd);
1301#endif
1302 }
1303 }
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01001304#else
1305 clip_mch_request_selection(cbd);
1306#endif
1307}
1308
1309#if (defined(FEAT_X11) && defined(FEAT_XCLIPBOARD) && defined(USE_SYSTEM)) \
1310 || defined(PROTO)
1311 static int
1312clip_x11_owner_exists(Clipboard_T *cbd)
1313{
1314 return XGetSelectionOwner(X_DISPLAY, cbd->sel_atom) != None;
1315}
1316#endif
1317
Foxe Chenb90c2392025-06-27 21:10:35 +02001318#if ((defined(FEAT_X11) || defined(FEAT_WAYLAND_CLIPBOARD)) \
1319 && defined(USE_SYSTEM)) || defined(PROTO)
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01001320 int
1321clip_gen_owner_exists(Clipboard_T *cbd UNUSED)
1322{
1323#ifdef FEAT_XCLIPBOARD
1324# ifdef FEAT_GUI_GTK
1325 if (gui.in_use)
1326 return clip_gtk_owner_exists(cbd);
1327 else
1328# endif
Foxe Chenb90c2392025-06-27 21:10:35 +02001329 {
1330 if (clipmethod == CLIPMETHOD_WAYLAND)
1331 {
1332#ifdef FEAT_WAYLAND_CLIPBOARD
1333 return clip_wl_owner_exists(cbd);
1334#endif
1335 }
1336 else if (clipmethod == CLIPMETHOD_X11)
1337 {
1338#ifdef FEAT_XCLIPBOARD
1339 return clip_x11_owner_exists(cbd);
1340#endif
1341 }
1342 else
1343 return FALSE;
1344 }
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01001345#else
1346 return TRUE;
1347#endif
1348}
1349#endif
1350
1351/*
1352 * Extract the items in the 'clipboard' option and set global values.
1353 * Return an error message or NULL for success.
1354 */
1355 char *
Yegappan Lakshmananaf936912023-02-20 12:16:39 +00001356did_set_clipboard(optset_T *args UNUSED)
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01001357{
1358 int new_unnamed = 0;
1359 int new_autoselect_star = FALSE;
1360 int new_autoselect_plus = FALSE;
1361 int new_autoselectml = FALSE;
1362 int new_html = FALSE;
1363 regprog_T *new_exclude_prog = NULL;
1364 char *errmsg = NULL;
1365 char_u *p;
1366
1367 for (p = p_cb; *p != NUL; )
1368 {
Yee Cheng Chin900894b2023-09-29 20:42:32 +02001369 // Note: Keep this in sync with p_cb_values.
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01001370 if (STRNCMP(p, "unnamed", 7) == 0 && (p[7] == ',' || p[7] == NUL))
1371 {
1372 new_unnamed |= CLIP_UNNAMED;
1373 p += 7;
1374 }
1375 else if (STRNCMP(p, "unnamedplus", 11) == 0
1376 && (p[11] == ',' || p[11] == NUL))
1377 {
1378 new_unnamed |= CLIP_UNNAMED_PLUS;
1379 p += 11;
1380 }
1381 else if (STRNCMP(p, "autoselect", 10) == 0
1382 && (p[10] == ',' || p[10] == NUL))
1383 {
1384 new_autoselect_star = TRUE;
1385 p += 10;
1386 }
1387 else if (STRNCMP(p, "autoselectplus", 14) == 0
1388 && (p[14] == ',' || p[14] == NUL))
1389 {
1390 new_autoselect_plus = TRUE;
1391 p += 14;
1392 }
1393 else if (STRNCMP(p, "autoselectml", 12) == 0
1394 && (p[12] == ',' || p[12] == NUL))
1395 {
1396 new_autoselectml = TRUE;
1397 p += 12;
1398 }
1399 else if (STRNCMP(p, "html", 4) == 0 && (p[4] == ',' || p[4] == NUL))
1400 {
1401 new_html = TRUE;
1402 p += 4;
1403 }
1404 else if (STRNCMP(p, "exclude:", 8) == 0 && new_exclude_prog == NULL)
1405 {
1406 p += 8;
1407 new_exclude_prog = vim_regcomp(p, RE_MAGIC);
1408 if (new_exclude_prog == NULL)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001409 errmsg = e_invalid_argument;
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01001410 break;
1411 }
1412 else
1413 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001414 errmsg = e_invalid_argument;
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01001415 break;
1416 }
1417 if (*p == ',')
1418 ++p;
1419 }
1420 if (errmsg == NULL)
1421 {
Bram Moolenaar07188fc2020-06-05 20:03:16 +02001422 if (global_busy)
1423 // clip_unnamed will be reset to clip_unnamed_saved
1424 // at end_global_changes
1425 clip_unnamed_saved = new_unnamed;
1426 else
1427 clip_unnamed = new_unnamed;
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01001428 clip_autoselect_star = new_autoselect_star;
1429 clip_autoselect_plus = new_autoselect_plus;
1430 clip_autoselectml = new_autoselectml;
1431 clip_html = new_html;
1432 vim_regfree(clip_exclude_prog);
1433 clip_exclude_prog = new_exclude_prog;
1434#ifdef FEAT_GUI_GTK
1435 if (gui.in_use)
1436 {
lilydjwg94ff09a2024-01-29 20:14:01 +01001437 gui_gtk_set_selection_targets((GdkAtom)GDK_SELECTION_PRIMARY);
1438 gui_gtk_set_selection_targets((GdkAtom)clip_plus.gtk_sel_atom);
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01001439 gui_gtk_set_dnd_targets();
1440 }
1441#endif
1442 }
1443 else
1444 vim_regfree(new_exclude_prog);
1445
1446 return errmsg;
1447}
1448
1449/*
1450 * Stuff for the X clipboard. Shared between VMS and Unix.
1451 */
1452
1453#if defined(FEAT_XCLIPBOARD) || defined(FEAT_GUI_X11) || defined(PROTO)
1454# include <X11/Xatom.h>
1455# include <X11/Intrinsic.h>
1456
1457/*
1458 * Open the application context (if it hasn't been opened yet).
Bram Moolenaar0b962e52022-04-03 18:02:37 +01001459 * Used for Motif GUI and the xterm clipboard.
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01001460 */
1461 void
1462open_app_context(void)
1463{
1464 if (app_context == NULL)
1465 {
1466 XtToolkitInitialize();
1467 app_context = XtCreateApplicationContext();
1468 }
1469}
1470
1471static Atom vim_atom; // Vim's own special selection format
1472static Atom vimenc_atom; // Vim's extended selection format
1473static Atom utf8_atom;
1474static Atom compound_text_atom;
1475static Atom text_atom;
1476static Atom targets_atom;
1477static Atom timestamp_atom; // Used to get a timestamp
1478
1479 void
1480x11_setup_atoms(Display *dpy)
1481{
1482 vim_atom = XInternAtom(dpy, VIM_ATOM_NAME, False);
1483 vimenc_atom = XInternAtom(dpy, VIMENC_ATOM_NAME,False);
1484 utf8_atom = XInternAtom(dpy, "UTF8_STRING", False);
1485 compound_text_atom = XInternAtom(dpy, "COMPOUND_TEXT", False);
1486 text_atom = XInternAtom(dpy, "TEXT", False);
1487 targets_atom = XInternAtom(dpy, "TARGETS", False);
1488 clip_star.sel_atom = XA_PRIMARY;
1489 clip_plus.sel_atom = XInternAtom(dpy, "CLIPBOARD", False);
1490 timestamp_atom = XInternAtom(dpy, "TIMESTAMP", False);
1491}
1492
1493/*
1494 * X Selection stuff, for cutting and pasting text to other windows.
1495 */
1496
1497 static Boolean
1498clip_x11_convert_selection_cb(
1499 Widget w UNUSED,
1500 Atom *sel_atom,
1501 Atom *target,
1502 Atom *type,
1503 XtPointer *value,
1504 long_u *length,
1505 int *format)
1506{
1507 static char_u *save_result = NULL;
1508 static long_u save_length = 0;
1509 char_u *string;
1510 int motion_type;
1511 Clipboard_T *cbd;
1512 int i;
1513
1514 if (*sel_atom == clip_plus.sel_atom)
1515 cbd = &clip_plus;
1516 else
1517 cbd = &clip_star;
1518
1519 if (!cbd->owned)
1520 return False; // Shouldn't ever happen
1521
1522 // requestor wants to know what target types we support
1523 if (*target == targets_atom)
1524 {
1525 static Atom array[7];
1526
1527 *value = (XtPointer)array;
1528 i = 0;
1529 array[i++] = targets_atom;
1530 array[i++] = vimenc_atom;
1531 array[i++] = vim_atom;
1532 if (enc_utf8)
1533 array[i++] = utf8_atom;
1534 array[i++] = XA_STRING;
1535 array[i++] = text_atom;
1536 array[i++] = compound_text_atom;
1537
1538 *type = XA_ATOM;
1539 // This used to be: *format = sizeof(Atom) * 8; but that caused
1540 // crashes on 64 bit machines. (Peter Derr)
1541 *format = 32;
1542 *length = i;
1543 return True;
1544 }
1545
1546 if ( *target != XA_STRING
1547 && *target != vimenc_atom
1548 && (*target != utf8_atom || !enc_utf8)
1549 && *target != vim_atom
1550 && *target != text_atom
1551 && *target != compound_text_atom)
1552 return False;
1553
1554 clip_get_selection(cbd);
1555 motion_type = clip_convert_selection(&string, length, cbd);
1556 if (motion_type < 0)
1557 return False;
1558
1559 // For our own format, the first byte contains the motion type
1560 if (*target == vim_atom)
1561 (*length)++;
1562
1563 // Our own format with encoding: motion 'encoding' NUL text
1564 if (*target == vimenc_atom)
1565 *length += STRLEN(p_enc) + 2;
1566
1567 if (save_length < *length || save_length / 2 >= *length)
1568 *value = XtRealloc((char *)save_result, (Cardinal)*length + 1);
1569 else
1570 *value = save_result;
1571 if (*value == NULL)
1572 {
1573 vim_free(string);
1574 return False;
1575 }
1576 save_result = (char_u *)*value;
1577 save_length = *length;
1578
1579 if (*target == XA_STRING || (*target == utf8_atom && enc_utf8))
1580 {
1581 mch_memmove(save_result, string, (size_t)(*length));
1582 *type = *target;
1583 }
1584 else if (*target == compound_text_atom || *target == text_atom)
1585 {
1586 XTextProperty text_prop;
1587 char *string_nt = (char *)save_result;
1588 int conv_result;
1589
1590 // create NUL terminated string which XmbTextListToTextProperty wants
1591 mch_memmove(string_nt, string, (size_t)*length);
1592 string_nt[*length] = NUL;
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00001593 conv_result = XmbTextListToTextProperty(X_DISPLAY, &string_nt,
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01001594 1, XCompoundTextStyle, &text_prop);
1595 if (conv_result != Success)
1596 {
1597 vim_free(string);
1598 return False;
1599 }
1600 *value = (XtPointer)(text_prop.value); // from plain text
1601 *length = text_prop.nitems;
1602 *type = compound_text_atom;
1603 XtFree((char *)save_result);
1604 save_result = (char_u *)*value;
1605 save_length = *length;
1606 }
1607 else if (*target == vimenc_atom)
1608 {
1609 int l = STRLEN(p_enc);
1610
1611 save_result[0] = motion_type;
1612 STRCPY(save_result + 1, p_enc);
1613 mch_memmove(save_result + l + 2, string, (size_t)(*length - l - 2));
1614 *type = vimenc_atom;
1615 }
1616 else
1617 {
1618 save_result[0] = motion_type;
1619 mch_memmove(save_result + 1, string, (size_t)(*length - 1));
1620 *type = vim_atom;
1621 }
1622 *format = 8; // 8 bits per char
1623 vim_free(string);
1624 return True;
1625}
1626
1627 static void
1628clip_x11_lose_ownership_cb(Widget w UNUSED, Atom *sel_atom)
1629{
1630 if (*sel_atom == clip_plus.sel_atom)
1631 clip_lose_selection(&clip_plus);
1632 else
1633 clip_lose_selection(&clip_star);
1634}
1635
1636 static void
1637clip_x11_notify_cb(Widget w UNUSED, Atom *sel_atom UNUSED, Atom *target UNUSED)
1638{
1639 // To prevent automatically freeing the selection value.
1640}
1641
1642/*
1643 * Property callback to get a timestamp for XtOwnSelection.
1644 */
Dominique Pelle748b3082022-01-08 12:41:16 +00001645# if (defined(FEAT_X11) && defined(FEAT_XCLIPBOARD)) || defined(PROTO)
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01001646 static void
1647clip_x11_timestamp_cb(
1648 Widget w,
1649 XtPointer n UNUSED,
1650 XEvent *event,
1651 Boolean *cont UNUSED)
1652{
1653 Atom actual_type;
1654 int format;
1655 unsigned long nitems, bytes_after;
1656 unsigned char *prop=NULL;
1657 XPropertyEvent *xproperty=&event->xproperty;
1658
1659 // Must be a property notify, state can't be Delete (True), has to be
1660 // one of the supported selection types.
1661 if (event->type != PropertyNotify || xproperty->state
1662 || (xproperty->atom != clip_star.sel_atom
1663 && xproperty->atom != clip_plus.sel_atom))
1664 return;
1665
1666 if (XGetWindowProperty(xproperty->display, xproperty->window,
1667 xproperty->atom, 0, 0, False, timestamp_atom, &actual_type, &format,
1668 &nitems, &bytes_after, &prop))
1669 return;
1670
1671 if (prop)
1672 XFree(prop);
1673
1674 // Make sure the property type is "TIMESTAMP" and it's 32 bits.
1675 if (actual_type != timestamp_atom || format != 32)
1676 return;
1677
1678 // Get the selection, using the event timestamp.
1679 if (XtOwnSelection(w, xproperty->atom, xproperty->time,
1680 clip_x11_convert_selection_cb, clip_x11_lose_ownership_cb,
1681 clip_x11_notify_cb) == OK)
1682 {
1683 // Set the "owned" flag now, there may have been a call to
1684 // lose_ownership_cb in between.
1685 if (xproperty->atom == clip_plus.sel_atom)
1686 clip_plus.owned = TRUE;
1687 else
1688 clip_star.owned = TRUE;
1689 }
1690}
1691
1692 void
1693x11_setup_selection(Widget w)
1694{
1695 XtAddEventHandler(w, PropertyChangeMask, False,
1696 /*(XtEventHandler)*/clip_x11_timestamp_cb, (XtPointer)NULL);
1697}
Dominique Pelle748b3082022-01-08 12:41:16 +00001698# endif
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01001699
1700 static void
1701clip_x11_request_selection_cb(
1702 Widget w UNUSED,
1703 XtPointer success,
1704 Atom *sel_atom,
1705 Atom *type,
1706 XtPointer value,
1707 long_u *length,
1708 int *format)
1709{
1710 int motion_type = MAUTO;
1711 long_u len;
1712 char_u *p;
1713 char **text_list = NULL;
1714 Clipboard_T *cbd;
1715 char_u *tmpbuf = NULL;
1716
1717 if (*sel_atom == clip_plus.sel_atom)
1718 cbd = &clip_plus;
1719 else
1720 cbd = &clip_star;
1721
1722 if (value == NULL || *length == 0)
1723 {
1724 clip_free_selection(cbd); // nothing received, clear register
1725 *(int *)success = FALSE;
1726 return;
1727 }
1728 p = (char_u *)value;
1729 len = *length;
1730 if (*type == vim_atom)
1731 {
1732 motion_type = *p++;
1733 len--;
1734 }
1735
1736 else if (*type == vimenc_atom)
1737 {
1738 char_u *enc;
1739 vimconv_T conv;
1740 int convlen;
1741
1742 motion_type = *p++;
1743 --len;
1744
1745 enc = p;
1746 p += STRLEN(p) + 1;
1747 len -= p - enc;
1748
1749 // If the encoding of the text is different from 'encoding', attempt
1750 // converting it.
1751 conv.vc_type = CONV_NONE;
1752 convert_setup(&conv, enc, p_enc);
1753 if (conv.vc_type != CONV_NONE)
1754 {
1755 convlen = len; // Need to use an int here.
1756 tmpbuf = string_convert(&conv, p, &convlen);
1757 len = convlen;
1758 if (tmpbuf != NULL)
1759 p = tmpbuf;
1760 convert_setup(&conv, NULL, NULL);
1761 }
1762 }
1763
1764 else if (*type == compound_text_atom
1765 || *type == utf8_atom
1766 || (enc_dbcs != 0 && *type == text_atom))
1767 {
1768 XTextProperty text_prop;
1769 int n_text = 0;
1770 int status;
1771
1772 text_prop.value = (unsigned char *)value;
1773 text_prop.encoding = *type;
1774 text_prop.format = *format;
1775 text_prop.nitems = len;
1776#if defined(X_HAVE_UTF8_STRING)
1777 if (*type == utf8_atom)
1778 status = Xutf8TextPropertyToTextList(X_DISPLAY, &text_prop,
1779 &text_list, &n_text);
1780 else
1781#endif
1782 status = XmbTextPropertyToTextList(X_DISPLAY, &text_prop,
1783 &text_list, &n_text);
1784 if (status != Success || n_text < 1)
1785 {
1786 *(int *)success = FALSE;
1787 return;
1788 }
1789 p = (char_u *)text_list[0];
1790 len = STRLEN(p);
1791 }
1792 clip_yank_selection(motion_type, p, (long)len, cbd);
1793
1794 if (text_list != NULL)
1795 XFreeStringList(text_list);
1796 vim_free(tmpbuf);
1797 XtFree((char *)value);
1798 *(int *)success = TRUE;
1799}
1800
1801 void
1802clip_x11_request_selection(
1803 Widget myShell,
1804 Display *dpy,
1805 Clipboard_T *cbd)
1806{
1807 XEvent event;
1808 Atom type;
1809 static int success;
1810 int i;
1811 time_t start_time;
1812 int timed_out = FALSE;
1813
1814 for (i = 0; i < 6; i++)
1815 {
1816 switch (i)
1817 {
1818 case 0: type = vimenc_atom; break;
1819 case 1: type = vim_atom; break;
1820 case 2: type = utf8_atom; break;
1821 case 3: type = compound_text_atom; break;
1822 case 4: type = text_atom; break;
1823 default: type = XA_STRING;
1824 }
1825 if (type == utf8_atom
1826# if defined(X_HAVE_UTF8_STRING)
1827 && !enc_utf8
1828# endif
1829 )
1830 // Only request utf-8 when 'encoding' is utf8 and
1831 // Xutf8TextPropertyToTextList is available.
1832 continue;
1833 success = MAYBE;
1834 XtGetSelectionValue(myShell, cbd->sel_atom, type,
1835 clip_x11_request_selection_cb, (XtPointer)&success, CurrentTime);
1836
1837 // Make sure the request for the selection goes out before waiting for
1838 // a response.
1839 XFlush(dpy);
1840
1841 /*
1842 * Wait for result of selection request, otherwise if we type more
1843 * characters, then they will appear before the one that requested the
1844 * paste! Don't worry, we will catch up with any other events later.
1845 */
1846 start_time = time(NULL);
1847 while (success == MAYBE)
1848 {
1849 if (XCheckTypedEvent(dpy, PropertyNotify, &event)
1850 || XCheckTypedEvent(dpy, SelectionNotify, &event)
1851 || XCheckTypedEvent(dpy, SelectionRequest, &event))
1852 {
1853 // This is where clip_x11_request_selection_cb() should be
1854 // called. It may actually happen a bit later, so we loop
1855 // until "success" changes.
1856 // We may get a SelectionRequest here and if we don't handle
1857 // it we hang. KDE klipper does this, for example.
1858 // We need to handle a PropertyNotify for large selections.
1859 XtDispatchEvent(&event);
1860 continue;
1861 }
1862
1863 // Time out after 2 to 3 seconds to avoid that we hang when the
1864 // other process doesn't respond. Note that the SelectionNotify
1865 // event may still come later when the selection owner comes back
1866 // to life and the text gets inserted unexpectedly. Don't know
1867 // why that happens or how to avoid that :-(.
1868 if (time(NULL) > start_time + 2)
1869 {
1870 timed_out = TRUE;
1871 break;
1872 }
1873
1874 // Do we need this? Probably not.
1875 XSync(dpy, False);
1876
1877 // Wait for 1 msec to avoid that we eat up all CPU time.
1878 ui_delay(1L, TRUE);
1879 }
1880
1881 if (success == TRUE)
1882 return;
1883
1884 // don't do a retry with another type after timing out, otherwise we
1885 // hang for 15 seconds.
1886 if (timed_out)
1887 break;
1888 }
1889
1890 // Final fallback position - use the X CUT_BUFFER0 store
1891 yank_cut_buffer0(dpy, cbd);
1892}
1893
1894 void
1895clip_x11_lose_selection(Widget myShell, Clipboard_T *cbd)
1896{
1897 XtDisownSelection(myShell, cbd->sel_atom,
1898 XtLastTimestampProcessed(XtDisplay(myShell)));
1899}
1900
1901 int
1902clip_x11_own_selection(Widget myShell, Clipboard_T *cbd)
1903{
1904 // When using the GUI we have proper timestamps, use the one of the last
1905 // event. When in the console we don't get events (the terminal gets
1906 // them), Get the time by a zero-length append, clip_x11_timestamp_cb will
1907 // be called with the current timestamp.
1908#ifdef FEAT_GUI
1909 if (gui.in_use)
1910 {
1911 if (XtOwnSelection(myShell, cbd->sel_atom,
1912 XtLastTimestampProcessed(XtDisplay(myShell)),
1913 clip_x11_convert_selection_cb, clip_x11_lose_ownership_cb,
1914 clip_x11_notify_cb) == False)
1915 return FAIL;
1916 }
1917 else
1918#endif
1919 {
1920 if (!XChangeProperty(XtDisplay(myShell), XtWindow(myShell),
1921 cbd->sel_atom, timestamp_atom, 32, PropModeAppend, NULL, 0))
1922 return FAIL;
1923 }
1924 // Flush is required in a terminal as nothing else is doing it.
1925 XFlush(XtDisplay(myShell));
1926 return OK;
1927}
1928
1929/*
1930 * Send the current selection to the clipboard. Do nothing for X because we
1931 * will fill in the selection only when requested by another app.
1932 */
1933 void
1934clip_x11_set_selection(Clipboard_T *cbd UNUSED)
1935{
1936}
1937
1938#endif
1939
1940#if defined(FEAT_XCLIPBOARD) || defined(FEAT_GUI_X11) \
1941 || defined(FEAT_GUI_GTK) || defined(PROTO)
1942/*
1943 * Get the contents of the X CUT_BUFFER0 and put it in "cbd".
1944 */
1945 void
1946yank_cut_buffer0(Display *dpy, Clipboard_T *cbd)
1947{
1948 int nbytes = 0;
1949 char_u *buffer = (char_u *)XFetchBuffer(dpy, &nbytes, 0);
1950
1951 if (nbytes > 0)
1952 {
1953 int done = FALSE;
1954
1955 // CUT_BUFFER0 is supposed to be always latin1. Convert to 'enc' when
1956 // using a multi-byte encoding. Conversion between two 8-bit
1957 // character sets usually fails and the text might actually be in
1958 // 'enc' anyway.
1959 if (has_mbyte)
1960 {
1961 char_u *conv_buf;
1962 vimconv_T vc;
1963
1964 vc.vc_type = CONV_NONE;
1965 if (convert_setup(&vc, (char_u *)"latin1", p_enc) == OK)
1966 {
1967 conv_buf = string_convert(&vc, buffer, &nbytes);
1968 if (conv_buf != NULL)
1969 {
1970 clip_yank_selection(MCHAR, conv_buf, (long)nbytes, cbd);
1971 vim_free(conv_buf);
1972 done = TRUE;
1973 }
1974 convert_setup(&vc, NULL, NULL);
1975 }
1976 }
1977 if (!done) // use the text without conversion
1978 clip_yank_selection(MCHAR, buffer, (long)nbytes, cbd);
1979 XFree((void *)buffer);
1980 if (p_verbose > 0)
1981 {
1982 verbose_enter();
1983 verb_msg(_("Used CUT_BUFFER0 instead of empty selection"));
1984 verbose_leave();
1985 }
1986 }
1987}
1988#endif
1989
1990/*
1991 * SELECTION / PRIMARY ('*')
1992 *
1993 * Text selection stuff that uses the GUI selection register '*'. When using a
1994 * GUI this may be text from another window, otherwise it is the last text we
1995 * had highlighted with VIsual mode. With mouse support, clicking the middle
1996 * button performs the paste, otherwise you will need to do <"*p>. "
1997 * If not under X, it is synonymous with the clipboard register '+'.
1998 *
1999 * X CLIPBOARD ('+')
2000 *
2001 * Text selection stuff that uses the GUI clipboard register '+'.
2002 * Under X, this matches the standard cut/paste buffer CLIPBOARD selection.
2003 * It will be used for unnamed cut/pasting is 'clipboard' contains "unnamed",
2004 * otherwise you will need to do <"+p>. "
2005 * If not under X, it is synonymous with the selection register '*'.
2006 */
2007
2008/*
2009 * Routine to export any final X selection we had to the environment
2010 * so that the text is still available after Vim has exited. X selections
2011 * only exist while the owning application exists, so we write to the
2012 * permanent (while X runs) store CUT_BUFFER0.
2013 * Dump the CLIPBOARD selection if we own it (it's logically the more
2014 * 'permanent' of the two), otherwise the PRIMARY one.
2015 * For now, use a hard-coded sanity limit of 1Mb of data.
2016 */
2017#if (defined(FEAT_X11) && defined(FEAT_CLIPBOARD)) || defined(PROTO)
2018 void
2019x11_export_final_selection(void)
2020{
2021 Display *dpy;
2022 char_u *str = NULL;
2023 long_u len = 0;
2024 int motion_type = -1;
2025
2026# ifdef FEAT_GUI
2027 if (gui.in_use)
2028 dpy = X_DISPLAY;
2029 else
2030# endif
2031# ifdef FEAT_XCLIPBOARD
2032 dpy = xterm_dpy;
2033# else
2034 return;
2035# endif
2036
2037 // Get selection to export
2038 if (clip_plus.owned)
2039 motion_type = clip_convert_selection(&str, &len, &clip_plus);
2040 else if (clip_star.owned)
2041 motion_type = clip_convert_selection(&str, &len, &clip_star);
2042
2043 // Check it's OK
2044 if (dpy != NULL && str != NULL && motion_type >= 0
2045 && len < 1024*1024 && len > 0)
2046 {
2047 int ok = TRUE;
2048
2049 // The CUT_BUFFER0 is supposed to always contain latin1. Convert from
2050 // 'enc' when it is a multi-byte encoding. When 'enc' is an 8-bit
2051 // encoding conversion usually doesn't work, so keep the text as-is.
2052 if (has_mbyte)
2053 {
2054 vimconv_T vc;
2055
2056 vc.vc_type = CONV_NONE;
2057 if (convert_setup(&vc, p_enc, (char_u *)"latin1") == OK)
2058 {
2059 int intlen = len;
2060 char_u *conv_str;
2061
2062 vc.vc_fail = TRUE;
2063 conv_str = string_convert(&vc, str, &intlen);
2064 len = intlen;
2065 if (conv_str != NULL)
2066 {
2067 vim_free(str);
2068 str = conv_str;
2069 }
2070 else
2071 {
2072 ok = FALSE;
2073 }
2074 convert_setup(&vc, NULL, NULL);
2075 }
2076 else
2077 {
2078 ok = FALSE;
2079 }
2080 }
2081
2082 // Do not store the string if conversion failed. Better to use any
2083 // other selection than garbled text.
2084 if (ok)
2085 {
2086 XStoreBuffer(dpy, (char *)str, (int)len, 0);
2087 XFlush(dpy);
2088 }
2089 }
2090
2091 vim_free(str);
2092}
2093#endif
2094
2095 void
2096clip_free_selection(Clipboard_T *cbd)
2097{
2098 yankreg_T *y_ptr = get_y_current();
2099
2100 if (cbd == &clip_plus)
2101 set_y_current(get_y_register(PLUS_REGISTER));
2102 else
2103 set_y_current(get_y_register(STAR_REGISTER));
2104 free_yank_all();
2105 get_y_current()->y_size = 0;
2106 set_y_current(y_ptr);
2107}
2108
2109/*
2110 * Get the selected text and put it in register '*' or '+'.
2111 */
2112 void
2113clip_get_selection(Clipboard_T *cbd)
2114{
2115 yankreg_T *old_y_previous, *old_y_current;
2116 pos_T old_cursor;
2117 pos_T old_visual;
2118 int old_visual_mode;
2119 colnr_T old_curswant;
2120 int old_set_curswant;
2121 pos_T old_op_start, old_op_end;
2122 oparg_T oa;
2123 cmdarg_T ca;
2124
2125 if (cbd->owned)
2126 {
2127 if ((cbd == &clip_plus
2128 && get_y_register(PLUS_REGISTER)->y_array != NULL)
2129 || (cbd == &clip_star
2130 && get_y_register(STAR_REGISTER)->y_array != NULL))
2131 return;
2132
Bram Moolenaarfccbf062020-11-26 20:34:00 +01002133 // Avoid triggering autocmds such as TextYankPost.
2134 block_autocmds();
2135
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01002136 // Get the text between clip_star.start & clip_star.end
2137 old_y_previous = get_y_previous();
2138 old_y_current = get_y_current();
2139 old_cursor = curwin->w_cursor;
2140 old_curswant = curwin->w_curswant;
2141 old_set_curswant = curwin->w_set_curswant;
2142 old_op_start = curbuf->b_op_start;
2143 old_op_end = curbuf->b_op_end;
2144 old_visual = VIsual;
2145 old_visual_mode = VIsual_mode;
2146 clear_oparg(&oa);
2147 oa.regname = (cbd == &clip_plus ? '+' : '*');
2148 oa.op_type = OP_YANK;
Bram Moolenaara80faa82020-04-12 19:37:17 +02002149 CLEAR_FIELD(ca);
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01002150 ca.oap = &oa;
2151 ca.cmdchar = 'y';
2152 ca.count1 = 1;
2153 ca.retval = CA_NO_ADJ_OP_END;
2154 do_pending_operator(&ca, 0, TRUE);
Bram Moolenaara7251492021-01-02 16:53:13 +01002155
2156 // restore things
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01002157 set_y_previous(old_y_previous);
2158 set_y_current(old_y_current);
2159 curwin->w_cursor = old_cursor;
2160 changed_cline_bef_curs(); // need to update w_virtcol et al
2161 curwin->w_curswant = old_curswant;
2162 curwin->w_set_curswant = old_set_curswant;
2163 curbuf->b_op_start = old_op_start;
2164 curbuf->b_op_end = old_op_end;
2165 VIsual = old_visual;
2166 VIsual_mode = old_visual_mode;
Bram Moolenaarfccbf062020-11-26 20:34:00 +01002167
2168 unblock_autocmds();
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01002169 }
2170 else if (!is_clipboard_needs_update())
2171 {
2172 clip_free_selection(cbd);
2173
2174 // Try to get selected text from another window
2175 clip_gen_request_selection(cbd);
2176 }
2177}
2178
2179/*
2180 * Convert from the GUI selection string into the '*'/'+' register.
2181 */
2182 void
2183clip_yank_selection(
2184 int type,
2185 char_u *str,
2186 long len,
2187 Clipboard_T *cbd)
2188{
2189 yankreg_T *y_ptr;
2190
2191 if (cbd == &clip_plus)
2192 y_ptr = get_y_register(PLUS_REGISTER);
2193 else
2194 y_ptr = get_y_register(STAR_REGISTER);
2195
2196 clip_free_selection(cbd);
2197
Bram Moolenaar6e0b5532021-06-04 17:11:47 +02002198 str_to_reg(y_ptr, type, str, len, -1, FALSE);
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01002199}
2200
2201/*
2202 * Convert the '*'/'+' register into a GUI selection string returned in *str
2203 * with length *len.
2204 * Returns the motion type, or -1 for failure.
2205 */
2206 int
2207clip_convert_selection(char_u **str, long_u *len, Clipboard_T *cbd)
2208{
2209 char_u *p;
2210 int lnum;
2211 int i, j;
2212 int_u eolsize;
2213 yankreg_T *y_ptr;
2214
2215 if (cbd == &clip_plus)
2216 y_ptr = get_y_register(PLUS_REGISTER);
2217 else
2218 y_ptr = get_y_register(STAR_REGISTER);
2219
2220# ifdef USE_CRNL
2221 eolsize = 2;
2222# else
2223 eolsize = 1;
2224# endif
2225
2226 *str = NULL;
2227 *len = 0;
2228 if (y_ptr->y_array == NULL)
2229 return -1;
2230
2231 for (i = 0; i < y_ptr->y_size; i++)
John Marriott79f6ffd2024-10-31 10:06:54 +01002232 *len += (long_u)y_ptr->y_array[i].length + eolsize;
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01002233
2234 // Don't want newline character at end of last line if we're in MCHAR mode.
2235 if (y_ptr->y_type == MCHAR && *len >= eolsize)
2236 *len -= eolsize;
2237
2238 p = *str = alloc(*len + 1); // add one to avoid zero
2239 if (p == NULL)
2240 return -1;
2241 lnum = 0;
2242 for (i = 0, j = 0; i < (int)*len; i++, j++)
2243 {
John Marriott79f6ffd2024-10-31 10:06:54 +01002244 if (y_ptr->y_array[lnum].string[j] == '\n')
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01002245 p[i] = NUL;
John Marriott79f6ffd2024-10-31 10:06:54 +01002246 else if (y_ptr->y_array[lnum].string[j] == NUL)
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01002247 {
2248# ifdef USE_CRNL
2249 p[i++] = '\r';
2250# endif
2251 p[i] = '\n';
2252 lnum++;
2253 j = -1;
2254 }
2255 else
John Marriott79f6ffd2024-10-31 10:06:54 +01002256 p[i] = y_ptr->y_array[lnum].string[j];
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01002257 }
2258 return y_ptr->y_type;
2259}
2260
2261/*
2262 * When "regname" is a clipboard register, obtain the selection. If it's not
2263 * available return zero, otherwise return "regname".
2264 */
2265 int
2266may_get_selection(int regname)
2267{
2268 if (regname == '*')
2269 {
2270 if (!clip_star.available)
2271 regname = 0;
2272 else
2273 clip_get_selection(&clip_star);
2274 }
2275 else if (regname == '+')
2276 {
2277 if (!clip_plus.available)
2278 regname = 0;
2279 else
2280 clip_get_selection(&clip_plus);
2281 }
2282 return regname;
2283}
2284
2285/*
2286 * If we have written to a clipboard register, send the text to the clipboard.
2287 */
2288 void
2289may_set_selection(void)
2290{
2291 if ((get_y_current() == get_y_register(STAR_REGISTER))
2292 && clip_star.available)
2293 {
2294 clip_own_selection(&clip_star);
2295 clip_gen_set_selection(&clip_star);
2296 }
2297 else if ((get_y_current() == get_y_register(PLUS_REGISTER))
2298 && clip_plus.available)
2299 {
2300 clip_own_selection(&clip_plus);
2301 clip_gen_set_selection(&clip_plus);
2302 }
2303}
2304
2305/*
2306 * Adjust the register name pointed to with "rp" for the clipboard being
2307 * used always and the clipboard being available.
2308 */
2309 void
2310adjust_clip_reg(int *rp)
2311{
2312 // If no reg. specified, and "unnamed" or "unnamedplus" is in 'clipboard',
2313 // use '*' or '+' reg, respectively. "unnamedplus" prevails.
2314 if (*rp == 0 && (clip_unnamed != 0 || clip_unnamed_saved != 0))
2315 {
2316 if (clip_unnamed != 0)
2317 *rp = ((clip_unnamed & CLIP_UNNAMED_PLUS) && clip_plus.available)
2318 ? '+' : '*';
2319 else
2320 *rp = ((clip_unnamed_saved & CLIP_UNNAMED_PLUS)
2321 && clip_plus.available) ? '+' : '*';
2322 }
Christian Brabandt45e07042024-11-11 20:52:55 +01002323 if ((!clip_star.available && *rp == '*') ||
Naruhiko Nishinoc2a90002025-05-04 20:05:47 +02002324 (!clip_plus.available && *rp == '+'))
Christian Brabandt45e07042024-11-11 20:52:55 +01002325 {
2326 msg_warn_missing_clipboard();
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01002327 *rp = 0;
Christian Brabandt45e07042024-11-11 20:52:55 +01002328 }
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01002329}
2330
Foxe Chenb90c2392025-06-27 21:10:35 +02002331#if defined(FEAT_WAYLAND_CLIPBOARD) || defined(PROTO)
2332
2333/*
2334 * Read data from a file descriptor and write it to the given clipboard.
2335 */
2336 static void
2337clip_wl_receive_data(Clipboard_T *cbd, const char *mime_type, int fd)
2338{
2339 char_u *start, *buf, *tmp, *final, *enc;
2340 int motion_type = MAUTO;
2341 ssize_t r = 0;
2342 size_t total = 0, max_total = 4096; // Initial buffer size, 4096
2343 // bytes seems reasonable.
2344#ifndef HAVE_SELECT
2345 struct pollfd pfd
2346
2347 pfd.fd = fd,
2348 pfd.events = POLLIN
2349#else
2350 fd_set rfds;
2351 struct timeval tv;
2352
2353 FD_ZERO(&rfds);
2354 FD_SET(fd, &rfds);
2355 tv.tv_sec = 0;
2356 tv.tv_usec = p_wtm * 1000;
2357#endif
2358
2359 // Make pipe (read end) non-blocking
2360 if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK) == -1)
2361 return;
2362
2363 if ((buf = alloc_clear(max_total)) == NULL)
2364 return;
2365 start = buf;
2366
2367 // Only poll before reading when we first start, then we do non-blocking
2368 // reads and check for EAGAIN or EINTR to signal to poll again.
2369 goto poll_data;
2370
2371 while (errno = 0, TRUE)
2372 {
2373 r = read(fd, start, max_total - 1 - total);
2374
2375 if (r == 0)
2376 break;
2377 else if (r < 0)
2378 {
2379 if (errno == EAGAIN || errno == EINTR)
2380 {
2381poll_data:
2382#ifndef HAVE_SELECT
2383 if (poll(&pfd, 1, p_wtm) > 0)
2384#else
2385 if (select(fd + 1, &rfds, NULL, NULL, &tv) > 0)
2386#endif
2387 continue;
2388 }
2389 break;
2390 }
2391
2392 start += r;
2393 total += (size_t)r;
2394
2395 // Realloc if we are at the end of the buffer
2396 if (total >= max_total - 1)
2397 {
2398 tmp = vim_realloc(buf, max_total * 2);
2399 if (tmp == NULL)
2400 break;
2401 max_total *= 2; // Double buffer size each time
2402 buf = tmp;
2403 start = buf + total;
2404 // Zero out the newly allocated memory part
2405 vim_memset(buf + total, 0, max_total - total);
2406 }
2407 }
2408
2409 if (total == 0)
2410 {
2411 clip_free_selection(cbd); // Nothing received, clear register
2412 vim_free(buf);
2413 return;
2414 }
2415
2416 final = buf;
2417
2418 if (STRCMP(mime_type, VIM_ATOM_NAME) == 0 && total >= 2)
2419 {
2420 motion_type = *final++;;
2421 total--;
2422 }
2423 else if (STRCMP(mime_type, VIMENC_ATOM_NAME) == 0 && total >= 3)
2424 {
2425 vimconv_T conv;
2426 int convlen;
2427
2428 // first byte is motion type
2429 motion_type = *final++;
2430 total--;
2431
2432 // Get encoding of selection
2433 enc = final;
2434
2435 // Skip the encoding type including null terminator in final text
2436 final += STRLEN(final) + 1;
2437
2438 // Subtract pointers to get length of encoding;
2439 total -= final - enc;
2440
2441 conv.vc_type = CONV_NONE;
2442 convert_setup(&conv, enc, p_enc);
2443 if (conv.vc_type != CONV_NONE)
2444 {
2445 convlen = total;
2446 tmp = string_convert(&conv, final, &convlen);
2447 total = convlen;
2448 if (tmp != NULL)
2449 final = tmp;
2450 convert_setup(&conv, NULL, NULL);
2451 }
2452 }
2453
2454 clip_yank_selection(motion_type, final, (long)total, cbd);
2455 vim_free(buf);
2456}
2457
2458/*
2459 * Get the current selection and fill the respective register for cbd with the
2460 * data.
2461 */
2462 void
2463clip_wl_request_selection(Clipboard_T *cbd)
2464{
2465 wayland_selection_T selection;
2466 garray_T *mime_types;
2467 int len;
2468 int fd;
2469 const char *chosen_mime = NULL;
2470
2471 if (cbd == &clip_star)
2472 selection = WAYLAND_SELECTION_PRIMARY;
2473 else if (cbd == &clip_plus)
2474 selection = WAYLAND_SELECTION_REGULAR;
2475 else
2476 return;
2477
2478 // Get mime types that the source client offers
2479 mime_types = wayland_cb_get_mime_types(selection);
2480
2481 if (mime_types == NULL || mime_types->ga_len == 0)
2482 {
2483 // Selection is empty/cleared
2484 clip_free_selection(cbd);
2485 return;
2486 }
2487
2488 len = ARRAY_LENGTH(supported_mimes);
2489
2490 // Loop through and pick the one we want to receive from
2491 for (int i = 0; i < len && chosen_mime == NULL; i++)
2492 {
2493 for (int k = 0; k < mime_types->ga_len && chosen_mime == NULL; k++)
2494 {
2495 char *mime_type = ((char**)mime_types->ga_data)[k];
2496
2497 if (STRCMP(mime_type, supported_mimes[i]) == 0)
2498 chosen_mime = supported_mimes[i];
2499 }
2500 }
2501 if (chosen_mime == NULL)
2502 return;
2503
2504 fd = wayland_cb_receive_data(chosen_mime, selection);
2505
2506 if (fd == -1)
2507 return;
2508
2509 // Start reading the file descriptor returned
2510 clip_wl_receive_data(cbd, chosen_mime, fd);
2511
2512 close(fd);
2513}
2514
2515/*
2516 * Write data from either the clip or plus register, depending on the given
2517 * selection, to the file descriptor that the receiving client will read from.
2518 */
2519 static void
2520clip_wl_send_data(
2521 const char *mime_type,
2522 int fd,
2523 wayland_selection_T selection)
2524{
2525 Clipboard_T *cbd;
2526 long_u length;
2527 char_u *string;
2528 ssize_t written = 0;
2529 size_t total = 0;
2530 int did_vimenc = TRUE;
2531 int did_motion_type = TRUE;
2532 int motion_type;
2533 int skip_len_check = FALSE;
2534#ifndef HAVE_SELECT
2535 struct pollfd pfd
2536
2537 pfd.fd = fd,
2538 pfd.events = POLLOUT
2539#else
2540 fd_set wfds;
2541 struct timeval tv;
2542
2543 FD_ZERO(&wfds);
2544 FD_SET(fd, &wfds);
2545 tv.tv_sec = 0;
2546 tv.tv_usec = p_wtm * 1000;
2547#endif
2548 if (selection == WAYLAND_SELECTION_REGULAR)
2549 cbd = &clip_plus;
2550 else if (selection == WAYLAND_SELECTION_PRIMARY)
2551 cbd = &clip_star;
2552 else
2553 return;
2554
2555 // Shouldn't happen unless there is a bug.
2556 if (!cbd->owned)
2557 return;
2558
2559 // Get the current selection
2560 clip_get_selection(cbd);
2561 motion_type = clip_convert_selection(&string, &length, cbd);
2562
2563 if (motion_type < 0)
2564 goto exit;
2565
2566 if (STRCMP(mime_type, VIMENC_ATOM_NAME) == 0)
2567 {
2568 did_vimenc = FALSE;
2569 did_motion_type = FALSE;
2570 }
2571 else if (STRCMP(mime_type, VIM_ATOM_NAME) == 0)
2572 did_motion_type = FALSE;
2573
2574 while ((total < (size_t)length || skip_len_check) &&
2575#ifndef HAVE_SELECT
2576 poll(&pfd, 1, p_wtm) > 0)
2577#else
2578 select(fd + 1, NULL, &wfds, NULL, &tv) > 0)
2579#endif
2580 {
2581 // First byte sent is motion type for vim specific formats
2582 if (!did_motion_type)
2583 {
2584 if (total == 1)
2585 {
2586 total = 0;
2587 did_motion_type = TRUE;
2588 continue;
2589 }
2590 // We cast to char so that we only send one byte
2591 written = write( fd, (char_u*)&motion_type, 1);
2592 skip_len_check = TRUE;
2593 }
2594 else if (!did_vimenc)
2595 {
2596 // For the vimenc format, after the first byte is the encoding type,
2597 // which is null terminated. Make sure we write that before writing
2598 // the actual selection.
2599 if (total == STRLEN(p_enc) + 1)
2600 {
2601 total = 0;
2602 did_vimenc = TRUE;
2603 continue;
2604 }
2605 // Include null terminator
2606 written = write(fd, p_enc + total, STRLEN(p_enc) + 1 - total);
2607 skip_len_check = TRUE;
2608 }
2609 else
2610 {
2611 // write the actual selection to the fd
2612 written = write(fd, string + total, length - total);
2613 if (skip_len_check)
2614 skip_len_check = FALSE;
2615 }
2616
2617 if (written == -1)
2618 break;
2619 total += written;
2620 }
2621exit:
2622 vim_free(string);
2623}
2624
2625/*
2626 * Called if another client gains ownership of the given selection. If so then
2627 * lose the selection internally.
2628 */
2629 static void
2630clip_wl_selection_cancelled(wayland_selection_T selection)
2631{
2632 if (selection == WAYLAND_SELECTION_REGULAR)
2633 clip_lose_selection(&clip_plus);
2634 else if (selection == WAYLAND_SELECTION_PRIMARY)
2635 clip_lose_selection(&clip_star);
2636}
2637
2638/*
2639 * Own the selection that cbd corresponds to. Start listening for requests from
2640 * other Wayland clients so they can receive data from us. Returns OK on success
2641 * and FAIL on failure.
2642 */
2643 int
2644clip_wl_own_selection(Clipboard_T *cbd)
2645{
2646 wayland_selection_T selection;
2647
2648 if (cbd == &clip_star)
2649 selection = WAYLAND_SELECTION_PRIMARY;
2650 else if (cbd == &clip_plus)
2651 selection = WAYLAND_SELECTION_REGULAR;
2652 else
2653 return FAIL;
2654
2655 return wayland_cb_own_selection(
2656 clip_wl_send_data,
2657 clip_wl_selection_cancelled,
2658 supported_mimes,
2659 sizeof(supported_mimes)/sizeof(*supported_mimes),
2660 selection);
2661}
2662
2663/*
2664 * Disown the selection that cbd corresponds to. Note that the the cancelled
2665 * event is not sent when the data source is destroyed.
2666 */
2667 void
2668clip_wl_lose_selection(Clipboard_T *cbd)
2669{
2670 if (cbd == &clip_plus)
2671 wayland_cb_lose_selection(WAYLAND_SELECTION_REGULAR);
2672 else if (cbd == &clip_star)
2673 wayland_cb_lose_selection(WAYLAND_SELECTION_PRIMARY);
2674
2675 /* wayland_cb_lose_selection(selection); */
2676}
2677
2678/*
Hirohito Higashi73b96502025-06-28 18:18:21 +02002679 * Send the current selection to the clipboard. Do nothing for Wayland because
Foxe Chenb90c2392025-06-27 21:10:35 +02002680 * we will fill in the selection only when requested by another client.
2681 */
2682 void
2683clip_wl_set_selection(Clipboard_T *cbd UNUSED)
2684{
2685}
2686
2687#if defined(USE_SYSTEM) && defined(PROTO)
2688/*
2689 * Return TRUE if we own the selection corresponding to cbd
2690 */
2691 static int
2692clip_wl_owner_exists(Clipboard_T *cbd)
2693{
2694 if (cbd == &clip_plus)
2695 return wayland_cb_selection_is_owned(WAYLAND_SELECTION_REGULAR);
2696 else if (cbd == &clip_star)
2697 return wayland_cb_selection_is_owned(WAYLAND_SELECTION_PRIMARY);
2698}
2699#endif
2700
2701#endif // FEAT_WAYLAND_CLIPBOARD
2702
2703
2704/*
2705 * Returns the first method for accessing the clipboard that is available/works,
2706 * depending on the order of values in str.
2707 */
2708 static clipmethod_T
2709get_clipmethod(char_u *str)
2710{
2711 int len = (int)STRLEN(str) + 1;
2712 char_u *buf = alloc(len);
2713
2714 if (buf == NULL)
2715 return CLIPMETHOD_FAIL;
2716
2717 clipmethod_T ret = CLIPMETHOD_FAIL;
2718 char_u *p = str;
2719
2720 while (*p != NUL)
2721 {
2722 clipmethod_T method = CLIPMETHOD_NONE;
2723
2724 (void)copy_option_part(&p, buf, len, ",");
2725
2726 if (STRCMP(buf, "wayland") == 0)
2727 {
2728#ifdef FEAT_WAYLAND_CLIPBOARD
2729 if (wayland_cb_is_ready())
2730 method = CLIPMETHOD_WAYLAND;
2731#endif
2732 }
2733 else if (STRCMP(buf, "x11") == 0)
2734 {
2735#ifdef FEAT_XCLIPBOARD
2736 // x_IOerror_handler() in os_unix.c should set xterm_dpy to NULL if
2737 // we lost connection to the X server.
2738 if (xterm_dpy != NULL)
2739 {
2740 // If the X connection is lost then that handler will longjmp
2741 // somewhere else, in that case we will call choose_clipmethod()
2742 // again from there, and this if block won't be executed since
2743 // xterm_dpy will be set to NULL.
2744 xterm_update();
2745 method = CLIPMETHOD_X11;
2746 }
2747#endif
2748 }
2749 else
2750 {
2751 ret = CLIPMETHOD_FAIL;
2752 goto exit;
2753 }
2754
2755 // Keep on going in order to catch errors
2756 if (method != CLIPMETHOD_NONE && ret == CLIPMETHOD_FAIL)
2757 ret = method;
2758 }
2759
2760 // No match found, use "none".
2761 ret = (ret == CLIPMETHOD_FAIL) ? CLIPMETHOD_NONE : ret;
2762
2763exit:
2764 vim_free(buf);
2765 return ret;
2766}
2767
2768
2769/*
2770 * Returns name of clipmethod in a statically allocated string.
2771 */
2772 static char *
2773clipmethod_to_str(clipmethod_T method)
2774{
2775 switch(method)
2776 {
2777 case CLIPMETHOD_WAYLAND:
2778 return "wayland";
2779 case CLIPMETHOD_X11:
2780 return "x11";
2781 default:
2782 return "none";
2783 }
2784}
2785
2786/*
2787 * Sets the current clipmethod to use given by `get_clipmethod()`. Returns an
2788 * error message on failure else NULL.
2789 */
2790 char *
2791choose_clipmethod(void)
2792{
2793 // We call get_clipmethod first so that we can catch any errors, even if
2794 // clipmethod is useless
2795 clipmethod_T method = get_clipmethod(p_cpm);
2796
2797 if (method == CLIPMETHOD_FAIL)
2798 return e_invalid_argument;
2799
Hirohito Higashi73b96502025-06-28 18:18:21 +02002800// If GUI is running or we are not on a system with Wayland or X11, then always
Foxe Chenb90c2392025-06-27 21:10:35 +02002801// return CLIPMETHOD_NONE. System or GUI clipboard handling always overrides.
2802#if defined(FEAT_XCLIPBOARD) || defined(FEAT_WAYLAND_CLIPBOARD)
2803#if defined(FEAT_GUI)
2804 if (gui.in_use)
2805 {
2806#ifdef FEAT_WAYLAND
Hirohito Higashi73b96502025-06-28 18:18:21 +02002807 // We only interact with Wayland for the clipboard, we can just deinit
Foxe Chenb90c2392025-06-27 21:10:35 +02002808 // everything.
2809 wayland_uninit_client();
2810#endif
2811
2812 method = CLIPMETHOD_NONE;
2813 goto lose_sel_exit;
2814 }
2815#endif
2816#else
2817 // If on a system like windows or macos, then clipmethod is irrelevant, we
2818 // use their way of accessing the clipboard.
2819 method = CLIPMETHOD_NONE;
2820 goto exit;
2821#endif
2822
2823 // Deinitialize clipboard if there is no way to access clipboard
2824 if (method == CLIPMETHOD_NONE)
2825 clip_init(FALSE);
2826 // If we have a clipmethod that works now, then initialize clipboard
2827 else if (clipmethod == CLIPMETHOD_NONE
2828 && method != CLIPMETHOD_NONE)
2829 {
2830 clip_init(TRUE);
2831 did_warn_clipboard = FALSE;
2832 }
2833
2834 // Disown clipboard if we are switching to a new method
2835 if (clipmethod != CLIPMETHOD_NONE && method != clipmethod)
2836 {
2837#if (defined(FEAT_XCLIPBOARD) || defined(FEAT_WAYLAND_CLIPBOARD)) \
2838 && defined(FEAT_GUI)
2839lose_sel_exit:
2840#endif
2841 if (clip_star.owned)
2842 clip_lose_selection(&clip_star);
2843 if (clip_plus.owned)
2844 clip_lose_selection(&clip_plus);
2845 }
2846
2847#if !defined(FEAT_XCLIPBOARD) && !defined(FEAT_WAYLAND_CLIPBOARD)
2848exit:
2849#endif
2850
2851 clipmethod = method;
2852
2853#ifdef FEAT_EVAL
2854 set_vim_var_string(VV_CLIPMETHOD, (char_u*)clipmethod_to_str(method), -1);
2855#endif
2856
2857 return NULL;
2858}
2859
2860/*
2861 * Call choose_clipmethod().
2862 */
2863 void
2864ex_clipreset(exarg_T *eap UNUSED)
2865{
2866 clipmethod_T prev = clipmethod;
2867
2868 choose_clipmethod();
2869
2870 if (clipmethod == CLIPMETHOD_NONE)
2871 smsg(_("Could not find a way to access the clipboard."));
2872 else if (clipmethod != prev)
2873 smsg(_("Switched to clipboard method '%s'."),
2874 clipmethod_to_str(clipmethod));
2875}
2876
Bram Moolenaar45fffdf2020-03-24 21:42:01 +01002877#endif // FEAT_CLIPBOARD