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