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