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