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