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