xref: /minix3/external/bsd/tmux/dist/window-copy.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1 /* Id */
2 
3 /*
4  * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 
21 #include <ctype.h>
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #include "tmux.h"
26 
27 struct screen *window_copy_init(struct window_pane *);
28 void	window_copy_free(struct window_pane *);
29 void	window_copy_resize(struct window_pane *, u_int, u_int);
30 void	window_copy_key(struct window_pane *, struct session *, int);
31 int	window_copy_key_input(struct window_pane *, int);
32 int	window_copy_key_numeric_prefix(struct window_pane *, int);
33 void	window_copy_mouse(
34 	    struct window_pane *, struct session *, struct mouse_event *);
35 
36 void	window_copy_redraw_lines(struct window_pane *, u_int, u_int);
37 void	window_copy_redraw_screen(struct window_pane *);
38 void	window_copy_write_line(
39 	    struct window_pane *, struct screen_write_ctx *, u_int);
40 void	window_copy_write_lines(
41 	    struct window_pane *, struct screen_write_ctx *, u_int, u_int);
42 
43 void	window_copy_scroll_to(struct window_pane *, u_int, u_int);
44 int	window_copy_search_compare(
45 	    struct grid *, u_int, u_int, struct grid *, u_int, int);
46 int	window_copy_search_lr(
47 	    struct grid *, struct grid *, u_int *, u_int, u_int, u_int, int);
48 int	window_copy_search_rl(
49 	    struct grid *, struct grid *, u_int *, u_int, u_int, u_int, int);
50 void	window_copy_search_up(struct window_pane *, const char *);
51 void	window_copy_search_down(struct window_pane *, const char *);
52 void	window_copy_goto_line(struct window_pane *, const char *);
53 void	window_copy_update_cursor(struct window_pane *, u_int, u_int);
54 void	window_copy_start_selection(struct window_pane *);
55 int	window_copy_update_selection(struct window_pane *, int);
56 void   *window_copy_get_selection(struct window_pane *, size_t *);
57 void	window_copy_copy_buffer(struct window_pane *, int, void *, size_t);
58 void	window_copy_copy_pipe(
59 	    struct window_pane *, struct session *, int, const char *);
60 void	window_copy_copy_selection(struct window_pane *, int);
61 void	window_copy_clear_selection(struct window_pane *);
62 void	window_copy_copy_line(
63 	    struct window_pane *, char **, size_t *, u_int, u_int, u_int);
64 int	window_copy_in_set(struct window_pane *, u_int, u_int, const char *);
65 u_int	window_copy_find_length(struct window_pane *, u_int);
66 void	window_copy_cursor_start_of_line(struct window_pane *);
67 void	window_copy_cursor_back_to_indentation(struct window_pane *);
68 void	window_copy_cursor_end_of_line(struct window_pane *);
69 void	window_copy_other_end(struct window_pane *);
70 void	window_copy_cursor_left(struct window_pane *);
71 void	window_copy_cursor_right(struct window_pane *);
72 void	window_copy_cursor_up(struct window_pane *, int);
73 void	window_copy_cursor_down(struct window_pane *, int);
74 void	window_copy_cursor_jump(struct window_pane *);
75 void	window_copy_cursor_jump_back(struct window_pane *);
76 void	window_copy_cursor_jump_to(struct window_pane *);
77 void	window_copy_cursor_jump_to_back(struct window_pane *);
78 void	window_copy_cursor_next_word(struct window_pane *, const char *);
79 void	window_copy_cursor_next_word_end(struct window_pane *, const char *);
80 void	window_copy_cursor_previous_word(struct window_pane *, const char *);
81 void	window_copy_scroll_up(struct window_pane *, u_int);
82 void	window_copy_scroll_down(struct window_pane *, u_int);
83 void	window_copy_rectangle_toggle(struct window_pane *);
84 
85 const struct window_mode window_copy_mode = {
86 	window_copy_init,
87 	window_copy_free,
88 	window_copy_resize,
89 	window_copy_key,
90 	window_copy_mouse,
91 	NULL,
92 };
93 
94 enum window_copy_input_type {
95 	WINDOW_COPY_OFF,
96 	WINDOW_COPY_NUMERICPREFIX,
97 	WINDOW_COPY_SEARCHUP,
98 	WINDOW_COPY_SEARCHDOWN,
99 	WINDOW_COPY_JUMPFORWARD,
100 	WINDOW_COPY_JUMPBACK,
101 	WINDOW_COPY_JUMPTOFORWARD,
102 	WINDOW_COPY_JUMPTOBACK,
103 	WINDOW_COPY_GOTOLINE,
104 };
105 
106 /*
107  * Copy-mode's visible screen (the "screen" field) is filled from one of
108  * two sources: the original contents of the pane (used when we
109  * actually enter via the "copy-mode" command, to copy the contents of
110  * the current pane), or else a series of lines containing the output
111  * from an output-writing tmux command (such as any of the "show-*" or
112  * "list-*" commands).
113  *
114  * In either case, the full content of the copy-mode grid is pointed at
115  * by the "backing" field, and is copied into "screen" as needed (that
116  * is, when scrolling occurs). When copy-mode is backed by a pane,
117  * backing points directly at that pane's screen structure (&wp->base);
118  * when backed by a list of output-lines from a command, it points at
119  * a newly-allocated screen structure (which is deallocated when the
120  * mode ends).
121  */
122 struct window_copy_mode_data {
123 	struct screen	screen;
124 
125 	struct screen  *backing;
126 	int		backing_written; /* backing display has started */
127 
128 	struct mode_key_data mdata;
129 
130 	u_int		oy;
131 
132 	u_int		selx;
133 	u_int		sely;
134 
135 	u_int		rectflag; /* are we in rectangle copy mode? */
136 
137 	u_int		cx;
138 	u_int		cy;
139 
140 	u_int		lastcx; /* position in last line with content */
141 	u_int		lastsx; /* size of last line with content */
142 
143 	enum window_copy_input_type inputtype;
144 	const char     *inputprompt;
145 	char	       *inputstr;
146 
147 	int		numprefix;
148 
149 	enum window_copy_input_type searchtype;
150 	char	       *searchstr;
151 
152 	enum window_copy_input_type jumptype;
153 	char		jumpchar;
154 };
155 
156 struct screen *
window_copy_init(struct window_pane * wp)157 window_copy_init(struct window_pane *wp)
158 {
159 	struct window_copy_mode_data	*data;
160 	struct screen			*s;
161 	int				 keys;
162 
163 	wp->modedata = data = xmalloc(sizeof *data);
164 	data->oy = 0;
165 	data->cx = 0;
166 	data->cy = 0;
167 
168 	data->lastcx = 0;
169 	data->lastsx = 0;
170 
171 	data->backing_written = 0;
172 
173 	data->rectflag = 0;
174 
175 	data->inputtype = WINDOW_COPY_OFF;
176 	data->inputprompt = NULL;
177 	data->inputstr = xstrdup("");
178 	data->numprefix = -1;
179 
180 	data->searchtype = WINDOW_COPY_OFF;
181 	data->searchstr = NULL;
182 
183 	if (wp->fd != -1)
184 		bufferevent_disable(wp->event, EV_READ|EV_WRITE);
185 
186 	data->jumptype = WINDOW_COPY_OFF;
187 	data->jumpchar = '\0';
188 
189 	s = &data->screen;
190 	screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
191 	if (options_get_number(&wp->window->options, "mode-mouse"))
192 		s->mode |= MODE_MOUSE_STANDARD;
193 
194 	keys = options_get_number(&wp->window->options, "mode-keys");
195 	if (keys == MODEKEY_EMACS)
196 		mode_key_init(&data->mdata, &mode_key_tree_emacs_copy);
197 	else
198 		mode_key_init(&data->mdata, &mode_key_tree_vi_copy);
199 
200 	data->backing = NULL;
201 
202 	return (s);
203 }
204 
205 void
window_copy_init_from_pane(struct window_pane * wp)206 window_copy_init_from_pane(struct window_pane *wp)
207 {
208 	struct window_copy_mode_data	*data = wp->modedata;
209 	struct screen			*s = &data->screen;
210 	struct screen_write_ctx	 	 ctx;
211 	u_int				 i;
212 
213 	if (wp->mode != &window_copy_mode)
214 		fatalx("not in copy mode");
215 
216 	data->backing = &wp->base;
217 	data->cx = data->backing->cx;
218 	data->cy = data->backing->cy;
219 
220 	s->cx = data->cx;
221 	s->cy = data->cy;
222 
223 	screen_write_start(&ctx, NULL, s);
224 	for (i = 0; i < screen_size_y(s); i++)
225 		window_copy_write_line(wp, &ctx, i);
226 	screen_write_cursormove(&ctx, data->cx, data->cy);
227 	screen_write_stop(&ctx);
228 }
229 
230 void
window_copy_init_for_output(struct window_pane * wp)231 window_copy_init_for_output(struct window_pane *wp)
232 {
233 	struct window_copy_mode_data	*data = wp->modedata;
234 
235 	data->backing = xmalloc(sizeof *data->backing);
236 	screen_init(data->backing, screen_size_x(&wp->base),
237 	    screen_size_y(&wp->base), UINT_MAX);
238 	data->backing->mode &= ~MODE_WRAP;
239 }
240 
241 void
window_copy_free(struct window_pane * wp)242 window_copy_free(struct window_pane *wp)
243 {
244 	struct window_copy_mode_data	*data = wp->modedata;
245 
246 	if (wp->fd != -1)
247 		bufferevent_enable(wp->event, EV_READ|EV_WRITE);
248 
249 	free(data->searchstr);
250 	free(data->inputstr);
251 
252 	if (data->backing != &wp->base) {
253 		screen_free(data->backing);
254 		free(data->backing);
255 	}
256 	screen_free(&data->screen);
257 
258 	free(data);
259 }
260 
261 void
window_copy_add(struct window_pane * wp,const char * fmt,...)262 window_copy_add(struct window_pane *wp, const char *fmt, ...)
263 {
264 	va_list	ap;
265 
266 	va_start(ap, fmt);
267 	window_copy_vadd(wp, fmt, ap);
268 	va_end(ap);
269 }
270 
271 void
window_copy_vadd(struct window_pane * wp,const char * fmt,va_list ap)272 window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap)
273 {
274 	struct window_copy_mode_data	*data = wp->modedata;
275 	struct screen			*backing = data->backing;
276 	struct screen_write_ctx	 	 back_ctx, ctx;
277 	struct grid_cell		 gc;
278 	int				 utf8flag;
279 	u_int				 old_hsize;
280 
281 	if (backing == &wp->base)
282 		return;
283 
284 	utf8flag = options_get_number(&wp->window->options, "utf8");
285 	memcpy(&gc, &grid_default_cell, sizeof gc);
286 
287 	old_hsize = screen_hsize(data->backing);
288 	screen_write_start(&back_ctx, NULL, backing);
289 	if (data->backing_written) {
290 		/*
291 		 * On the second or later line, do a CRLF before writing
292 		 * (so it's on a new line).
293 		 */
294 		screen_write_carriagereturn(&back_ctx);
295 		screen_write_linefeed(&back_ctx, 0);
296 	} else
297 		data->backing_written = 1;
298 	screen_write_vnputs(&back_ctx, 0, &gc, utf8flag, fmt, ap);
299 	screen_write_stop(&back_ctx);
300 
301 	data->oy += screen_hsize(data->backing) - old_hsize;
302 
303 	screen_write_start(&ctx, wp, &data->screen);
304 
305 	/*
306 	 * If the history has changed, draw the top line.
307 	 * (If there's any history at all, it has changed.)
308 	 */
309 	if (screen_hsize(data->backing))
310 		window_copy_redraw_lines(wp, 0, 1);
311 
312 	/* Write the line, if it's visible. */
313 	if (backing->cy + data->oy < screen_size_y(backing))
314 		window_copy_redraw_lines(wp, backing->cy, 1);
315 
316 	screen_write_stop(&ctx);
317 }
318 
319 void
window_copy_pageup(struct window_pane * wp)320 window_copy_pageup(struct window_pane *wp)
321 {
322 	struct window_copy_mode_data	*data = wp->modedata;
323 	struct screen			*s = &data->screen;
324 	u_int				 n;
325 
326 	n = 1;
327 	if (screen_size_y(s) > 2)
328 		n = screen_size_y(s) - 2;
329 	if (data->oy + n > screen_hsize(data->backing))
330 		data->oy = screen_hsize(data->backing);
331 	else
332 		data->oy += n;
333 	window_copy_update_selection(wp, 1);
334 	window_copy_redraw_screen(wp);
335 }
336 
337 void
window_copy_resize(struct window_pane * wp,u_int sx,u_int sy)338 window_copy_resize(struct window_pane *wp, u_int sx, u_int sy)
339 {
340 	struct window_copy_mode_data	*data = wp->modedata;
341 	struct screen			*s = &data->screen;
342 	struct screen_write_ctx	 	 ctx;
343 
344 	screen_resize(s, sx, sy, 0);
345 	if (data->backing != &wp->base)
346 		screen_resize(data->backing, sx, sy, 0);
347 
348 	if (data->cy > sy - 1)
349 		data->cy = sy - 1;
350 	if (data->cx > sx)
351 		data->cx = sx;
352 	if (data->oy > screen_hsize(data->backing))
353 		data->oy = screen_hsize(data->backing);
354 
355 	window_copy_clear_selection(wp);
356 
357 	screen_write_start(&ctx, NULL, s);
358 	window_copy_write_lines(wp, &ctx, 0, screen_size_y(s) - 1);
359 	screen_write_stop(&ctx);
360 
361 	window_copy_redraw_screen(wp);
362 }
363 
364 void
window_copy_key(struct window_pane * wp,struct session * sess,int key)365 window_copy_key(struct window_pane *wp, struct session *sess, int key)
366 {
367 	const char			*word_separators;
368 	struct window_copy_mode_data	*data = wp->modedata;
369 	struct screen			*s = &data->screen;
370 	u_int				 n;
371 	int				 np, keys;
372 	enum mode_key_cmd		 cmd;
373 	const char			*arg;
374 
375 	np = data->numprefix;
376 	if (np <= 0)
377 		np = 1;
378 
379 	if (data->inputtype == WINDOW_COPY_JUMPFORWARD ||
380 	    data->inputtype == WINDOW_COPY_JUMPBACK ||
381 	    data->inputtype == WINDOW_COPY_JUMPTOFORWARD ||
382 	    data->inputtype == WINDOW_COPY_JUMPTOBACK) {
383 		/* Ignore keys with modifiers. */
384 		if ((key & KEYC_MASK_MOD) == 0) {
385 			data->jumpchar = key;
386 			if (data->inputtype == WINDOW_COPY_JUMPFORWARD) {
387 				for (; np != 0; np--)
388 					window_copy_cursor_jump(wp);
389 			} else if (data->inputtype == WINDOW_COPY_JUMPBACK) {
390 				for (; np != 0; np--)
391 					window_copy_cursor_jump_back(wp);
392 			} else if (data->inputtype == WINDOW_COPY_JUMPTOFORWARD) {
393 				for (; np != 0; np--)
394 					window_copy_cursor_jump_to(wp);
395 			} else if (data->inputtype == WINDOW_COPY_JUMPTOBACK) {
396 				for (; np != 0; np--)
397 					window_copy_cursor_jump_to_back(wp);
398 			}
399 		}
400 		data->jumptype = data->inputtype;
401 		data->inputtype = WINDOW_COPY_OFF;
402 		window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
403 		return;
404 	} else if (data->inputtype == WINDOW_COPY_NUMERICPREFIX) {
405 		if (window_copy_key_numeric_prefix(wp, key) == 0)
406 			return;
407 		data->inputtype = WINDOW_COPY_OFF;
408 		window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
409 	} else if (data->inputtype != WINDOW_COPY_OFF) {
410 		if (window_copy_key_input(wp, key) != 0)
411 			goto input_off;
412 		return;
413 	}
414 
415 	cmd = mode_key_lookup(&data->mdata, key, &arg);
416 	switch (cmd) {
417 	case MODEKEYCOPY_CANCEL:
418 		window_pane_reset_mode(wp);
419 		return;
420 	case MODEKEYCOPY_OTHEREND:
421 		for (; np != 0; np--)
422 			window_copy_other_end(wp);
423 		break;
424 	case MODEKEYCOPY_LEFT:
425 		for (; np != 0; np--)
426 			window_copy_cursor_left(wp);
427 		break;
428 	case MODEKEYCOPY_RIGHT:
429 		for (; np != 0; np--)
430 			window_copy_cursor_right(wp);
431 		break;
432 	case MODEKEYCOPY_UP:
433 		for (; np != 0; np--)
434 			window_copy_cursor_up(wp, 0);
435 		break;
436 	case MODEKEYCOPY_DOWN:
437 		for (; np != 0; np--)
438 			window_copy_cursor_down(wp, 0);
439 		break;
440 	case MODEKEYCOPY_SCROLLUP:
441 		for (; np != 0; np--)
442 			window_copy_cursor_up(wp, 1);
443 		break;
444 	case MODEKEYCOPY_SCROLLDOWN:
445 		for (; np != 0; np--)
446 			window_copy_cursor_down(wp, 1);
447 		break;
448 	case MODEKEYCOPY_PREVIOUSPAGE:
449 		for (; np != 0; np--)
450 			window_copy_pageup(wp);
451 		break;
452 	case MODEKEYCOPY_NEXTPAGE:
453 		n = 1;
454 		if (screen_size_y(s) > 2)
455 			n = screen_size_y(s) - 2;
456 		for (; np != 0; np--) {
457 			if (data->oy < n)
458 				data->oy = 0;
459 			else
460 				data->oy -= n;
461 		}
462 		window_copy_update_selection(wp, 1);
463 		window_copy_redraw_screen(wp);
464 		break;
465 	case MODEKEYCOPY_HALFPAGEUP:
466 		n = screen_size_y(s) / 2;
467 		for (; np != 0; np--) {
468 			if (data->oy + n > screen_hsize(data->backing))
469 				data->oy = screen_hsize(data->backing);
470 			else
471 				data->oy += n;
472 		}
473 		window_copy_update_selection(wp, 1);
474 		window_copy_redraw_screen(wp);
475 		break;
476 	case MODEKEYCOPY_HALFPAGEDOWN:
477 		n = screen_size_y(s) / 2;
478 		for (; np != 0; np--) {
479 			if (data->oy < n)
480 				data->oy = 0;
481 			else
482 				data->oy -= n;
483 		}
484 		window_copy_update_selection(wp, 1);
485 		window_copy_redraw_screen(wp);
486 		break;
487 	case MODEKEYCOPY_TOPLINE:
488 		data->cx = 0;
489 		data->cy = 0;
490 		window_copy_update_selection(wp, 1);
491 		window_copy_redraw_screen(wp);
492 		break;
493 	case MODEKEYCOPY_MIDDLELINE:
494 		data->cx = 0;
495 		data->cy = (screen_size_y(s) - 1) / 2;
496 		window_copy_update_selection(wp, 1);
497 		window_copy_redraw_screen(wp);
498 		break;
499 	case MODEKEYCOPY_BOTTOMLINE:
500 		data->cx = 0;
501 		data->cy = screen_size_y(s) - 1;
502 		window_copy_update_selection(wp, 1);
503 		window_copy_redraw_screen(wp);
504 		break;
505 	case MODEKEYCOPY_HISTORYTOP:
506 		data->cx = 0;
507 		data->cy = 0;
508 		data->oy = screen_hsize(data->backing);
509 		window_copy_update_selection(wp, 1);
510 		window_copy_redraw_screen(wp);
511 		break;
512 	case MODEKEYCOPY_HISTORYBOTTOM:
513 		data->cx = 0;
514 		data->cy = screen_size_y(s) - 1;
515 		data->oy = 0;
516 		window_copy_update_selection(wp, 1);
517 		window_copy_redraw_screen(wp);
518 		break;
519 	case MODEKEYCOPY_STARTSELECTION:
520 		window_copy_start_selection(wp);
521 		window_copy_redraw_screen(wp);
522 		break;
523 	case MODEKEYCOPY_COPYLINE:
524 	case MODEKEYCOPY_SELECTLINE:
525 		window_copy_cursor_start_of_line(wp);
526 		/* FALLTHROUGH */
527 	case MODEKEYCOPY_COPYENDOFLINE:
528 		window_copy_start_selection(wp);
529 		for (; np > 1; np--)
530 			window_copy_cursor_down(wp, 0);
531 		window_copy_cursor_end_of_line(wp);
532 		window_copy_redraw_screen(wp);
533 
534 		/* If a copy command then copy the selection and exit. */
535 		if (sess != NULL &&
536 		    (cmd == MODEKEYCOPY_COPYLINE ||
537 		    cmd == MODEKEYCOPY_COPYENDOFLINE)) {
538 			window_copy_copy_selection(wp, -1);
539 			window_pane_reset_mode(wp);
540 			return;
541 		}
542 		break;
543 	case MODEKEYCOPY_CLEARSELECTION:
544 		window_copy_clear_selection(wp);
545 		window_copy_redraw_screen(wp);
546 		break;
547 	case MODEKEYCOPY_COPYPIPE:
548 		if (sess != NULL) {
549 			window_copy_copy_pipe(wp, sess, data->numprefix, arg);
550 			window_pane_reset_mode(wp);
551 			return;
552 		}
553 		break;
554 	case MODEKEYCOPY_COPYSELECTION:
555 		if (sess != NULL) {
556 			window_copy_copy_selection(wp, data->numprefix);
557 			window_pane_reset_mode(wp);
558 			return;
559 		}
560 		break;
561 	case MODEKEYCOPY_STARTOFLINE:
562 		window_copy_cursor_start_of_line(wp);
563 		break;
564 	case MODEKEYCOPY_BACKTOINDENTATION:
565 		window_copy_cursor_back_to_indentation(wp);
566 		break;
567 	case MODEKEYCOPY_ENDOFLINE:
568 		window_copy_cursor_end_of_line(wp);
569 		break;
570 	case MODEKEYCOPY_NEXTSPACE:
571 		for (; np != 0; np--)
572 			window_copy_cursor_next_word(wp, " ");
573 		break;
574 	case MODEKEYCOPY_NEXTSPACEEND:
575 		for (; np != 0; np--)
576 			window_copy_cursor_next_word_end(wp, " ");
577 		break;
578 	case MODEKEYCOPY_NEXTWORD:
579 		word_separators =
580 		    options_get_string(&sess->options, "word-separators");
581 		for (; np != 0; np--)
582 			window_copy_cursor_next_word(wp, word_separators);
583 		break;
584 	case MODEKEYCOPY_NEXTWORDEND:
585 		word_separators =
586 		    options_get_string(&sess->options, "word-separators");
587 		for (; np != 0; np--)
588 			window_copy_cursor_next_word_end(wp, word_separators);
589 		break;
590 	case MODEKEYCOPY_PREVIOUSSPACE:
591 		for (; np != 0; np--)
592 			window_copy_cursor_previous_word(wp, " ");
593 		break;
594 	case MODEKEYCOPY_PREVIOUSWORD:
595 		word_separators =
596 		    options_get_string(&sess->options, "word-separators");
597 		for (; np != 0; np--)
598 			window_copy_cursor_previous_word(wp, word_separators);
599 		break;
600 	case MODEKEYCOPY_JUMP:
601 		data->inputtype = WINDOW_COPY_JUMPFORWARD;
602 		data->inputprompt = "Jump Forward";
603 		*data->inputstr = '\0';
604 		window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
605 		return; /* skip numprefix reset */
606 	case MODEKEYCOPY_JUMPAGAIN:
607 		if (data->jumptype == WINDOW_COPY_JUMPFORWARD) {
608 			for (; np != 0; np--)
609 				window_copy_cursor_jump(wp);
610 		} else if (data->jumptype == WINDOW_COPY_JUMPBACK) {
611 			for (; np != 0; np--)
612 				window_copy_cursor_jump_back(wp);
613 		} else if (data->jumptype == WINDOW_COPY_JUMPTOFORWARD) {
614 			for (; np != 0; np--)
615 				window_copy_cursor_jump_to(wp);
616 		} else if (data->jumptype == WINDOW_COPY_JUMPTOBACK) {
617 			for (; np != 0; np--)
618 				window_copy_cursor_jump_to_back(wp);
619 		}
620 		break;
621 	case MODEKEYCOPY_JUMPREVERSE:
622 		if (data->jumptype == WINDOW_COPY_JUMPFORWARD) {
623 			for (; np != 0; np--)
624 				window_copy_cursor_jump_back(wp);
625 		} else if (data->jumptype == WINDOW_COPY_JUMPBACK) {
626 			for (; np != 0; np--)
627 				window_copy_cursor_jump(wp);
628 		} else if (data->jumptype == WINDOW_COPY_JUMPTOFORWARD) {
629 			for (; np != 0; np--)
630 				window_copy_cursor_jump_to_back(wp);
631 		} else if (data->jumptype == WINDOW_COPY_JUMPTOBACK) {
632 			for (; np != 0; np--)
633 				window_copy_cursor_jump_to(wp);
634 		}
635 		break;
636 	case MODEKEYCOPY_JUMPBACK:
637 		data->inputtype = WINDOW_COPY_JUMPBACK;
638 		data->inputprompt = "Jump Back";
639 		*data->inputstr = '\0';
640 		window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
641 		return; /* skip numprefix reset */
642 	case MODEKEYCOPY_JUMPTO:
643 		data->inputtype = WINDOW_COPY_JUMPTOFORWARD;
644 		data->inputprompt = "Jump To";
645 		*data->inputstr = '\0';
646 		window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
647 		return; /* skip numprefix reset */
648 	case MODEKEYCOPY_JUMPTOBACK:
649 		data->inputtype = WINDOW_COPY_JUMPTOBACK;
650 		data->inputprompt = "Jump To Back";
651 		*data->inputstr = '\0';
652 		window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
653 		return; /* skip numprefix reset */
654 	case MODEKEYCOPY_SEARCHUP:
655 		data->inputtype = WINDOW_COPY_SEARCHUP;
656 		data->inputprompt = "Search Up";
657 		goto input_on;
658 	case MODEKEYCOPY_SEARCHDOWN:
659 		data->inputtype = WINDOW_COPY_SEARCHDOWN;
660 		data->inputprompt = "Search Down";
661 		goto input_on;
662 	case MODEKEYCOPY_SEARCHAGAIN:
663 	case MODEKEYCOPY_SEARCHREVERSE:
664 		switch (data->searchtype) {
665 		case WINDOW_COPY_OFF:
666 		case WINDOW_COPY_GOTOLINE:
667 		case WINDOW_COPY_JUMPFORWARD:
668 		case WINDOW_COPY_JUMPBACK:
669 		case WINDOW_COPY_JUMPTOFORWARD:
670 		case WINDOW_COPY_JUMPTOBACK:
671 		case WINDOW_COPY_NUMERICPREFIX:
672 			break;
673 		case WINDOW_COPY_SEARCHUP:
674 			if (cmd == MODEKEYCOPY_SEARCHAGAIN) {
675 				for (; np != 0; np--) {
676 					window_copy_search_up(
677 					    wp, data->searchstr);
678 				}
679 			} else {
680 				for (; np != 0; np--) {
681 					window_copy_search_down(
682 					    wp, data->searchstr);
683 				}
684 			}
685 			break;
686 		case WINDOW_COPY_SEARCHDOWN:
687 			if (cmd == MODEKEYCOPY_SEARCHAGAIN) {
688 				for (; np != 0; np--) {
689 					window_copy_search_down(
690 					    wp, data->searchstr);
691 				}
692 			} else {
693 				for (; np != 0; np--) {
694 					window_copy_search_up(
695 					    wp, data->searchstr);
696 				}
697 			}
698 			break;
699 		}
700 		break;
701 	case MODEKEYCOPY_GOTOLINE:
702 		data->inputtype = WINDOW_COPY_GOTOLINE;
703 		data->inputprompt = "Goto Line";
704 		*data->inputstr = '\0';
705 		goto input_on;
706 	case MODEKEYCOPY_STARTNUMBERPREFIX:
707 		key &= KEYC_MASK_KEY;
708 		if (key >= '0' && key <= '9') {
709 			data->inputtype = WINDOW_COPY_NUMERICPREFIX;
710 			data->numprefix = 0;
711 			window_copy_key_numeric_prefix(wp, key);
712 			return;
713 		}
714 		break;
715 	case MODEKEYCOPY_RECTANGLETOGGLE:
716 		window_copy_rectangle_toggle(wp);
717 		break;
718 	default:
719 		break;
720 	}
721 
722 	data->numprefix = -1;
723 	return;
724 
725 input_on:
726 	keys = options_get_number(&wp->window->options, "mode-keys");
727 	if (keys == MODEKEY_EMACS)
728 		mode_key_init(&data->mdata, &mode_key_tree_emacs_edit);
729 	else
730 		mode_key_init(&data->mdata, &mode_key_tree_vi_edit);
731 
732 	window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
733 	return;
734 
735 input_off:
736 	keys = options_get_number(&wp->window->options, "mode-keys");
737 	if (keys == MODEKEY_EMACS)
738 		mode_key_init(&data->mdata, &mode_key_tree_emacs_copy);
739 	else
740 		mode_key_init(&data->mdata, &mode_key_tree_vi_copy);
741 
742 	data->inputtype = WINDOW_COPY_OFF;
743 	data->inputprompt = NULL;
744 
745 	window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
746 }
747 
748 int
window_copy_key_input(struct window_pane * wp,int key)749 window_copy_key_input(struct window_pane *wp, int key)
750 {
751 	struct window_copy_mode_data	*data = wp->modedata;
752 	struct screen			*s = &data->screen;
753 	size_t				 inputlen, n;
754 	int				 np;
755 	struct paste_buffer		*pb;
756 	u_char				 ch;
757 
758 	switch (mode_key_lookup(&data->mdata, key, NULL)) {
759 	case MODEKEYEDIT_CANCEL:
760 		data->numprefix = -1;
761 		return (-1);
762 	case MODEKEYEDIT_BACKSPACE:
763 		inputlen = strlen(data->inputstr);
764 		if (inputlen > 0)
765 			data->inputstr[inputlen - 1] = '\0';
766 		break;
767 	case MODEKEYEDIT_DELETELINE:
768 		*data->inputstr = '\0';
769 		break;
770 	case MODEKEYEDIT_PASTE:
771 		if ((pb = paste_get_top(&global_buffers)) == NULL)
772 			break;
773 		for (n = 0; n < pb->size; n++) {
774 			ch = (u_char) pb->data[n];
775 			if (ch < 32 || ch == 127)
776 				break;
777 		}
778 		inputlen = strlen(data->inputstr);
779 
780 		data->inputstr = xrealloc(data->inputstr, 1, inputlen + n + 1);
781 		memcpy(data->inputstr + inputlen, pb->data, n);
782 		data->inputstr[inputlen + n] = '\0';
783 		break;
784 	case MODEKEYEDIT_ENTER:
785 		np = data->numprefix;
786 		if (np <= 0)
787 			np = 1;
788 
789 		switch (data->inputtype) {
790 		case WINDOW_COPY_OFF:
791 		case WINDOW_COPY_JUMPFORWARD:
792 		case WINDOW_COPY_JUMPBACK:
793 		case WINDOW_COPY_JUMPTOFORWARD:
794 		case WINDOW_COPY_JUMPTOBACK:
795 		case WINDOW_COPY_NUMERICPREFIX:
796 			break;
797 		case WINDOW_COPY_SEARCHUP:
798 			for (; np != 0; np--)
799 				window_copy_search_up(wp, data->inputstr);
800 			data->searchtype = data->inputtype;
801 			data->searchstr = xstrdup(data->inputstr);
802 			break;
803 		case WINDOW_COPY_SEARCHDOWN:
804 			for (; np != 0; np--)
805 				window_copy_search_down(wp, data->inputstr);
806 			data->searchtype = data->inputtype;
807 			data->searchstr = xstrdup(data->inputstr);
808 			break;
809 		case WINDOW_COPY_GOTOLINE:
810 			window_copy_goto_line(wp, data->inputstr);
811 			*data->inputstr = '\0';
812 			break;
813 		}
814 		data->numprefix = -1;
815 		return (1);
816 	case MODEKEY_OTHER:
817 		if (key < 32 || key > 126)
818 			break;
819 		inputlen = strlen(data->inputstr) + 2;
820 
821 		data->inputstr = xrealloc(data->inputstr, 1, inputlen);
822 		data->inputstr[inputlen - 2] = key;
823 		data->inputstr[inputlen - 1] = '\0';
824 		break;
825 	default:
826 		break;
827 	}
828 
829 	window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
830 	return (0);
831 }
832 
833 int
window_copy_key_numeric_prefix(struct window_pane * wp,int key)834 window_copy_key_numeric_prefix(struct window_pane *wp, int key)
835 {
836 	struct window_copy_mode_data	*data = wp->modedata;
837 	struct screen			*s = &data->screen;
838 
839 	key &= KEYC_MASK_KEY;
840 	if (key < '0' || key > '9')
841 		return (1);
842 
843 	if (data->numprefix >= 100) 	/* no more than three digits */
844 		return (0);
845 	data->numprefix = data->numprefix * 10 + key - '0';
846 
847 	window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
848 	return (0);
849 }
850 
851 void
window_copy_mouse(struct window_pane * wp,struct session * sess,struct mouse_event * m)852 window_copy_mouse(
853     struct window_pane *wp, struct session *sess, struct mouse_event *m)
854 {
855 	struct window_copy_mode_data	*data = wp->modedata;
856 	struct screen			*s = &data->screen;
857 	u_int				 i;
858 
859 	if (m->x >= screen_size_x(s))
860 		return;
861 	if (m->y >= screen_size_y(s))
862 		return;
863 
864 	/* If mouse wheel (buttons 4 and 5), scroll. */
865 	if (m->event == MOUSE_EVENT_WHEEL) {
866 		if (m->wheel == MOUSE_WHEEL_UP) {
867 			for (i = 0; i < 5; i++)
868 				window_copy_cursor_up(wp, 1);
869 		} else if (m->wheel == MOUSE_WHEEL_DOWN) {
870 			for (i = 0; i < 5; i++)
871 				window_copy_cursor_down(wp, 1);
872 			/*
873 			 * We reached the bottom, leave copy mode,
874 			 * but only if no selection is in progress.
875 			 */
876 			if (data->oy == 0 && !s->sel.flag)
877 			    goto reset_mode;
878 		}
879 		return;
880 	}
881 
882 	/*
883 	 * If already reading motion, move the cursor while buttons are still
884 	 * pressed, or stop the selection on their release.
885 	 */
886 	if (s->mode & MODE_MOUSE_BUTTON) {
887 		if (~m->event & MOUSE_EVENT_UP) {
888 			window_copy_update_cursor(wp, m->x, m->y);
889 			if (window_copy_update_selection(wp, 1))
890 				window_copy_redraw_screen(wp);
891 			return;
892 		}
893 		goto reset_mode;
894 	}
895 
896 	/* Otherwise if other buttons pressed, start selection and motion. */
897 	if (~m->event & MOUSE_EVENT_UP) {
898 		s->mode &= ~MODE_MOUSE_STANDARD;
899 		s->mode |= MODE_MOUSE_BUTTON;
900 
901 		window_copy_update_cursor(wp, m->x, m->y);
902 		window_copy_start_selection(wp);
903 		window_copy_redraw_screen(wp);
904 	}
905 
906 	return;
907 
908 reset_mode:
909 	s->mode &= ~MODE_MOUSE_BUTTON;
910 	s->mode |= MODE_MOUSE_STANDARD;
911 	if (sess != NULL) {
912 		window_copy_copy_selection(wp, -1);
913 		window_pane_reset_mode(wp);
914 	}
915 }
916 
917 void
window_copy_scroll_to(struct window_pane * wp,u_int px,u_int py)918 window_copy_scroll_to(struct window_pane *wp, u_int px, u_int py)
919 {
920 	struct window_copy_mode_data	*data = wp->modedata;
921 	struct grid			*gd = data->backing->grid;
922 	u_int				 offset, gap;
923 
924 	data->cx = px;
925 
926 	gap = gd->sy / 4;
927 	if (py < gd->sy) {
928 		offset = 0;
929 		data->cy = py;
930 	} else if (py > gd->hsize + gd->sy - gap) {
931 		offset = gd->hsize;
932 		data->cy = py - gd->hsize;
933 	} else {
934 		offset = py + gap - gd->sy;
935 		data->cy = py - offset;
936 	}
937 	data->oy = gd->hsize - offset;
938 
939 	window_copy_update_selection(wp, 1);
940 	window_copy_redraw_screen(wp);
941 }
942 
943 int
window_copy_search_compare(struct grid * gd,u_int px,u_int py,struct grid * sgd,u_int spx,int cis)944 window_copy_search_compare(
945     struct grid *gd, u_int px, u_int py, struct grid *sgd, u_int spx, int cis)
946 {
947 	const struct grid_cell	*gc, *sgc;
948 	struct utf8_data	 ud, sud;
949 
950 	gc = grid_peek_cell(gd, px, py);
951 	grid_cell_get(gc, &ud);
952 	sgc = grid_peek_cell(sgd, spx, 0);
953 	grid_cell_get(sgc, &sud);
954 
955 	if (ud.size != sud.size || ud.width != sud.width)
956 		return (0);
957 
958 	if (cis && ud.size == 1)
959 		return (tolower(ud.data[0]) == sud.data[0]);
960 
961 	return (memcmp(ud.data, sud.data, ud.size) == 0);
962 }
963 
964 int
window_copy_search_lr(struct grid * gd,struct grid * sgd,u_int * ppx,u_int py,u_int first,u_int last,int cis)965 window_copy_search_lr(struct grid *gd,
966     struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis)
967 {
968 	u_int	ax, bx, px;
969 	int	matched;
970 
971 	for (ax = first; ax < last; ax++) {
972 		if (ax + sgd->sx >= gd->sx)
973 			break;
974 		for (bx = 0; bx < sgd->sx; bx++) {
975 			px = ax + bx;
976 			matched = window_copy_search_compare(gd, px, py, sgd,
977 			    bx, cis);
978 			if (!matched)
979 				break;
980 		}
981 		if (bx == sgd->sx) {
982 			*ppx = ax;
983 			return (1);
984 		}
985 	}
986 	return (0);
987 }
988 
989 int
window_copy_search_rl(struct grid * gd,struct grid * sgd,u_int * ppx,u_int py,u_int first,u_int last,int cis)990 window_copy_search_rl(struct grid *gd,
991     struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis)
992 {
993 	u_int	ax, bx, px;
994 	int	matched;
995 
996 	for (ax = last + 1; ax > first; ax--) {
997 		if (gd->sx - (ax - 1) < sgd->sx)
998 			continue;
999 		for (bx = 0; bx < sgd->sx; bx++) {
1000 			px = ax - 1 + bx;
1001 			matched = window_copy_search_compare(gd, px, py, sgd,
1002 			    bx, cis);
1003 			if (!matched)
1004 				break;
1005 		}
1006 		if (bx == sgd->sx) {
1007 			*ppx = ax - 1;
1008 			return (1);
1009 		}
1010 	}
1011 	return (0);
1012 }
1013 
1014 void
window_copy_search_up(struct window_pane * wp,const char * searchstr)1015 window_copy_search_up(struct window_pane *wp, const char *searchstr)
1016 {
1017 	struct window_copy_mode_data	*data = wp->modedata;
1018 	struct screen			*s = data->backing, ss;
1019 	struct screen_write_ctx		 ctx;
1020 	struct grid			*gd = s->grid, *sgd;
1021 	struct grid_cell	 	 gc;
1022 	size_t				 searchlen;
1023 	u_int				 i, last, fx, fy, px;
1024 	int				 utf8flag, n, wrapped, wrapflag, cis;
1025 	const char			*ptr;
1026 
1027 	if (*searchstr == '\0')
1028 		return;
1029 	utf8flag = options_get_number(&wp->window->options, "utf8");
1030 	wrapflag = options_get_number(&wp->window->options, "wrap-search");
1031 	searchlen = screen_write_strlen(utf8flag, "%s", searchstr);
1032 
1033 	screen_init(&ss, searchlen, 1, 0);
1034 	screen_write_start(&ctx, NULL, &ss);
1035 	memcpy(&gc, &grid_default_cell, sizeof gc);
1036 	screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr);
1037 	screen_write_stop(&ctx);
1038 
1039 	fx = data->cx;
1040 	fy = gd->hsize - data->oy + data->cy;
1041 
1042 	if (fx == 0) {
1043 		if (fy == 0)
1044 			return;
1045 		fx = gd->sx - 1;
1046 		fy--;
1047 	} else
1048 		fx--;
1049 	n = wrapped = 0;
1050 
1051 	cis = 1;
1052 	for (ptr = searchstr; *ptr != '\0'; ptr++) {
1053 		if (*ptr != tolower((u_char)*ptr)) {
1054 			cis = 0;
1055 			break;
1056 		}
1057 	}
1058 
1059 retry:
1060 	sgd = ss.grid;
1061 	for (i = fy + 1; i > 0; i--) {
1062 		last = screen_size_x(s);
1063 		if (i == fy + 1)
1064 			last = fx;
1065 		n = window_copy_search_rl(gd, sgd, &px, i - 1, 0, last, cis);
1066 		if (n) {
1067 			window_copy_scroll_to(wp, px, i - 1);
1068 			break;
1069 		}
1070 	}
1071 	if (wrapflag && !n && !wrapped) {
1072 		fx = gd->sx - 1;
1073 		fy = gd->hsize + gd->sy - 1;
1074 		wrapped = 1;
1075 		goto retry;
1076 	}
1077 
1078 	screen_free(&ss);
1079 }
1080 
1081 void
window_copy_search_down(struct window_pane * wp,const char * searchstr)1082 window_copy_search_down(struct window_pane *wp, const char *searchstr)
1083 {
1084 	struct window_copy_mode_data	*data = wp->modedata;
1085 	struct screen			*s = data->backing, ss;
1086 	struct screen_write_ctx		 ctx;
1087 	struct grid			*gd = s->grid, *sgd;
1088 	struct grid_cell	 	 gc;
1089 	size_t				 searchlen;
1090 	u_int				 i, first, fx, fy, px;
1091 	int				 utf8flag, n, wrapped, wrapflag, cis;
1092 	const char			*ptr;
1093 
1094 	if (*searchstr == '\0')
1095 		return;
1096 	utf8flag = options_get_number(&wp->window->options, "utf8");
1097 	wrapflag = options_get_number(&wp->window->options, "wrap-search");
1098 	searchlen = screen_write_strlen(utf8flag, "%s", searchstr);
1099 
1100 	screen_init(&ss, searchlen, 1, 0);
1101 	screen_write_start(&ctx, NULL, &ss);
1102 	memcpy(&gc, &grid_default_cell, sizeof gc);
1103 	screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr);
1104 	screen_write_stop(&ctx);
1105 
1106 	fx = data->cx;
1107 	fy = gd->hsize - data->oy + data->cy;
1108 
1109 	if (fx == gd->sx - 1) {
1110 		if (fy == gd->hsize + gd->sy)
1111 			return;
1112 		fx = 0;
1113 		fy++;
1114 	} else
1115 		fx++;
1116 	n = wrapped = 0;
1117 
1118 	cis = 1;
1119 	for (ptr = searchstr; *ptr != '\0'; ptr++) {
1120 		if (*ptr != tolower((u_char)*ptr)) {
1121 			cis = 0;
1122 			break;
1123 		}
1124 	}
1125 
1126 retry:
1127 	sgd = ss.grid;
1128 	for (i = fy + 1; i < gd->hsize + gd->sy + 1; i++) {
1129 		first = 0;
1130 		if (i == fy + 1)
1131 			first = fx;
1132 		n = window_copy_search_lr(gd, sgd, &px, i - 1, first, gd->sx,
1133 		    cis);
1134 		if (n) {
1135 			window_copy_scroll_to(wp, px, i - 1);
1136 			break;
1137 		}
1138 	}
1139 	if (wrapflag && !n && !wrapped) {
1140 		fx = 0;
1141 		fy = 0;
1142 		wrapped = 1;
1143 		goto retry;
1144 	}
1145 
1146 	screen_free(&ss);
1147 }
1148 
1149 void
window_copy_goto_line(struct window_pane * wp,const char * linestr)1150 window_copy_goto_line(struct window_pane *wp, const char *linestr)
1151 {
1152 	struct window_copy_mode_data	*data = wp->modedata;
1153 	const char			*errstr;
1154 	u_int				 lineno;
1155 
1156 	lineno = strtonum(linestr, 0, screen_hsize(data->backing), &errstr);
1157 	if (errstr != NULL)
1158 		return;
1159 
1160 	data->oy = lineno;
1161 	window_copy_update_selection(wp, 1);
1162 	window_copy_redraw_screen(wp);
1163 }
1164 
1165 void
window_copy_write_line(struct window_pane * wp,struct screen_write_ctx * ctx,u_int py)1166 window_copy_write_line(
1167     struct window_pane *wp, struct screen_write_ctx *ctx, u_int py)
1168 {
1169 	struct window_copy_mode_data	*data = wp->modedata;
1170 	struct screen			*s = &data->screen;
1171 	struct options			*oo = &wp->window->options;
1172 	struct grid_cell		 gc;
1173 	char				 hdr[512];
1174 	size_t				 last, xoff = 0, size = 0, limit;
1175 
1176 	style_apply(&gc, oo, "mode-style");
1177 
1178 	last = screen_size_y(s) - 1;
1179 	if (py == 0) {
1180 		size = xsnprintf(hdr, sizeof hdr,
1181 		    "[%u/%u]", data->oy, screen_hsize(data->backing));
1182 		if (size > screen_size_x(s))
1183 			size = screen_size_x(s);
1184 		screen_write_cursormove(ctx, screen_size_x(s) - size, 0);
1185 		screen_write_puts(ctx, &gc, "%s", hdr);
1186 	} else if (py == last && data->inputtype != WINDOW_COPY_OFF) {
1187 		limit = sizeof hdr;
1188 		if (limit > screen_size_x(s))
1189 			limit = screen_size_x(s);
1190 		if (data->inputtype == WINDOW_COPY_NUMERICPREFIX) {
1191 			xoff = size = xsnprintf(hdr, limit,
1192 			    "Repeat: %u", data->numprefix);
1193 		} else {
1194 			xoff = size = xsnprintf(hdr, limit,
1195 			    "%s: %s", data->inputprompt, data->inputstr);
1196 		}
1197 		screen_write_cursormove(ctx, 0, last);
1198 		screen_write_puts(ctx, &gc, "%s", hdr);
1199 	} else
1200 		size = 0;
1201 
1202 	screen_write_cursormove(ctx, xoff, py);
1203 	screen_write_copy(ctx, data->backing, xoff,
1204 	    (screen_hsize(data->backing) - data->oy) + py,
1205 	    screen_size_x(s) - size, 1);
1206 
1207 	if (py == data->cy && data->cx == screen_size_x(s)) {
1208 		memcpy(&gc, &grid_default_cell, sizeof gc);
1209 		screen_write_cursormove(ctx, screen_size_x(s) - 1, py);
1210 		screen_write_putc(ctx, &gc, '$');
1211 	}
1212 }
1213 
1214 void
window_copy_write_lines(struct window_pane * wp,struct screen_write_ctx * ctx,u_int py,u_int ny)1215 window_copy_write_lines(
1216     struct window_pane *wp, struct screen_write_ctx *ctx, u_int py, u_int ny)
1217 {
1218 	u_int	yy;
1219 
1220 	for (yy = py; yy < py + ny; yy++)
1221 		window_copy_write_line(wp, ctx, py);
1222 }
1223 
1224 void
window_copy_redraw_lines(struct window_pane * wp,u_int py,u_int ny)1225 window_copy_redraw_lines(struct window_pane *wp, u_int py, u_int ny)
1226 {
1227 	struct window_copy_mode_data	*data = wp->modedata;
1228 	struct screen_write_ctx	 	 ctx;
1229 	u_int				 i;
1230 
1231 	screen_write_start(&ctx, wp, NULL);
1232 	for (i = py; i < py + ny; i++)
1233 		window_copy_write_line(wp, &ctx, i);
1234 	screen_write_cursormove(&ctx, data->cx, data->cy);
1235 	screen_write_stop(&ctx);
1236 }
1237 
1238 void
window_copy_redraw_screen(struct window_pane * wp)1239 window_copy_redraw_screen(struct window_pane *wp)
1240 {
1241 	struct window_copy_mode_data	*data = wp->modedata;
1242 
1243 	window_copy_redraw_lines(wp, 0, screen_size_y(&data->screen));
1244 }
1245 
1246 void
window_copy_update_cursor(struct window_pane * wp,u_int cx,u_int cy)1247 window_copy_update_cursor(struct window_pane *wp, u_int cx, u_int cy)
1248 {
1249 	struct window_copy_mode_data	*data = wp->modedata;
1250 	struct screen			*s = &data->screen;
1251 	struct screen_write_ctx		 ctx;
1252 	u_int				 old_cx, old_cy;
1253 
1254 	old_cx = data->cx; old_cy = data->cy;
1255 	data->cx = cx; data->cy = cy;
1256 	if (old_cx == screen_size_x(s))
1257 		window_copy_redraw_lines(wp, old_cy, 1);
1258 	if (data->cx == screen_size_x(s))
1259 		window_copy_redraw_lines(wp, data->cy, 1);
1260 	else {
1261 		screen_write_start(&ctx, wp, NULL);
1262 		screen_write_cursormove(&ctx, data->cx, data->cy);
1263 		screen_write_stop(&ctx);
1264 	}
1265 }
1266 
1267 void
window_copy_start_selection(struct window_pane * wp)1268 window_copy_start_selection(struct window_pane *wp)
1269 {
1270 	struct window_copy_mode_data	*data = wp->modedata;
1271 	struct screen			*s = &data->screen;
1272 
1273 	data->selx = data->cx;
1274 	data->sely = screen_hsize(data->backing) + data->cy - data->oy;
1275 
1276 	s->sel.flag = 1;
1277 	window_copy_update_selection(wp, 1);
1278 }
1279 
1280 int
window_copy_update_selection(struct window_pane * wp,int may_redraw)1281 window_copy_update_selection(struct window_pane *wp, int may_redraw)
1282 {
1283 	struct window_copy_mode_data	*data = wp->modedata;
1284 	struct screen			*s = &data->screen;
1285 	struct options			*oo = &wp->window->options;
1286 	struct grid_cell		 gc;
1287 	u_int				 sx, sy, ty, cy;
1288 
1289 	if (!s->sel.flag)
1290 		return (0);
1291 
1292 	/* Set colours. */
1293 	style_apply(&gc, oo, "mode-style");
1294 
1295 	/* Find top of screen. */
1296 	ty = screen_hsize(data->backing) - data->oy;
1297 
1298 	/* Adjust the selection. */
1299 	sx = data->selx;
1300 	sy = data->sely;
1301 	if (sy < ty) {					/* above screen */
1302 		if (!data->rectflag)
1303 			sx = 0;
1304 		sy = 0;
1305 	} else if (sy > ty + screen_size_y(s) - 1) {	/* below screen */
1306 		if (!data->rectflag)
1307 			sx = screen_size_x(s) - 1;
1308 		sy = screen_size_y(s) - 1;
1309 	} else
1310 		sy -= ty;
1311 	sy = screen_hsize(s) + sy;
1312 
1313 	screen_set_selection(s,
1314 	    sx, sy, data->cx, screen_hsize(s) + data->cy, data->rectflag, &gc);
1315 
1316 	if (data->rectflag && may_redraw) {
1317 		/*
1318 		 * Can't rely on the caller to redraw the right lines for
1319 		 * rectangle selection - find the highest line and the number
1320 		 * of lines, and redraw just past that in both directions
1321 		 */
1322 		cy = data->cy;
1323 		if (sy < cy)
1324 			window_copy_redraw_lines(wp, sy, cy - sy + 1);
1325 		else
1326 			window_copy_redraw_lines(wp, cy, sy - cy + 1);
1327 	}
1328 
1329 	return (1);
1330 }
1331 
1332 void *
window_copy_get_selection(struct window_pane * wp,size_t * len)1333 window_copy_get_selection(struct window_pane *wp, size_t *len)
1334 {
1335 	struct window_copy_mode_data	*data = wp->modedata;
1336 	struct screen			*s = &data->screen;
1337 	char				*buf;
1338 	size_t				 off;
1339 	u_int				 i, xx, yy, sx, sy, ex, ey;
1340 	u_int				 firstsx, lastex, restex, restsx;
1341 	int				 keys;
1342 
1343 	if (!s->sel.flag)
1344 		return (NULL);
1345 
1346 	buf = xmalloc(1);
1347 	off = 0;
1348 
1349 	*buf = '\0';
1350 
1351 	/*
1352 	 * The selection extends from selx,sely to (adjusted) cx,cy on
1353 	 * the base screen.
1354 	 */
1355 
1356 	/* Find start and end. */
1357 	xx = data->cx;
1358 	yy = screen_hsize(data->backing) + data->cy - data->oy;
1359 	if (yy < data->sely || (yy == data->sely && xx < data->selx)) {
1360 		sx = xx; sy = yy;
1361 		ex = data->selx; ey = data->sely;
1362 	} else {
1363 		sx = data->selx; sy = data->sely;
1364 		ex = xx; ey = yy;
1365 	}
1366 
1367 	/* Trim ex to end of line. */
1368 	xx = window_copy_find_length(wp, ey);
1369 	if (ex > xx)
1370 		ex = xx;
1371 
1372 	/*
1373 	 * Deal with rectangle-copy if necessary; four situations: start of
1374 	 * first line (firstsx), end of last line (lastex), start (restsx) and
1375 	 * end (restex) of all other lines.
1376 	 */
1377 	xx = screen_size_x(s);
1378 
1379 	/*
1380 	 * Behave according to mode-keys. If it is emacs, copy like emacs,
1381 	 * keeping the top-left-most character, and dropping the
1382 	 * bottom-right-most, regardless of copy direction. If it is vi, also
1383 	 * keep bottom-right-most character.
1384 	 */
1385 	keys = options_get_number(&wp->window->options, "mode-keys");
1386 	if (data->rectflag) {
1387 		/*
1388 		 * Need to ignore the column with the cursor in it, which for
1389 		 * rectangular copy means knowing which side the cursor is on.
1390 		 */
1391 		if (data->selx < data->cx) {
1392 			/* Selection start is on the left. */
1393 			if (keys == MODEKEY_EMACS) {
1394 				lastex = data->cx;
1395 				restex = data->cx;
1396 			}
1397 			else {
1398 				lastex = data->cx + 1;
1399 				restex = data->cx + 1;
1400 			}
1401 			firstsx = data->selx;
1402 			restsx = data->selx;
1403 		} else {
1404 			/* Cursor is on the left. */
1405 			lastex = data->selx + 1;
1406 			restex = data->selx + 1;
1407 			firstsx = data->cx;
1408 			restsx = data->cx;
1409 		}
1410 	} else {
1411 		if (keys == MODEKEY_EMACS)
1412 			lastex = ex;
1413 		else
1414 			lastex = ex + 1;
1415 		restex = xx;
1416 		firstsx = sx;
1417 		restsx = 0;
1418 	}
1419 
1420 	/* Copy the lines. */
1421 	if (sy == ey)
1422 		window_copy_copy_line(wp, &buf, &off, sy, firstsx, lastex);
1423 	else {
1424 		window_copy_copy_line(wp, &buf, &off, sy, firstsx, restex);
1425 		if (ey - sy > 1) {
1426 			for (i = sy + 1; i < ey; i++) {
1427 				window_copy_copy_line(
1428 				    wp, &buf, &off, i, restsx, restex);
1429 			}
1430 		}
1431 		window_copy_copy_line(wp, &buf, &off, ey, restsx, lastex);
1432 	}
1433 
1434 	/* Don't bother if no data. */
1435 	if (off == 0) {
1436 		free(buf);
1437 		return (NULL);
1438 	}
1439 	*len = off - 1;	/* remove final \n */
1440 	return (buf);
1441 }
1442 
1443 void
window_copy_copy_buffer(struct window_pane * wp,int idx,void * buf,size_t len)1444 window_copy_copy_buffer(struct window_pane *wp, int idx, void *buf, size_t len)
1445 {
1446 	u_int			limit;
1447 	struct screen_write_ctx	ctx;
1448 
1449 	if (options_get_number(&global_options, "set-clipboard")) {
1450 		screen_write_start(&ctx, wp, NULL);
1451 		screen_write_setselection(&ctx, buf, len);
1452 		screen_write_stop(&ctx);
1453 	}
1454 
1455 	if (idx == -1) {
1456 		limit = options_get_number(&global_options, "buffer-limit");
1457 		paste_add(&global_buffers, buf, len, limit);
1458 	} else if (paste_replace(&global_buffers, idx, buf, len) != 0)
1459 		free(buf);
1460 }
1461 
1462 void
window_copy_copy_pipe(struct window_pane * wp,struct session * sess,int idx,const char * arg)1463 window_copy_copy_pipe(
1464     struct window_pane *wp, struct session *sess, int idx, const char *arg)
1465 {
1466 	void		*buf;
1467 	size_t		 len;
1468 	struct job	*job;
1469 
1470 
1471 	buf = window_copy_get_selection(wp, &len);
1472 	if (buf == NULL)
1473 		return;
1474 
1475 	job = job_run(arg, sess, NULL, NULL, NULL);
1476 	bufferevent_write(job->event, buf, len);
1477 
1478 	window_copy_copy_buffer(wp, idx, buf, len);
1479 }
1480 
1481 void
window_copy_copy_selection(struct window_pane * wp,int idx)1482 window_copy_copy_selection(struct window_pane *wp, int idx)
1483 {
1484 	void*	buf;
1485 	size_t	len;
1486 
1487 	buf = window_copy_get_selection(wp, &len);
1488 	if (buf == NULL)
1489 		return;
1490 
1491 	window_copy_copy_buffer(wp, idx, buf, len);
1492 }
1493 
1494 void
window_copy_copy_line(struct window_pane * wp,char ** buf,size_t * off,u_int sy,u_int sx,u_int ex)1495 window_copy_copy_line(struct window_pane *wp,
1496     char **buf, size_t *off, u_int sy, u_int sx, u_int ex)
1497 {
1498 	struct window_copy_mode_data	*data = wp->modedata;
1499 	struct grid			*gd = data->backing->grid;
1500 	const struct grid_cell		*gc;
1501 	struct grid_line		*gl;
1502 	struct utf8_data		 ud;
1503 	u_int				 i, xx, wrapped = 0;
1504 
1505 	if (sx > ex)
1506 		return;
1507 
1508 	/*
1509 	 * Work out if the line was wrapped at the screen edge and all of it is
1510 	 * on screen.
1511 	 */
1512 	gl = &gd->linedata[sy];
1513 	if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx)
1514 		wrapped = 1;
1515 
1516 	/* If the line was wrapped, don't strip spaces (use the full length). */
1517 	if (wrapped)
1518 		xx = gl->cellsize;
1519 	else
1520 		xx = window_copy_find_length(wp, sy);
1521 	if (ex > xx)
1522 		ex = xx;
1523 	if (sx > xx)
1524 		sx = xx;
1525 
1526 	if (sx < ex) {
1527 		for (i = sx; i < ex; i++) {
1528 			gc = grid_peek_cell(gd, i, sy);
1529 			if (gc->flags & GRID_FLAG_PADDING)
1530 				continue;
1531 			grid_cell_get(gc, &ud);
1532 
1533 			*buf = xrealloc(*buf, 1, (*off) + ud.size);
1534 			memcpy(*buf + *off, ud.data, ud.size);
1535 			*off += ud.size;
1536 		}
1537 	}
1538 
1539 	/* Only add a newline if the line wasn't wrapped. */
1540 	if (!wrapped || ex != xx) {
1541 		*buf = xrealloc(*buf, 1, (*off) + 1);
1542 		(*buf)[(*off)++] = '\n';
1543 	}
1544 }
1545 
1546 void
window_copy_clear_selection(struct window_pane * wp)1547 window_copy_clear_selection(struct window_pane *wp)
1548 {
1549 	struct window_copy_mode_data   *data = wp->modedata;
1550 	u_int				px, py;
1551 
1552 	screen_clear_selection(&data->screen);
1553 
1554 	py = screen_hsize(data->backing) + data->cy - data->oy;
1555 	px = window_copy_find_length(wp, py);
1556 	if (data->cx > px)
1557 		window_copy_update_cursor(wp, px, data->cy);
1558 }
1559 
1560 int
window_copy_in_set(struct window_pane * wp,u_int px,u_int py,const char * set)1561 window_copy_in_set(struct window_pane *wp, u_int px, u_int py, const char *set)
1562 {
1563 	struct window_copy_mode_data	*data = wp->modedata;
1564 	const struct grid_cell		*gc;
1565 	struct utf8_data		 ud;
1566 
1567 	gc = grid_peek_cell(data->backing->grid, px, py);
1568 	grid_cell_get(gc, &ud);
1569 	if (ud.size != 1 || gc->flags & GRID_FLAG_PADDING)
1570 		return (0);
1571 	if (*ud.data == 0x00 || *ud.data == 0x7f)
1572 		return (0);
1573 	return (strchr(set, *ud.data) != NULL);
1574 }
1575 
1576 u_int
window_copy_find_length(struct window_pane * wp,u_int py)1577 window_copy_find_length(struct window_pane *wp, u_int py)
1578 {
1579 	struct window_copy_mode_data	*data = wp->modedata;
1580 	struct screen			*s = data->backing;
1581 	const struct grid_cell		*gc;
1582 	struct utf8_data		 ud;
1583 	u_int				 px;
1584 
1585 	/*
1586 	 * If the pane has been resized, its grid can contain old overlong
1587 	 * lines. grid_peek_cell does not allow accessing cells beyond the
1588 	 * width of the grid, and screen_write_copy treats them as spaces, so
1589 	 * ignore them here too.
1590 	 */
1591 	px = s->grid->linedata[py].cellsize;
1592 	if (px > screen_size_x(s))
1593 		px = screen_size_x(s);
1594 	while (px > 0) {
1595 		gc = grid_peek_cell(s->grid, px - 1, py);
1596 		grid_cell_get(gc, &ud);
1597 		if (ud.size != 1 || *ud.data != ' ')
1598 			break;
1599 		px--;
1600 	}
1601 	return (px);
1602 }
1603 
1604 void
window_copy_cursor_start_of_line(struct window_pane * wp)1605 window_copy_cursor_start_of_line(struct window_pane *wp)
1606 {
1607 	struct window_copy_mode_data	*data = wp->modedata;
1608 	struct screen			*back_s = data->backing;
1609 	struct grid			*gd = back_s->grid;
1610 	u_int				 py;
1611 
1612 	if (data->cx == 0) {
1613 		py = screen_hsize(back_s) + data->cy - data->oy;
1614 		while (py > 0 && gd->linedata[py-1].flags & GRID_LINE_WRAPPED) {
1615 			window_copy_cursor_up(wp, 0);
1616 			py = screen_hsize(back_s) + data->cy - data->oy;
1617 		}
1618 	}
1619 	window_copy_update_cursor(wp, 0, data->cy);
1620 	if (window_copy_update_selection(wp, 1))
1621 		window_copy_redraw_lines(wp, data->cy, 1);
1622 }
1623 
1624 void
window_copy_cursor_back_to_indentation(struct window_pane * wp)1625 window_copy_cursor_back_to_indentation(struct window_pane *wp)
1626 {
1627 	struct window_copy_mode_data	*data = wp->modedata;
1628 	u_int				 px, py, xx;
1629 	const struct grid_cell		*gc;
1630 	struct utf8_data		 ud;
1631 
1632 	px = 0;
1633 	py = screen_hsize(data->backing) + data->cy - data->oy;
1634 	xx = window_copy_find_length(wp, py);
1635 
1636 	while (px < xx) {
1637 		gc = grid_peek_cell(data->backing->grid, px, py);
1638 		grid_cell_get(gc, &ud);
1639 		if (ud.size != 1 || *ud.data != ' ')
1640 			break;
1641 		px++;
1642 	}
1643 
1644 	window_copy_update_cursor(wp, px, data->cy);
1645 	if (window_copy_update_selection(wp, 1))
1646 		window_copy_redraw_lines(wp, data->cy, 1);
1647 }
1648 
1649 void
window_copy_cursor_end_of_line(struct window_pane * wp)1650 window_copy_cursor_end_of_line(struct window_pane *wp)
1651 {
1652 	struct window_copy_mode_data	*data = wp->modedata;
1653 	struct screen			*back_s = data->backing;
1654 	struct grid			*gd = back_s->grid;
1655 	u_int				 px, py;
1656 
1657 	py = screen_hsize(back_s) + data->cy - data->oy;
1658 	px = window_copy_find_length(wp, py);
1659 
1660 	if (data->cx == px) {
1661 		if (data->screen.sel.flag && data->rectflag)
1662 			px = screen_size_x(back_s);
1663 		if (gd->linedata[py].flags & GRID_LINE_WRAPPED) {
1664 			while (py < gd->sy + gd->hsize &&
1665 			    gd->linedata[py].flags & GRID_LINE_WRAPPED) {
1666 				window_copy_cursor_down(wp, 0);
1667 				py = screen_hsize(back_s)
1668 				     + data->cy - data->oy;
1669 			}
1670 			px = window_copy_find_length(wp, py);
1671 		}
1672 	}
1673 	window_copy_update_cursor(wp, px, data->cy);
1674 
1675 	if (window_copy_update_selection(wp, 1))
1676 		window_copy_redraw_lines(wp, data->cy, 1);
1677 }
1678 
1679 void
window_copy_other_end(struct window_pane * wp)1680 window_copy_other_end(struct window_pane *wp)
1681 {
1682 	struct window_copy_mode_data	*data = wp->modedata;
1683 	struct screen			*s = &data->screen;
1684 	u_int				 selx, sely, cx, cy, yy;
1685 
1686 	if (!s->sel.flag)
1687 		return;
1688 
1689 	selx = data->selx;
1690 	sely = data->sely;
1691 	cx = data->cx;
1692 	cy = data->cy;
1693 	yy = screen_hsize(data->backing) + data->cy - data->oy;
1694 
1695 	data->selx = cx;
1696 	data->sely = yy;
1697 	data->cx = selx;
1698 
1699 	if (sely < screen_hsize(data->backing) - data->oy) {
1700 		data->oy = screen_hsize(data->backing) - sely;
1701 		data->cy = 0;
1702 	} else if (sely > screen_hsize(data->backing) - data->oy + screen_size_y(s)) {
1703 		data->oy = screen_hsize(data->backing) - sely + screen_size_y(s) - 1;
1704 		data->cy = screen_size_y(s) - 1;
1705 
1706 	} else
1707 		data->cy = cy + sely - yy;
1708 
1709 	window_copy_redraw_screen(wp);
1710 }
1711 
1712 void
window_copy_cursor_left(struct window_pane * wp)1713 window_copy_cursor_left(struct window_pane *wp)
1714 {
1715 	struct window_copy_mode_data	*data = wp->modedata;
1716 
1717 	if (data->cx == 0) {
1718 		window_copy_cursor_up(wp, 0);
1719 		window_copy_cursor_end_of_line(wp);
1720 	} else {
1721 		window_copy_update_cursor(wp, data->cx - 1, data->cy);
1722 		if (window_copy_update_selection(wp, 1))
1723 			window_copy_redraw_lines(wp, data->cy, 1);
1724 	}
1725 }
1726 
1727 void
window_copy_cursor_right(struct window_pane * wp)1728 window_copy_cursor_right(struct window_pane *wp)
1729 {
1730 	struct window_copy_mode_data	*data = wp->modedata;
1731 	u_int				 px, py;
1732 
1733 	if (data->screen.sel.flag && data->rectflag)
1734 		px = screen_size_x(&data->screen);
1735 	else {
1736 		py = screen_hsize(data->backing) + data->cy - data->oy;
1737 		px = window_copy_find_length(wp, py);
1738 	}
1739 
1740 	if (data->cx >= px) {
1741 		window_copy_cursor_start_of_line(wp);
1742 		window_copy_cursor_down(wp, 0);
1743 	} else {
1744 		window_copy_update_cursor(wp, data->cx + 1, data->cy);
1745 		if (window_copy_update_selection(wp, 1))
1746 			window_copy_redraw_lines(wp, data->cy, 1);
1747 	}
1748 }
1749 
1750 void
window_copy_cursor_up(struct window_pane * wp,int scroll_only)1751 window_copy_cursor_up(struct window_pane *wp, int scroll_only)
1752 {
1753 	struct window_copy_mode_data	*data = wp->modedata;
1754 	struct screen			*s = &data->screen;
1755 	u_int				 ox, oy, px, py;
1756 
1757 	oy = screen_hsize(data->backing) + data->cy - data->oy;
1758 	ox = window_copy_find_length(wp, oy);
1759 	if (data->cx != ox) {
1760 		data->lastcx = data->cx;
1761 		data->lastsx = ox;
1762 	}
1763 
1764 	data->cx = data->lastcx;
1765 	if (scroll_only || data->cy == 0) {
1766 		window_copy_scroll_down(wp, 1);
1767 		if (scroll_only) {
1768 			if (data->cy == screen_size_y(s) - 1)
1769 				window_copy_redraw_lines(wp, data->cy, 1);
1770 			else
1771 				window_copy_redraw_lines(wp, data->cy, 2);
1772 		}
1773 	} else {
1774 		window_copy_update_cursor(wp, data->cx, data->cy - 1);
1775 		if (window_copy_update_selection(wp, 1)) {
1776 			if (data->cy == screen_size_y(s) - 1)
1777 				window_copy_redraw_lines(wp, data->cy, 1);
1778 			else
1779 				window_copy_redraw_lines(wp, data->cy, 2);
1780 		}
1781 	}
1782 
1783 	if (!data->screen.sel.flag || !data->rectflag) {
1784 		py = screen_hsize(data->backing) + data->cy - data->oy;
1785 		px = window_copy_find_length(wp, py);
1786 		if ((data->cx >= data->lastsx && data->cx != px) ||
1787 		    data->cx > px)
1788 			window_copy_cursor_end_of_line(wp);
1789 	}
1790 }
1791 
1792 void
window_copy_cursor_down(struct window_pane * wp,int scroll_only)1793 window_copy_cursor_down(struct window_pane *wp, int scroll_only)
1794 {
1795 	struct window_copy_mode_data	*data = wp->modedata;
1796 	struct screen			*s = &data->screen;
1797 	u_int				 ox, oy, px, py;
1798 
1799 	oy = screen_hsize(data->backing) + data->cy - data->oy;
1800 	ox = window_copy_find_length(wp, oy);
1801 	if (data->cx != ox) {
1802 		data->lastcx = data->cx;
1803 		data->lastsx = ox;
1804 	}
1805 
1806 	data->cx = data->lastcx;
1807 	if (scroll_only || data->cy == screen_size_y(s) - 1) {
1808 		window_copy_scroll_up(wp, 1);
1809 		if (scroll_only && data->cy > 0)
1810 			window_copy_redraw_lines(wp, data->cy - 1, 2);
1811 	} else {
1812 		window_copy_update_cursor(wp, data->cx, data->cy + 1);
1813 		if (window_copy_update_selection(wp, 1))
1814 			window_copy_redraw_lines(wp, data->cy - 1, 2);
1815 	}
1816 
1817 	if (!data->screen.sel.flag || !data->rectflag) {
1818 		py = screen_hsize(data->backing) + data->cy - data->oy;
1819 		px = window_copy_find_length(wp, py);
1820 		if ((data->cx >= data->lastsx && data->cx != px) ||
1821 		    data->cx > px)
1822 			window_copy_cursor_end_of_line(wp);
1823 	}
1824 }
1825 
1826 void
window_copy_cursor_jump(struct window_pane * wp)1827 window_copy_cursor_jump(struct window_pane *wp)
1828 {
1829 	struct window_copy_mode_data	*data = wp->modedata;
1830 	struct screen			*back_s = data->backing;
1831 	const struct grid_cell		*gc;
1832 	struct utf8_data		 ud;
1833 	u_int				 px, py, xx;
1834 
1835 	px = data->cx + 1;
1836 	py = screen_hsize(back_s) + data->cy - data->oy;
1837 	xx = window_copy_find_length(wp, py);
1838 
1839 	while (px < xx) {
1840 		gc = grid_peek_cell(back_s->grid, px, py);
1841 		grid_cell_get(gc, &ud);
1842 		if (!(gc->flags & GRID_FLAG_PADDING) &&
1843 		    ud.size == 1 && *ud.data == data->jumpchar) {
1844 			window_copy_update_cursor(wp, px, data->cy);
1845 			if (window_copy_update_selection(wp, 1))
1846 				window_copy_redraw_lines(wp, data->cy, 1);
1847 			return;
1848 		}
1849 		px++;
1850 	}
1851 }
1852 
1853 void
window_copy_cursor_jump_back(struct window_pane * wp)1854 window_copy_cursor_jump_back(struct window_pane *wp)
1855 {
1856 	struct window_copy_mode_data	*data = wp->modedata;
1857 	struct screen			*back_s = data->backing;
1858 	const struct grid_cell		*gc;
1859 	struct utf8_data		 ud;
1860 	u_int				 px, py;
1861 
1862 	px = data->cx;
1863 	py = screen_hsize(back_s) + data->cy - data->oy;
1864 
1865 	if (px > 0)
1866 		px--;
1867 
1868 	for (;;) {
1869 		gc = grid_peek_cell(back_s->grid, px, py);
1870 		grid_cell_get(gc, &ud);
1871 		if (!(gc->flags & GRID_FLAG_PADDING) &&
1872 		    ud.size == 1 && *ud.data == data->jumpchar) {
1873 			window_copy_update_cursor(wp, px, data->cy);
1874 			if (window_copy_update_selection(wp, 1))
1875 				window_copy_redraw_lines(wp, data->cy, 1);
1876 			return;
1877 		}
1878 		if (px == 0)
1879 			break;
1880 		px--;
1881 	}
1882 }
1883 
1884 void
window_copy_cursor_jump_to(struct window_pane * wp)1885 window_copy_cursor_jump_to(struct window_pane *wp)
1886 {
1887 	struct window_copy_mode_data	*data = wp->modedata;
1888 	struct screen			*back_s = data->backing;
1889 	const struct grid_cell		*gc;
1890 	struct utf8_data		 ud;
1891 	u_int				 px, py, xx;
1892 
1893 	px = data->cx + 1;
1894 	py = screen_hsize(back_s) + data->cy - data->oy;
1895 	xx = window_copy_find_length(wp, py);
1896 
1897 	while (px < xx) {
1898 		gc = grid_peek_cell(back_s->grid, px, py);
1899 		grid_cell_get(gc, &ud);
1900 		if (!(gc->flags & GRID_FLAG_PADDING) &&
1901 		    ud.size == 1 && *ud.data == data->jumpchar) {
1902 			window_copy_update_cursor(wp, px - 1, data->cy);
1903 			if (window_copy_update_selection(wp, 1))
1904 				window_copy_redraw_lines(wp, data->cy, 1);
1905 			return;
1906 		}
1907 		px++;
1908 	}
1909 }
1910 
1911 void
window_copy_cursor_jump_to_back(struct window_pane * wp)1912 window_copy_cursor_jump_to_back(struct window_pane *wp)
1913 {
1914 	struct window_copy_mode_data	*data = wp->modedata;
1915 	struct screen			*back_s = data->backing;
1916 	const struct grid_cell		*gc;
1917 	struct utf8_data		 ud;
1918 	u_int				 px, py;
1919 
1920 	px = data->cx;
1921 	py = screen_hsize(back_s) + data->cy - data->oy;
1922 
1923 	if (px > 0)
1924 		px--;
1925 
1926 	for (;;) {
1927 		gc = grid_peek_cell(back_s->grid, px, py);
1928 		grid_cell_get(gc, &ud);
1929 		if (!(gc->flags & GRID_FLAG_PADDING) &&
1930 		    ud.size == 1 && *ud.data == data->jumpchar) {
1931 			window_copy_update_cursor(wp, px + 1, data->cy);
1932 			if (window_copy_update_selection(wp, 1))
1933 				window_copy_redraw_lines(wp, data->cy, 1);
1934 			return;
1935 		}
1936 		if (px == 0)
1937 			break;
1938 		px--;
1939 	}
1940 }
1941 
1942 void
window_copy_cursor_next_word(struct window_pane * wp,const char * separators)1943 window_copy_cursor_next_word(struct window_pane *wp, const char *separators)
1944 {
1945 	struct window_copy_mode_data	*data = wp->modedata;
1946 	struct screen			*back_s = data->backing;
1947 	u_int				 px, py, xx, yy;
1948 	int				 expected = 0;
1949 
1950 	px = data->cx;
1951 	py = screen_hsize(back_s) + data->cy - data->oy;
1952 	xx = window_copy_find_length(wp, py);
1953 	yy = screen_hsize(back_s) + screen_size_y(back_s) - 1;
1954 
1955 	/*
1956 	 * First skip past any nonword characters and then any word characters.
1957 	 *
1958 	 * expected is initially set to 0 for the former and then 1 for the
1959 	 * latter.
1960 	 */
1961 	do {
1962 		while (px > xx ||
1963 		    window_copy_in_set(wp, px, py, separators) == expected) {
1964 			/* Move down if we're past the end of the line. */
1965 			if (px > xx) {
1966 				if (py == yy)
1967 					return;
1968 				window_copy_cursor_down(wp, 0);
1969 				px = 0;
1970 
1971 				py = screen_hsize(back_s) + data->cy - data->oy;
1972 				xx = window_copy_find_length(wp, py);
1973 			} else
1974 				px++;
1975 		}
1976 		expected = !expected;
1977 	} while (expected == 1);
1978 
1979 	window_copy_update_cursor(wp, px, data->cy);
1980 	if (window_copy_update_selection(wp, 1))
1981 		window_copy_redraw_lines(wp, data->cy, 1);
1982 }
1983 
1984 void
window_copy_cursor_next_word_end(struct window_pane * wp,const char * separators)1985 window_copy_cursor_next_word_end(struct window_pane *wp, const char *separators)
1986 {
1987 	struct window_copy_mode_data	*data = wp->modedata;
1988 	struct options			*oo = &wp->window->options;
1989 	struct screen			*back_s = data->backing;
1990 	u_int				 px, py, xx, yy;
1991 	int				 keys, expected = 1;
1992 
1993 	px = data->cx;
1994 	py = screen_hsize(back_s) + data->cy - data->oy;
1995 	xx = window_copy_find_length(wp, py);
1996 	yy = screen_hsize(back_s) + screen_size_y(back_s) - 1;
1997 
1998 	keys = options_get_number(oo, "mode-keys");
1999 	if (keys == MODEKEY_VI && !window_copy_in_set(wp, px, py, separators))
2000 		px++;
2001 
2002 	/*
2003 	 * First skip past any word characters, then any nonword characters.
2004 	 *
2005 	 * expected is initially set to 1 for the former and then 0 for the
2006 	 * latter.
2007 	 */
2008 	do {
2009 		while (px > xx ||
2010 		    window_copy_in_set(wp, px, py, separators) == expected) {
2011 			/* Move down if we're past the end of the line. */
2012 			if (px > xx) {
2013 				if (py == yy)
2014 					return;
2015 				window_copy_cursor_down(wp, 0);
2016 				px = 0;
2017 
2018 				py = screen_hsize(back_s) + data->cy - data->oy;
2019 				xx = window_copy_find_length(wp, py);
2020 			} else
2021 				px++;
2022 		}
2023 		expected = !expected;
2024 	} while (expected == 0);
2025 
2026 	if (keys == MODEKEY_VI && px != 0)
2027 		px--;
2028 
2029 	window_copy_update_cursor(wp, px, data->cy);
2030 	if (window_copy_update_selection(wp, 1))
2031 		window_copy_redraw_lines(wp, data->cy, 1);
2032 }
2033 
2034 /* Move to the previous place where a word begins. */
2035 void
window_copy_cursor_previous_word(struct window_pane * wp,const char * separators)2036 window_copy_cursor_previous_word(struct window_pane *wp, const char *separators)
2037 {
2038 	struct window_copy_mode_data	*data = wp->modedata;
2039 	u_int				 px, py;
2040 
2041 	px = data->cx;
2042 	py = screen_hsize(data->backing) + data->cy - data->oy;
2043 
2044 	/* Move back to the previous word character. */
2045 	for (;;) {
2046 		if (px > 0) {
2047 			px--;
2048 			if (!window_copy_in_set(wp, px, py, separators))
2049 				break;
2050 		} else {
2051 			if (data->cy == 0 &&
2052 			    (screen_hsize(data->backing) == 0 ||
2053 			    data->oy >= screen_hsize(data->backing) - 1))
2054 				goto out;
2055 			window_copy_cursor_up(wp, 0);
2056 
2057 			py = screen_hsize(data->backing) + data->cy - data->oy;
2058 			px = window_copy_find_length(wp, py);
2059 		}
2060 	}
2061 
2062 	/* Move back to the beginning of this word. */
2063 	while (px > 0 && !window_copy_in_set(wp, px - 1, py, separators))
2064 		px--;
2065 
2066 out:
2067 	window_copy_update_cursor(wp, px, data->cy);
2068 	if (window_copy_update_selection(wp, 1))
2069 		window_copy_redraw_lines(wp, data->cy, 1);
2070 }
2071 
2072 void
window_copy_scroll_up(struct window_pane * wp,u_int ny)2073 window_copy_scroll_up(struct window_pane *wp, u_int ny)
2074 {
2075 	struct window_copy_mode_data	*data = wp->modedata;
2076 	struct screen			*s = &data->screen;
2077 	struct screen_write_ctx		 ctx;
2078 
2079 	if (data->oy < ny)
2080 		ny = data->oy;
2081 	if (ny == 0)
2082 		return;
2083 	data->oy -= ny;
2084 
2085 	window_copy_update_selection(wp, 0);
2086 
2087 	screen_write_start(&ctx, wp, NULL);
2088 	screen_write_cursormove(&ctx, 0, 0);
2089 	screen_write_deleteline(&ctx, ny);
2090 	window_copy_write_lines(wp, &ctx, screen_size_y(s) - ny, ny);
2091 	window_copy_write_line(wp, &ctx, 0);
2092 	if (screen_size_y(s) > 1)
2093 		window_copy_write_line(wp, &ctx, 1);
2094 	if (screen_size_y(s) > 3)
2095 		window_copy_write_line(wp, &ctx, screen_size_y(s) - 2);
2096 	if (s->sel.flag && screen_size_y(s) > ny)
2097 		window_copy_write_line(wp, &ctx, screen_size_y(s) - ny - 1);
2098 	screen_write_cursormove(&ctx, data->cx, data->cy);
2099 	screen_write_stop(&ctx);
2100 }
2101 
2102 void
window_copy_scroll_down(struct window_pane * wp,u_int ny)2103 window_copy_scroll_down(struct window_pane *wp, u_int ny)
2104 {
2105 	struct window_copy_mode_data	*data = wp->modedata;
2106 	struct screen			*s = &data->screen;
2107 	struct screen_write_ctx		 ctx;
2108 
2109 	if (ny > screen_hsize(data->backing))
2110 		return;
2111 
2112 	if (data->oy > screen_hsize(data->backing) - ny)
2113 		ny = screen_hsize(data->backing) - data->oy;
2114 	if (ny == 0)
2115 		return;
2116 	data->oy += ny;
2117 
2118 	window_copy_update_selection(wp, 0);
2119 
2120 	screen_write_start(&ctx, wp, NULL);
2121 	screen_write_cursormove(&ctx, 0, 0);
2122 	screen_write_insertline(&ctx, ny);
2123 	window_copy_write_lines(wp, &ctx, 0, ny);
2124 	if (s->sel.flag && screen_size_y(s) > ny)
2125 		window_copy_write_line(wp, &ctx, ny);
2126 	else if (ny == 1) /* nuke position */
2127 		window_copy_write_line(wp, &ctx, 1);
2128 	screen_write_cursormove(&ctx, data->cx, data->cy);
2129 	screen_write_stop(&ctx);
2130 }
2131 
2132 void
window_copy_rectangle_toggle(struct window_pane * wp)2133 window_copy_rectangle_toggle(struct window_pane *wp)
2134 {
2135 	struct window_copy_mode_data	*data = wp->modedata;
2136 	u_int				 px, py;
2137 
2138 	data->rectflag = !data->rectflag;
2139 
2140 	py = screen_hsize(data->backing) + data->cy - data->oy;
2141 	px = window_copy_find_length(wp, py);
2142 	if (data->cx > px)
2143 		window_copy_update_cursor(wp, px, data->cy);
2144 
2145 	window_copy_update_selection(wp, 1);
2146 	window_copy_redraw_screen(wp);
2147 }
2148