1 /* 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 
34 /*
35  * Selection stuff using Visual mode, for cutting and pasting text to other
36  * windows.
37  */
38 
39 /*
40  * Call this to initialise the clipboard.  Pass it FALSE if the clipboard code
41  * is included, but the clipboard can not be used, or TRUE if the clipboard can
42  * be used.  Eg unix may call this with FALSE, then call it again with TRUE if
43  * the GUI starts.
44  */
45     void
clip_init(int can_use)46 clip_init(int can_use)
47 {
48     Clipboard_T *cb;
49 
50     cb = &clip_star;
51     for (;;)
52     {
53 	cb->available  = can_use;
54 	cb->owned      = FALSE;
55 	cb->start.lnum = 0;
56 	cb->start.col  = 0;
57 	cb->end.lnum   = 0;
58 	cb->end.col    = 0;
59 	cb->state      = SELECT_CLEARED;
60 
61 	if (cb == &clip_plus)
62 	    break;
63 	cb = &clip_plus;
64     }
65 }
66 
67 /*
68  * Check whether the VIsual area has changed, and if so try to become the owner
69  * of the selection, and free any old converted selection we may still have
70  * lying around.  If the VIsual mode has ended, make a copy of what was
71  * selected so we can still give it to others.	Will probably have to make sure
72  * this is called whenever VIsual mode is ended.
73  */
74     void
clip_update_selection(Clipboard_T * clip)75 clip_update_selection(Clipboard_T *clip)
76 {
77     pos_T	    start, end;
78 
79     // If visual mode is only due to a redo command ("."), then ignore it
80     if (!redo_VIsual_busy && VIsual_active && (State & NORMAL))
81     {
82 	if (LT_POS(VIsual, curwin->w_cursor))
83 	{
84 	    start = VIsual;
85 	    end = curwin->w_cursor;
86 	    if (has_mbyte)
87 		end.col += (*mb_ptr2len)(ml_get_cursor()) - 1;
88 	}
89 	else
90 	{
91 	    start = curwin->w_cursor;
92 	    end = VIsual;
93 	}
94 	if (!EQUAL_POS(clip->start, start)
95 		|| !EQUAL_POS(clip->end, end)
96 		|| clip->vmode != VIsual_mode)
97 	{
98 	    clip_clear_selection(clip);
99 	    clip->start = start;
100 	    clip->end = end;
101 	    clip->vmode = VIsual_mode;
102 	    clip_free_selection(clip);
103 	    clip_own_selection(clip);
104 	    clip_gen_set_selection(clip);
105 	}
106     }
107 }
108 
109     static int
clip_gen_own_selection(Clipboard_T * cbd)110 clip_gen_own_selection(Clipboard_T *cbd)
111 {
112 #ifdef FEAT_XCLIPBOARD
113 # ifdef FEAT_GUI
114     if (gui.in_use)
115 	return clip_mch_own_selection(cbd);
116     else
117 # endif
118 	return clip_xterm_own_selection(cbd);
119 #else
120     return clip_mch_own_selection(cbd);
121 #endif
122 }
123 
124     void
clip_own_selection(Clipboard_T * cbd)125 clip_own_selection(Clipboard_T *cbd)
126 {
127     /*
128      * Also want to check somehow that we are reading from the keyboard rather
129      * than a mapping etc.
130      */
131 #ifdef FEAT_X11
132     // Always own the selection, we might have lost it without being
133     // notified, e.g. during a ":sh" command.
134     if (cbd->available)
135     {
136 	int was_owned = cbd->owned;
137 
138 	cbd->owned = (clip_gen_own_selection(cbd) == OK);
139 	if (!was_owned && (cbd == &clip_star || cbd == &clip_plus))
140 	{
141 	    // May have to show a different kind of highlighting for the
142 	    // selected area.  There is no specific redraw command for this,
143 	    // just redraw all windows on the current buffer.
144 	    if (cbd->owned
145 		    && (get_real_state() == VISUAL
146 					    || get_real_state() == SELECTMODE)
147 		    && (cbd == &clip_star ? clip_isautosel_star()
148 						      : clip_isautosel_plus())
149 		    && HL_ATTR(HLF_V) != HL_ATTR(HLF_VNC))
150 		redraw_curbuf_later(INVERTED_ALL);
151 	}
152     }
153 #else
154     // Only own the clipboard when we didn't own it yet.
155     if (!cbd->owned && cbd->available)
156 	cbd->owned = (clip_gen_own_selection(cbd) == OK);
157 #endif
158 }
159 
160     static void
clip_gen_lose_selection(Clipboard_T * cbd)161 clip_gen_lose_selection(Clipboard_T *cbd)
162 {
163 #ifdef FEAT_XCLIPBOARD
164 # ifdef FEAT_GUI
165     if (gui.in_use)
166 	clip_mch_lose_selection(cbd);
167     else
168 # endif
169 	clip_xterm_lose_selection(cbd);
170 #else
171     clip_mch_lose_selection(cbd);
172 #endif
173 }
174 
175     void
clip_lose_selection(Clipboard_T * cbd)176 clip_lose_selection(Clipboard_T *cbd)
177 {
178 #ifdef FEAT_X11
179     int	    was_owned = cbd->owned;
180 #endif
181     int     visual_selection = FALSE;
182 
183     if (cbd == &clip_star || cbd == &clip_plus)
184 	visual_selection = TRUE;
185 
186     clip_free_selection(cbd);
187     cbd->owned = FALSE;
188     if (visual_selection)
189 	clip_clear_selection(cbd);
190     clip_gen_lose_selection(cbd);
191 #ifdef FEAT_X11
192     if (visual_selection)
193     {
194 	// May have to show a different kind of highlighting for the selected
195 	// area.  There is no specific redraw command for this, just redraw all
196 	// windows on the current buffer.
197 	if (was_owned
198 		&& (get_real_state() == VISUAL
199 					    || get_real_state() == SELECTMODE)
200 		&& (cbd == &clip_star ?
201 				clip_isautosel_star() : clip_isautosel_plus())
202 		&& HL_ATTR(HLF_V) != HL_ATTR(HLF_VNC)
203 		&& !exiting)
204 	{
205 	    update_curbuf(INVERTED_ALL);
206 	    setcursor();
207 	    cursor_on();
208 	    out_flush_cursor(TRUE, FALSE);
209 	}
210     }
211 #endif
212 }
213 
214     static void
clip_copy_selection(Clipboard_T * clip)215 clip_copy_selection(Clipboard_T *clip)
216 {
217     if (VIsual_active && (State & NORMAL) && clip->available)
218     {
219 	clip_update_selection(clip);
220 	clip_free_selection(clip);
221 	clip_own_selection(clip);
222 	if (clip->owned)
223 	    clip_get_selection(clip);
224 	clip_gen_set_selection(clip);
225     }
226 }
227 
228 /*
229  * Save and restore clip_unnamed before doing possibly many changes. This
230  * prevents accessing the clipboard very often which might slow down Vim
231  * considerably.
232  */
233 static int global_change_count = 0; // if set, inside a start_global_changes
234 static int clipboard_needs_update = FALSE; // clipboard needs to be updated
235 static int clip_did_set_selection = TRUE;
236 
237 /*
238  * Save clip_unnamed and reset it.
239  */
240     void
start_global_changes(void)241 start_global_changes(void)
242 {
243     if (++global_change_count > 1)
244 	return;
245     clip_unnamed_saved = clip_unnamed;
246     clipboard_needs_update = FALSE;
247 
248     if (clip_did_set_selection)
249     {
250 	clip_unnamed = 0;
251 	clip_did_set_selection = FALSE;
252     }
253 }
254 
255 /*
256  * Return TRUE if setting the clipboard was postponed, it already contains the
257  * right text.
258  */
259     static int
is_clipboard_needs_update()260 is_clipboard_needs_update()
261 {
262     return clipboard_needs_update;
263 }
264 
265 /*
266  * Restore clip_unnamed and set the selection when needed.
267  */
268     void
end_global_changes(void)269 end_global_changes(void)
270 {
271     if (--global_change_count > 0)
272 	// recursive
273 	return;
274     if (!clip_did_set_selection)
275     {
276 	clip_did_set_selection = TRUE;
277 	clip_unnamed = clip_unnamed_saved;
278 	clip_unnamed_saved = 0;
279 	if (clipboard_needs_update)
280 	{
281 	    // only store something in the clipboard,
282 	    // if we have yanked anything to it
283 	    if (clip_unnamed & CLIP_UNNAMED)
284 	    {
285 		clip_own_selection(&clip_star);
286 		clip_gen_set_selection(&clip_star);
287 	    }
288 	    if (clip_unnamed & CLIP_UNNAMED_PLUS)
289 	    {
290 		clip_own_selection(&clip_plus);
291 		clip_gen_set_selection(&clip_plus);
292 	    }
293 	}
294     }
295     clipboard_needs_update = FALSE;
296 }
297 
298 /*
299  * Called when Visual mode is ended: update the selection.
300  */
301     void
clip_auto_select(void)302 clip_auto_select(void)
303 {
304     if (clip_isautosel_star())
305 	clip_copy_selection(&clip_star);
306     if (clip_isautosel_plus())
307 	clip_copy_selection(&clip_plus);
308 }
309 
310 /*
311  * Return TRUE if automatic selection of Visual area is desired for the *
312  * register.
313  */
314     int
clip_isautosel_star(void)315 clip_isautosel_star(void)
316 {
317     return (
318 #ifdef FEAT_GUI
319 	    gui.in_use ? (vim_strchr(p_go, GO_ASEL) != NULL) :
320 #endif
321 	    clip_autoselect_star);
322 }
323 
324 /*
325  * Return TRUE if automatic selection of Visual area is desired for the +
326  * register.
327  */
328     int
clip_isautosel_plus(void)329 clip_isautosel_plus(void)
330 {
331     return (
332 #ifdef FEAT_GUI
333 	    gui.in_use ? (vim_strchr(p_go, GO_ASELPLUS) != NULL) :
334 #endif
335 	    clip_autoselect_plus);
336 }
337 
338 
339 /*
340  * Stuff for general mouse selection, without using Visual mode.
341  */
342 
343 /*
344  * Compare two screen positions ala strcmp()
345  */
346     static int
clip_compare_pos(int row1,int col1,int row2,int col2)347 clip_compare_pos(
348     int		row1,
349     int		col1,
350     int		row2,
351     int		col2)
352 {
353     if (row1 > row2) return(1);
354     if (row1 < row2) return(-1);
355     if (col1 > col2) return(1);
356     if (col1 < col2) return(-1);
357     return(0);
358 }
359 
360 // "how" flags for clip_invert_area()
361 #define CLIP_CLEAR	1
362 #define CLIP_SET	2
363 #define CLIP_TOGGLE	3
364 
365 /*
366  * Invert or un-invert a rectangle of the screen.
367  * "invert" is true if the result is inverted.
368  */
369     static void
clip_invert_rectangle(Clipboard_T * cbd UNUSED,int row_arg,int col_arg,int height_arg,int width_arg,int invert)370 clip_invert_rectangle(
371 	Clipboard_T	*cbd UNUSED,
372 	int		row_arg,
373 	int		col_arg,
374 	int		height_arg,
375 	int		width_arg,
376 	int		invert)
377 {
378     int		row = row_arg;
379     int		col = col_arg;
380     int		height = height_arg;
381     int		width = width_arg;
382 
383 #ifdef FEAT_PROP_POPUP
384     // this goes on top of all popup windows
385     screen_zindex = CLIP_ZINDEX;
386 
387     if (col < cbd->min_col)
388     {
389 	width -= cbd->min_col - col;
390 	col = cbd->min_col;
391     }
392     if (width > cbd->max_col - col)
393 	width = cbd->max_col - col;
394     if (row < cbd->min_row)
395     {
396 	height -= cbd->min_row - row;
397 	row = cbd->min_row;
398     }
399     if (height > cbd->max_row - row + 1)
400 	height = cbd->max_row - row + 1;
401 #endif
402 #ifdef FEAT_GUI
403     if (gui.in_use)
404 	gui_mch_invert_rectangle(row, col, height, width);
405     else
406 #endif
407 	screen_draw_rectangle(row, col, height, width, invert);
408 #ifdef FEAT_PROP_POPUP
409     screen_zindex = 0;
410 #endif
411 }
412 
413 /*
414  * Invert a region of the display between a starting and ending row and column
415  * Values for "how":
416  * CLIP_CLEAR:  undo inversion
417  * CLIP_SET:    set inversion
418  * CLIP_TOGGLE: set inversion if pos1 < pos2, undo inversion otherwise.
419  * 0: invert (GUI only).
420  */
421     static void
clip_invert_area(Clipboard_T * cbd,int row1,int col1,int row2,int col2,int how)422 clip_invert_area(
423 	Clipboard_T	*cbd,
424 	int		row1,
425 	int		col1,
426 	int		row2,
427 	int		col2,
428 	int		how)
429 {
430     int		invert = FALSE;
431     int		max_col;
432 
433 #ifdef FEAT_PROP_POPUP
434     max_col = cbd->max_col - 1;
435 #else
436     max_col = Columns - 1;
437 #endif
438 
439     if (how == CLIP_SET)
440 	invert = TRUE;
441 
442     // Swap the from and to positions so the from is always before
443     if (clip_compare_pos(row1, col1, row2, col2) > 0)
444     {
445 	int tmp_row, tmp_col;
446 
447 	tmp_row = row1;
448 	tmp_col = col1;
449 	row1	= row2;
450 	col1	= col2;
451 	row2	= tmp_row;
452 	col2	= tmp_col;
453     }
454     else if (how == CLIP_TOGGLE)
455 	invert = TRUE;
456 
457     // If all on the same line, do it the easy way
458     if (row1 == row2)
459     {
460 	clip_invert_rectangle(cbd, row1, col1, 1, col2 - col1, invert);
461     }
462     else
463     {
464 	// Handle a piece of the first line
465 	if (col1 > 0)
466 	{
467 	    clip_invert_rectangle(cbd, row1, col1, 1,
468 						  (int)Columns - col1, invert);
469 	    row1++;
470 	}
471 
472 	// Handle a piece of the last line
473 	if (col2 < max_col)
474 	{
475 	    clip_invert_rectangle(cbd, row2, 0, 1, col2, invert);
476 	    row2--;
477 	}
478 
479 	// Handle the rectangle that's left
480 	if (row2 >= row1)
481 	    clip_invert_rectangle(cbd, row1, 0, row2 - row1 + 1,
482 							 (int)Columns, invert);
483     }
484 }
485 
486 /*
487  * Start, continue or end a modeless selection.  Used when editing the
488  * command-line, in the cmdline window and when the mouse is in a popup window.
489  */
490     void
clip_modeless(int button,int is_click,int is_drag)491 clip_modeless(int button, int is_click, int is_drag)
492 {
493     int		repeat;
494 
495     repeat = ((clip_star.mode == SELECT_MODE_CHAR
496 		|| clip_star.mode == SELECT_MODE_LINE)
497 					      && (mod_mask & MOD_MASK_2CLICK))
498 	    || (clip_star.mode == SELECT_MODE_WORD
499 					     && (mod_mask & MOD_MASK_3CLICK));
500     if (is_click && button == MOUSE_RIGHT)
501     {
502 	// Right mouse button: If there was no selection, start one.
503 	// Otherwise extend the existing selection.
504 	if (clip_star.state == SELECT_CLEARED)
505 	    clip_start_selection(mouse_col, mouse_row, FALSE);
506 	clip_process_selection(button, mouse_col, mouse_row, repeat);
507     }
508     else if (is_click)
509 	clip_start_selection(mouse_col, mouse_row, repeat);
510     else if (is_drag)
511     {
512 	// Don't try extending a selection if there isn't one.  Happens when
513 	// button-down is in the cmdline and them moving mouse upwards.
514 	if (clip_star.state != SELECT_CLEARED)
515 	    clip_process_selection(button, mouse_col, mouse_row, repeat);
516     }
517     else // release
518 	clip_process_selection(MOUSE_RELEASE, mouse_col, mouse_row, FALSE);
519 }
520 
521 /*
522  * Update the currently selected region by adding and/or subtracting from the
523  * beginning or end and inverting the changed area(s).
524  */
525     static void
clip_update_modeless_selection(Clipboard_T * cb,int row1,int col1,int row2,int col2)526 clip_update_modeless_selection(
527     Clipboard_T    *cb,
528     int		    row1,
529     int		    col1,
530     int		    row2,
531     int		    col2)
532 {
533     // See if we changed at the beginning of the selection
534     if (row1 != cb->start.lnum || col1 != (int)cb->start.col)
535     {
536 	clip_invert_area(cb, row1, col1, (int)cb->start.lnum, cb->start.col,
537 								 CLIP_TOGGLE);
538 	cb->start.lnum = row1;
539 	cb->start.col  = col1;
540     }
541 
542     // See if we changed at the end of the selection
543     if (row2 != cb->end.lnum || col2 != (int)cb->end.col)
544     {
545 	clip_invert_area(cb, (int)cb->end.lnum, cb->end.col, row2, col2,
546 								 CLIP_TOGGLE);
547 	cb->end.lnum = row2;
548 	cb->end.col  = col2;
549     }
550 }
551 
552 /*
553  * Find the starting and ending positions of the word at the given row and
554  * column.  Only white-separated words are recognized here.
555  */
556 #define CHAR_CLASS(c)	(c <= ' ' ? ' ' : vim_iswordc(c))
557 
558     static void
clip_get_word_boundaries(Clipboard_T * cb,int row,int col)559 clip_get_word_boundaries(Clipboard_T *cb, int row, int col)
560 {
561     int		start_class;
562     int		temp_col;
563     char_u	*p;
564     int		mboff;
565 
566     if (row >= screen_Rows || col >= screen_Columns || ScreenLines == NULL)
567 	return;
568 
569     p = ScreenLines + LineOffset[row];
570     // Correct for starting in the right halve of a double-wide char
571     if (enc_dbcs != 0)
572 	col -= dbcs_screen_head_off(p, p + col);
573     else if (enc_utf8 && p[col] == 0)
574 	--col;
575     start_class = CHAR_CLASS(p[col]);
576 
577     temp_col = col;
578     for ( ; temp_col > 0; temp_col--)
579 	if (enc_dbcs != 0
580 		   && (mboff = dbcs_screen_head_off(p, p + temp_col - 1)) > 0)
581 	    temp_col -= mboff;
582 	else if (CHAR_CLASS(p[temp_col - 1]) != start_class
583 		&& !(enc_utf8 && p[temp_col - 1] == 0))
584 	    break;
585     cb->word_start_col = temp_col;
586 
587     temp_col = col;
588     for ( ; temp_col < screen_Columns; temp_col++)
589 	if (enc_dbcs != 0 && dbcs_ptr2cells(p + temp_col) == 2)
590 	    ++temp_col;
591 	else if (CHAR_CLASS(p[temp_col]) != start_class
592 		&& !(enc_utf8 && p[temp_col] == 0))
593 	    break;
594     cb->word_end_col = temp_col;
595 }
596 
597 /*
598  * Find the column position for the last non-whitespace character on the given
599  * line at or before start_col.
600  */
601     static int
clip_get_line_end(Clipboard_T * cbd UNUSED,int row)602 clip_get_line_end(Clipboard_T *cbd UNUSED, int row)
603 {
604     int	    i;
605 
606     if (row >= screen_Rows || ScreenLines == NULL)
607 	return 0;
608     for (i =
609 #ifdef FEAT_PROP_POPUP
610 	    cbd->max_col;
611 #else
612 	    screen_Columns;
613 #endif
614 			    i > 0; i--)
615 	if (ScreenLines[LineOffset[row] + i - 1] != ' ')
616 	    break;
617     return i;
618 }
619 
620 /*
621  * Start the selection
622  */
623     void
clip_start_selection(int col,int row,int repeated_click)624 clip_start_selection(int col, int row, int repeated_click)
625 {
626     Clipboard_T	*cb = &clip_star;
627 #ifdef FEAT_PROP_POPUP
628     win_T	*wp;
629     int		row_cp = row;
630     int		col_cp = col;
631 
632     wp = mouse_find_win(&row_cp, &col_cp, FIND_POPUP);
633     if (wp != NULL && WIN_IS_POPUP(wp)
634 				  && popup_is_in_scrollbar(wp, row_cp, col_cp))
635 	// click or double click in scrollbar does not start a selection
636 	return;
637 #endif
638 
639     if (cb->state == SELECT_DONE)
640 	clip_clear_selection(cb);
641 
642     row = check_row(row);
643     col = check_col(col);
644     col = mb_fix_col(col, row);
645 
646     cb->start.lnum  = row;
647     cb->start.col   = col;
648     cb->end	    = cb->start;
649     cb->origin_row  = (short_u)cb->start.lnum;
650     cb->state	    = SELECT_IN_PROGRESS;
651 #ifdef FEAT_PROP_POPUP
652     if (wp != NULL && WIN_IS_POPUP(wp))
653     {
654 	// Click in a popup window restricts selection to that window,
655 	// excluding the border.
656 	cb->min_col = wp->w_wincol + wp->w_popup_border[3];
657 	cb->max_col = wp->w_wincol + popup_width(wp)
658 				 - wp->w_popup_border[1] - wp->w_has_scrollbar;
659 	if (cb->max_col > screen_Columns)
660 	    cb->max_col = screen_Columns;
661 	cb->min_row = wp->w_winrow + wp->w_popup_border[0];
662 	cb->max_row = wp->w_winrow + popup_height(wp) - 1
663 						   - wp->w_popup_border[2];
664     }
665     else
666     {
667 	cb->min_col = 0;
668 	cb->max_col = screen_Columns;
669 	cb->min_row = 0;
670 	cb->max_row = screen_Rows;
671     }
672 #endif
673 
674     if (repeated_click)
675     {
676 	if (++cb->mode > SELECT_MODE_LINE)
677 	    cb->mode = SELECT_MODE_CHAR;
678     }
679     else
680 	cb->mode = SELECT_MODE_CHAR;
681 
682 #ifdef FEAT_GUI
683     // clear the cursor until the selection is made
684     if (gui.in_use)
685 	gui_undraw_cursor();
686 #endif
687 
688     switch (cb->mode)
689     {
690 	case SELECT_MODE_CHAR:
691 	    cb->origin_start_col = cb->start.col;
692 	    cb->word_end_col = clip_get_line_end(cb, (int)cb->start.lnum);
693 	    break;
694 
695 	case SELECT_MODE_WORD:
696 	    clip_get_word_boundaries(cb, (int)cb->start.lnum, cb->start.col);
697 	    cb->origin_start_col = cb->word_start_col;
698 	    cb->origin_end_col	 = cb->word_end_col;
699 
700 	    clip_invert_area(cb, (int)cb->start.lnum, cb->word_start_col,
701 			    (int)cb->end.lnum, cb->word_end_col, CLIP_SET);
702 	    cb->start.col = cb->word_start_col;
703 	    cb->end.col   = cb->word_end_col;
704 	    break;
705 
706 	case SELECT_MODE_LINE:
707 	    clip_invert_area(cb, (int)cb->start.lnum, 0, (int)cb->start.lnum,
708 			    (int)Columns, CLIP_SET);
709 	    cb->start.col = 0;
710 	    cb->end.col   = Columns;
711 	    break;
712     }
713 
714     cb->prev = cb->start;
715 
716 #ifdef DEBUG_SELECTION
717     printf("Selection started at (%ld,%d)\n", cb->start.lnum, cb->start.col);
718 #endif
719 }
720 
721 /*
722  * Continue processing the selection
723  */
724     void
clip_process_selection(int button,int col,int row,int_u repeated_click)725 clip_process_selection(
726     int		button,
727     int		col,
728     int		row,
729     int_u	repeated_click)
730 {
731     Clipboard_T	*cb = &clip_star;
732     int		diff;
733     int		slen = 1;	// cursor shape width
734 
735     if (button == MOUSE_RELEASE)
736     {
737 	if (cb->state != SELECT_IN_PROGRESS)
738 	    return;
739 
740 	// Check to make sure we have something selected
741 	if (cb->start.lnum == cb->end.lnum && cb->start.col == cb->end.col)
742 	{
743 #ifdef FEAT_GUI
744 	    if (gui.in_use)
745 		gui_update_cursor(FALSE, FALSE);
746 #endif
747 	    cb->state = SELECT_CLEARED;
748 	    return;
749 	}
750 
751 #ifdef DEBUG_SELECTION
752 	printf("Selection ended: (%ld,%d) to (%ld,%d)\n", cb->start.lnum,
753 		cb->start.col, cb->end.lnum, cb->end.col);
754 #endif
755 	if (clip_isautosel_star()
756 		|| (
757 #ifdef FEAT_GUI
758 		    gui.in_use ? (vim_strchr(p_go, GO_ASELML) != NULL) :
759 #endif
760 		    clip_autoselectml))
761 	    clip_copy_modeless_selection(FALSE);
762 #ifdef FEAT_GUI
763 	if (gui.in_use)
764 	    gui_update_cursor(FALSE, FALSE);
765 #endif
766 
767 	cb->state = SELECT_DONE;
768 	return;
769     }
770 
771     row = check_row(row);
772     col = check_col(col);
773     col = mb_fix_col(col, row);
774 
775     if (col == (int)cb->prev.col && row == cb->prev.lnum && !repeated_click)
776 	return;
777 
778     /*
779      * When extending the selection with the right mouse button, swap the
780      * start and end if the position is before half the selection
781      */
782     if (cb->state == SELECT_DONE && button == MOUSE_RIGHT)
783     {
784 	/*
785 	 * If the click is before the start, or the click is inside the
786 	 * selection and the start is the closest side, set the origin to the
787 	 * end of the selection.
788 	 */
789 	if (clip_compare_pos(row, col, (int)cb->start.lnum, cb->start.col) < 0
790 		|| (clip_compare_pos(row, col,
791 					   (int)cb->end.lnum, cb->end.col) < 0
792 		    && (((cb->start.lnum == cb->end.lnum
793 			    && cb->end.col - col > col - cb->start.col))
794 			|| ((diff = (cb->end.lnum - row) -
795 						   (row - cb->start.lnum)) > 0
796 			    || (diff == 0 && col < (int)(cb->start.col +
797 							 cb->end.col) / 2)))))
798 	{
799 	    cb->origin_row = (short_u)cb->end.lnum;
800 	    cb->origin_start_col = cb->end.col - 1;
801 	    cb->origin_end_col = cb->end.col;
802 	}
803 	else
804 	{
805 	    cb->origin_row = (short_u)cb->start.lnum;
806 	    cb->origin_start_col = cb->start.col;
807 	    cb->origin_end_col = cb->start.col;
808 	}
809 	if (cb->mode == SELECT_MODE_WORD && !repeated_click)
810 	    cb->mode = SELECT_MODE_CHAR;
811     }
812 
813     // set state, for when using the right mouse button
814     cb->state = SELECT_IN_PROGRESS;
815 
816 #ifdef DEBUG_SELECTION
817     printf("Selection extending to (%d,%d)\n", row, col);
818 #endif
819 
820     if (repeated_click && ++cb->mode > SELECT_MODE_LINE)
821 	cb->mode = SELECT_MODE_CHAR;
822 
823     switch (cb->mode)
824     {
825 	case SELECT_MODE_CHAR:
826 	    // If we're on a different line, find where the line ends
827 	    if (row != cb->prev.lnum)
828 		cb->word_end_col = clip_get_line_end(cb, row);
829 
830 	    // See if we are before or after the origin of the selection
831 	    if (clip_compare_pos(row, col, cb->origin_row,
832 						   cb->origin_start_col) >= 0)
833 	    {
834 		if (col >= (int)cb->word_end_col)
835 		    clip_update_modeless_selection(cb, cb->origin_row,
836 			    cb->origin_start_col, row, (int)Columns);
837 		else
838 		{
839 		    if (has_mbyte && mb_lefthalve(row, col))
840 			slen = 2;
841 		    clip_update_modeless_selection(cb, cb->origin_row,
842 			    cb->origin_start_col, row, col + slen);
843 		}
844 	    }
845 	    else
846 	    {
847 		if (has_mbyte
848 			&& mb_lefthalve(cb->origin_row, cb->origin_start_col))
849 		    slen = 2;
850 		if (col >= (int)cb->word_end_col)
851 		    clip_update_modeless_selection(cb, row, cb->word_end_col,
852 			    cb->origin_row, cb->origin_start_col + slen);
853 		else
854 		    clip_update_modeless_selection(cb, row, col,
855 			    cb->origin_row, cb->origin_start_col + slen);
856 	    }
857 	    break;
858 
859 	case SELECT_MODE_WORD:
860 	    // If we are still within the same word, do nothing
861 	    if (row == cb->prev.lnum && col >= (int)cb->word_start_col
862 		    && col < (int)cb->word_end_col && !repeated_click)
863 		return;
864 
865 	    // Get new word boundaries
866 	    clip_get_word_boundaries(cb, row, col);
867 
868 	    // Handle being after the origin point of selection
869 	    if (clip_compare_pos(row, col, cb->origin_row,
870 		    cb->origin_start_col) >= 0)
871 		clip_update_modeless_selection(cb, cb->origin_row,
872 			cb->origin_start_col, row, cb->word_end_col);
873 	    else
874 		clip_update_modeless_selection(cb, row, cb->word_start_col,
875 			cb->origin_row, cb->origin_end_col);
876 	    break;
877 
878 	case SELECT_MODE_LINE:
879 	    if (row == cb->prev.lnum && !repeated_click)
880 		return;
881 
882 	    if (clip_compare_pos(row, col, cb->origin_row,
883 		    cb->origin_start_col) >= 0)
884 		clip_update_modeless_selection(cb, cb->origin_row, 0, row,
885 			(int)Columns);
886 	    else
887 		clip_update_modeless_selection(cb, row, 0, cb->origin_row,
888 			(int)Columns);
889 	    break;
890     }
891 
892     cb->prev.lnum = row;
893     cb->prev.col  = col;
894 
895 #ifdef DEBUG_SELECTION
896 	printf("Selection is: (%ld,%d) to (%ld,%d)\n", cb->start.lnum,
897 		cb->start.col, cb->end.lnum, cb->end.col);
898 #endif
899 }
900 
901 # if defined(FEAT_GUI) || defined(PROTO)
902 /*
903  * Redraw part of the selection if character at "row,col" is inside of it.
904  * Only used for the GUI.
905  */
906     void
clip_may_redraw_selection(int row,int col,int len)907 clip_may_redraw_selection(int row, int col, int len)
908 {
909     int		start = col;
910     int		end = col + len;
911 
912     if (clip_star.state != SELECT_CLEARED
913 	    && row >= clip_star.start.lnum
914 	    && row <= clip_star.end.lnum)
915     {
916 	if (row == clip_star.start.lnum && start < (int)clip_star.start.col)
917 	    start = clip_star.start.col;
918 	if (row == clip_star.end.lnum && end > (int)clip_star.end.col)
919 	    end = clip_star.end.col;
920 	if (end > start)
921 	    clip_invert_area(&clip_star, row, start, row, end, 0);
922     }
923 }
924 # endif
925 
926 /*
927  * Called from outside to clear selected region from the display
928  */
929     void
clip_clear_selection(Clipboard_T * cbd)930 clip_clear_selection(Clipboard_T *cbd)
931 {
932 
933     if (cbd->state == SELECT_CLEARED)
934 	return;
935 
936     clip_invert_area(cbd, (int)cbd->start.lnum, cbd->start.col,
937 				 (int)cbd->end.lnum, cbd->end.col, CLIP_CLEAR);
938     cbd->state = SELECT_CLEARED;
939 }
940 
941 /*
942  * Clear the selection if any lines from "row1" to "row2" are inside of it.
943  */
944     void
clip_may_clear_selection(int row1,int row2)945 clip_may_clear_selection(int row1, int row2)
946 {
947     if (clip_star.state == SELECT_DONE
948 	    && row2 >= clip_star.start.lnum
949 	    && row1 <= clip_star.end.lnum)
950 	clip_clear_selection(&clip_star);
951 }
952 
953 /*
954  * Called before the screen is scrolled up or down.  Adjusts the line numbers
955  * of the selection.  Call with big number when clearing the screen.
956  */
957     void
clip_scroll_selection(int rows)958 clip_scroll_selection(
959     int	    rows)		// negative for scroll down
960 {
961     int	    lnum;
962 
963     if (clip_star.state == SELECT_CLEARED)
964 	return;
965 
966     lnum = clip_star.start.lnum - rows;
967     if (lnum <= 0)
968 	clip_star.start.lnum = 0;
969     else if (lnum >= screen_Rows)	// scrolled off of the screen
970 	clip_star.state = SELECT_CLEARED;
971     else
972 	clip_star.start.lnum = lnum;
973 
974     lnum = clip_star.end.lnum - rows;
975     if (lnum < 0)			// scrolled off of the screen
976 	clip_star.state = SELECT_CLEARED;
977     else if (lnum >= screen_Rows)
978 	clip_star.end.lnum = screen_Rows - 1;
979     else
980 	clip_star.end.lnum = lnum;
981 }
982 
983 /*
984  * Copy the currently selected area into the '*' register so it will be
985  * available for pasting.
986  * When "both" is TRUE also copy to the '+' register.
987  */
988     void
clip_copy_modeless_selection(int both UNUSED)989 clip_copy_modeless_selection(int both UNUSED)
990 {
991     char_u	*buffer;
992     char_u	*bufp;
993     int		row;
994     int		start_col;
995     int		end_col;
996     int		line_end_col;
997     int		add_newline_flag = FALSE;
998     int		len;
999     char_u	*p;
1000     int		row1 = clip_star.start.lnum;
1001     int		col1 = clip_star.start.col;
1002     int		row2 = clip_star.end.lnum;
1003     int		col2 = clip_star.end.col;
1004 
1005     // Can't use ScreenLines unless initialized
1006     if (ScreenLines == NULL)
1007 	return;
1008 
1009     /*
1010      * Make sure row1 <= row2, and if row1 == row2 that col1 <= col2.
1011      */
1012     if (row1 > row2)
1013     {
1014 	row = row1; row1 = row2; row2 = row;
1015 	row = col1; col1 = col2; col2 = row;
1016     }
1017     else if (row1 == row2 && col1 > col2)
1018     {
1019 	row = col1; col1 = col2; col2 = row;
1020     }
1021 #ifdef FEAT_PROP_POPUP
1022     if (col1 < clip_star.min_col)
1023 	col1 = clip_star.min_col;
1024     if (col2 > clip_star.max_col)
1025 	col2 = clip_star.max_col;
1026     if (row1 > clip_star.max_row || row2 < clip_star.min_row)
1027 	return;
1028     if (row1 < clip_star.min_row)
1029 	row1 = clip_star.min_row;
1030     if (row2 > clip_star.max_row)
1031 	row2 = clip_star.max_row;
1032 #endif
1033     // correct starting point for being on right halve of double-wide char
1034     p = ScreenLines + LineOffset[row1];
1035     if (enc_dbcs != 0)
1036 	col1 -= (*mb_head_off)(p, p + col1);
1037     else if (enc_utf8 && p[col1] == 0)
1038 	--col1;
1039 
1040     // Create a temporary buffer for storing the text
1041     len = (row2 - row1 + 1) * Columns + 1;
1042     if (enc_dbcs != 0)
1043 	len *= 2;	// max. 2 bytes per display cell
1044     else if (enc_utf8)
1045 	len *= MB_MAXBYTES;
1046     buffer = alloc(len);
1047     if (buffer == NULL)	    // out of memory
1048 	return;
1049 
1050     // Process each row in the selection
1051     for (bufp = buffer, row = row1; row <= row2; row++)
1052     {
1053 	if (row == row1)
1054 	    start_col = col1;
1055 	else
1056 #ifdef FEAT_PROP_POPUP
1057 	    start_col = clip_star.min_col;
1058 #else
1059 	    start_col = 0;
1060 #endif
1061 
1062 	if (row == row2)
1063 	    end_col = col2;
1064 	else
1065 #ifdef FEAT_PROP_POPUP
1066 	    end_col = clip_star.max_col;
1067 #else
1068 	    end_col = Columns;
1069 #endif
1070 
1071 	line_end_col = clip_get_line_end(&clip_star, row);
1072 
1073 	// See if we need to nuke some trailing whitespace
1074 	if (end_col >=
1075 #ifdef FEAT_PROP_POPUP
1076 		clip_star.max_col
1077 #else
1078 		Columns
1079 #endif
1080 		    && (row < row2 || end_col > line_end_col))
1081 	{
1082 	    // Get rid of trailing whitespace
1083 	    end_col = line_end_col;
1084 	    if (end_col < start_col)
1085 		end_col = start_col;
1086 
1087 	    // If the last line extended to the end, add an extra newline
1088 	    if (row == row2)
1089 		add_newline_flag = TRUE;
1090 	}
1091 
1092 	// If after the first row, we need to always add a newline
1093 	if (row > row1 && !LineWraps[row - 1])
1094 	    *bufp++ = NL;
1095 
1096 	// Safetey check for in case resizing went wrong
1097 	if (row < screen_Rows && end_col <= screen_Columns)
1098 	{
1099 	    if (enc_dbcs != 0)
1100 	    {
1101 		int	i;
1102 
1103 		p = ScreenLines + LineOffset[row];
1104 		for (i = start_col; i < end_col; ++i)
1105 		    if (enc_dbcs == DBCS_JPNU && p[i] == 0x8e)
1106 		    {
1107 			// single-width double-byte char
1108 			*bufp++ = 0x8e;
1109 			*bufp++ = ScreenLines2[LineOffset[row] + i];
1110 		    }
1111 		    else
1112 		    {
1113 			*bufp++ = p[i];
1114 			if (MB_BYTE2LEN(p[i]) == 2)
1115 			    *bufp++ = p[++i];
1116 		    }
1117 	    }
1118 	    else if (enc_utf8)
1119 	    {
1120 		int	off;
1121 		int	i;
1122 		int	ci;
1123 
1124 		off = LineOffset[row];
1125 		for (i = start_col; i < end_col; ++i)
1126 		{
1127 		    // The base character is either in ScreenLinesUC[] or
1128 		    // ScreenLines[].
1129 		    if (ScreenLinesUC[off + i] == 0)
1130 			*bufp++ = ScreenLines[off + i];
1131 		    else
1132 		    {
1133 			bufp += utf_char2bytes(ScreenLinesUC[off + i], bufp);
1134 			for (ci = 0; ci < Screen_mco; ++ci)
1135 			{
1136 			    // Add a composing character.
1137 			    if (ScreenLinesC[ci][off + i] == 0)
1138 				break;
1139 			    bufp += utf_char2bytes(ScreenLinesC[ci][off + i],
1140 									bufp);
1141 			}
1142 		    }
1143 		    // Skip right halve of double-wide character.
1144 		    if (ScreenLines[off + i + 1] == 0)
1145 			++i;
1146 		}
1147 	    }
1148 	    else
1149 	    {
1150 		STRNCPY(bufp, ScreenLines + LineOffset[row] + start_col,
1151 							 end_col - start_col);
1152 		bufp += end_col - start_col;
1153 	    }
1154 	}
1155     }
1156 
1157     // Add a newline at the end if the selection ended there
1158     if (add_newline_flag)
1159 	*bufp++ = NL;
1160 
1161     // First cleanup any old selection and become the owner.
1162     clip_free_selection(&clip_star);
1163     clip_own_selection(&clip_star);
1164 
1165     // Yank the text into the '*' register.
1166     clip_yank_selection(MCHAR, buffer, (long)(bufp - buffer), &clip_star);
1167 
1168     // Make the register contents available to the outside world.
1169     clip_gen_set_selection(&clip_star);
1170 
1171 #ifdef FEAT_X11
1172     if (both)
1173     {
1174 	// Do the same for the '+' register.
1175 	clip_free_selection(&clip_plus);
1176 	clip_own_selection(&clip_plus);
1177 	clip_yank_selection(MCHAR, buffer, (long)(bufp - buffer), &clip_plus);
1178 	clip_gen_set_selection(&clip_plus);
1179     }
1180 #endif
1181     vim_free(buffer);
1182 }
1183 
1184     void
clip_gen_set_selection(Clipboard_T * cbd)1185 clip_gen_set_selection(Clipboard_T *cbd)
1186 {
1187     if (!clip_did_set_selection)
1188     {
1189 	// Updating postponed, so that accessing the system clipboard won't
1190 	// hang Vim when accessing it many times (e.g. on a :g command).
1191 	if ((cbd == &clip_plus && (clip_unnamed_saved & CLIP_UNNAMED_PLUS))
1192 		|| (cbd == &clip_star && (clip_unnamed_saved & CLIP_UNNAMED)))
1193 	{
1194 	    clipboard_needs_update = TRUE;
1195 	    return;
1196 	}
1197     }
1198 #ifdef FEAT_XCLIPBOARD
1199 # ifdef FEAT_GUI
1200     if (gui.in_use)
1201 	clip_mch_set_selection(cbd);
1202     else
1203 # endif
1204 	clip_xterm_set_selection(cbd);
1205 #else
1206     clip_mch_set_selection(cbd);
1207 #endif
1208 }
1209 
1210     static void
clip_gen_request_selection(Clipboard_T * cbd)1211 clip_gen_request_selection(Clipboard_T *cbd)
1212 {
1213 #ifdef FEAT_XCLIPBOARD
1214 # ifdef FEAT_GUI
1215     if (gui.in_use)
1216 	clip_mch_request_selection(cbd);
1217     else
1218 # endif
1219 	clip_xterm_request_selection(cbd);
1220 #else
1221     clip_mch_request_selection(cbd);
1222 #endif
1223 }
1224 
1225 #if (defined(FEAT_X11) && defined(FEAT_XCLIPBOARD) && defined(USE_SYSTEM)) \
1226 	|| defined(PROTO)
1227     static int
clip_x11_owner_exists(Clipboard_T * cbd)1228 clip_x11_owner_exists(Clipboard_T *cbd)
1229 {
1230     return XGetSelectionOwner(X_DISPLAY, cbd->sel_atom) != None;
1231 }
1232 #endif
1233 
1234 #if (defined(FEAT_X11) && defined(USE_SYSTEM)) || defined(PROTO)
1235     int
clip_gen_owner_exists(Clipboard_T * cbd UNUSED)1236 clip_gen_owner_exists(Clipboard_T *cbd UNUSED)
1237 {
1238 #ifdef FEAT_XCLIPBOARD
1239 # ifdef FEAT_GUI_GTK
1240     if (gui.in_use)
1241 	return clip_gtk_owner_exists(cbd);
1242     else
1243 # endif
1244 	return clip_x11_owner_exists(cbd);
1245 #else
1246     return TRUE;
1247 #endif
1248 }
1249 #endif
1250 
1251 /*
1252  * Extract the items in the 'clipboard' option and set global values.
1253  * Return an error message or NULL for success.
1254  */
1255     char *
check_clipboard_option(void)1256 check_clipboard_option(void)
1257 {
1258     int		new_unnamed = 0;
1259     int		new_autoselect_star = FALSE;
1260     int		new_autoselect_plus = FALSE;
1261     int		new_autoselectml = FALSE;
1262     int		new_html = FALSE;
1263     regprog_T	*new_exclude_prog = NULL;
1264     char	*errmsg = NULL;
1265     char_u	*p;
1266 
1267     for (p = p_cb; *p != NUL; )
1268     {
1269 	if (STRNCMP(p, "unnamed", 7) == 0 && (p[7] == ',' || p[7] == NUL))
1270 	{
1271 	    new_unnamed |= CLIP_UNNAMED;
1272 	    p += 7;
1273 	}
1274 	else if (STRNCMP(p, "unnamedplus", 11) == 0
1275 					    && (p[11] == ',' || p[11] == NUL))
1276 	{
1277 	    new_unnamed |= CLIP_UNNAMED_PLUS;
1278 	    p += 11;
1279 	}
1280 	else if (STRNCMP(p, "autoselect", 10) == 0
1281 					    && (p[10] == ',' || p[10] == NUL))
1282 	{
1283 	    new_autoselect_star = TRUE;
1284 	    p += 10;
1285 	}
1286 	else if (STRNCMP(p, "autoselectplus", 14) == 0
1287 					    && (p[14] == ',' || p[14] == NUL))
1288 	{
1289 	    new_autoselect_plus = TRUE;
1290 	    p += 14;
1291 	}
1292 	else if (STRNCMP(p, "autoselectml", 12) == 0
1293 					    && (p[12] == ',' || p[12] == NUL))
1294 	{
1295 	    new_autoselectml = TRUE;
1296 	    p += 12;
1297 	}
1298 	else if (STRNCMP(p, "html", 4) == 0 && (p[4] == ',' || p[4] == NUL))
1299 	{
1300 	    new_html = TRUE;
1301 	    p += 4;
1302 	}
1303 	else if (STRNCMP(p, "exclude:", 8) == 0 && new_exclude_prog == NULL)
1304 	{
1305 	    p += 8;
1306 	    new_exclude_prog = vim_regcomp(p, RE_MAGIC);
1307 	    if (new_exclude_prog == NULL)
1308 		errmsg = e_invarg;
1309 	    break;
1310 	}
1311 	else
1312 	{
1313 	    errmsg = e_invarg;
1314 	    break;
1315 	}
1316 	if (*p == ',')
1317 	    ++p;
1318     }
1319     if (errmsg == NULL)
1320     {
1321 	if (global_busy)
1322 	    // clip_unnamed will be reset to clip_unnamed_saved
1323 	    // at end_global_changes
1324 	    clip_unnamed_saved = new_unnamed;
1325 	else
1326 	    clip_unnamed = new_unnamed;
1327 	clip_autoselect_star = new_autoselect_star;
1328 	clip_autoselect_plus = new_autoselect_plus;
1329 	clip_autoselectml = new_autoselectml;
1330 	clip_html = new_html;
1331 	vim_regfree(clip_exclude_prog);
1332 	clip_exclude_prog = new_exclude_prog;
1333 #ifdef FEAT_GUI_GTK
1334 	if (gui.in_use)
1335 	{
1336 	    gui_gtk_set_selection_targets();
1337 	    gui_gtk_set_dnd_targets();
1338 	}
1339 #endif
1340     }
1341     else
1342 	vim_regfree(new_exclude_prog);
1343 
1344     return errmsg;
1345 }
1346 
1347 /*
1348  * Stuff for the X clipboard.  Shared between VMS and Unix.
1349  */
1350 
1351 #if defined(FEAT_XCLIPBOARD) || defined(FEAT_GUI_X11) || defined(PROTO)
1352 # include <X11/Xatom.h>
1353 # include <X11/Intrinsic.h>
1354 
1355 /*
1356  * Open the application context (if it hasn't been opened yet).
1357  * Used for Motif and Athena GUI and the xterm clipboard.
1358  */
1359     void
open_app_context(void)1360 open_app_context(void)
1361 {
1362     if (app_context == NULL)
1363     {
1364 	XtToolkitInitialize();
1365 	app_context = XtCreateApplicationContext();
1366     }
1367 }
1368 
1369 static Atom	vim_atom;	// Vim's own special selection format
1370 static Atom	vimenc_atom;	// Vim's extended selection format
1371 static Atom	utf8_atom;
1372 static Atom	compound_text_atom;
1373 static Atom	text_atom;
1374 static Atom	targets_atom;
1375 static Atom	timestamp_atom;	// Used to get a timestamp
1376 
1377     void
x11_setup_atoms(Display * dpy)1378 x11_setup_atoms(Display *dpy)
1379 {
1380     vim_atom	       = XInternAtom(dpy, VIM_ATOM_NAME,   False);
1381     vimenc_atom	       = XInternAtom(dpy, VIMENC_ATOM_NAME,False);
1382     utf8_atom	       = XInternAtom(dpy, "UTF8_STRING",   False);
1383     compound_text_atom = XInternAtom(dpy, "COMPOUND_TEXT", False);
1384     text_atom	       = XInternAtom(dpy, "TEXT",	   False);
1385     targets_atom       = XInternAtom(dpy, "TARGETS",	   False);
1386     clip_star.sel_atom = XA_PRIMARY;
1387     clip_plus.sel_atom = XInternAtom(dpy, "CLIPBOARD",	   False);
1388     timestamp_atom     = XInternAtom(dpy, "TIMESTAMP",	   False);
1389 }
1390 
1391 /*
1392  * X Selection stuff, for cutting and pasting text to other windows.
1393  */
1394 
1395     static Boolean
clip_x11_convert_selection_cb(Widget w UNUSED,Atom * sel_atom,Atom * target,Atom * type,XtPointer * value,long_u * length,int * format)1396 clip_x11_convert_selection_cb(
1397     Widget	w UNUSED,
1398     Atom	*sel_atom,
1399     Atom	*target,
1400     Atom	*type,
1401     XtPointer	*value,
1402     long_u	*length,
1403     int		*format)
1404 {
1405     static char_u   *save_result = NULL;
1406     static long_u   save_length = 0;
1407     char_u	    *string;
1408     int		    motion_type;
1409     Clipboard_T    *cbd;
1410     int		    i;
1411 
1412     if (*sel_atom == clip_plus.sel_atom)
1413 	cbd = &clip_plus;
1414     else
1415 	cbd = &clip_star;
1416 
1417     if (!cbd->owned)
1418 	return False;	    // Shouldn't ever happen
1419 
1420     // requestor wants to know what target types we support
1421     if (*target == targets_atom)
1422     {
1423 	static Atom array[7];
1424 
1425 	*value = (XtPointer)array;
1426 	i = 0;
1427 	array[i++] = targets_atom;
1428 	array[i++] = vimenc_atom;
1429 	array[i++] = vim_atom;
1430 	if (enc_utf8)
1431 	    array[i++] = utf8_atom;
1432 	array[i++] = XA_STRING;
1433 	array[i++] = text_atom;
1434 	array[i++] = compound_text_atom;
1435 
1436 	*type = XA_ATOM;
1437 	// This used to be: *format = sizeof(Atom) * 8; but that caused
1438 	// crashes on 64 bit machines. (Peter Derr)
1439 	*format = 32;
1440 	*length = i;
1441 	return True;
1442     }
1443 
1444     if (       *target != XA_STRING
1445 	    && *target != vimenc_atom
1446 	    && (*target != utf8_atom || !enc_utf8)
1447 	    && *target != vim_atom
1448 	    && *target != text_atom
1449 	    && *target != compound_text_atom)
1450 	return False;
1451 
1452     clip_get_selection(cbd);
1453     motion_type = clip_convert_selection(&string, length, cbd);
1454     if (motion_type < 0)
1455 	return False;
1456 
1457     // For our own format, the first byte contains the motion type
1458     if (*target == vim_atom)
1459 	(*length)++;
1460 
1461     // Our own format with encoding: motion 'encoding' NUL text
1462     if (*target == vimenc_atom)
1463 	*length += STRLEN(p_enc) + 2;
1464 
1465     if (save_length < *length || save_length / 2 >= *length)
1466 	*value = XtRealloc((char *)save_result, (Cardinal)*length + 1);
1467     else
1468 	*value = save_result;
1469     if (*value == NULL)
1470     {
1471 	vim_free(string);
1472 	return False;
1473     }
1474     save_result = (char_u *)*value;
1475     save_length = *length;
1476 
1477     if (*target == XA_STRING || (*target == utf8_atom && enc_utf8))
1478     {
1479 	mch_memmove(save_result, string, (size_t)(*length));
1480 	*type = *target;
1481     }
1482     else if (*target == compound_text_atom || *target == text_atom)
1483     {
1484 	XTextProperty	text_prop;
1485 	char		*string_nt = (char *)save_result;
1486 	int		conv_result;
1487 
1488 	// create NUL terminated string which XmbTextListToTextProperty wants
1489 	mch_memmove(string_nt, string, (size_t)*length);
1490 	string_nt[*length] = NUL;
1491 	conv_result = XmbTextListToTextProperty(X_DISPLAY, (char **)&string_nt,
1492 					   1, XCompoundTextStyle, &text_prop);
1493 	if (conv_result != Success)
1494 	{
1495 	    vim_free(string);
1496 	    return False;
1497 	}
1498 	*value = (XtPointer)(text_prop.value);	//    from plain text
1499 	*length = text_prop.nitems;
1500 	*type = compound_text_atom;
1501 	XtFree((char *)save_result);
1502 	save_result = (char_u *)*value;
1503 	save_length = *length;
1504     }
1505     else if (*target == vimenc_atom)
1506     {
1507 	int l = STRLEN(p_enc);
1508 
1509 	save_result[0] = motion_type;
1510 	STRCPY(save_result + 1, p_enc);
1511 	mch_memmove(save_result + l + 2, string, (size_t)(*length - l - 2));
1512 	*type = vimenc_atom;
1513     }
1514     else
1515     {
1516 	save_result[0] = motion_type;
1517 	mch_memmove(save_result + 1, string, (size_t)(*length - 1));
1518 	*type = vim_atom;
1519     }
1520     *format = 8;	    // 8 bits per char
1521     vim_free(string);
1522     return True;
1523 }
1524 
1525     static void
clip_x11_lose_ownership_cb(Widget w UNUSED,Atom * sel_atom)1526 clip_x11_lose_ownership_cb(Widget w UNUSED, Atom *sel_atom)
1527 {
1528     if (*sel_atom == clip_plus.sel_atom)
1529 	clip_lose_selection(&clip_plus);
1530     else
1531 	clip_lose_selection(&clip_star);
1532 }
1533 
1534     static void
clip_x11_notify_cb(Widget w UNUSED,Atom * sel_atom UNUSED,Atom * target UNUSED)1535 clip_x11_notify_cb(Widget w UNUSED, Atom *sel_atom UNUSED, Atom *target UNUSED)
1536 {
1537     // To prevent automatically freeing the selection value.
1538 }
1539 
1540 /*
1541  * Property callback to get a timestamp for XtOwnSelection.
1542  */
1543     static void
clip_x11_timestamp_cb(Widget w,XtPointer n UNUSED,XEvent * event,Boolean * cont UNUSED)1544 clip_x11_timestamp_cb(
1545     Widget	w,
1546     XtPointer	n UNUSED,
1547     XEvent	*event,
1548     Boolean	*cont UNUSED)
1549 {
1550     Atom	    actual_type;
1551     int		    format;
1552     unsigned  long  nitems, bytes_after;
1553     unsigned char   *prop=NULL;
1554     XPropertyEvent  *xproperty=&event->xproperty;
1555 
1556     // Must be a property notify, state can't be Delete (True), has to be
1557     // one of the supported selection types.
1558     if (event->type != PropertyNotify || xproperty->state
1559 	    || (xproperty->atom != clip_star.sel_atom
1560 				    && xproperty->atom != clip_plus.sel_atom))
1561 	return;
1562 
1563     if (XGetWindowProperty(xproperty->display, xproperty->window,
1564 	  xproperty->atom, 0, 0, False, timestamp_atom, &actual_type, &format,
1565 						&nitems, &bytes_after, &prop))
1566 	return;
1567 
1568     if (prop)
1569 	XFree(prop);
1570 
1571     // Make sure the property type is "TIMESTAMP" and it's 32 bits.
1572     if (actual_type != timestamp_atom || format != 32)
1573 	return;
1574 
1575     // Get the selection, using the event timestamp.
1576     if (XtOwnSelection(w, xproperty->atom, xproperty->time,
1577 	    clip_x11_convert_selection_cb, clip_x11_lose_ownership_cb,
1578 	    clip_x11_notify_cb) == OK)
1579     {
1580 	// Set the "owned" flag now, there may have been a call to
1581 	// lose_ownership_cb in between.
1582 	if (xproperty->atom == clip_plus.sel_atom)
1583 	    clip_plus.owned = TRUE;
1584 	else
1585 	    clip_star.owned = TRUE;
1586     }
1587 }
1588 
1589     void
x11_setup_selection(Widget w)1590 x11_setup_selection(Widget w)
1591 {
1592     XtAddEventHandler(w, PropertyChangeMask, False,
1593 	    /*(XtEventHandler)*/clip_x11_timestamp_cb, (XtPointer)NULL);
1594 }
1595 
1596     static void
clip_x11_request_selection_cb(Widget w UNUSED,XtPointer success,Atom * sel_atom,Atom * type,XtPointer value,long_u * length,int * format)1597 clip_x11_request_selection_cb(
1598     Widget	w UNUSED,
1599     XtPointer	success,
1600     Atom	*sel_atom,
1601     Atom	*type,
1602     XtPointer	value,
1603     long_u	*length,
1604     int		*format)
1605 {
1606     int		motion_type = MAUTO;
1607     long_u	len;
1608     char_u	*p;
1609     char	**text_list = NULL;
1610     Clipboard_T	*cbd;
1611     char_u	*tmpbuf = NULL;
1612 
1613     if (*sel_atom == clip_plus.sel_atom)
1614 	cbd = &clip_plus;
1615     else
1616 	cbd = &clip_star;
1617 
1618     if (value == NULL || *length == 0)
1619     {
1620 	clip_free_selection(cbd);	// nothing received, clear register
1621 	*(int *)success = FALSE;
1622 	return;
1623     }
1624     p = (char_u *)value;
1625     len = *length;
1626     if (*type == vim_atom)
1627     {
1628 	motion_type = *p++;
1629 	len--;
1630     }
1631 
1632     else if (*type == vimenc_atom)
1633     {
1634 	char_u		*enc;
1635 	vimconv_T	conv;
1636 	int		convlen;
1637 
1638 	motion_type = *p++;
1639 	--len;
1640 
1641 	enc = p;
1642 	p += STRLEN(p) + 1;
1643 	len -= p - enc;
1644 
1645 	// If the encoding of the text is different from 'encoding', attempt
1646 	// converting it.
1647 	conv.vc_type = CONV_NONE;
1648 	convert_setup(&conv, enc, p_enc);
1649 	if (conv.vc_type != CONV_NONE)
1650 	{
1651 	    convlen = len;	// Need to use an int here.
1652 	    tmpbuf = string_convert(&conv, p, &convlen);
1653 	    len = convlen;
1654 	    if (tmpbuf != NULL)
1655 		p = tmpbuf;
1656 	    convert_setup(&conv, NULL, NULL);
1657 	}
1658     }
1659 
1660     else if (*type == compound_text_atom
1661 	    || *type == utf8_atom
1662 	    || (enc_dbcs != 0 && *type == text_atom))
1663     {
1664 	XTextProperty	text_prop;
1665 	int		n_text = 0;
1666 	int		status;
1667 
1668 	text_prop.value = (unsigned char *)value;
1669 	text_prop.encoding = *type;
1670 	text_prop.format = *format;
1671 	text_prop.nitems = len;
1672 #if defined(X_HAVE_UTF8_STRING)
1673 	if (*type == utf8_atom)
1674 	    status = Xutf8TextPropertyToTextList(X_DISPLAY, &text_prop,
1675 							 &text_list, &n_text);
1676 	else
1677 #endif
1678 	    status = XmbTextPropertyToTextList(X_DISPLAY, &text_prop,
1679 							 &text_list, &n_text);
1680 	if (status != Success || n_text < 1)
1681 	{
1682 	    *(int *)success = FALSE;
1683 	    return;
1684 	}
1685 	p = (char_u *)text_list[0];
1686 	len = STRLEN(p);
1687     }
1688     clip_yank_selection(motion_type, p, (long)len, cbd);
1689 
1690     if (text_list != NULL)
1691 	XFreeStringList(text_list);
1692     vim_free(tmpbuf);
1693     XtFree((char *)value);
1694     *(int *)success = TRUE;
1695 }
1696 
1697     void
clip_x11_request_selection(Widget myShell,Display * dpy,Clipboard_T * cbd)1698 clip_x11_request_selection(
1699     Widget	myShell,
1700     Display	*dpy,
1701     Clipboard_T	*cbd)
1702 {
1703     XEvent	event;
1704     Atom	type;
1705     static int	success;
1706     int		i;
1707     time_t	start_time;
1708     int		timed_out = FALSE;
1709 
1710     for (i = 0; i < 6; i++)
1711     {
1712 	switch (i)
1713 	{
1714 	    case 0:  type = vimenc_atom;	break;
1715 	    case 1:  type = vim_atom;		break;
1716 	    case 2:  type = utf8_atom;		break;
1717 	    case 3:  type = compound_text_atom; break;
1718 	    case 4:  type = text_atom;		break;
1719 	    default: type = XA_STRING;
1720 	}
1721 	if (type == utf8_atom
1722 # if defined(X_HAVE_UTF8_STRING)
1723 		&& !enc_utf8
1724 # endif
1725 		)
1726 	    // Only request utf-8 when 'encoding' is utf8 and
1727 	    // Xutf8TextPropertyToTextList is available.
1728 	    continue;
1729 	success = MAYBE;
1730 	XtGetSelectionValue(myShell, cbd->sel_atom, type,
1731 	    clip_x11_request_selection_cb, (XtPointer)&success, CurrentTime);
1732 
1733 	// Make sure the request for the selection goes out before waiting for
1734 	// a response.
1735 	XFlush(dpy);
1736 
1737 	/*
1738 	 * Wait for result of selection request, otherwise if we type more
1739 	 * characters, then they will appear before the one that requested the
1740 	 * paste!  Don't worry, we will catch up with any other events later.
1741 	 */
1742 	start_time = time(NULL);
1743 	while (success == MAYBE)
1744 	{
1745 	    if (XCheckTypedEvent(dpy, PropertyNotify, &event)
1746 		    || XCheckTypedEvent(dpy, SelectionNotify, &event)
1747 		    || XCheckTypedEvent(dpy, SelectionRequest, &event))
1748 	    {
1749 		// This is where clip_x11_request_selection_cb() should be
1750 		// called.  It may actually happen a bit later, so we loop
1751 		// until "success" changes.
1752 		// We may get a SelectionRequest here and if we don't handle
1753 		// it we hang.  KDE klipper does this, for example.
1754 		// We need to handle a PropertyNotify for large selections.
1755 		XtDispatchEvent(&event);
1756 		continue;
1757 	    }
1758 
1759 	    // Time out after 2 to 3 seconds to avoid that we hang when the
1760 	    // other process doesn't respond.  Note that the SelectionNotify
1761 	    // event may still come later when the selection owner comes back
1762 	    // to life and the text gets inserted unexpectedly.  Don't know
1763 	    // why that happens or how to avoid that :-(.
1764 	    if (time(NULL) > start_time + 2)
1765 	    {
1766 		timed_out = TRUE;
1767 		break;
1768 	    }
1769 
1770 	    // Do we need this?  Probably not.
1771 	    XSync(dpy, False);
1772 
1773 	    // Wait for 1 msec to avoid that we eat up all CPU time.
1774 	    ui_delay(1L, TRUE);
1775 	}
1776 
1777 	if (success == TRUE)
1778 	    return;
1779 
1780 	// don't do a retry with another type after timing out, otherwise we
1781 	// hang for 15 seconds.
1782 	if (timed_out)
1783 	    break;
1784     }
1785 
1786     // Final fallback position - use the X CUT_BUFFER0 store
1787     yank_cut_buffer0(dpy, cbd);
1788 }
1789 
1790     void
clip_x11_lose_selection(Widget myShell,Clipboard_T * cbd)1791 clip_x11_lose_selection(Widget myShell, Clipboard_T *cbd)
1792 {
1793     XtDisownSelection(myShell, cbd->sel_atom,
1794 				XtLastTimestampProcessed(XtDisplay(myShell)));
1795 }
1796 
1797     int
clip_x11_own_selection(Widget myShell,Clipboard_T * cbd)1798 clip_x11_own_selection(Widget myShell, Clipboard_T *cbd)
1799 {
1800     // When using the GUI we have proper timestamps, use the one of the last
1801     // event.  When in the console we don't get events (the terminal gets
1802     // them), Get the time by a zero-length append, clip_x11_timestamp_cb will
1803     // be called with the current timestamp.
1804 #ifdef FEAT_GUI
1805     if (gui.in_use)
1806     {
1807 	if (XtOwnSelection(myShell, cbd->sel_atom,
1808 	       XtLastTimestampProcessed(XtDisplay(myShell)),
1809 	       clip_x11_convert_selection_cb, clip_x11_lose_ownership_cb,
1810 	       clip_x11_notify_cb) == False)
1811 	    return FAIL;
1812     }
1813     else
1814 #endif
1815     {
1816 	if (!XChangeProperty(XtDisplay(myShell), XtWindow(myShell),
1817 		  cbd->sel_atom, timestamp_atom, 32, PropModeAppend, NULL, 0))
1818 	    return FAIL;
1819     }
1820     // Flush is required in a terminal as nothing else is doing it.
1821     XFlush(XtDisplay(myShell));
1822     return OK;
1823 }
1824 
1825 /*
1826  * Send the current selection to the clipboard.  Do nothing for X because we
1827  * will fill in the selection only when requested by another app.
1828  */
1829     void
clip_x11_set_selection(Clipboard_T * cbd UNUSED)1830 clip_x11_set_selection(Clipboard_T *cbd UNUSED)
1831 {
1832 }
1833 
1834 #endif
1835 
1836 #if defined(FEAT_XCLIPBOARD) || defined(FEAT_GUI_X11) \
1837     || defined(FEAT_GUI_GTK) || defined(PROTO)
1838 /*
1839  * Get the contents of the X CUT_BUFFER0 and put it in "cbd".
1840  */
1841     void
yank_cut_buffer0(Display * dpy,Clipboard_T * cbd)1842 yank_cut_buffer0(Display *dpy, Clipboard_T *cbd)
1843 {
1844     int		nbytes = 0;
1845     char_u	*buffer = (char_u *)XFetchBuffer(dpy, &nbytes, 0);
1846 
1847     if (nbytes > 0)
1848     {
1849 	int  done = FALSE;
1850 
1851 	// CUT_BUFFER0 is supposed to be always latin1.  Convert to 'enc' when
1852 	// using a multi-byte encoding.  Conversion between two 8-bit
1853 	// character sets usually fails and the text might actually be in
1854 	// 'enc' anyway.
1855 	if (has_mbyte)
1856 	{
1857 	    char_u	*conv_buf;
1858 	    vimconv_T	vc;
1859 
1860 	    vc.vc_type = CONV_NONE;
1861 	    if (convert_setup(&vc, (char_u *)"latin1", p_enc) == OK)
1862 	    {
1863 		conv_buf = string_convert(&vc, buffer, &nbytes);
1864 		if (conv_buf != NULL)
1865 		{
1866 		    clip_yank_selection(MCHAR, conv_buf, (long)nbytes, cbd);
1867 		    vim_free(conv_buf);
1868 		    done = TRUE;
1869 		}
1870 		convert_setup(&vc, NULL, NULL);
1871 	    }
1872 	}
1873 	if (!done)  // use the text without conversion
1874 	    clip_yank_selection(MCHAR, buffer, (long)nbytes, cbd);
1875 	XFree((void *)buffer);
1876 	if (p_verbose > 0)
1877 	{
1878 	    verbose_enter();
1879 	    verb_msg(_("Used CUT_BUFFER0 instead of empty selection"));
1880 	    verbose_leave();
1881 	}
1882     }
1883 }
1884 #endif
1885 
1886 /*
1887  * SELECTION / PRIMARY ('*')
1888  *
1889  * Text selection stuff that uses the GUI selection register '*'.  When using a
1890  * GUI this may be text from another window, otherwise it is the last text we
1891  * had highlighted with VIsual mode.  With mouse support, clicking the middle
1892  * button performs the paste, otherwise you will need to do <"*p>. "
1893  * If not under X, it is synonymous with the clipboard register '+'.
1894  *
1895  * X CLIPBOARD ('+')
1896  *
1897  * Text selection stuff that uses the GUI clipboard register '+'.
1898  * Under X, this matches the standard cut/paste buffer CLIPBOARD selection.
1899  * It will be used for unnamed cut/pasting is 'clipboard' contains "unnamed",
1900  * otherwise you will need to do <"+p>. "
1901  * If not under X, it is synonymous with the selection register '*'.
1902  */
1903 
1904 /*
1905  * Routine to export any final X selection we had to the environment
1906  * so that the text is still available after Vim has exited. X selections
1907  * only exist while the owning application exists, so we write to the
1908  * permanent (while X runs) store CUT_BUFFER0.
1909  * Dump the CLIPBOARD selection if we own it (it's logically the more
1910  * 'permanent' of the two), otherwise the PRIMARY one.
1911  * For now, use a hard-coded sanity limit of 1Mb of data.
1912  */
1913 #if (defined(FEAT_X11) && defined(FEAT_CLIPBOARD)) || defined(PROTO)
1914     void
x11_export_final_selection(void)1915 x11_export_final_selection(void)
1916 {
1917     Display	*dpy;
1918     char_u	*str = NULL;
1919     long_u	len = 0;
1920     int		motion_type = -1;
1921 
1922 # ifdef FEAT_GUI
1923     if (gui.in_use)
1924 	dpy = X_DISPLAY;
1925     else
1926 # endif
1927 # ifdef FEAT_XCLIPBOARD
1928 	dpy = xterm_dpy;
1929 # else
1930 	return;
1931 # endif
1932 
1933     // Get selection to export
1934     if (clip_plus.owned)
1935 	motion_type = clip_convert_selection(&str, &len, &clip_plus);
1936     else if (clip_star.owned)
1937 	motion_type = clip_convert_selection(&str, &len, &clip_star);
1938 
1939     // Check it's OK
1940     if (dpy != NULL && str != NULL && motion_type >= 0
1941 					       && len < 1024*1024 && len > 0)
1942     {
1943 	int ok = TRUE;
1944 
1945 	// The CUT_BUFFER0 is supposed to always contain latin1.  Convert from
1946 	// 'enc' when it is a multi-byte encoding.  When 'enc' is an 8-bit
1947 	// encoding conversion usually doesn't work, so keep the text as-is.
1948 	if (has_mbyte)
1949 	{
1950 	    vimconv_T	vc;
1951 
1952 	    vc.vc_type = CONV_NONE;
1953 	    if (convert_setup(&vc, p_enc, (char_u *)"latin1") == OK)
1954 	    {
1955 		int	intlen = len;
1956 		char_u	*conv_str;
1957 
1958 		vc.vc_fail = TRUE;
1959 		conv_str = string_convert(&vc, str, &intlen);
1960 		len = intlen;
1961 		if (conv_str != NULL)
1962 		{
1963 		    vim_free(str);
1964 		    str = conv_str;
1965 		}
1966 		else
1967 		{
1968 		    ok = FALSE;
1969 		}
1970 		convert_setup(&vc, NULL, NULL);
1971 	    }
1972 	    else
1973 	    {
1974 		ok = FALSE;
1975 	    }
1976 	}
1977 
1978 	// Do not store the string if conversion failed.  Better to use any
1979 	// other selection than garbled text.
1980 	if (ok)
1981 	{
1982 	    XStoreBuffer(dpy, (char *)str, (int)len, 0);
1983 	    XFlush(dpy);
1984 	}
1985     }
1986 
1987     vim_free(str);
1988 }
1989 #endif
1990 
1991     void
clip_free_selection(Clipboard_T * cbd)1992 clip_free_selection(Clipboard_T *cbd)
1993 {
1994     yankreg_T *y_ptr = get_y_current();
1995 
1996     if (cbd == &clip_plus)
1997 	set_y_current(get_y_register(PLUS_REGISTER));
1998     else
1999 	set_y_current(get_y_register(STAR_REGISTER));
2000     free_yank_all();
2001     get_y_current()->y_size = 0;
2002     set_y_current(y_ptr);
2003 }
2004 
2005 /*
2006  * Get the selected text and put it in register '*' or '+'.
2007  */
2008     void
clip_get_selection(Clipboard_T * cbd)2009 clip_get_selection(Clipboard_T *cbd)
2010 {
2011     yankreg_T	*old_y_previous, *old_y_current;
2012     pos_T	old_cursor;
2013     pos_T	old_visual;
2014     int		old_visual_mode;
2015     colnr_T	old_curswant;
2016     int		old_set_curswant;
2017     pos_T	old_op_start, old_op_end;
2018     oparg_T	oa;
2019     cmdarg_T	ca;
2020 
2021     if (cbd->owned)
2022     {
2023 	if ((cbd == &clip_plus
2024 		&& get_y_register(PLUS_REGISTER)->y_array != NULL)
2025 		|| (cbd == &clip_star
2026 		    && get_y_register(STAR_REGISTER)->y_array != NULL))
2027 	    return;
2028 
2029 	// Avoid triggering autocmds such as TextYankPost.
2030 	block_autocmds();
2031 
2032 	// Get the text between clip_star.start & clip_star.end
2033 	old_y_previous = get_y_previous();
2034 	old_y_current = get_y_current();
2035 	old_cursor = curwin->w_cursor;
2036 	old_curswant = curwin->w_curswant;
2037 	old_set_curswant = curwin->w_set_curswant;
2038 	old_op_start = curbuf->b_op_start;
2039 	old_op_end = curbuf->b_op_end;
2040 	old_visual = VIsual;
2041 	old_visual_mode = VIsual_mode;
2042 	clear_oparg(&oa);
2043 	oa.regname = (cbd == &clip_plus ? '+' : '*');
2044 	oa.op_type = OP_YANK;
2045 	CLEAR_FIELD(ca);
2046 	ca.oap = &oa;
2047 	ca.cmdchar = 'y';
2048 	ca.count1 = 1;
2049 	ca.retval = CA_NO_ADJ_OP_END;
2050 	do_pending_operator(&ca, 0, TRUE);
2051 
2052 	// restore things
2053 	set_y_previous(old_y_previous);
2054 	set_y_current(old_y_current);
2055 	curwin->w_cursor = old_cursor;
2056 	changed_cline_bef_curs();   // need to update w_virtcol et al
2057 	curwin->w_curswant = old_curswant;
2058 	curwin->w_set_curswant = old_set_curswant;
2059 	curbuf->b_op_start = old_op_start;
2060 	curbuf->b_op_end = old_op_end;
2061 	VIsual = old_visual;
2062 	VIsual_mode = old_visual_mode;
2063 
2064 	unblock_autocmds();
2065     }
2066     else if (!is_clipboard_needs_update())
2067     {
2068 	clip_free_selection(cbd);
2069 
2070 	// Try to get selected text from another window
2071 	clip_gen_request_selection(cbd);
2072     }
2073 }
2074 
2075 /*
2076  * Convert from the GUI selection string into the '*'/'+' register.
2077  */
2078     void
clip_yank_selection(int type,char_u * str,long len,Clipboard_T * cbd)2079 clip_yank_selection(
2080     int		type,
2081     char_u	*str,
2082     long	len,
2083     Clipboard_T *cbd)
2084 {
2085     yankreg_T *y_ptr;
2086 
2087     if (cbd == &clip_plus)
2088 	y_ptr = get_y_register(PLUS_REGISTER);
2089     else
2090 	y_ptr = get_y_register(STAR_REGISTER);
2091 
2092     clip_free_selection(cbd);
2093 
2094     str_to_reg(y_ptr, type, str, len, -1, FALSE);
2095 }
2096 
2097 /*
2098  * Convert the '*'/'+' register into a GUI selection string returned in *str
2099  * with length *len.
2100  * Returns the motion type, or -1 for failure.
2101  */
2102     int
clip_convert_selection(char_u ** str,long_u * len,Clipboard_T * cbd)2103 clip_convert_selection(char_u **str, long_u *len, Clipboard_T *cbd)
2104 {
2105     char_u	*p;
2106     int		lnum;
2107     int		i, j;
2108     int_u	eolsize;
2109     yankreg_T	*y_ptr;
2110 
2111     if (cbd == &clip_plus)
2112 	y_ptr = get_y_register(PLUS_REGISTER);
2113     else
2114 	y_ptr = get_y_register(STAR_REGISTER);
2115 
2116 # ifdef USE_CRNL
2117     eolsize = 2;
2118 # else
2119     eolsize = 1;
2120 # endif
2121 
2122     *str = NULL;
2123     *len = 0;
2124     if (y_ptr->y_array == NULL)
2125 	return -1;
2126 
2127     for (i = 0; i < y_ptr->y_size; i++)
2128 	*len += (long_u)STRLEN(y_ptr->y_array[i]) + eolsize;
2129 
2130     // Don't want newline character at end of last line if we're in MCHAR mode.
2131     if (y_ptr->y_type == MCHAR && *len >= eolsize)
2132 	*len -= eolsize;
2133 
2134     p = *str = alloc(*len + 1);	// add one to avoid zero
2135     if (p == NULL)
2136 	return -1;
2137     lnum = 0;
2138     for (i = 0, j = 0; i < (int)*len; i++, j++)
2139     {
2140 	if (y_ptr->y_array[lnum][j] == '\n')
2141 	    p[i] = NUL;
2142 	else if (y_ptr->y_array[lnum][j] == NUL)
2143 	{
2144 # ifdef USE_CRNL
2145 	    p[i++] = '\r';
2146 # endif
2147 	    p[i] = '\n';
2148 	    lnum++;
2149 	    j = -1;
2150 	}
2151 	else
2152 	    p[i] = y_ptr->y_array[lnum][j];
2153     }
2154     return y_ptr->y_type;
2155 }
2156 
2157 /*
2158  * When "regname" is a clipboard register, obtain the selection.  If it's not
2159  * available return zero, otherwise return "regname".
2160  */
2161     int
may_get_selection(int regname)2162 may_get_selection(int regname)
2163 {
2164     if (regname == '*')
2165     {
2166 	if (!clip_star.available)
2167 	    regname = 0;
2168 	else
2169 	    clip_get_selection(&clip_star);
2170     }
2171     else if (regname == '+')
2172     {
2173 	if (!clip_plus.available)
2174 	    regname = 0;
2175 	else
2176 	    clip_get_selection(&clip_plus);
2177     }
2178     return regname;
2179 }
2180 
2181 /*
2182  * If we have written to a clipboard register, send the text to the clipboard.
2183  */
2184     void
may_set_selection(void)2185 may_set_selection(void)
2186 {
2187     if ((get_y_current() == get_y_register(STAR_REGISTER))
2188 	    && clip_star.available)
2189     {
2190 	clip_own_selection(&clip_star);
2191 	clip_gen_set_selection(&clip_star);
2192     }
2193     else if ((get_y_current() == get_y_register(PLUS_REGISTER))
2194 	    && clip_plus.available)
2195     {
2196 	clip_own_selection(&clip_plus);
2197 	clip_gen_set_selection(&clip_plus);
2198     }
2199 }
2200 
2201 /*
2202  * Adjust the register name pointed to with "rp" for the clipboard being
2203  * used always and the clipboard being available.
2204  */
2205     void
adjust_clip_reg(int * rp)2206 adjust_clip_reg(int *rp)
2207 {
2208     // If no reg. specified, and "unnamed" or "unnamedplus" is in 'clipboard',
2209     // use '*' or '+' reg, respectively. "unnamedplus" prevails.
2210     if (*rp == 0 && (clip_unnamed != 0 || clip_unnamed_saved != 0))
2211     {
2212 	if (clip_unnamed != 0)
2213 	    *rp = ((clip_unnamed & CLIP_UNNAMED_PLUS) && clip_plus.available)
2214 								  ? '+' : '*';
2215 	else
2216 	    *rp = ((clip_unnamed_saved & CLIP_UNNAMED_PLUS)
2217 					   && clip_plus.available) ? '+' : '*';
2218     }
2219     if (!clip_star.available && *rp == '*')
2220 	*rp = 0;
2221     if (!clip_plus.available && *rp == '+')
2222 	*rp = 0;
2223 }
2224 
2225 #endif // FEAT_CLIPBOARD
2226