xref: /openbsd-src/usr.bin/tmux/window-copy.c (revision 6a13ef69787db04ae501a22e92fa10865b44fd7c)
1 /* $OpenBSD: window-copy.c,v 1.164 2017/01/07 15:28:13 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
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 static const char *window_copy_key_table(struct window_pane *);
28 static void	window_copy_command(struct window_pane *, struct client *,
29 		    struct session *, struct args *, struct mouse_event *);
30 static struct screen *window_copy_init(struct window_pane *);
31 static void	window_copy_free(struct window_pane *);
32 static void	window_copy_pagedown(struct window_pane *, int);
33 static void	window_copy_next_paragraph(struct window_pane *);
34 static void	window_copy_previous_paragraph(struct window_pane *);
35 static void	window_copy_resize(struct window_pane *, u_int, u_int);
36 
37 static void	window_copy_redraw_selection(struct window_pane *, u_int);
38 static void	window_copy_redraw_lines(struct window_pane *, u_int, u_int);
39 static void	window_copy_redraw_screen(struct window_pane *);
40 static void	window_copy_write_line(struct window_pane *,
41 		    struct screen_write_ctx *, u_int);
42 static void	window_copy_write_lines(struct window_pane *,
43 		    struct screen_write_ctx *, u_int, u_int);
44 
45 static void	window_copy_scroll_to(struct window_pane *, u_int, u_int);
46 static int	window_copy_search_compare(struct grid *, u_int, u_int,
47 		    struct grid *, u_int, int);
48 static int	window_copy_search_lr(struct grid *, struct grid *, u_int *,
49 		    u_int, u_int, u_int, int);
50 static int	window_copy_search_rl(struct grid *, struct grid *, u_int *,
51 		    u_int, u_int, u_int, int);
52 static int	window_copy_search_marks(struct window_pane *, struct screen *);
53 static void	window_copy_clear_marks(struct window_pane *);
54 static void	window_copy_move_left(struct screen *, u_int *, u_int *);
55 static void	window_copy_move_right(struct screen *, u_int *, u_int *);
56 static int	window_copy_is_lowercase(const char *);
57 static int	window_copy_search_jump(struct window_pane *, struct grid *,
58 		    struct grid *, u_int, u_int, u_int, int, int, int);
59 static int	window_copy_search(struct window_pane *, int, int);
60 static int	window_copy_search_up(struct window_pane *, int);
61 static int	window_copy_search_down(struct window_pane *, int);
62 static void	window_copy_goto_line(struct window_pane *, const char *);
63 static void	window_copy_update_cursor(struct window_pane *, u_int, u_int);
64 static void	window_copy_start_selection(struct window_pane *);
65 static int	window_copy_adjust_selection(struct window_pane *, u_int *,
66 		    u_int *);
67 static int	window_copy_update_selection(struct window_pane *, int);
68 static void	window_copy_synchronize_cursor(struct window_pane *wp);
69 static void    *window_copy_get_selection(struct window_pane *, size_t *);
70 static void	window_copy_copy_buffer(struct window_pane *, const char *,
71 		    void *, size_t);
72 static void	window_copy_copy_pipe(struct window_pane *, struct session *,
73 		    const char *, const char *);
74 static void	window_copy_copy_selection(struct window_pane *, const char *);
75 static void	window_copy_append_selection(struct window_pane *,
76 		    const char *);
77 static void	window_copy_clear_selection(struct window_pane *);
78 static void	window_copy_copy_line(struct window_pane *, char **, size_t *,
79 		    u_int, u_int, u_int);
80 static int	window_copy_in_set(struct window_pane *, u_int, u_int,
81 		    const char *);
82 static u_int	window_copy_find_length(struct window_pane *, u_int);
83 static void	window_copy_cursor_start_of_line(struct window_pane *);
84 static void	window_copy_cursor_back_to_indentation(struct window_pane *);
85 static void	window_copy_cursor_end_of_line(struct window_pane *);
86 static void	window_copy_other_end(struct window_pane *);
87 static void	window_copy_cursor_left(struct window_pane *);
88 static void	window_copy_cursor_right(struct window_pane *);
89 static void	window_copy_cursor_up(struct window_pane *, int);
90 static void	window_copy_cursor_down(struct window_pane *, int);
91 static void	window_copy_cursor_jump(struct window_pane *);
92 static void	window_copy_cursor_jump_back(struct window_pane *);
93 static void	window_copy_cursor_jump_to(struct window_pane *, int);
94 static void	window_copy_cursor_jump_to_back(struct window_pane *, int);
95 static void	window_copy_cursor_next_word(struct window_pane *,
96 		    const char *);
97 static void	window_copy_cursor_next_word_end(struct window_pane *,
98 		    const char *);
99 static void	window_copy_cursor_previous_word(struct window_pane *,
100 		    const char *);
101 static void	window_copy_scroll_up(struct window_pane *, u_int);
102 static void	window_copy_scroll_down(struct window_pane *, u_int);
103 static void	window_copy_rectangle_toggle(struct window_pane *);
104 static void	window_copy_move_mouse(struct mouse_event *);
105 static void	window_copy_drag_update(struct client *, struct mouse_event *);
106 
107 const struct window_mode window_copy_mode = {
108 	.init = window_copy_init,
109 	.free = window_copy_free,
110 	.resize = window_copy_resize,
111 	.key_table = window_copy_key_table,
112 	.command = window_copy_command,
113 };
114 
115 enum {
116 	WINDOW_COPY_OFF,
117 	WINDOW_COPY_SEARCHUP,
118 	WINDOW_COPY_SEARCHDOWN,
119 	WINDOW_COPY_JUMPFORWARD,
120 	WINDOW_COPY_JUMPBACKWARD,
121 	WINDOW_COPY_JUMPTOFORWARD,
122 	WINDOW_COPY_JUMPTOBACKWARD,
123 };
124 
125 enum {
126 	WINDOW_COPY_REL_POS_ABOVE,
127 	WINDOW_COPY_REL_POS_ON_SCREEN,
128 	WINDOW_COPY_REL_POS_BELOW,
129 };
130 
131 /*
132  * Copy mode's visible screen (the "screen" field) is filled from one of two
133  * sources: the original contents of the pane (used when we actually enter via
134  * the "copy-mode" command, to copy the contents of the current pane), or else
135  * a series of lines containing the output from an output-writing tmux command
136  * (such as any of the "show-*" or "list-*" commands).
137  *
138  * In either case, the full content of the copy-mode grid is pointed at by the
139  * "backing" field, and is copied into "screen" as needed (that is, when
140  * scrolling occurs). When copy-mode is backed by a pane, backing points
141  * directly at that pane's screen structure (&wp->base); when backed by a list
142  * of output-lines from a command, it points at a newly-allocated screen
143  * structure (which is deallocated when the mode ends).
144  */
145 struct window_copy_mode_data {
146 	struct screen	 screen;
147 
148 	struct screen	*backing;
149 	int		 backing_written; /* backing display started */
150 
151 	u_int		 oy; /* number of lines scrolled up */
152 
153 	u_int		 selx; /* beginning of selection */
154 	u_int		 sely;
155 
156 	u_int		 endselx; /* end of selection */
157 	u_int		 endsely;
158 
159 	enum {
160 		CURSORDRAG_NONE,	/* selection is independent of cursor */
161 		CURSORDRAG_ENDSEL,	/* end is synchronized with cursor */
162 		CURSORDRAG_SEL,		/* start is synchronized with cursor */
163 	} cursordrag;
164 
165 	int		 rectflag;	/* in rectangle copy mode? */
166 	int		 scroll_exit;	/* exit on scroll to end? */
167 
168 	u_int		 cx;
169 	u_int		 cy;
170 
171 	u_int		 lastcx; /* position in last line w/ content */
172 	u_int		 lastsx; /* size of last line w/ content */
173 
174 	int		 searchtype;
175 	char		*searchstr;
176 	bitstr_t        *searchmark;
177 	int		 searchx;
178 	int		 searchy;
179 	int		 searcho;
180 
181 	int		 jumptype;
182 	char		 jumpchar;
183 };
184 
185 static struct screen *
186 window_copy_init(struct window_pane *wp)
187 {
188 	struct window_copy_mode_data	*data;
189 	struct screen			*s;
190 
191 	wp->modedata = data = xmalloc(sizeof *data);
192 
193 	data->oy = 0;
194 	data->cx = 0;
195 	data->cy = 0;
196 
197 	data->cursordrag = CURSORDRAG_NONE;
198 
199 	data->lastcx = 0;
200 	data->lastsx = 0;
201 
202 	data->backing_written = 0;
203 
204 	data->rectflag = 0;
205 	data->scroll_exit = 0;
206 
207 	data->searchtype = WINDOW_COPY_OFF;
208 	data->searchstr = NULL;
209 	data->searchmark = NULL;
210 	data->searchx = data->searchy = data->searcho = -1;
211 
212 	if (wp->fd != -1)
213 		bufferevent_disable(wp->event, EV_READ|EV_WRITE);
214 
215 	data->jumptype = WINDOW_COPY_OFF;
216 	data->jumpchar = '\0';
217 
218 	s = &data->screen;
219 	screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
220 	s->sel.modekeys = options_get_number(wp->window->options, "mode-keys");
221 
222 	data->backing = NULL;
223 
224 	return (s);
225 }
226 
227 void
228 window_copy_init_from_pane(struct window_pane *wp, int scroll_exit)
229 {
230 	struct window_copy_mode_data	*data = wp->modedata;
231 	struct screen			*s = &data->screen;
232 	struct screen_write_ctx	 	 ctx;
233 	u_int				 i;
234 
235 	if (wp->mode != &window_copy_mode)
236 		fatalx("not in copy mode");
237 
238 	data->backing = &wp->base;
239 	data->cx = data->backing->cx;
240 	data->cy = data->backing->cy;
241 	data->scroll_exit = scroll_exit;
242 
243 	s->cx = data->cx;
244 	s->cy = data->cy;
245 
246 	screen_write_start(&ctx, NULL, s);
247 	for (i = 0; i < screen_size_y(s); i++)
248 		window_copy_write_line(wp, &ctx, i);
249 	screen_write_cursormove(&ctx, data->cx, data->cy);
250 	screen_write_stop(&ctx);
251 }
252 
253 void
254 window_copy_init_for_output(struct window_pane *wp)
255 {
256 	struct window_copy_mode_data	*data = wp->modedata;
257 
258 	data->backing = xmalloc(sizeof *data->backing);
259 	screen_init(data->backing, screen_size_x(&wp->base),
260 	    screen_size_y(&wp->base), UINT_MAX);
261 }
262 
263 static void
264 window_copy_free(struct window_pane *wp)
265 {
266 	struct window_copy_mode_data	*data = wp->modedata;
267 
268 	if (wp->fd != -1)
269 		bufferevent_enable(wp->event, EV_READ|EV_WRITE);
270 
271 	free(data->searchmark);
272 	free(data->searchstr);
273 
274 	if (data->backing != &wp->base) {
275 		screen_free(data->backing);
276 		free(data->backing);
277 	}
278 	screen_free(&data->screen);
279 
280 	free(data);
281 }
282 
283 void
284 window_copy_add(struct window_pane *wp, const char *fmt, ...)
285 {
286 	va_list	ap;
287 
288 	va_start(ap, fmt);
289 	window_copy_vadd(wp, fmt, ap);
290 	va_end(ap);
291 }
292 
293 void
294 window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap)
295 {
296 	struct window_copy_mode_data	*data = wp->modedata;
297 	struct screen			*backing = data->backing;
298 	struct screen_write_ctx	 	 back_ctx, ctx;
299 	struct grid_cell		 gc;
300 	u_int				 old_hsize, old_cy;
301 
302 	if (backing == &wp->base)
303 		return;
304 
305 	memcpy(&gc, &grid_default_cell, sizeof gc);
306 
307 	old_hsize = screen_hsize(data->backing);
308 	screen_write_start(&back_ctx, NULL, backing);
309 	if (data->backing_written) {
310 		/*
311 		 * On the second or later line, do a CRLF before writing
312 		 * (so it's on a new line).
313 		 */
314 		screen_write_carriagereturn(&back_ctx);
315 		screen_write_linefeed(&back_ctx, 0);
316 	} else
317 		data->backing_written = 1;
318 	old_cy = backing->cy;
319 	screen_write_vnputs(&back_ctx, 0, &gc, fmt, ap);
320 	screen_write_stop(&back_ctx);
321 
322 	data->oy += screen_hsize(data->backing) - old_hsize;
323 
324 	screen_write_start(&ctx, wp, &data->screen);
325 
326 	/*
327 	 * If the history has changed, draw the top line.
328 	 * (If there's any history at all, it has changed.)
329 	 */
330 	if (screen_hsize(data->backing))
331 		window_copy_redraw_lines(wp, 0, 1);
332 
333 	/* Write the new lines. */
334 	window_copy_redraw_lines(wp, old_cy, backing->cy - old_cy + 1);
335 
336 	screen_write_stop(&ctx);
337 }
338 
339 void
340 window_copy_pageup(struct window_pane *wp, int half_page)
341 {
342 	struct window_copy_mode_data	*data = wp->modedata;
343 	struct screen			*s = &data->screen;
344 	u_int				 n, ox, oy, px, py;
345 
346 	oy = screen_hsize(data->backing) + data->cy - data->oy;
347 	ox = window_copy_find_length(wp, oy);
348 
349 	if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely)
350 		window_copy_other_end(wp);
351 
352 	if (data->cx != ox) {
353 		data->lastcx = data->cx;
354 		data->lastsx = ox;
355 	}
356 	data->cx = data->lastcx;
357 
358 	n = 1;
359 	if (screen_size_y(s) > 2) {
360 		if (half_page)
361 			n = screen_size_y(s) / 2;
362 		else
363 			n = screen_size_y(s) - 2;
364 	}
365 
366 	if (data->oy + n > screen_hsize(data->backing))
367 		data->oy = screen_hsize(data->backing);
368 	else
369 		data->oy += n;
370 
371 	if (!data->screen.sel.flag || !data->rectflag) {
372 		py = screen_hsize(data->backing) + data->cy - data->oy;
373 		px = window_copy_find_length(wp, py);
374 		if ((data->cx >= data->lastsx && data->cx != px) ||
375 		    data->cx > px)
376 			window_copy_cursor_end_of_line(wp);
377 	}
378 
379 	window_copy_update_selection(wp, 1);
380 	window_copy_redraw_screen(wp);
381 }
382 
383 static void
384 window_copy_pagedown(struct window_pane *wp, int half_page)
385 {
386 	struct window_copy_mode_data	*data = wp->modedata;
387 	struct screen			*s = &data->screen;
388 	u_int				 n, ox, oy, px, py;
389 
390 	oy = screen_hsize(data->backing) + data->cy - data->oy;
391 	ox = window_copy_find_length(wp, oy);
392 
393 	if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT && oy == data->sely)
394 		window_copy_other_end(wp);
395 
396 	if (data->cx != ox) {
397 		data->lastcx = data->cx;
398 		data->lastsx = ox;
399 	}
400 	data->cx = data->lastcx;
401 
402 	n = 1;
403 	if (screen_size_y(s) > 2) {
404 		if (half_page)
405 			n = screen_size_y(s) / 2;
406 		else
407 			n = screen_size_y(s) - 2;
408 	}
409 
410 	if (data->oy < n)
411 		data->oy = 0;
412 	else
413 		data->oy -= n;
414 
415 	if (!data->screen.sel.flag || !data->rectflag) {
416 		py = screen_hsize(data->backing) + data->cy - data->oy;
417 		px = window_copy_find_length(wp, py);
418 		if ((data->cx >= data->lastsx && data->cx != px) ||
419 		    data->cx > px)
420 			window_copy_cursor_end_of_line(wp);
421 	}
422 
423 	if (data->scroll_exit && data->oy == 0) {
424 		window_pane_reset_mode(wp);
425 		return;
426 	}
427 
428 	window_copy_update_selection(wp, 1);
429 	window_copy_redraw_screen(wp);
430 }
431 
432 static void
433 window_copy_previous_paragraph(struct window_pane *wp)
434 {
435 	struct window_copy_mode_data	*data = wp->modedata;
436 	u_int				 oy;
437 
438 	oy = screen_hsize(data->backing) + data->cy - data->oy;
439 
440 	while (oy > 0 && window_copy_find_length(wp, oy) == 0)
441 		oy--;
442 
443 	while (oy > 0 && window_copy_find_length(wp, oy) > 0)
444 		oy--;
445 
446 	window_copy_scroll_to(wp, 0, oy);
447 }
448 
449 static void
450 window_copy_next_paragraph(struct window_pane *wp)
451 {
452 	struct window_copy_mode_data	*data = wp->modedata;
453 	struct screen			*s = &data->screen;
454 	u_int				 maxy, ox, oy;
455 
456 	oy = screen_hsize(data->backing) + data->cy - data->oy;
457 	maxy = screen_hsize(data->backing) + screen_size_y(s) - 1;
458 
459 	while (oy < maxy && window_copy_find_length(wp, oy) == 0)
460 		oy++;
461 
462 	while (oy < maxy && window_copy_find_length(wp, oy) > 0)
463 		oy++;
464 
465 	ox = window_copy_find_length(wp, oy);
466 	window_copy_scroll_to(wp, ox, oy);
467 }
468 
469 static void
470 window_copy_resize(struct window_pane *wp, u_int sx, u_int sy)
471 {
472 	struct window_copy_mode_data	*data = wp->modedata;
473 	struct screen			*s = &data->screen;
474 	struct screen_write_ctx	 	 ctx;
475 
476 	screen_resize(s, sx, sy, 1);
477 	if (data->backing != &wp->base)
478 		screen_resize(data->backing, sx, sy, 1);
479 
480 	if (data->cy > sy - 1)
481 		data->cy = sy - 1;
482 	if (data->cx > sx)
483 		data->cx = sx;
484 	if (data->oy > screen_hsize(data->backing))
485 		data->oy = screen_hsize(data->backing);
486 
487 	window_copy_clear_selection(wp);
488 
489 	screen_write_start(&ctx, NULL, s);
490 	window_copy_write_lines(wp, &ctx, 0, screen_size_y(s) - 1);
491 	screen_write_stop(&ctx);
492 
493 	if (data->searchmark != NULL)
494 		window_copy_search_marks(wp, NULL);
495 	data->searchx = data->cx;
496 	data->searchy = data->cy;
497 	data->searcho = data->oy;
498 
499 	window_copy_redraw_screen(wp);
500 }
501 
502 static const char *
503 window_copy_key_table(struct window_pane *wp)
504 {
505 	if (options_get_number(wp->window->options, "mode-keys") == MODEKEY_VI)
506 		return ("copy-mode-vi");
507 	return ("copy-mode");
508 }
509 
510 static void
511 window_copy_command(struct window_pane *wp, struct client *c, struct session *s,
512     struct args *args, struct mouse_event *m)
513 {
514 	struct window_copy_mode_data	*data = wp->modedata;
515 	struct screen			*sn = &data->screen;
516 	const char			*command, *argument, *ws;
517 	u_int				 np = wp->modeprefix;
518 	int				 cancel = 0, redraw = 0;
519 	char				 prefix;
520 
521 	if (args->argc == 0)
522 		return;
523 	command = args->argv[0];
524 
525 	if (m != NULL && m->valid)
526 		window_copy_move_mouse(m);
527 
528 	if (args->argc == 1) {
529 		if (strcmp(command, "append-selection") == 0) {
530 			if (s != NULL)
531 				window_copy_append_selection(wp, NULL);
532 			window_copy_clear_selection(wp);
533 			redraw = 1;
534 		}
535 		if (strcmp(command, "append-selection-and-cancel") == 0) {
536 			if (s != NULL)
537 				window_copy_append_selection(wp, NULL);
538 			window_copy_clear_selection(wp);
539 			redraw = 1;
540 			cancel = 1;
541 		}
542 		if (strcmp(command, "back-to-indentation") == 0)
543 			window_copy_cursor_back_to_indentation(wp);
544 		if (strcmp(command, "begin-selection") == 0) {
545 			if (m != NULL)
546 				window_copy_start_drag(c, m);
547 			else {
548 				sn->sel.lineflag = LINE_SEL_NONE;
549 				window_copy_start_selection(wp);
550 				redraw = 1;
551 			}
552 		}
553 		if (strcmp(command, "stop-selection") == 0)
554 			data->cursordrag = CURSORDRAG_NONE;
555 		if (strcmp(command, "bottom-line") == 0) {
556 			data->cx = 0;
557 			data->cy = screen_size_y(sn) - 1;
558 			window_copy_update_selection(wp, 1);
559 			redraw = 1;
560 		}
561 		if (strcmp(command, "cancel") == 0)
562 			cancel = 1;
563 		if (strcmp(command, "clear-selection") == 0) {
564 			window_copy_clear_selection(wp);
565 			redraw = 1;
566 		}
567 		if (strcmp(command, "copy-end-of-line") == 0) {
568 			window_copy_start_selection(wp);
569 			for (; np > 1; np--)
570 				window_copy_cursor_down(wp, 0);
571 			window_copy_cursor_end_of_line(wp);
572 			redraw = 1;
573 
574 			if (s != NULL) {
575 				window_copy_copy_selection(wp, NULL);
576 				cancel = 1;
577 			}
578 		}
579 		if (strcmp(command, "copy-line") == 0) {
580 			window_copy_cursor_start_of_line(wp);
581 			window_copy_start_selection(wp);
582 			for (; np > 1; np--)
583 				window_copy_cursor_down(wp, 0);
584 			window_copy_cursor_end_of_line(wp);
585 			redraw = 1;
586 
587 			if (s != NULL) {
588 				window_copy_copy_selection(wp, NULL);
589 				cancel = 1;
590 			}
591 		}
592 		if (strcmp(command, "copy-selection") == 0) {
593 			if (s != NULL)
594 				window_copy_copy_selection(wp, NULL);
595 			window_copy_clear_selection(wp);
596 			redraw = 1;
597 		}
598 		if (strcmp(command, "copy-selection-and-cancel") == 0) {
599 			if (s != NULL)
600 				window_copy_copy_selection(wp, NULL);
601 			window_copy_clear_selection(wp);
602 			redraw = 1;
603 			cancel = 1;
604 		}
605 		if (strcmp(command, "cursor-down") == 0) {
606 			for (; np != 0; np--)
607 				window_copy_cursor_down(wp, 0);
608 		}
609 		if (strcmp(command, "cursor-left") == 0) {
610 			for (; np != 0; np--)
611 				window_copy_cursor_left(wp);
612 		}
613 		if (strcmp(command, "cursor-right") == 0) {
614 			for (; np != 0; np--)
615 				window_copy_cursor_right(wp);
616 		}
617 		if (strcmp(command, "cursor-up") == 0) {
618 			for (; np != 0; np--)
619 				window_copy_cursor_up(wp, 0);
620 		}
621 		if (strcmp(command, "end-of-line") == 0)
622 			window_copy_cursor_end_of_line(wp);
623 		if (strcmp(command, "halfpage-down") == 0) {
624 			for (; np != 0; np--)
625 				window_copy_pagedown(wp, 1);
626 		}
627 		if (strcmp(command, "halfpage-up") == 0) {
628 			for (; np != 0; np--)
629 				window_copy_pageup(wp, 1);
630 		}
631 		if (strcmp(command, "history-bottom") == 0) {
632 			data->cx = 0;
633 			data->cy = screen_size_y(sn) - 1;
634 			data->oy = 0;
635 			window_copy_update_selection(wp, 1);
636 			redraw = 1;
637 		}
638 		if (strcmp(command, "history-top") == 0) {
639 			data->cx = 0;
640 			data->cy = 0;
641 			data->oy = screen_hsize(data->backing);
642 			window_copy_update_selection(wp, 1);
643 			redraw = 1;
644 		}
645 		if (strcmp(command, "jump-again") == 0) {
646 			switch (data->jumptype) {
647 			case WINDOW_COPY_JUMPFORWARD:
648 				for (; np != 0; np--)
649 					window_copy_cursor_jump(wp);
650 				break;
651 			case WINDOW_COPY_JUMPBACKWARD:
652 				for (; np != 0; np--)
653 					window_copy_cursor_jump_back(wp);
654 				break;
655 			case WINDOW_COPY_JUMPTOFORWARD:
656 				for (; np != 0; np--)
657 					window_copy_cursor_jump_to(wp, 1);
658 				break;
659 			case WINDOW_COPY_JUMPTOBACKWARD:
660 				for (; np != 0; np--)
661 					window_copy_cursor_jump_to_back(wp, 1);
662 				break;
663 			}
664 		}
665 		if (strcmp(command, "jump-reverse") == 0) {
666 			switch (data->jumptype) {
667 			case WINDOW_COPY_JUMPFORWARD:
668 				for (; np != 0; np--)
669 					window_copy_cursor_jump_back(wp);
670 				break;
671 			case WINDOW_COPY_JUMPBACKWARD:
672 				for (; np != 0; np--)
673 					window_copy_cursor_jump(wp);
674 				break;
675 			case WINDOW_COPY_JUMPTOFORWARD:
676 				for (; np != 0; np--)
677 					window_copy_cursor_jump_to_back(wp, 1);
678 				break;
679 			case WINDOW_COPY_JUMPTOBACKWARD:
680 				for (; np != 0; np--)
681 					window_copy_cursor_jump_to(wp, 1);
682 				break;
683 			}
684 		}
685 		if (strcmp(command, "middle-line") == 0) {
686 			data->cx = 0;
687 			data->cy = (screen_size_y(sn) - 1) / 2;
688 			window_copy_update_selection(wp, 1);
689 			redraw = 1;
690 		}
691 		if (strcmp(command, "next-paragraph") == 0) {
692 			for (; np != 0; np--)
693 				window_copy_next_paragraph(wp);
694 		}
695 		if (strcmp(command, "next-space") == 0) {
696 			for (; np != 0; np--)
697 				window_copy_cursor_next_word(wp, " ");
698 		}
699 		if (strcmp(command, "next-space-end") == 0) {
700 			for (; np != 0; np--)
701 				window_copy_cursor_next_word_end(wp, " ");
702 		}
703 		if (strcmp(command, "next-word") == 0) {
704 			ws = options_get_string(s->options, "word-separators");
705 			for (; np != 0; np--)
706 				window_copy_cursor_next_word(wp, ws);
707 		}
708 		if (strcmp(command, "next-word-end") == 0) {
709 			ws = options_get_string(s->options, "word-separators");
710 			for (; np != 0; np--)
711 				window_copy_cursor_next_word_end(wp, ws);
712 		}
713 		if (strcmp(command, "other-end") == 0) {
714 			if ((np % 2) != 0)
715 				window_copy_other_end(wp);
716 		}
717 		if (strcmp(command, "page-down") == 0) {
718 			for (; np != 0; np--)
719 				window_copy_pagedown(wp, 0);
720 		}
721 		if (strcmp(command, "page-up") == 0) {
722 			for (; np != 0; np--)
723 				window_copy_pageup(wp, 0);
724 		}
725 		if (strcmp(command, "previous-paragraph") == 0) {
726 			for (; np != 0; np--)
727 				window_copy_previous_paragraph(wp);
728 		}
729 		if (strcmp(command, "previous-space") == 0) {
730 			for (; np != 0; np--)
731 				window_copy_cursor_previous_word(wp, " ");
732 		}
733 		if (strcmp(command, "previous-word") == 0) {
734 			ws = options_get_string(s->options, "word-separators");
735 			for (; np != 0; np--)
736 				window_copy_cursor_previous_word(wp, ws);
737 		}
738 		if (strcmp(command, "rectangle-toggle") == 0) {
739 			sn->sel.lineflag = LINE_SEL_NONE;
740 			window_copy_rectangle_toggle(wp);
741 		}
742 		if (strcmp(command, "scroll-down") == 0) {
743 			for (; np != 0; np--)
744 				window_copy_cursor_down(wp, 1);
745 			if (data->scroll_exit && data->oy == 0)
746 				cancel = 1;
747 		}
748 		if (strcmp(command, "scroll-up") == 0) {
749 			for (; np != 0; np--)
750 				window_copy_cursor_up(wp, 1);
751 		}
752 		if (strcmp(command, "search-again") == 0) {
753 			if (data->searchtype == WINDOW_COPY_SEARCHUP) {
754 				for (; np != 0; np--)
755 					window_copy_search_up(wp, 1);
756 			} else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
757 				for (; np != 0; np--)
758 					window_copy_search_down(wp, 1);
759 			}
760 		}
761 		if (strcmp(command, "search-reverse") == 0) {
762 			if (data->searchtype == WINDOW_COPY_SEARCHUP) {
763 				for (; np != 0; np--)
764 					window_copy_search_down(wp, 1);
765 			} else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
766 				for (; np != 0; np--)
767 					window_copy_search_up(wp, 1);
768 			}
769 		}
770 		if (strcmp(command, "select-line") == 0) {
771 			sn->sel.lineflag = LINE_SEL_LEFT_RIGHT;
772 			data->rectflag = 0;
773 			window_copy_cursor_start_of_line(wp);
774 			window_copy_start_selection(wp);
775 			for (; np > 1; np--)
776 				window_copy_cursor_down(wp, 0);
777 			window_copy_cursor_end_of_line(wp);
778 			redraw = 1;
779 		}
780 		if (strcmp(command, "select-word") == 0) {
781 			sn->sel.lineflag = LINE_SEL_LEFT_RIGHT;
782 			data->rectflag = 0;
783 			ws = options_get_string(s->options, "word-separators");
784 			window_copy_cursor_previous_word(wp, ws);
785 			window_copy_start_selection(wp);
786 			window_copy_cursor_next_word_end(wp, ws);
787 			redraw = 1;
788 		}
789 		if (strcmp(command, "start-of-line") == 0)
790 			window_copy_cursor_start_of_line(wp);
791 		if (strcmp(command, "top-line") == 0) {
792 			data->cx = 0;
793 			data->cy = 0;
794 			window_copy_update_selection(wp, 1);
795 			redraw = 1;
796 		}
797 	} else if (args->argc == 2 && *args->argv[1] != '\0') {
798 		argument = args->argv[1];
799 		if (strcmp(command, "copy-pipe") == 0) {
800 			if (s != NULL)
801 				window_copy_copy_pipe(wp, s, NULL, argument);
802 		}
803 		if (strcmp(command, "copy-pipe-and-cancel") == 0) {
804 			if (s != NULL) {
805 				window_copy_copy_pipe(wp, s, NULL, argument);
806 				cancel = 1;
807 			}
808 		}
809 		if (strcmp(command, "goto-line") == 0)
810 			window_copy_goto_line(wp, argument);
811 		if (strcmp(command, "jump-backward") == 0) {
812 			data->jumptype = WINDOW_COPY_JUMPBACKWARD;
813 			data->jumpchar = *argument;
814 			for (; np != 0; np--)
815 				window_copy_cursor_jump_back(wp);
816 		}
817 		if (strcmp(command, "jump-forward") == 0) {
818 			data->jumptype = WINDOW_COPY_JUMPFORWARD;
819 			data->jumpchar = *argument;
820 			for (; np != 0; np--)
821 				window_copy_cursor_jump(wp);
822 		}
823 		if (strcmp(command, "jump-to-backward") == 0) {
824 			data->jumptype = WINDOW_COPY_JUMPTOBACKWARD;
825 			data->jumpchar = *argument;
826 			for (; np != 0; np--)
827 				window_copy_cursor_jump_to_back(wp, 1);
828 		}
829 		if (strcmp(command, "jump-to-forward") == 0) {
830 			data->jumptype = WINDOW_COPY_JUMPTOFORWARD;
831 			data->jumpchar = *argument;
832 			for (; np != 0; np--)
833 				window_copy_cursor_jump_to(wp, 1);
834 		}
835 		if (strcmp(command, "search-backward") == 0) {
836 			data->searchtype = WINDOW_COPY_SEARCHUP;
837 			free(data->searchstr);
838 			data->searchstr = xstrdup(argument);
839 			for (; np != 0; np--)
840 				window_copy_search_up(wp, 1);
841 		}
842 		if (strcmp(command, "search-forward") == 0) {
843 			data->searchtype = WINDOW_COPY_SEARCHDOWN;
844 			free(data->searchstr);
845 			data->searchstr = xstrdup(argument);
846 			for (; np != 0; np--)
847 				window_copy_search_down(wp, 1);
848 		}
849 		if (strcmp(command, "search-backward-incremental") == 0) {
850 			prefix = *argument++;
851 			if (data->searchx == -1 || data->searchy == -1) {
852 				data->searchx = data->cx;
853 				data->searchy = data->cy;
854 				data->searcho = data->oy;
855 			} else if (data->searchstr != NULL &&
856 			    strcmp(argument, data->searchstr) != 0) {
857 				data->cx = data->searchx;
858 				data->cy = data->searchy;
859 				data->oy = data->searcho;
860 				redraw = 1;
861 			}
862 			if (*argument == '\0') {
863 				window_copy_clear_marks(wp);
864 				redraw = 1;
865 			} else if (prefix == '=' || prefix == '-') {
866 				data->searchtype = WINDOW_COPY_SEARCHUP;
867 				free(data->searchstr);
868 				data->searchstr = xstrdup(argument);
869 				if (!window_copy_search_up(wp, 1)) {
870 					window_copy_clear_marks(wp);
871 					redraw = 1;
872 				}
873 			} else if (prefix == '+') {
874 				data->searchtype = WINDOW_COPY_SEARCHDOWN;
875 				free(data->searchstr);
876 				data->searchstr = xstrdup(argument);
877 				if (!window_copy_search_down(wp, 1)) {
878 					window_copy_clear_marks(wp);
879 					redraw = 1;
880 				}
881 			}
882 		}
883 		if (strcmp(command, "search-forward-incremental") == 0) {
884 			prefix = *argument++;
885 			if (data->searchx == -1 || data->searchy == -1) {
886 				data->searchx = data->cx;
887 				data->searchy = data->cy;
888 				data->searcho = data->oy;
889 			} else if (data->searchstr != NULL &&
890 			    strcmp(argument, data->searchstr) != 0) {
891 				data->cx = data->searchx;
892 				data->cy = data->searchy;
893 				data->oy = data->searcho;
894 				redraw = 1;
895 			}
896 			if (*argument == '\0') {
897 				window_copy_clear_marks(wp);
898 				redraw = 1;
899 			} else if (prefix == '=' || prefix == '+') {
900 				data->searchtype = WINDOW_COPY_SEARCHDOWN;
901 				free(data->searchstr);
902 				data->searchstr = xstrdup(argument);
903 				if (!window_copy_search_down(wp, 1)) {
904 					window_copy_clear_marks(wp);
905 					redraw = 1;
906 				}
907 			} else if (prefix == '-') {
908 				data->searchtype = WINDOW_COPY_SEARCHUP;
909 				free(data->searchstr);
910 				data->searchstr = xstrdup(argument);
911 				if (!window_copy_search_up(wp, 1)) {
912 					window_copy_clear_marks(wp);
913 					redraw = 1;
914 				}
915 			}
916 		}
917 	}
918 
919 	if (strncmp(command, "search-", 7) != 0 && data->searchmark != NULL) {
920 		window_copy_clear_marks(wp);
921 		redraw = 1;
922 		data->searchx = data->searchy = -1;
923 	}
924 
925 	if (cancel)
926 		window_pane_reset_mode(wp);
927 	else if (redraw)
928 		window_copy_redraw_screen(wp);
929 	wp->modeprefix = 1;
930 }
931 
932 static void
933 window_copy_scroll_to(struct window_pane *wp, u_int px, u_int py)
934 {
935 	struct window_copy_mode_data	*data = wp->modedata;
936 	struct grid			*gd = data->backing->grid;
937 	u_int				 offset, gap;
938 
939 	data->cx = px;
940 
941 	gap = gd->sy / 4;
942 	if (py < gd->sy) {
943 		offset = 0;
944 		data->cy = py;
945 	} else if (py > gd->hsize + gd->sy - gap) {
946 		offset = gd->hsize;
947 		data->cy = py - gd->hsize;
948 	} else {
949 		offset = py + gap - gd->sy;
950 		data->cy = py - offset;
951 	}
952 	data->oy = gd->hsize - offset;
953 
954 	window_copy_update_selection(wp, 1);
955 	window_copy_redraw_screen(wp);
956 }
957 
958 static int
959 window_copy_search_compare(struct grid *gd, u_int px, u_int py,
960     struct grid *sgd, u_int spx, int cis)
961 {
962 	struct grid_cell	 gc, sgc;
963 	const struct utf8_data	*ud, *sud;
964 
965 	grid_get_cell(gd, px, py, &gc);
966 	ud = &gc.data;
967 	grid_get_cell(sgd, spx, 0, &sgc);
968 	sud = &sgc.data;
969 
970 	if (ud->size != sud->size || ud->width != sud->width)
971 		return (0);
972 
973 	if (cis && ud->size == 1)
974 		return (tolower(ud->data[0]) == sud->data[0]);
975 
976 	return (memcmp(ud->data, sud->data, ud->size) == 0);
977 }
978 
979 static int
980 window_copy_search_lr(struct grid *gd,
981     struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis)
982 {
983 	u_int	ax, bx, px;
984 	int	matched;
985 
986 	for (ax = first; ax < last; ax++) {
987 		if (ax + sgd->sx >= gd->sx)
988 			break;
989 		for (bx = 0; bx < sgd->sx; bx++) {
990 			px = ax + bx;
991 			matched = window_copy_search_compare(gd, px, py, sgd,
992 			    bx, cis);
993 			if (!matched)
994 				break;
995 		}
996 		if (bx == sgd->sx) {
997 			*ppx = ax;
998 			return (1);
999 		}
1000 	}
1001 	return (0);
1002 }
1003 
1004 static int
1005 window_copy_search_rl(struct grid *gd,
1006     struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis)
1007 {
1008 	u_int	ax, bx, px;
1009 	int	matched;
1010 
1011 	for (ax = last + 1; ax > first; ax--) {
1012 		if (gd->sx - (ax - 1) < sgd->sx)
1013 			continue;
1014 		for (bx = 0; bx < sgd->sx; bx++) {
1015 			px = ax - 1 + bx;
1016 			matched = window_copy_search_compare(gd, px, py, sgd,
1017 			    bx, cis);
1018 			if (!matched)
1019 				break;
1020 		}
1021 		if (bx == sgd->sx) {
1022 			*ppx = ax - 1;
1023 			return (1);
1024 		}
1025 	}
1026 	return (0);
1027 }
1028 
1029 static void
1030 window_copy_move_left(struct screen *s, u_int *fx, u_int *fy)
1031 {
1032 	if (*fx == 0) {	/* left */
1033 		if (*fy == 0) /* top */
1034 			return;
1035 		*fx = screen_size_x(s) - 1;
1036 		*fy = *fy - 1;
1037 	} else
1038 		*fx = *fx - 1;
1039 }
1040 
1041 static void
1042 window_copy_move_right(struct screen *s, u_int *fx, u_int *fy)
1043 {
1044 	if (*fx == screen_size_x(s) - 1) { /* right */
1045 		if (*fy == screen_hsize(s) + screen_size_y(s)) /* bottom */
1046 			return;
1047 		*fx = 0;
1048 		*fy = *fy + 1;
1049 	} else
1050 		*fx = *fx + 1;
1051 }
1052 
1053 static int
1054 window_copy_is_lowercase(const char *ptr)
1055 {
1056 	while (*ptr != '\0') {
1057 		if (*ptr != tolower((u_char)*ptr))
1058 			return (0);
1059 		++ptr;
1060 	}
1061 	return (1);
1062 }
1063 
1064 /*
1065  * Search for text stored in sgd starting from position fx,fy up to endline. If
1066  * found, jump to it. If cis then ignore case. The direction is 0 for searching
1067  * up, down otherwise. If wrap then go to begin/end of grid and try again if
1068  * not found.
1069  */
1070 static int
1071 window_copy_search_jump(struct window_pane *wp, struct grid *gd,
1072     struct grid *sgd, u_int fx, u_int fy, u_int endline, int cis, int wrap,
1073     int direction)
1074 {
1075 	u_int	i, px;
1076 	int	found;
1077 
1078 	found = 0;
1079 	if (direction) {
1080 		for (i = fy; i <= endline; i++) {
1081 			found = window_copy_search_lr(gd, sgd, &px, i, fx,
1082 			    gd->sx, cis);
1083 			if (found)
1084 				break;
1085 			fx = 0;
1086 		}
1087 	} else {
1088 		for (i = fy + 1; endline < i; i--) {
1089 			found = window_copy_search_rl(gd, sgd, &px, i - 1, 0,
1090 			    fx, cis);
1091 			if (found) {
1092 				i--;
1093 				break;
1094 			}
1095 			fx = gd->sx;
1096 		}
1097 	}
1098 
1099 	if (found) {
1100 		window_copy_scroll_to(wp, px, i);
1101 		return (1);
1102 	}
1103 	if (wrap) {
1104 		return (window_copy_search_jump(wp, gd, sgd,
1105 		    direction ? 0 : gd->sx - 1,
1106 		    direction ? 0 : gd->hsize + gd->sy - 1, fy, cis, 0,
1107 		    direction));
1108 	}
1109 	return (0);
1110 }
1111 
1112 /*
1113  * Search in for text searchstr. If direction is 0 then search up, otherwise
1114  * down. If moveflag is 0 then look for string at the current cursor position
1115  * as well.
1116  */
1117 static int
1118 window_copy_search(struct window_pane *wp, int direction, int moveflag)
1119 {
1120 	struct window_copy_mode_data	*data = wp->modedata;
1121 	struct screen			*s = data->backing, ss;
1122 	struct screen_write_ctx		 ctx;
1123 	struct grid			*gd = s->grid;
1124 	u_int				 fx, fy, endline;
1125 	int				 wrapflag, cis, found;
1126 
1127 	fx = data->cx;
1128 	fy = screen_hsize(data->backing) - data->oy + data->cy;
1129 
1130 	screen_init(&ss, screen_write_strlen("%s", data->searchstr), 1, 0);
1131 	screen_write_start(&ctx, NULL, &ss);
1132 	screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", data->searchstr);
1133 	screen_write_stop(&ctx);
1134 
1135 	if (moveflag) {
1136 		if (direction)
1137 			window_copy_move_right(s, &fx, &fy);
1138 		else
1139 			window_copy_move_left(s, &fx, &fy);
1140 	}
1141 	window_copy_clear_selection(wp);
1142 
1143 	wrapflag = options_get_number(wp->window->options, "wrap-search");
1144 	cis = window_copy_is_lowercase(data->searchstr);
1145 
1146 	if (direction)
1147 		endline = gd->hsize + gd->sy - 1;
1148 	else
1149 		endline = 0;
1150 	found = window_copy_search_jump(wp, gd, ss.grid, fx, fy, endline, cis,
1151 	    wrapflag, direction);
1152 
1153 	if (window_copy_search_marks(wp, &ss))
1154 		window_copy_redraw_screen(wp);
1155 
1156 	screen_free(&ss);
1157 	return (found);
1158 }
1159 
1160 static int
1161 window_copy_search_marks(struct window_pane *wp, struct screen *ssp)
1162 {
1163 	struct window_copy_mode_data	*data = wp->modedata;
1164 	struct screen			*s = data->backing, ss;
1165 	struct screen_write_ctx		 ctx;
1166 	struct grid			*gd = s->grid;
1167 	int				 found, cis;
1168 	u_int				 px, py, b, nfound = 0, width;
1169 
1170 	if (ssp == NULL) {
1171 		width = screen_write_strlen("%s", data->searchstr);
1172 		screen_init(&ss, width, 1, 0);
1173 		screen_write_start(&ctx, NULL, &ss);
1174 		screen_write_nputs(&ctx, -1, &grid_default_cell, "%s",
1175 		    data->searchstr);
1176 		screen_write_stop(&ctx);
1177 		ssp = &ss;
1178 	} else
1179 		width = screen_size_x(ssp);
1180 
1181 	cis = window_copy_is_lowercase(data->searchstr);
1182 
1183 	free(data->searchmark);
1184 	data->searchmark = bit_alloc((gd->hsize + gd->sy) * gd->sx);
1185 
1186 	for (py = 0; py < gd->hsize + gd->sy; py++) {
1187 		px = 0;
1188 		for (;;) {
1189 			found = window_copy_search_lr(gd, ssp->grid, &px, py,
1190 			    px, gd->sx, cis);
1191 			if (!found)
1192 				break;
1193 			nfound++;
1194 
1195 			b = (py * gd->sx) + px;
1196 			bit_nset(data->searchmark, b, b + width - 1);
1197 
1198 			px++;
1199 		}
1200 	}
1201 
1202 	if (ssp == &ss)
1203 		screen_free(&ss);
1204 	return (nfound);
1205 }
1206 
1207 static void
1208 window_copy_clear_marks(struct window_pane *wp)
1209 {
1210 	struct window_copy_mode_data	*data = wp->modedata;
1211 
1212 	free(data->searchmark);
1213 	data->searchmark = NULL;
1214 }
1215 
1216 static int
1217 window_copy_search_up(struct window_pane *wp, int moveflag)
1218 {
1219 	return (window_copy_search(wp, 0, moveflag));
1220 }
1221 
1222 static int
1223 window_copy_search_down(struct window_pane *wp, int moveflag)
1224 {
1225 	return (window_copy_search(wp, 1, moveflag));
1226 }
1227 
1228 static void
1229 window_copy_goto_line(struct window_pane *wp, const char *linestr)
1230 {
1231 	struct window_copy_mode_data	*data = wp->modedata;
1232 	const char			*errstr;
1233 	u_int				 lineno;
1234 
1235 	lineno = strtonum(linestr, 0, screen_hsize(data->backing), &errstr);
1236 	if (errstr != NULL)
1237 		return;
1238 
1239 	data->oy = lineno;
1240 	window_copy_update_selection(wp, 1);
1241 	window_copy_redraw_screen(wp);
1242 }
1243 
1244 static void
1245 window_copy_write_line(struct window_pane *wp, struct screen_write_ctx *ctx,
1246     u_int py)
1247 {
1248 	struct window_copy_mode_data	*data = wp->modedata;
1249 	struct screen			*s = &data->screen;
1250 	struct options			*oo = wp->window->options;
1251 	struct grid_cell		 gc;
1252 	char				 hdr[512];
1253 	size_t				 size = 0;
1254 
1255 	style_apply(&gc, oo, "mode-style");
1256 	gc.flags |= GRID_FLAG_NOPALETTE;
1257 
1258 	if (py == 0) {
1259 		size = xsnprintf(hdr, sizeof hdr,
1260 		    "[%u/%u]", data->oy, screen_hsize(data->backing));
1261 		if (size > screen_size_x(s))
1262 			size = screen_size_x(s);
1263 		screen_write_cursormove(ctx, screen_size_x(s) - size, 0);
1264 		screen_write_puts(ctx, &gc, "%s", hdr);
1265 	} else
1266 		size = 0;
1267 
1268 	if (size < screen_size_x(s)) {
1269 		screen_write_cursormove(ctx, 0, py);
1270 		screen_write_copy(ctx, data->backing, 0,
1271 		    (screen_hsize(data->backing) - data->oy) + py,
1272 		    screen_size_x(s) - size, 1, data->searchmark, &gc);
1273 	}
1274 
1275 	if (py == data->cy && data->cx == screen_size_x(s)) {
1276 		memcpy(&gc, &grid_default_cell, sizeof gc);
1277 		screen_write_cursormove(ctx, screen_size_x(s) - 1, py);
1278 		screen_write_putc(ctx, &gc, '$');
1279 	}
1280 }
1281 
1282 static void
1283 window_copy_write_lines(struct window_pane *wp, struct screen_write_ctx *ctx,
1284     u_int py, u_int ny)
1285 {
1286 	u_int	yy;
1287 
1288 	for (yy = py; yy < py + ny; yy++)
1289 		window_copy_write_line(wp, ctx, py);
1290 }
1291 
1292 static void
1293 window_copy_redraw_selection(struct window_pane *wp, u_int old_y)
1294 {
1295 	struct window_copy_mode_data	*data = wp->modedata;
1296 	u_int				 new_y, start, end;
1297 
1298 	new_y = data->cy;
1299 	if (old_y <= new_y) {
1300 		start = old_y;
1301 		end = new_y;
1302 	} else {
1303 		start = new_y;
1304 		end = old_y;
1305 	}
1306 	window_copy_redraw_lines(wp, start, end - start + 1);
1307 }
1308 
1309 static void
1310 window_copy_redraw_lines(struct window_pane *wp, u_int py, u_int ny)
1311 {
1312 	struct window_copy_mode_data	*data = wp->modedata;
1313 	struct screen_write_ctx	 	 ctx;
1314 	u_int				 i;
1315 
1316 	screen_write_start(&ctx, wp, NULL);
1317 	for (i = py; i < py + ny; i++)
1318 		window_copy_write_line(wp, &ctx, i);
1319 	screen_write_cursormove(&ctx, data->cx, data->cy);
1320 	screen_write_stop(&ctx);
1321 }
1322 
1323 static void
1324 window_copy_redraw_screen(struct window_pane *wp)
1325 {
1326 	struct window_copy_mode_data	*data = wp->modedata;
1327 
1328 	window_copy_redraw_lines(wp, 0, screen_size_y(&data->screen));
1329 }
1330 
1331 static void
1332 window_copy_synchronize_cursor(struct window_pane *wp)
1333 {
1334 	struct window_copy_mode_data	*data = wp->modedata;
1335 	u_int				 xx, yy;
1336 
1337 	xx = data->cx;
1338 	yy = screen_hsize(data->backing) + data->cy - data->oy;
1339 
1340 	switch (data->cursordrag) {
1341 	case CURSORDRAG_ENDSEL:
1342 		data->endselx = xx;
1343 		data->endsely = yy;
1344 		break;
1345 	case CURSORDRAG_SEL:
1346 		data->selx = xx;
1347 		data->sely = yy;
1348 		break;
1349 	case CURSORDRAG_NONE:
1350 		break;
1351 	}
1352 }
1353 
1354 static void
1355 window_copy_update_cursor(struct window_pane *wp, u_int cx, u_int cy)
1356 {
1357 	struct window_copy_mode_data	*data = wp->modedata;
1358 	struct screen			*s = &data->screen;
1359 	struct screen_write_ctx		 ctx;
1360 	u_int				 old_cx, old_cy;
1361 
1362 	old_cx = data->cx; old_cy = data->cy;
1363 	data->cx = cx; data->cy = cy;
1364 	if (old_cx == screen_size_x(s))
1365 		window_copy_redraw_lines(wp, old_cy, 1);
1366 	if (data->cx == screen_size_x(s))
1367 		window_copy_redraw_lines(wp, data->cy, 1);
1368 	else {
1369 		screen_write_start(&ctx, wp, NULL);
1370 		screen_write_cursormove(&ctx, data->cx, data->cy);
1371 		screen_write_stop(&ctx);
1372 	}
1373 }
1374 
1375 static void
1376 window_copy_start_selection(struct window_pane *wp)
1377 {
1378 	struct window_copy_mode_data	*data = wp->modedata;
1379 	struct screen			*s = &data->screen;
1380 
1381 	data->selx = data->cx;
1382 	data->sely = screen_hsize(data->backing) + data->cy - data->oy;
1383 
1384 	data->endselx = data->selx;
1385 	data->endsely = data->sely;
1386 
1387 	data->cursordrag = CURSORDRAG_ENDSEL;
1388 
1389 	s->sel.flag = 1;
1390 	window_copy_update_selection(wp, 1);
1391 }
1392 
1393 static int
1394 window_copy_adjust_selection(struct window_pane *wp, u_int *selx, u_int *sely)
1395 {
1396 	struct window_copy_mode_data	*data = wp->modedata;
1397 	struct screen			*s = &data->screen;
1398 	u_int 				 sx, sy, ty;
1399 	int				 relpos;
1400 
1401 	sx = *selx;
1402 	sy = *sely;
1403 
1404 	ty = screen_hsize(data->backing) - data->oy;
1405 	if (sy < ty) {
1406 		relpos = WINDOW_COPY_REL_POS_ABOVE;
1407 		if (!data->rectflag)
1408 			sx = 0;
1409 		sy = 0;
1410 	} else if (sy > ty + screen_size_y(s) - 1) {
1411 		relpos = WINDOW_COPY_REL_POS_BELOW;
1412 		if (!data->rectflag)
1413 			sx = screen_size_x(s) - 1;
1414 		sy = screen_size_y(s) - 1;
1415 	} else {
1416 		relpos = WINDOW_COPY_REL_POS_ON_SCREEN;
1417 		sy -= ty;
1418 	}
1419 
1420 	*selx = sx;
1421 	*sely = screen_hsize(s) + sy;
1422 	return (relpos);
1423 }
1424 
1425 static int
1426 window_copy_update_selection(struct window_pane *wp, int may_redraw)
1427 {
1428 	struct window_copy_mode_data	*data = wp->modedata;
1429 	struct screen			*s = &data->screen;
1430 	struct options			*oo = wp->window->options;
1431 	struct grid_cell		 gc;
1432 	u_int				 sx, sy, cy, endsx, endsy;
1433 	int				 startrelpos, endrelpos;
1434 
1435 	if (!s->sel.flag && s->sel.lineflag == LINE_SEL_NONE)
1436 		return (0);
1437 
1438 	window_copy_synchronize_cursor(wp);
1439 
1440 	/* Adjust the selection. */
1441 	sx = data->selx;
1442 	sy = data->sely;
1443 	startrelpos = window_copy_adjust_selection(wp, &sx, &sy);
1444 
1445 	/* Adjust the end of selection. */
1446 	endsx = data->endselx;
1447 	endsy = data->endsely;
1448 	endrelpos = window_copy_adjust_selection(wp, &endsx, &endsy);
1449 
1450 	/* Selection is outside of the current screen */
1451 	if (startrelpos == endrelpos &&
1452 	    startrelpos != WINDOW_COPY_REL_POS_ON_SCREEN) {
1453 		screen_hide_selection(s);
1454 		return (0);
1455 	}
1456 
1457 	/* Set colours and selection. */
1458 	style_apply(&gc, oo, "mode-style");
1459 	gc.flags |= GRID_FLAG_NOPALETTE;
1460 	screen_set_selection(s, sx, sy, endsx, endsy, data->rectflag, &gc);
1461 
1462 	if (data->rectflag && may_redraw) {
1463 		/*
1464 		 * Can't rely on the caller to redraw the right lines for
1465 		 * rectangle selection - find the highest line and the number
1466 		 * of lines, and redraw just past that in both directions
1467 		 */
1468 		cy = data->cy;
1469 		if (sy < cy)
1470 			window_copy_redraw_lines(wp, sy, cy - sy + 1);
1471 		else
1472 			window_copy_redraw_lines(wp, cy, sy - cy + 1);
1473 	}
1474 
1475 	return (1);
1476 }
1477 
1478 static void *
1479 window_copy_get_selection(struct window_pane *wp, size_t *len)
1480 {
1481 	struct window_copy_mode_data	*data = wp->modedata;
1482 	struct screen			*s = &data->screen;
1483 	char				*buf;
1484 	size_t				 off;
1485 	u_int				 i, xx, yy, sx, sy, ex, ey, ey_last;
1486 	u_int				 firstsx, lastex, restex, restsx;
1487 	int				 keys;
1488 
1489 	if (!s->sel.flag && s->sel.lineflag == LINE_SEL_NONE)
1490 		return (NULL);
1491 
1492 	buf = xmalloc(1);
1493 	off = 0;
1494 
1495 	*buf = '\0';
1496 
1497 	/*
1498 	 * The selection extends from selx,sely to (adjusted) cx,cy on
1499 	 * the base screen.
1500 	 */
1501 
1502 	/* Find start and end. */
1503 	xx = data->endselx;
1504 	yy = data->endsely;
1505 	if (yy < data->sely || (yy == data->sely && xx < data->selx)) {
1506 		sx = xx; sy = yy;
1507 		ex = data->selx; ey = data->sely;
1508 	} else {
1509 		sx = data->selx; sy = data->sely;
1510 		ex = xx; ey = yy;
1511 	}
1512 
1513 	/* Trim ex to end of line. */
1514 	ey_last = window_copy_find_length(wp, ey);
1515 	if (ex > ey_last)
1516 		ex = ey_last;
1517 
1518 	/*
1519 	 * Deal with rectangle-copy if necessary; four situations: start of
1520 	 * first line (firstsx), end of last line (lastex), start (restsx) and
1521 	 * end (restex) of all other lines.
1522 	 */
1523 	xx = screen_size_x(s);
1524 
1525 	/*
1526 	 * Behave according to mode-keys. If it is emacs, copy like emacs,
1527 	 * keeping the top-left-most character, and dropping the
1528 	 * bottom-right-most, regardless of copy direction. If it is vi, also
1529 	 * keep bottom-right-most character.
1530 	 */
1531 	keys = options_get_number(wp->window->options, "mode-keys");
1532 	if (data->rectflag) {
1533 		/*
1534 		 * Need to ignore the column with the cursor in it, which for
1535 		 * rectangular copy means knowing which side the cursor is on.
1536 		 */
1537 		if (data->selx < data->cx) {
1538 			/* Selection start is on the left. */
1539 			if (keys == MODEKEY_EMACS) {
1540 				lastex = data->cx;
1541 				restex = data->cx;
1542 			}
1543 			else {
1544 				lastex = data->cx + 1;
1545 				restex = data->cx + 1;
1546 			}
1547 			firstsx = data->selx;
1548 			restsx = data->selx;
1549 		} else {
1550 			/* Cursor is on the left. */
1551 			lastex = data->selx + 1;
1552 			restex = data->selx + 1;
1553 			firstsx = data->cx;
1554 			restsx = data->cx;
1555 		}
1556 	} else {
1557 		if (keys == MODEKEY_EMACS)
1558 			lastex = ex;
1559 		else
1560 			lastex = ex + 1;
1561 		restex = xx;
1562 		firstsx = sx;
1563 		restsx = 0;
1564 	}
1565 
1566 	/* Copy the lines. */
1567 	for (i = sy; i <= ey; i++) {
1568 		window_copy_copy_line(wp, &buf, &off, i,
1569 		    (i == sy ? firstsx : restsx),
1570 		    (i == ey ? lastex : restex));
1571 	}
1572 
1573 	/* Don't bother if no data. */
1574 	if (off == 0) {
1575 		free(buf);
1576 		return (NULL);
1577 	}
1578 	if (keys == MODEKEY_EMACS || lastex <= ey_last)
1579 		off -= 1; /* remove final \n (unless at end in vi mode) */
1580 	*len = off;
1581 	return (buf);
1582 }
1583 
1584 static void
1585 window_copy_copy_buffer(struct window_pane *wp, const char *bufname, void *buf,
1586     size_t len)
1587 {
1588 	struct screen_write_ctx	ctx;
1589 
1590 	if (options_get_number(global_options, "set-clipboard")) {
1591 		screen_write_start(&ctx, wp, NULL);
1592 		screen_write_setselection(&ctx, buf, len);
1593 		screen_write_stop(&ctx);
1594 	}
1595 
1596 	if (paste_set(buf, len, bufname, NULL) != 0)
1597 		free(buf);
1598 }
1599 
1600 static void
1601 window_copy_copy_pipe(struct window_pane *wp, struct session *sess,
1602     const char *bufname, const char *arg)
1603 {
1604 	void			*buf;
1605 	size_t			 len;
1606 	struct job		*job;
1607 	struct format_tree	*ft;
1608 	char			*expanded;
1609 
1610 	buf = window_copy_get_selection(wp, &len);
1611 	if (buf == NULL)
1612 		return;
1613 
1614 	ft = format_create(NULL, 0);
1615 	format_defaults(ft, NULL, sess, NULL, wp);
1616 	expanded = format_expand(ft, arg);
1617 
1618 	job = job_run(expanded, sess, NULL, NULL, NULL, NULL);
1619 	bufferevent_write(job->event, buf, len);
1620 
1621 	free(expanded);
1622 	format_free(ft);
1623 
1624 	window_copy_copy_buffer(wp, bufname, buf, len);
1625 }
1626 
1627 static void
1628 window_copy_copy_selection(struct window_pane *wp, const char *bufname)
1629 {
1630 	void	*buf;
1631 	size_t	 len;
1632 
1633 	buf = window_copy_get_selection(wp, &len);
1634 	if (buf == NULL)
1635 		return;
1636 
1637 	window_copy_copy_buffer(wp, bufname, buf, len);
1638 }
1639 
1640 static void
1641 window_copy_append_selection(struct window_pane *wp, const char *bufname)
1642 {
1643 	char				*buf;
1644 	struct paste_buffer		*pb;
1645 	const char			*bufdata;
1646 	size_t				 len, bufsize;
1647 	struct screen_write_ctx		 ctx;
1648 
1649 	buf = window_copy_get_selection(wp, &len);
1650 	if (buf == NULL)
1651 		return;
1652 
1653 	if (options_get_number(global_options, "set-clipboard")) {
1654 		screen_write_start(&ctx, wp, NULL);
1655 		screen_write_setselection(&ctx, buf, len);
1656 		screen_write_stop(&ctx);
1657 	}
1658 
1659 	if (bufname == NULL || *bufname == '\0')
1660 		pb = paste_get_top(&bufname);
1661 	else
1662 		pb = paste_get_name(bufname);
1663 	if (pb != NULL) {
1664 		bufdata = paste_buffer_data(pb, &bufsize);
1665 		buf = xrealloc(buf, len + bufsize);
1666 		memmove(buf + bufsize, buf, len);
1667 		memcpy(buf, bufdata, bufsize);
1668 		len += bufsize;
1669 	}
1670 	if (paste_set(buf, len, bufname, NULL) != 0)
1671 		free(buf);
1672 }
1673 
1674 static void
1675 window_copy_copy_line(struct window_pane *wp, char **buf, size_t *off, u_int sy,
1676     u_int sx, u_int ex)
1677 {
1678 	struct window_copy_mode_data	*data = wp->modedata;
1679 	struct grid			*gd = data->backing->grid;
1680 	struct grid_cell		 gc;
1681 	struct grid_line		*gl;
1682 	struct utf8_data		 ud;
1683 	u_int				 i, xx, wrapped = 0;
1684 	const char			*s;
1685 
1686 	if (sx > ex)
1687 		return;
1688 
1689 	/*
1690 	 * Work out if the line was wrapped at the screen edge and all of it is
1691 	 * on screen.
1692 	 */
1693 	gl = &gd->linedata[sy];
1694 	if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx)
1695 		wrapped = 1;
1696 
1697 	/* If the line was wrapped, don't strip spaces (use the full length). */
1698 	if (wrapped)
1699 		xx = gl->cellsize;
1700 	else
1701 		xx = window_copy_find_length(wp, sy);
1702 	if (ex > xx)
1703 		ex = xx;
1704 	if (sx > xx)
1705 		sx = xx;
1706 
1707 	if (sx < ex) {
1708 		for (i = sx; i < ex; i++) {
1709 			grid_get_cell(gd, i, sy, &gc);
1710 			if (gc.flags & GRID_FLAG_PADDING)
1711 				continue;
1712 			utf8_copy(&ud, &gc.data);
1713 			if (ud.size == 1 && (gc.attr & GRID_ATTR_CHARSET)) {
1714 				s = tty_acs_get(NULL, ud.data[0]);
1715 				if (s != NULL && strlen(s) <= sizeof ud.data) {
1716 					ud.size = strlen(s);
1717 					memcpy(ud.data, s, ud.size);
1718 				}
1719 			}
1720 
1721 			*buf = xrealloc(*buf, (*off) + ud.size);
1722 			memcpy(*buf + *off, ud.data, ud.size);
1723 			*off += ud.size;
1724 		}
1725 	}
1726 
1727 	/* Only add a newline if the line wasn't wrapped. */
1728 	if (!wrapped || ex != xx) {
1729 		*buf = xrealloc(*buf, (*off) + 1);
1730 		(*buf)[(*off)++] = '\n';
1731 	}
1732 }
1733 
1734 static void
1735 window_copy_clear_selection(struct window_pane *wp)
1736 {
1737 	struct window_copy_mode_data   *data = wp->modedata;
1738 	u_int				px, py;
1739 
1740 	screen_clear_selection(&data->screen);
1741 
1742 	data->cursordrag = CURSORDRAG_NONE;
1743 
1744 	py = screen_hsize(data->backing) + data->cy - data->oy;
1745 	px = window_copy_find_length(wp, py);
1746 	if (data->cx > px)
1747 		window_copy_update_cursor(wp, px, data->cy);
1748 }
1749 
1750 static int
1751 window_copy_in_set(struct window_pane *wp, u_int px, u_int py, const char *set)
1752 {
1753 	struct window_copy_mode_data	*data = wp->modedata;
1754 	struct grid_cell		 gc;
1755 	const struct utf8_data		*ud;
1756 
1757 	grid_get_cell(data->backing->grid, px, py, &gc);
1758 
1759 	ud = &gc.data;
1760 	if (ud->size != 1 || (gc.flags & GRID_FLAG_PADDING))
1761 		return (0);
1762 	if (*ud->data == 0x00 || *ud->data == 0x7f)
1763 		return (0);
1764 	return (strchr(set, *ud->data) != NULL);
1765 }
1766 
1767 static u_int
1768 window_copy_find_length(struct window_pane *wp, u_int py)
1769 {
1770 	struct window_copy_mode_data	*data = wp->modedata;
1771 	struct screen			*s = data->backing;
1772 	struct grid_cell		 gc;
1773 	u_int				 px;
1774 
1775 	/*
1776 	 * If the pane has been resized, its grid can contain old overlong
1777 	 * lines. grid_peek_cell does not allow accessing cells beyond the
1778 	 * width of the grid, and screen_write_copy treats them as spaces, so
1779 	 * ignore them here too.
1780 	 */
1781 	px = s->grid->linedata[py].cellsize;
1782 	if (px > screen_size_x(s))
1783 		px = screen_size_x(s);
1784 	while (px > 0) {
1785 		grid_get_cell(s->grid, px - 1, py, &gc);
1786 		if (gc.data.size != 1 || *gc.data.data != ' ')
1787 			break;
1788 		px--;
1789 	}
1790 	return (px);
1791 }
1792 
1793 static void
1794 window_copy_cursor_start_of_line(struct window_pane *wp)
1795 {
1796 	struct window_copy_mode_data	*data = wp->modedata;
1797 	struct screen			*back_s = data->backing;
1798 	struct screen			*s = &data->screen;
1799 	struct grid			*gd = back_s->grid;
1800 	u_int				 py;
1801 
1802 	if (data->cx == 0 && s->sel.lineflag == LINE_SEL_NONE) {
1803 		py = screen_hsize(back_s) + data->cy - data->oy;
1804 		while (py > 0 &&
1805 		    gd->linedata[py-1].flags & GRID_LINE_WRAPPED) {
1806 			window_copy_cursor_up(wp, 0);
1807 			py = screen_hsize(back_s) + data->cy - data->oy;
1808 		}
1809 	}
1810 	window_copy_update_cursor(wp, 0, data->cy);
1811 	if (window_copy_update_selection(wp, 1))
1812 		window_copy_redraw_lines(wp, data->cy, 1);
1813 }
1814 
1815 static void
1816 window_copy_cursor_back_to_indentation(struct window_pane *wp)
1817 {
1818 	struct window_copy_mode_data	*data = wp->modedata;
1819 	u_int				 px, py, xx;
1820 	struct grid_cell		 gc;
1821 
1822 	px = 0;
1823 	py = screen_hsize(data->backing) + data->cy - data->oy;
1824 	xx = window_copy_find_length(wp, py);
1825 
1826 	while (px < xx) {
1827 		grid_get_cell(data->backing->grid, px, py, &gc);
1828 		if (gc.data.size != 1 || *gc.data.data != ' ')
1829 			break;
1830 		px++;
1831 	}
1832 
1833 	window_copy_update_cursor(wp, px, data->cy);
1834 	if (window_copy_update_selection(wp, 1))
1835 		window_copy_redraw_lines(wp, data->cy, 1);
1836 }
1837 
1838 static void
1839 window_copy_cursor_end_of_line(struct window_pane *wp)
1840 {
1841 	struct window_copy_mode_data	*data = wp->modedata;
1842 	struct screen			*back_s = data->backing;
1843 	struct screen			*s = &data->screen;
1844 	struct grid			*gd = back_s->grid;
1845 	u_int				 px, py;
1846 
1847 	py = screen_hsize(back_s) + data->cy - data->oy;
1848 	px = window_copy_find_length(wp, py);
1849 
1850 	if (data->cx == px && s->sel.lineflag == LINE_SEL_NONE) {
1851 		if (data->screen.sel.flag && data->rectflag)
1852 			px = screen_size_x(back_s);
1853 		if (gd->linedata[py].flags & GRID_LINE_WRAPPED) {
1854 			while (py < gd->sy + gd->hsize &&
1855 			    gd->linedata[py].flags & GRID_LINE_WRAPPED) {
1856 				window_copy_cursor_down(wp, 0);
1857 				py = screen_hsize(back_s)
1858 				     + data->cy - data->oy;
1859 			}
1860 			px = window_copy_find_length(wp, py);
1861 		}
1862 	}
1863 	window_copy_update_cursor(wp, px, data->cy);
1864 
1865 	if (window_copy_update_selection(wp, 1))
1866 		window_copy_redraw_lines(wp, data->cy, 1);
1867 }
1868 
1869 static void
1870 window_copy_other_end(struct window_pane *wp)
1871 {
1872 	struct window_copy_mode_data	*data = wp->modedata;
1873 	struct screen			*s = &data->screen;
1874 	u_int				 selx, sely, cy, yy, hsize;
1875 
1876 	if (!s->sel.flag && s->sel.lineflag == LINE_SEL_NONE)
1877 		return;
1878 
1879 	if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT)
1880 		s->sel.lineflag = LINE_SEL_RIGHT_LEFT;
1881 	else if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT)
1882 		s->sel.lineflag = LINE_SEL_LEFT_RIGHT;
1883 
1884 	switch (data->cursordrag) {
1885 		case CURSORDRAG_NONE:
1886 		case CURSORDRAG_SEL:
1887 			data->cursordrag = CURSORDRAG_ENDSEL;
1888 			break;
1889 		case CURSORDRAG_ENDSEL:
1890 			data->cursordrag = CURSORDRAG_SEL;
1891 			break;
1892 	}
1893 
1894 	selx = data->endselx;
1895 	sely = data->endsely;
1896 	if (data->cursordrag == CURSORDRAG_SEL) {
1897 		selx = data->selx;
1898 		sely = data->sely;
1899 	}
1900 
1901 	cy = data->cy;
1902 	yy = screen_hsize(data->backing) + data->cy - data->oy;
1903 
1904 	data->cx = selx;
1905 
1906 	hsize = screen_hsize(data->backing);
1907 	if (sely < hsize - data->oy) { /* above */
1908 		data->oy = hsize - sely;
1909 		data->cy = 0;
1910 	} else if (sely > hsize - data->oy + screen_size_y(s)) { /* below */
1911 		data->oy = hsize - sely + screen_size_y(s) - 1;
1912 		data->cy = screen_size_y(s) - 1;
1913 	} else
1914 		data->cy = cy + sely - yy;
1915 
1916 	window_copy_update_selection(wp, 1);
1917 	window_copy_redraw_screen(wp);
1918 }
1919 
1920 static void
1921 window_copy_cursor_left(struct window_pane *wp)
1922 {
1923 	struct window_copy_mode_data	*data = wp->modedata;
1924 	u_int				 py;
1925 
1926 	py = screen_hsize(data->backing) + data->cy - data->oy;
1927 	if (data->cx == 0 && py > 0) {
1928 		window_copy_cursor_up(wp, 0);
1929 		window_copy_cursor_end_of_line(wp);
1930 	} else if (data->cx > 0) {
1931 		window_copy_update_cursor(wp, data->cx - 1, data->cy);
1932 		if (window_copy_update_selection(wp, 1))
1933 			window_copy_redraw_lines(wp, data->cy, 1);
1934 	}
1935 }
1936 
1937 static void
1938 window_copy_cursor_right(struct window_pane *wp)
1939 {
1940 	struct window_copy_mode_data	*data = wp->modedata;
1941 	u_int				 px, py, yy;
1942 
1943 	py = screen_hsize(data->backing) + data->cy - data->oy;
1944 	yy = screen_hsize(data->backing) + screen_size_y(data->backing) - 1;
1945 	if (data->screen.sel.flag && data->rectflag)
1946 		px = screen_size_x(&data->screen);
1947 	else {
1948 		px = window_copy_find_length(wp, py);
1949 	}
1950 
1951 	if (data->cx >= px && py < yy) {
1952 		window_copy_cursor_start_of_line(wp);
1953 		window_copy_cursor_down(wp, 0);
1954 	} else if (data->cx < px) {
1955 		window_copy_update_cursor(wp, data->cx + 1, data->cy);
1956 		if (window_copy_update_selection(wp, 1))
1957 			window_copy_redraw_lines(wp, data->cy, 1);
1958 	}
1959 }
1960 
1961 static void
1962 window_copy_cursor_up(struct window_pane *wp, int scroll_only)
1963 {
1964 	struct window_copy_mode_data	*data = wp->modedata;
1965 	struct screen			*s = &data->screen;
1966 	u_int				 ox, oy, px, py;
1967 
1968 	oy = screen_hsize(data->backing) + data->cy - data->oy;
1969 	ox = window_copy_find_length(wp, oy);
1970 	if (data->cx != ox) {
1971 		data->lastcx = data->cx;
1972 		data->lastsx = ox;
1973 	}
1974 
1975 	if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely)
1976 		window_copy_other_end(wp);
1977 
1978 	data->cx = data->lastcx;
1979 	if (scroll_only || data->cy == 0) {
1980 		window_copy_scroll_down(wp, 1);
1981 		if (scroll_only) {
1982 			if (data->cy == screen_size_y(s) - 1)
1983 				window_copy_redraw_lines(wp, data->cy, 1);
1984 			else
1985 				window_copy_redraw_lines(wp, data->cy, 2);
1986 		}
1987 	} else {
1988 		window_copy_update_cursor(wp, data->cx, data->cy - 1);
1989 		if (window_copy_update_selection(wp, 1)) {
1990 			if (data->cy == screen_size_y(s) - 1)
1991 				window_copy_redraw_lines(wp, data->cy, 1);
1992 			else
1993 				window_copy_redraw_lines(wp, data->cy, 2);
1994 		}
1995 	}
1996 
1997 	if (!data->screen.sel.flag || !data->rectflag) {
1998 		py = screen_hsize(data->backing) + data->cy - data->oy;
1999 		px = window_copy_find_length(wp, py);
2000 		if ((data->cx >= data->lastsx && data->cx != px) ||
2001 		    data->cx > px)
2002 			window_copy_cursor_end_of_line(wp);
2003 	}
2004 
2005 	if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT)
2006 		window_copy_cursor_end_of_line(wp);
2007 	else if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT)
2008 		window_copy_cursor_start_of_line(wp);
2009 }
2010 
2011 static void
2012 window_copy_cursor_down(struct window_pane *wp, int scroll_only)
2013 {
2014 	struct window_copy_mode_data	*data = wp->modedata;
2015 	struct screen			*s = &data->screen;
2016 	u_int				 ox, oy, px, py;
2017 
2018 	oy = screen_hsize(data->backing) + data->cy - data->oy;
2019 	ox = window_copy_find_length(wp, oy);
2020 	if (data->cx != ox) {
2021 		data->lastcx = data->cx;
2022 		data->lastsx = ox;
2023 	}
2024 
2025 	if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT && oy == data->sely)
2026 		window_copy_other_end(wp);
2027 
2028 	data->cx = data->lastcx;
2029 	if (scroll_only || data->cy == screen_size_y(s) - 1) {
2030 		window_copy_scroll_up(wp, 1);
2031 		if (scroll_only && data->cy > 0)
2032 			window_copy_redraw_lines(wp, data->cy - 1, 2);
2033 	} else {
2034 		window_copy_update_cursor(wp, data->cx, data->cy + 1);
2035 		if (window_copy_update_selection(wp, 1))
2036 			window_copy_redraw_lines(wp, data->cy - 1, 2);
2037 	}
2038 
2039 	if (!data->screen.sel.flag || !data->rectflag) {
2040 		py = screen_hsize(data->backing) + data->cy - data->oy;
2041 		px = window_copy_find_length(wp, py);
2042 		if ((data->cx >= data->lastsx && data->cx != px) ||
2043 		    data->cx > px)
2044 			window_copy_cursor_end_of_line(wp);
2045 	}
2046 
2047 	if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT)
2048 		window_copy_cursor_end_of_line(wp);
2049 	else if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT)
2050 		window_copy_cursor_start_of_line(wp);
2051 }
2052 
2053 static void
2054 window_copy_cursor_jump(struct window_pane *wp)
2055 {
2056 	struct window_copy_mode_data	*data = wp->modedata;
2057 	struct screen			*back_s = data->backing;
2058 	struct grid_cell		 gc;
2059 	u_int				 px, py, xx;
2060 
2061 	px = data->cx + 1;
2062 	py = screen_hsize(back_s) + data->cy - data->oy;
2063 	xx = window_copy_find_length(wp, py);
2064 
2065 	while (px < xx) {
2066 		grid_get_cell(back_s->grid, px, py, &gc);
2067 		if (!(gc.flags & GRID_FLAG_PADDING) &&
2068 		    gc.data.size == 1 && *gc.data.data == data->jumpchar) {
2069 			window_copy_update_cursor(wp, px, data->cy);
2070 			if (window_copy_update_selection(wp, 1))
2071 				window_copy_redraw_lines(wp, data->cy, 1);
2072 			return;
2073 		}
2074 		px++;
2075 	}
2076 }
2077 
2078 static void
2079 window_copy_cursor_jump_back(struct window_pane *wp)
2080 {
2081 	struct window_copy_mode_data	*data = wp->modedata;
2082 	struct screen			*back_s = data->backing;
2083 	struct grid_cell		 gc;
2084 	u_int				 px, py;
2085 
2086 	px = data->cx;
2087 	py = screen_hsize(back_s) + data->cy - data->oy;
2088 
2089 	if (px > 0)
2090 		px--;
2091 
2092 	for (;;) {
2093 		grid_get_cell(back_s->grid, px, py, &gc);
2094 		if (!(gc.flags & GRID_FLAG_PADDING) &&
2095 		    gc.data.size == 1 && *gc.data.data == data->jumpchar) {
2096 			window_copy_update_cursor(wp, px, data->cy);
2097 			if (window_copy_update_selection(wp, 1))
2098 				window_copy_redraw_lines(wp, data->cy, 1);
2099 			return;
2100 		}
2101 		if (px == 0)
2102 			break;
2103 		px--;
2104 	}
2105 }
2106 
2107 static void
2108 window_copy_cursor_jump_to(struct window_pane *wp, int jump_again)
2109 {
2110 	struct window_copy_mode_data	*data = wp->modedata;
2111 	struct screen			*back_s = data->backing;
2112 	struct grid_cell		 gc;
2113 	u_int				 px, py, xx;
2114 
2115 	px = data->cx + 1 + jump_again;
2116 	py = screen_hsize(back_s) + data->cy - data->oy;
2117 	xx = window_copy_find_length(wp, py);
2118 
2119 	while (px < xx) {
2120 		grid_get_cell(back_s->grid, px, py, &gc);
2121 		if (!(gc.flags & GRID_FLAG_PADDING) &&
2122 		    gc.data.size == 1 && *gc.data.data == data->jumpchar) {
2123 			window_copy_update_cursor(wp, px - 1, data->cy);
2124 			if (window_copy_update_selection(wp, 1))
2125 				window_copy_redraw_lines(wp, data->cy, 1);
2126 			return;
2127 		}
2128 		px++;
2129 	}
2130 }
2131 
2132 static void
2133 window_copy_cursor_jump_to_back(struct window_pane *wp, int jump_again)
2134 {
2135 	struct window_copy_mode_data	*data = wp->modedata;
2136 	struct screen			*back_s = data->backing;
2137 	struct grid_cell		 gc;
2138 	u_int				 px, py;
2139 
2140 	px = data->cx;
2141 	py = screen_hsize(back_s) + data->cy - data->oy;
2142 
2143 	if (px > 0)
2144 		px--;
2145 
2146 	if (jump_again && px > 0)
2147 		px--;
2148 
2149 	for (;;) {
2150 		grid_get_cell(back_s->grid, px, py, &gc);
2151 		if (!(gc.flags & GRID_FLAG_PADDING) &&
2152 		    gc.data.size == 1 && *gc.data.data == data->jumpchar) {
2153 			window_copy_update_cursor(wp, px + 1, data->cy);
2154 			if (window_copy_update_selection(wp, 1))
2155 				window_copy_redraw_lines(wp, data->cy, 1);
2156 			return;
2157 		}
2158 		if (px == 0)
2159 			break;
2160 		px--;
2161 	}
2162 }
2163 
2164 static void
2165 window_copy_cursor_next_word(struct window_pane *wp, const char *separators)
2166 {
2167 	struct window_copy_mode_data	*data = wp->modedata;
2168 	struct screen			*back_s = data->backing;
2169 	u_int				 px, py, xx, yy;
2170 	int				 expected = 0;
2171 
2172 	px = data->cx;
2173 	py = screen_hsize(back_s) + data->cy - data->oy;
2174 	xx = window_copy_find_length(wp, py);
2175 	yy = screen_hsize(back_s) + screen_size_y(back_s) - 1;
2176 
2177 	/*
2178 	 * First skip past any nonword characters and then any word characters.
2179 	 *
2180 	 * expected is initially set to 0 for the former and then 1 for the
2181 	 * latter.
2182 	 */
2183 	do {
2184 		while (px > xx ||
2185 		    window_copy_in_set(wp, px, py, separators) == expected) {
2186 			/* Move down if we're past the end of the line. */
2187 			if (px > xx) {
2188 				if (py == yy)
2189 					return;
2190 				window_copy_cursor_down(wp, 0);
2191 				px = 0;
2192 
2193 				py = screen_hsize(back_s) + data->cy - data->oy;
2194 				xx = window_copy_find_length(wp, py);
2195 			} else
2196 				px++;
2197 		}
2198 		expected = !expected;
2199 	} while (expected == 1);
2200 
2201 	window_copy_update_cursor(wp, px, data->cy);
2202 	if (window_copy_update_selection(wp, 1))
2203 		window_copy_redraw_lines(wp, data->cy, 1);
2204 }
2205 
2206 static void
2207 window_copy_cursor_next_word_end(struct window_pane *wp,
2208     const char *separators)
2209 {
2210 	struct window_copy_mode_data	*data = wp->modedata;
2211 	struct options			*oo = wp->window->options;
2212 	struct screen			*back_s = data->backing;
2213 	u_int				 px, py, xx, yy;
2214 	int				 keys, expected = 1;
2215 
2216 	px = data->cx;
2217 	py = screen_hsize(back_s) + data->cy - data->oy;
2218 	xx = window_copy_find_length(wp, py);
2219 	yy = screen_hsize(back_s) + screen_size_y(back_s) - 1;
2220 
2221 	keys = options_get_number(oo, "mode-keys");
2222 	if (keys == MODEKEY_VI && !window_copy_in_set(wp, px, py, separators))
2223 		px++;
2224 
2225 	/*
2226 	 * First skip past any word characters, then any nonword characters.
2227 	 *
2228 	 * expected is initially set to 1 for the former and then 0 for the
2229 	 * latter.
2230 	 */
2231 	do {
2232 		while (px > xx ||
2233 		    window_copy_in_set(wp, px, py, separators) == expected) {
2234 			/* Move down if we're past the end of the line. */
2235 			if (px > xx) {
2236 				if (py == yy)
2237 					return;
2238 				window_copy_cursor_down(wp, 0);
2239 				px = 0;
2240 
2241 				py = screen_hsize(back_s) + data->cy - data->oy;
2242 				xx = window_copy_find_length(wp, py);
2243 			} else
2244 				px++;
2245 		}
2246 		expected = !expected;
2247 	} while (expected == 0);
2248 
2249 	if (keys == MODEKEY_VI && px != 0)
2250 		px--;
2251 
2252 	window_copy_update_cursor(wp, px, data->cy);
2253 	if (window_copy_update_selection(wp, 1))
2254 		window_copy_redraw_lines(wp, data->cy, 1);
2255 }
2256 
2257 /* Move to the previous place where a word begins. */
2258 static void
2259 window_copy_cursor_previous_word(struct window_pane *wp,
2260     const char *separators)
2261 {
2262 	struct window_copy_mode_data	*data = wp->modedata;
2263 	u_int				 px, py;
2264 
2265 	px = data->cx;
2266 	py = screen_hsize(data->backing) + data->cy - data->oy;
2267 
2268 	/* Move back to the previous word character. */
2269 	for (;;) {
2270 		if (px > 0) {
2271 			px--;
2272 			if (!window_copy_in_set(wp, px, py, separators))
2273 				break;
2274 		} else {
2275 			if (data->cy == 0 &&
2276 			    (screen_hsize(data->backing) == 0 ||
2277 			    data->oy >= screen_hsize(data->backing) - 1))
2278 				goto out;
2279 			window_copy_cursor_up(wp, 0);
2280 
2281 			py = screen_hsize(data->backing) + data->cy - data->oy;
2282 			px = window_copy_find_length(wp, py);
2283 		}
2284 	}
2285 
2286 	/* Move back to the beginning of this word. */
2287 	while (px > 0 && !window_copy_in_set(wp, px - 1, py, separators))
2288 		px--;
2289 
2290 out:
2291 	window_copy_update_cursor(wp, px, data->cy);
2292 	if (window_copy_update_selection(wp, 1))
2293 		window_copy_redraw_lines(wp, data->cy, 1);
2294 }
2295 
2296 static void
2297 window_copy_scroll_up(struct window_pane *wp, u_int ny)
2298 {
2299 	struct window_copy_mode_data	*data = wp->modedata;
2300 	struct screen			*s = &data->screen;
2301 	struct screen_write_ctx		 ctx;
2302 
2303 	if (data->oy < ny)
2304 		ny = data->oy;
2305 	if (ny == 0)
2306 		return;
2307 	data->oy -= ny;
2308 
2309 	window_copy_update_selection(wp, 0);
2310 
2311 	screen_write_start(&ctx, wp, NULL);
2312 	screen_write_cursormove(&ctx, 0, 0);
2313 	screen_write_deleteline(&ctx, ny, 8);
2314 	window_copy_write_lines(wp, &ctx, screen_size_y(s) - ny, ny);
2315 	window_copy_write_line(wp, &ctx, 0);
2316 	if (screen_size_y(s) > 1)
2317 		window_copy_write_line(wp, &ctx, 1);
2318 	if (screen_size_y(s) > 3)
2319 		window_copy_write_line(wp, &ctx, screen_size_y(s) - 2);
2320 	if (s->sel.flag && screen_size_y(s) > ny)
2321 		window_copy_write_line(wp, &ctx, screen_size_y(s) - ny - 1);
2322 	screen_write_cursormove(&ctx, data->cx, data->cy);
2323 	screen_write_stop(&ctx);
2324 }
2325 
2326 static void
2327 window_copy_scroll_down(struct window_pane *wp, u_int ny)
2328 {
2329 	struct window_copy_mode_data	*data = wp->modedata;
2330 	struct screen			*s = &data->screen;
2331 	struct screen_write_ctx		 ctx;
2332 
2333 	if (ny > screen_hsize(data->backing))
2334 		return;
2335 
2336 	if (data->oy > screen_hsize(data->backing) - ny)
2337 		ny = screen_hsize(data->backing) - data->oy;
2338 	if (ny == 0)
2339 		return;
2340 	data->oy += ny;
2341 
2342 	window_copy_update_selection(wp, 0);
2343 
2344 	screen_write_start(&ctx, wp, NULL);
2345 	screen_write_cursormove(&ctx, 0, 0);
2346 	screen_write_insertline(&ctx, ny, 8);
2347 	window_copy_write_lines(wp, &ctx, 0, ny);
2348 	if (s->sel.flag && screen_size_y(s) > ny)
2349 		window_copy_write_line(wp, &ctx, ny);
2350 	else if (ny == 1) /* nuke position */
2351 		window_copy_write_line(wp, &ctx, 1);
2352 	screen_write_cursormove(&ctx, data->cx, data->cy);
2353 	screen_write_stop(&ctx);
2354 }
2355 
2356 int
2357 window_copy_scroll_position(struct window_pane *wp)
2358 {
2359 	struct window_copy_mode_data	*data = wp->modedata;
2360 
2361 	if (wp->mode != &window_copy_mode)
2362 		return (-1);
2363 	return (data->oy);
2364 }
2365 
2366 static void
2367 window_copy_rectangle_toggle(struct window_pane *wp)
2368 {
2369 	struct window_copy_mode_data	*data = wp->modedata;
2370 	u_int				 px, py;
2371 
2372 	data->rectflag = !data->rectflag;
2373 
2374 	py = screen_hsize(data->backing) + data->cy - data->oy;
2375 	px = window_copy_find_length(wp, py);
2376 	if (data->cx > px)
2377 		window_copy_update_cursor(wp, px, data->cy);
2378 
2379 	window_copy_update_selection(wp, 1);
2380 	window_copy_redraw_screen(wp);
2381 }
2382 
2383 static void
2384 window_copy_move_mouse(struct mouse_event *m)
2385 {
2386 	struct window_pane	*wp;
2387 	u_int			 x, y;
2388 
2389 	wp = cmd_mouse_pane(m, NULL, NULL);
2390 	if (wp == NULL || wp->mode != &window_copy_mode)
2391 		return;
2392 
2393 	if (cmd_mouse_at(wp, m, &x, &y, 1) != 0)
2394 		return;
2395 
2396 	window_copy_update_cursor(wp, x, y);
2397 }
2398 
2399 void
2400 window_copy_start_drag(struct client *c, struct mouse_event *m)
2401 {
2402 	struct window_pane	*wp;
2403 	u_int			 x, y;
2404 
2405 	if (c == NULL)
2406 		return;
2407 
2408 	wp = cmd_mouse_pane(m, NULL, NULL);
2409 	if (wp == NULL || wp->mode != &window_copy_mode)
2410 		return;
2411 
2412 	if (cmd_mouse_at(wp, m, &x, &y, 1) != 0)
2413 		return;
2414 
2415 	c->tty.mouse_drag_update = window_copy_drag_update;
2416 	c->tty.mouse_drag_release = NULL; /* will fire MouseDragEnd key */
2417 
2418 	window_copy_update_cursor(wp, x, y);
2419 	window_copy_start_selection(wp);
2420 	window_copy_redraw_screen(wp);
2421 }
2422 
2423 static void
2424 window_copy_drag_update(__unused struct client *c, struct mouse_event *m)
2425 {
2426 	struct window_pane		*wp;
2427 	struct window_copy_mode_data	*data;
2428 	u_int				 x, y, old_cy;
2429 
2430 	wp = cmd_mouse_pane(m, NULL, NULL);
2431 	if (wp == NULL || wp->mode != &window_copy_mode)
2432 		return;
2433 	data = wp->modedata;
2434 
2435 	if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
2436 		return;
2437 	old_cy = data->cy;
2438 
2439 	window_copy_update_cursor(wp, x, y);
2440 	if (window_copy_update_selection(wp, 1))
2441 		window_copy_redraw_selection(wp, old_cy);
2442 }
2443