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