xref: /openbsd-src/usr.bin/tmux/window-copy.c (revision 5054e3e78af0749a9bb00ba9a024b3ee2d90290f)
1 /* $OpenBSD: window-copy.c,v 1.33 2009/11/18 17:03:16 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
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 <stdlib.h>
22 #include <string.h>
23 
24 #include "tmux.h"
25 
26 struct screen *window_copy_init(struct window_pane *);
27 void	window_copy_free(struct window_pane *);
28 void	window_copy_resize(struct window_pane *, u_int, u_int);
29 void	window_copy_key(struct window_pane *, struct client *, int);
30 int	window_copy_key_input(struct window_pane *, int);
31 void	window_copy_mouse(
32     	    struct window_pane *, struct client *, struct mouse_event *);
33 
34 void	window_copy_redraw_lines(struct window_pane *, u_int, u_int);
35 void	window_copy_redraw_screen(struct window_pane *);
36 void	window_copy_write_line(
37     	    struct window_pane *, struct screen_write_ctx *, u_int);
38 void	window_copy_write_lines(
39     	    struct window_pane *, struct screen_write_ctx *, u_int, u_int);
40 
41 void	window_copy_scroll_to(struct window_pane *, u_int, u_int);
42 int	window_copy_search_compare(
43 	    struct grid *, u_int, u_int, struct grid *, u_int);
44 int	window_copy_search_lr(
45 	    struct grid *, struct grid *, u_int *, u_int, u_int, u_int);
46 int	window_copy_search_rl(
47 	    struct grid *, struct grid *, u_int *, u_int, u_int, u_int);
48 void	window_copy_search_up(struct window_pane *, const char *);
49 void	window_copy_search_down(struct window_pane *, const char *);
50 void	window_copy_goto_line(struct window_pane *, const char *);
51 void	window_copy_update_cursor(struct window_pane *, u_int, u_int);
52 void	window_copy_start_selection(struct window_pane *);
53 int	window_copy_update_selection(struct window_pane *);
54 void	window_copy_copy_selection(struct window_pane *, struct client *);
55 void	window_copy_copy_line(
56 	    struct window_pane *, char **, size_t *, u_int, u_int, u_int);
57 int	window_copy_is_space(struct window_pane *, u_int, u_int);
58 u_int	window_copy_find_length(struct window_pane *, u_int);
59 void	window_copy_cursor_start_of_line(struct window_pane *);
60 void	window_copy_cursor_back_to_indentation(struct window_pane *);
61 void	window_copy_cursor_end_of_line(struct window_pane *);
62 void	window_copy_cursor_left(struct window_pane *);
63 void	window_copy_cursor_right(struct window_pane *);
64 void	window_copy_cursor_up(struct window_pane *, int);
65 void	window_copy_cursor_down(struct window_pane *, int);
66 void	window_copy_cursor_next_word(struct window_pane *);
67 void	window_copy_cursor_previous_word(struct window_pane *);
68 void	window_copy_scroll_up(struct window_pane *, u_int);
69 void	window_copy_scroll_down(struct window_pane *, u_int);
70 
71 const struct window_mode window_copy_mode = {
72 	window_copy_init,
73 	window_copy_free,
74 	window_copy_resize,
75 	window_copy_key,
76 	window_copy_mouse,
77 	NULL,
78 };
79 
80 enum window_copy_input_type {
81 	WINDOW_COPY_OFF,
82 	WINDOW_COPY_SEARCHUP,
83 	WINDOW_COPY_SEARCHDOWN,
84 	WINDOW_COPY_GOTOLINE,
85 };
86 
87 struct window_copy_mode_data {
88 	struct screen	screen;
89 
90 	struct mode_key_data mdata;
91 
92 	u_int		oy;
93 
94 	u_int		selx;
95 	u_int		sely;
96 
97 	u_int		cx;
98 	u_int		cy;
99 
100 	u_int		lastcx; /* position in last line with content */
101 	u_int		lastsx; /* size of last line with content */
102 
103 	enum window_copy_input_type inputtype;
104 	const char     *inputprompt;
105 	char   	       *inputstr;
106 
107 	enum window_copy_input_type searchtype;
108 	char	       *searchstr;
109 };
110 
111 struct screen *
112 window_copy_init(struct window_pane *wp)
113 {
114 	struct window_copy_mode_data	*data;
115 	struct screen			*s;
116 	struct screen_write_ctx	 	 ctx;
117 	u_int				 i;
118 	int				 keys;
119 
120 	wp->modedata = data = xmalloc(sizeof *data);
121 	data->oy = 0;
122 	data->cx = wp->base.cx;
123 	data->cy = wp->base.cy;
124 
125 	data->lastcx = 0;
126 	data->lastsx = 0;
127 
128 	data->inputtype = WINDOW_COPY_OFF;
129 	data->inputprompt = NULL;
130 	data->inputstr = xstrdup("");
131 
132 	data->searchtype = WINDOW_COPY_OFF;
133 	data->searchstr = NULL;
134 
135 	s = &data->screen;
136 	screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
137 	if (options_get_number(&wp->window->options, "mode-mouse"))
138 		s->mode |= MODE_MOUSE;
139 
140 	keys = options_get_number(&wp->window->options, "mode-keys");
141 	if (keys == MODEKEY_EMACS)
142 		mode_key_init(&data->mdata, &mode_key_tree_emacs_copy);
143 	else
144 		mode_key_init(&data->mdata, &mode_key_tree_vi_copy);
145 
146 	s->cx = data->cx;
147 	s->cy = data->cy;
148 
149 	screen_write_start(&ctx, NULL, s);
150 	for (i = 0; i < screen_size_y(s); i++)
151 		window_copy_write_line(wp, &ctx, i);
152 	screen_write_cursormove(&ctx, data->cx, data->cy);
153 	screen_write_stop(&ctx);
154 
155 	return (s);
156 }
157 
158 void
159 window_copy_free(struct window_pane *wp)
160 {
161 	struct window_copy_mode_data	*data = wp->modedata;
162 
163 	if (data->searchstr != NULL)
164 		xfree(data->searchstr);
165 	xfree(data->inputstr);
166 
167 	screen_free(&data->screen);
168 
169 	xfree(data);
170 }
171 
172 void
173 window_copy_pageup(struct window_pane *wp)
174 {
175 	struct window_copy_mode_data	*data = wp->modedata;
176 	struct screen			*s = &data->screen;
177 	u_int				 n;
178 
179 	n = 1;
180 	if (screen_size_y(s) > 2)
181 		n = screen_size_y(s) - 2;
182 	if (data->oy + n > screen_hsize(&wp->base))
183 		data->oy = screen_hsize(&wp->base);
184 	else
185 		data->oy += n;
186 	window_copy_update_selection(wp);
187 	window_copy_redraw_screen(wp);
188 }
189 
190 void
191 window_copy_resize(struct window_pane *wp, u_int sx, u_int sy)
192 {
193 	struct window_copy_mode_data	*data = wp->modedata;
194 	struct screen			*s = &data->screen;
195 	struct screen_write_ctx	 	 ctx;
196 
197 	screen_resize(s, sx, sy);
198 
199 	if (data->cy > sy - 1)
200 		data->cy = sy - 1;
201 	if (data->cx > sx)
202 		data->cx = sx;
203 
204 	screen_clear_selection(&data->screen);
205 
206 	screen_write_start(&ctx, NULL, s);
207 	window_copy_write_lines(wp, &ctx, 0, screen_size_y(s) - 1);
208 	screen_write_stop(&ctx);
209 
210 	window_copy_redraw_screen(wp);
211 }
212 
213 void
214 window_copy_key(struct window_pane *wp, struct client *c, int key)
215 {
216 	struct window_copy_mode_data	*data = wp->modedata;
217 	struct screen			*s = &data->screen;
218 	u_int				 n;
219 	int				 keys;
220 
221 	if (data->inputtype != WINDOW_COPY_OFF) {
222 		if (window_copy_key_input(wp, key) != 0)
223 			goto input_off;
224 		return;
225 	}
226 
227 	switch (mode_key_lookup(&data->mdata, key)) {
228 	case MODEKEYCOPY_CANCEL:
229 		window_pane_reset_mode(wp);
230 		break;
231 	case MODEKEYCOPY_LEFT:
232 		window_copy_cursor_left(wp);
233 		return;
234 	case MODEKEYCOPY_RIGHT:
235 		window_copy_cursor_right(wp);
236  		return;
237 	case MODEKEYCOPY_UP:
238 		window_copy_cursor_up(wp, 0);
239 		return;
240 	case MODEKEYCOPY_DOWN:
241 		window_copy_cursor_down(wp, 0);
242 		return;
243 	case MODEKEYCOPY_SCROLLUP:
244 		window_copy_cursor_up(wp, 1);
245 		break;
246 	case MODEKEYCOPY_SCROLLDOWN:
247 		window_copy_cursor_down(wp, 1);
248 		break;
249 	case MODEKEYCOPY_PREVIOUSPAGE:
250 		window_copy_pageup(wp);
251 		break;
252 	case MODEKEYCOPY_NEXTPAGE:
253 		n = 1;
254 		if (screen_size_y(s) > 2)
255 			n = screen_size_y(s) - 2;
256 		if (data->oy < n)
257 			data->oy = 0;
258 		else
259 			data->oy -= n;
260 		window_copy_update_selection(wp);
261 		window_copy_redraw_screen(wp);
262 		break;
263 	case MODEKEYCOPY_HALFPAGEUP:
264 		n = screen_size_y(s) / 2;
265 		if (data->oy + n > screen_hsize(&wp->base))
266 			data->oy = screen_hsize(&wp->base);
267 		else
268 			data->oy += n;
269 		window_copy_update_selection(wp);
270 		window_copy_redraw_screen(wp);
271 		break;
272 	case MODEKEYCOPY_HALFPAGEDOWN:
273 		n = screen_size_y(s) / 2;
274 		if (data->oy < n)
275 			data->oy = 0;
276 		else
277 			data->oy -= n;
278 		window_copy_update_selection(wp);
279 		window_copy_redraw_screen(wp);
280 		break;
281 	case MODEKEYCOPY_TOPLINE:
282 		data->cx = 0;
283 		data->cy = 0;
284 		window_copy_update_selection(wp);
285 		window_copy_redraw_screen(wp);
286 		break;
287 	case MODEKEYCOPY_MIDDLELINE:
288 		data->cx = 0;
289 		data->cy = (screen_size_y(s) - 1) / 2;
290 		window_copy_update_selection(wp);
291 		window_copy_redraw_screen(wp);
292 		break;
293 	case MODEKEYCOPY_BOTTOMLINE:
294 		data->cx = 0;
295 		data->cy = screen_size_y(s) - 1;
296 		window_copy_update_selection(wp);
297 		window_copy_redraw_screen(wp);
298 		break;
299 	case MODEKEYCOPY_STARTSELECTION:
300  		window_copy_start_selection(wp);
301 		window_copy_redraw_screen(wp);
302 		break;
303 	case MODEKEYCOPY_CLEARSELECTION:
304 		screen_clear_selection(&data->screen);
305 		window_copy_redraw_screen(wp);
306 		break;
307 	case MODEKEYCOPY_COPYSELECTION:
308 		if (c != NULL && c->session != NULL) {
309 			window_copy_copy_selection(wp, c);
310 			window_pane_reset_mode(wp);
311 		}
312 		break;
313 	case MODEKEYCOPY_STARTOFLINE:
314 		window_copy_cursor_start_of_line(wp);
315 		break;
316 	case MODEKEYCOPY_BACKTOINDENTATION:
317 		window_copy_cursor_back_to_indentation(wp);
318 		break;
319 	case MODEKEYCOPY_ENDOFLINE:
320 		window_copy_cursor_end_of_line(wp);
321 		break;
322 	case MODEKEYCOPY_NEXTWORD:
323 		window_copy_cursor_next_word(wp);
324 		break;
325 	case MODEKEYCOPY_PREVIOUSWORD:
326 		window_copy_cursor_previous_word(wp);
327 		break;
328 	case MODEKEYCOPY_SEARCHUP:
329 		data->inputtype = WINDOW_COPY_SEARCHUP;
330 		data->inputprompt = "Search Up";
331 		goto input_on;
332 	case MODEKEYCOPY_SEARCHDOWN:
333 		data->inputtype = WINDOW_COPY_SEARCHDOWN;
334 		data->inputprompt = "Search Down";
335 		goto input_on;
336 	case MODEKEYCOPY_SEARCHAGAIN:
337 		switch (data->searchtype) {
338 		case WINDOW_COPY_OFF:
339 		case WINDOW_COPY_GOTOLINE:
340 			break;
341 		case WINDOW_COPY_SEARCHUP:
342 			window_copy_search_up(wp, data->searchstr);
343 			break;
344 		case WINDOW_COPY_SEARCHDOWN:
345 			window_copy_search_down(wp, data->searchstr);
346 			break;
347 		}
348 		break;
349 	case MODEKEYCOPY_GOTOLINE:
350 		data->inputtype = WINDOW_COPY_GOTOLINE;
351 		data->inputprompt = "Goto Line";
352 		*data->inputstr = '\0';
353 		goto input_on;
354 	default:
355 		break;
356 	}
357 
358 	return;
359 
360 input_on:
361 	keys = options_get_number(&wp->window->options, "mode-keys");
362 	if (keys == MODEKEY_EMACS)
363 		mode_key_init(&data->mdata, &mode_key_tree_emacs_edit);
364 	else
365 		mode_key_init(&data->mdata, &mode_key_tree_vi_edit);
366 
367 	window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
368 	return;
369 
370 input_off:
371 	keys = options_get_number(&wp->window->options, "mode-keys");
372 	if (keys == MODEKEY_EMACS)
373 		mode_key_init(&data->mdata, &mode_key_tree_emacs_copy);
374 	else
375 		mode_key_init(&data->mdata, &mode_key_tree_vi_copy);
376 
377 	data->inputtype = WINDOW_COPY_OFF;
378 	data->inputprompt = NULL;
379 
380 	window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
381 }
382 
383 int
384 window_copy_key_input(struct window_pane *wp, int key)
385 {
386 	struct window_copy_mode_data	*data = wp->modedata;
387 	struct screen			*s = &data->screen;
388 	size_t				 inputlen;
389 
390 	switch (mode_key_lookup(&data->mdata, key)) {
391 	case MODEKEYEDIT_CANCEL:
392 		return (-1);
393 	case MODEKEYEDIT_BACKSPACE:
394 		inputlen = strlen(data->inputstr);
395 		if (inputlen > 0)
396 			data->inputstr[inputlen - 1] = '\0';
397 		break;
398 	case MODEKEYEDIT_DELETELINE:
399 		*data->inputstr = '\0';
400 		break;
401 	case MODEKEYEDIT_ENTER:
402 		switch (data->inputtype) {
403 		case WINDOW_COPY_OFF:
404 			break;
405 		case WINDOW_COPY_SEARCHUP:
406 			window_copy_search_up(wp, data->inputstr);
407 			data->searchtype = data->inputtype;
408 			data->searchstr = xstrdup(data->inputstr);
409 			break;
410 		case WINDOW_COPY_SEARCHDOWN:
411 			window_copy_search_down(wp, data->inputstr);
412 			data->searchtype = data->inputtype;
413 			data->searchstr = xstrdup(data->inputstr);
414 			break;
415 		case WINDOW_COPY_GOTOLINE:
416 			window_copy_goto_line(wp, data->inputstr);
417 			*data->inputstr = '\0';
418 			break;
419 		}
420 		return (1);
421 	case MODEKEY_OTHER:
422 		if (key < 32 || key > 126)
423 			break;
424 		inputlen = strlen(data->inputstr) + 2;
425 
426 		data->inputstr = xrealloc(data->inputstr, 1, inputlen);
427 		data->inputstr[inputlen - 2] = key;
428 		data->inputstr[inputlen - 1] = '\0';
429 		break;
430 	default:
431 		break;
432 	}
433 
434 	window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
435 	return (0);
436 }
437 
438 void
439 window_copy_mouse(
440     struct window_pane *wp, unused struct client *c, struct mouse_event *m)
441 {
442 	struct window_copy_mode_data	*data = wp->modedata;
443 	struct screen			*s = &data->screen;
444 
445 	if ((m->b & 3) == 3)
446 		return;
447 	if (m->x >= screen_size_x(s))
448 		return;
449 	if (m->y >= screen_size_y(s))
450 		return;
451 
452 	window_copy_update_cursor(wp, m->x, m->y);
453 	if (window_copy_update_selection(wp))
454  		window_copy_redraw_screen(wp);
455 }
456 
457 void
458 window_copy_scroll_to(struct window_pane *wp, u_int px, u_int py)
459 {
460 	struct window_copy_mode_data	*data = wp->modedata;
461 	struct screen			*s = &wp->base;
462 	struct grid			*gd = s->grid;
463 	u_int				 offset, gap;
464 
465 	data->cx = px;
466 
467 	gap = gd->sy / 4;
468 	if (py < gd->sy) {
469 		offset = 0;
470 		data->cy = py;
471 	} else if (py > gd->hsize + gd->sy - gap) {
472 		offset = gd->hsize;
473 		data->cy = py - gd->hsize;
474 	} else {
475 		offset = py + gap - gd->sy;
476 		data->cy = py - offset;
477 	}
478  	data->oy = gd->hsize - offset;
479 
480 	window_copy_redraw_screen(wp);
481 }
482 
483 int
484 window_copy_search_compare(
485     struct grid *gd, u_int px, u_int py, struct grid *sgd, u_int spx)
486 {
487 	const struct grid_cell	*gc, *sgc;
488 	const struct grid_utf8	*gu, *sgu;
489 
490 	gc = grid_peek_cell(gd, px, py);
491 	sgc = grid_peek_cell(sgd, spx, 0);
492 
493 	if ((gc->flags & GRID_FLAG_UTF8) != (sgc->flags & GRID_FLAG_UTF8))
494 		return (0);
495 
496 	if (gc->flags & GRID_FLAG_UTF8) {
497 		gu = grid_peek_utf8(gd, px, py);
498 		sgu = grid_peek_utf8(sgd, spx, 0);
499 		if (grid_utf8_compare(gu, sgu))
500 			return (1);
501 	} else {
502 		if (gc->data == sgc->data)
503 			return (1);
504 	}
505 	return (0);
506 }
507 
508 int
509 window_copy_search_lr(struct grid *gd,
510     struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last)
511 {
512 	u_int	ax, bx, px;
513 
514 	for (ax = first; ax < last; ax++) {
515 		if (ax + sgd->sx >= gd->sx)
516 			break;
517 		for (bx = 0; bx < sgd->sx; bx++) {
518 			px = ax + bx;
519 			if (!window_copy_search_compare(gd, px, py, sgd, bx))
520 				break;
521 		}
522 		if (bx == sgd->sx) {
523 			*ppx = ax;
524 			return (1);
525 		}
526 	}
527 	return (0);
528 }
529 
530 int
531 window_copy_search_rl(struct grid *gd,
532     struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last)
533 {
534 	u_int	ax, bx, px;
535 
536 	for (ax = last + 1; ax > first; ax--) {
537 		if (gd->sx - (ax - 1) < sgd->sx)
538 			continue;
539 		for (bx = 0; bx < sgd->sx; bx++) {
540 			px = ax - 1 + bx;
541 			if (!window_copy_search_compare(gd, px, py, sgd, bx))
542 				break;
543 		}
544 		if (bx == sgd->sx) {
545 			*ppx = ax - 1;
546 			return (1);
547 		}
548 	}
549 	return (0);
550 }
551 
552 void
553 window_copy_search_up(struct window_pane *wp, const char *searchstr)
554 {
555 	struct window_copy_mode_data	*data = wp->modedata;
556 	struct screen			*s = &wp->base, ss;
557 	struct screen_write_ctx		 ctx;
558 	struct grid			*gd = s->grid, *sgd;
559 	struct grid_cell	 	 gc;
560 	size_t				 searchlen;
561 	u_int				 i, last, fx, fy, px;
562 	int				 utf8flag, n, wrapped;
563 
564 	if (*searchstr == '\0')
565 		return;
566 	utf8flag = options_get_number(&wp->window->options, "utf8");
567 	searchlen = screen_write_strlen(utf8flag, "%s", searchstr);
568 
569 	screen_init(&ss, searchlen, 1, 0);
570 	screen_write_start(&ctx, NULL, &ss);
571 	memcpy(&gc, &grid_default_cell, sizeof gc);
572 	screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr);
573 	screen_write_stop(&ctx);
574 
575 	fx = data->cx;
576 	fy = gd->hsize - data->oy + data->cy;
577 
578 	if (fx == 0) {
579 		if (fy == 0)
580 			return;
581 		fx = gd->sx - 1;
582 		fy--;
583 	} else
584 		fx--;
585 	n = wrapped = 0;
586 
587 retry:
588 	sgd = ss.grid;
589 	for (i = fy + 1; i > 0; i--) {
590 		last = screen_size_x(s);
591 		if (i == fy + 1)
592 			last = fx;
593 		n = window_copy_search_rl(gd, sgd, &px, i - 1, 0, last);
594 		if (n) {
595 			window_copy_scroll_to(wp, px, i - 1);
596 			break;
597 		}
598 	}
599 	if (!n && !wrapped) {
600 		fx = gd->sx - 1;
601 		fy = gd->hsize + gd->sy - 1;
602 		wrapped = 1;
603 		goto retry;
604 	}
605 
606 	screen_free(&ss);
607 }
608 
609 void
610 window_copy_search_down(struct window_pane *wp, const char *searchstr)
611 {
612 	struct window_copy_mode_data	*data = wp->modedata;
613 	struct screen			*s = &wp->base, ss;
614 	struct screen_write_ctx		 ctx;
615 	struct grid			*gd = s->grid, *sgd;
616 	struct grid_cell	 	 gc;
617 	size_t				 searchlen;
618 	u_int				 i, first, fx, fy, px;
619 	int				 utf8flag, n, wrapped;
620 
621 	if (*searchstr == '\0')
622 		return;
623 	utf8flag = options_get_number(&wp->window->options, "utf8");
624 	searchlen = screen_write_strlen(utf8flag, "%s", searchstr);
625 
626 	screen_init(&ss, searchlen, 1, 0);
627 	screen_write_start(&ctx, NULL, &ss);
628 	memcpy(&gc, &grid_default_cell, sizeof gc);
629 	screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr);
630 	screen_write_stop(&ctx);
631 
632 	fx = data->cx;
633 	fy = gd->hsize - data->oy + data->cy;
634 
635 	if (fx == gd->sx - 1) {
636 		if (fy == gd->hsize + gd->sy)
637 			return;
638 		fx = 0;
639 		fy++;
640 	} else
641 		fx++;
642 	n = wrapped = 0;
643 
644 retry:
645 	sgd = ss.grid;
646 	for (i = fy + 1; i < gd->hsize + gd->sy; i++) {
647 		first = 0;
648 		if (i == fy + 1)
649 			first = fx;
650 		n = window_copy_search_lr(gd, sgd, &px, i - 1, first, gd->sx);
651 		if (n) {
652 			window_copy_scroll_to(wp, px, i - 1);
653 			break;
654 		}
655 	}
656 	if (!n && !wrapped) {
657 		fx = 0;
658 		fy = 0;
659 		wrapped = 1;
660 		goto retry;
661 	}
662 
663 	screen_free(&ss);
664 }
665 
666 void
667 window_copy_goto_line(struct window_pane *wp, const char *linestr)
668 {
669 	struct window_copy_mode_data	*data = wp->modedata;
670 	const char			*errstr;
671 	u_int				 lineno;
672 
673 	lineno = strtonum(linestr, 0, screen_hsize(&wp->base), &errstr);
674 	if (errstr != NULL)
675 		return;
676 
677 	data->oy = lineno;
678 	window_copy_redraw_screen(wp);
679 }
680 
681 void
682 window_copy_write_line(
683     struct window_pane *wp, struct screen_write_ctx *ctx, u_int py)
684 {
685 	struct window_copy_mode_data	*data = wp->modedata;
686 	struct screen			*s = &data->screen;
687 	struct options			*oo = &wp->window->options;
688 	struct grid_cell		 gc;
689 	char				 hdr[32];
690 	size_t	 			 last, xoff = 0, size = 0;
691 
692 	memcpy(&gc, &grid_default_cell, sizeof gc);
693 	colour_set_fg(&gc, options_get_number(oo, "mode-fg"));
694 	colour_set_bg(&gc, options_get_number(oo, "mode-bg"));
695 	gc.attr |= options_get_number(oo, "mode-attr");
696 
697 	last = screen_size_y(s) - 1;
698 	if (py == 0) {
699 		size = xsnprintf(hdr, sizeof hdr,
700 		    "[%u/%u]", data->oy, screen_hsize(&wp->base));
701 		screen_write_cursormove(ctx, screen_size_x(s) - size, 0);
702 		screen_write_puts(ctx, &gc, "%s", hdr);
703 	} else if (py == last && data->inputtype != WINDOW_COPY_OFF) {
704 		xoff = size = xsnprintf(hdr, sizeof hdr,
705 		    "%s: %s", data->inputprompt, data->inputstr);
706 		screen_write_cursormove(ctx, 0, last);
707 		screen_write_puts(ctx, &gc, "%s", hdr);
708 	} else
709 		size = 0;
710 
711 	screen_write_cursormove(ctx, xoff, py);
712 	screen_write_copy(ctx, &wp->base, xoff, (screen_hsize(&wp->base) -
713 	    data->oy) + py, screen_size_x(s) - size, 1);
714 
715 	if (py == data->cy && data->cx == screen_size_x(s)) {
716 		memcpy(&gc, &grid_default_cell, sizeof gc);
717 		screen_write_cursormove(ctx, screen_size_x(s) - 1, py);
718 		screen_write_putc(ctx, &gc, '$');
719 	}
720 }
721 
722 void
723 window_copy_write_lines(
724     struct window_pane *wp, struct screen_write_ctx *ctx, u_int py, u_int ny)
725 {
726 	u_int	yy;
727 
728 	for (yy = py; yy < py + ny; yy++)
729 		window_copy_write_line(wp, ctx, py);
730 }
731 
732 void
733 window_copy_redraw_lines(struct window_pane *wp, u_int py, u_int ny)
734 {
735 	struct window_copy_mode_data	*data = wp->modedata;
736 	struct screen_write_ctx	 	 ctx;
737 	u_int				 i;
738 
739 	screen_write_start(&ctx, wp, NULL);
740 	for (i = py; i < py + ny; i++)
741 		window_copy_write_line(wp, &ctx, i);
742 	screen_write_cursormove(&ctx, data->cx, data->cy);
743 	screen_write_stop(&ctx);
744 }
745 
746 void
747 window_copy_redraw_screen(struct window_pane *wp)
748 {
749 	struct window_copy_mode_data	*data = wp->modedata;
750 
751 	window_copy_redraw_lines(wp, 0, screen_size_y(&data->screen));
752 }
753 
754 void
755 window_copy_update_cursor(struct window_pane *wp, u_int cx, u_int cy)
756 {
757 	struct window_copy_mode_data	*data = wp->modedata;
758 	struct screen			*s = &data->screen;
759 	struct screen_write_ctx		 ctx;
760 	u_int				 old_cx, old_cy;
761 
762 	old_cx = data->cx; old_cy = data->cy;
763 	data->cx = cx; data->cy = cy;
764 	if (old_cx == screen_size_x(s))
765 		window_copy_redraw_lines(wp, old_cy, 1);
766 	if (data->cx == screen_size_x(s))
767 		window_copy_redraw_lines(wp, data->cy, 1);
768 	else {
769 		screen_write_start(&ctx, wp, NULL);
770 		screen_write_cursormove(&ctx, data->cx, data->cy);
771 		screen_write_stop(&ctx);
772 	}
773 }
774 
775 void
776 window_copy_start_selection(struct window_pane *wp)
777 {
778 	struct window_copy_mode_data	*data = wp->modedata;
779 	struct screen			*s = &data->screen;
780 
781 	data->selx = data->cx;
782 	data->sely = screen_hsize(&wp->base) + data->cy - data->oy;
783 
784 	s->sel.flag = 1;
785 	window_copy_update_selection(wp);
786 }
787 
788 int
789 window_copy_update_selection(struct window_pane *wp)
790 {
791 	struct window_copy_mode_data	*data = wp->modedata;
792 	struct screen			*s = &data->screen;
793 	struct options			*oo = &wp->window->options;
794 	struct grid_cell		 gc;
795 	u_int				 sx, sy, ty;
796 
797 	if (!s->sel.flag)
798 		return (0);
799 
800 	/* Set colours. */
801 	memcpy(&gc, &grid_default_cell, sizeof gc);
802 	colour_set_fg(&gc, options_get_number(oo, "mode-fg"));
803 	colour_set_bg(&gc, options_get_number(oo, "mode-bg"));
804 	gc.attr |= options_get_number(oo, "mode-attr");
805 
806 	/* Find top of screen. */
807 	ty = screen_hsize(&wp->base) - data->oy;
808 
809 	/* Adjust the selection. */
810 	sx = data->selx;
811 	sy = data->sely;
812 	if (sy < ty) {					/* above screen */
813 		sx = 0;
814 		sy = 0;
815 	} else if (sy > ty + screen_size_y(s) - 1) {	/* below screen */
816 		sx = screen_size_x(s) - 1;
817 		sy = screen_size_y(s) - 1;
818 	} else
819 		sy -= ty;
820 	sy = screen_hsize(s) + sy;
821 
822 	screen_set_selection(
823 	    s, sx, sy, data->cx, screen_hsize(s) + data->cy, &gc);
824 	return (1);
825 }
826 
827 void
828 window_copy_copy_selection(struct window_pane *wp, struct client *c)
829 {
830 	struct window_copy_mode_data	*data = wp->modedata;
831 	struct screen			*s = &data->screen;
832 	char				*buf;
833 	size_t	 			 off;
834 	u_int	 			 i, xx, yy, sx, sy, ex, ey, limit;
835 
836 	if (!s->sel.flag)
837 		return;
838 
839 	buf = xmalloc(1);
840 	off = 0;
841 
842 	*buf = '\0';
843 
844 	/*
845 	 * The selection extends from selx,sely to (adjusted) cx,cy on
846 	 * the base screen.
847 	 */
848 
849 	/* Find start and end. */
850 	xx = data->cx;
851 	yy = screen_hsize(&wp->base) + data->cy - data->oy;
852 	if (yy < data->sely || (yy == data->sely && xx < data->selx)) {
853 		sx = xx; sy = yy;
854 		ex = data->selx; ey = data->sely;
855 	} else {
856 		sx = data->selx; sy = data->sely;
857 		ex = xx; ey = yy;
858 	}
859 
860 	/* Trim ex to end of line. */
861 	xx = window_copy_find_length(wp, ey);
862 	if (ex > xx)
863 		ex = xx;
864 
865 	/* Copy the lines. */
866 	if (sy == ey)
867 		window_copy_copy_line(wp, &buf, &off, sy, sx, ex);
868 	else {
869 		xx = screen_size_x(s);
870 		window_copy_copy_line(wp, &buf, &off, sy, sx, xx);
871 		if (ey - sy > 1) {
872 			for (i = sy + 1; i < ey; i++)
873 				window_copy_copy_line(wp, &buf, &off, i, 0, xx);
874 		}
875 		window_copy_copy_line(wp, &buf, &off, ey, 0, ex);
876 	}
877 
878 	/* Don't bother if no data. */
879 	if (off == 0) {
880 		xfree(buf);
881 		return;
882 	}
883 	off--;	/* remove final \n */
884 
885 	/* Add the buffer to the stack. */
886 	limit = options_get_number(&c->session->options, "buffer-limit");
887 	paste_add(&c->session->buffers, buf, off, limit);
888 }
889 
890 void
891 window_copy_copy_line(struct window_pane *wp,
892     char **buf, size_t *off, u_int sy, u_int sx, u_int ex)
893 {
894 	struct grid		*gd = wp->base.grid;
895  	const struct grid_cell	*gc;
896  	const struct grid_utf8	*gu;
897 	struct grid_line	*gl;
898 	u_int			 i, xx, wrapped = 0;
899 	size_t			 size;
900 
901 	if (sx > ex)
902 		return;
903 
904 	/*
905 	 * Work out if the line was wrapped at the screen edge and all of it is
906 	 * on screen.
907 	 */
908 	gl = &gd->linedata[sy];
909  	if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx)
910 		wrapped = 1;
911 
912 	/* If the line was wrapped, don't strip spaces (use the full length). */
913 	if (wrapped)
914 		xx = gl->cellsize;
915 	else
916 		xx = window_copy_find_length(wp, sy);
917 	if (ex > xx)
918 		ex = xx;
919 	if (sx > xx)
920 		sx = xx;
921 
922 	if (sx < ex) {
923 		for (i = sx; i < ex; i++) {
924 			gc = grid_peek_cell(gd, i, sy);
925 			if (gc->flags & GRID_FLAG_PADDING)
926 				continue;
927 			if (!(gc->flags & GRID_FLAG_UTF8)) {
928 				*buf = xrealloc(*buf, 1, (*off) + 1);
929 				(*buf)[(*off)++] = gc->data;
930 			} else {
931 				gu = grid_peek_utf8(gd, i, sy);
932 				size = grid_utf8_size(gu);
933 				*buf = xrealloc(*buf, 1, (*off) + size);
934 				*off += grid_utf8_copy(gu, *buf + *off, size);
935 			}
936 		}
937 	}
938 
939 	/* Only add a newline if the line wasn't wrapped. */
940  	if (!wrapped) {
941 		*buf = xrealloc(*buf, 1, (*off) + 1);
942 		(*buf)[(*off)++] = '\n';
943 	}
944 }
945 
946 int
947 window_copy_is_space(struct window_pane *wp, u_int px, u_int py)
948 {
949 	const struct grid_cell	*gc;
950 	const char     		*spaces = " -_@";
951 
952 	gc = grid_peek_cell(wp->base.grid, px, py);
953 	if (gc->flags & (GRID_FLAG_PADDING|GRID_FLAG_UTF8))
954 		return (0);
955 	if (gc->data == 0x00 || gc->data == 0x7f)
956 		return (0);
957 	return (strchr(spaces, gc->data) != NULL);
958 }
959 
960 u_int
961 window_copy_find_length(struct window_pane *wp, u_int py)
962 {
963 	const struct grid_cell	*gc;
964 	u_int			 px;
965 
966 	/*
967 	 * If the pane has been resized, its grid can contain old overlong
968 	 * lines. grid_peek_cell does not allow accessing cells beyond the
969 	 * width of the grid, and screen_write_copy treats them as spaces, so
970 	 * ignore them here too.
971 	 */
972 	px = wp->base.grid->linedata[py].cellsize;
973 	if (px > screen_size_x(&wp->base))
974 		px = screen_size_x(&wp->base);
975 	while (px > 0) {
976 		gc = grid_peek_cell(wp->base.grid, px - 1, py);
977 		if (gc->flags & GRID_FLAG_UTF8)
978 			break;
979 		if (gc->data != ' ')
980 			break;
981 		px--;
982 	}
983 	return (px);
984 }
985 
986 void
987 window_copy_cursor_start_of_line(struct window_pane *wp)
988 {
989 	struct window_copy_mode_data	*data = wp->modedata;
990 
991 	window_copy_update_cursor(wp, 0, data->cy);
992 	if (window_copy_update_selection(wp))
993 		window_copy_redraw_lines(wp, data->cy, 1);
994 }
995 
996 void
997 window_copy_cursor_back_to_indentation(struct window_pane *wp)
998 {
999 	struct window_copy_mode_data	*data = wp->modedata;
1000 	u_int				 px, py, xx;
1001 	const struct grid_cell		*gc;
1002 
1003 	px = 0;
1004 	py = screen_hsize(&wp->base) + data->cy - data->oy;
1005 	xx = window_copy_find_length(wp, py);
1006 
1007 	/*
1008 	 * Don't use window_copy_is_space because that treats some word
1009 	 * delimiters as spaces.
1010 	 */
1011 	while (px < xx) {
1012 		gc = grid_peek_cell(wp->base.grid, px, py);
1013 		if (gc->flags & GRID_FLAG_UTF8)
1014 			break;
1015 		if (gc->data != ' ')
1016 			break;
1017 		px++;
1018 	}
1019 
1020 	window_copy_update_cursor(wp, px, data->cy);
1021 	if (window_copy_update_selection(wp))
1022 		window_copy_redraw_lines(wp, data->cy, 1);
1023 }
1024 
1025 void
1026 window_copy_cursor_end_of_line(struct window_pane *wp)
1027 {
1028 	struct window_copy_mode_data	*data = wp->modedata;
1029 	u_int				 px, py;
1030 
1031 	py = screen_hsize(&wp->base) + data->cy - data->oy;
1032 	px = window_copy_find_length(wp, py);
1033 
1034 	window_copy_update_cursor(wp, px, data->cy);
1035 	if (window_copy_update_selection(wp))
1036 		window_copy_redraw_lines(wp, data->cy, 1);
1037 }
1038 
1039 void
1040 window_copy_cursor_left(struct window_pane *wp)
1041 {
1042 	struct window_copy_mode_data	*data = wp->modedata;
1043 
1044 	if (data->cx == 0) {
1045 		window_copy_cursor_up(wp, 0);
1046 		window_copy_cursor_end_of_line(wp);
1047 	} else {
1048 		window_copy_update_cursor(wp, data->cx - 1, data->cy);
1049 		if (window_copy_update_selection(wp))
1050 			window_copy_redraw_lines(wp, data->cy, 1);
1051 	}
1052 }
1053 
1054 void
1055 window_copy_cursor_right(struct window_pane *wp)
1056 {
1057 	struct window_copy_mode_data	*data = wp->modedata;
1058 	u_int				 px, py;
1059 
1060 	py = screen_hsize(&wp->base) + data->cy - data->oy;
1061 	px = window_copy_find_length(wp, py);
1062 
1063 	if (data->cx >= px) {
1064 		window_copy_cursor_start_of_line(wp);
1065 		window_copy_cursor_down(wp, 0);
1066 	} else {
1067 		window_copy_update_cursor(wp, data->cx + 1, data->cy);
1068 		if (window_copy_update_selection(wp))
1069 			window_copy_redraw_lines(wp, data->cy, 1);
1070 	}
1071 }
1072 
1073 void
1074 window_copy_cursor_up(struct window_pane *wp, int scroll_only)
1075 {
1076 	struct window_copy_mode_data	*data = wp->modedata;
1077 	u_int				 ox, oy, px, py;
1078 
1079 	oy = screen_hsize(&wp->base) + data->cy - data->oy;
1080 	ox = window_copy_find_length(wp, oy);
1081 	if (ox != 0) {
1082 		data->lastcx = data->cx;
1083 		data->lastsx = ox;
1084 	}
1085 
1086 	data->cx = data->lastcx;
1087 	if (scroll_only || data->cy == 0) {
1088 		window_copy_scroll_down(wp, 1);
1089 		if (scroll_only)
1090 			window_copy_redraw_lines(wp, data->cy, 2);
1091 	} else {
1092 		window_copy_update_cursor(wp, data->cx, data->cy - 1);
1093 		if (window_copy_update_selection(wp))
1094 			window_copy_redraw_lines(wp, data->cy, 2);
1095 	}
1096 
1097 	py = screen_hsize(&wp->base) + data->cy - data->oy;
1098 	px = window_copy_find_length(wp, py);
1099 	if (data->cx >= data->lastsx || data->cx > px)
1100 		window_copy_cursor_end_of_line(wp);
1101 }
1102 
1103 void
1104 window_copy_cursor_down(struct window_pane *wp, int scroll_only)
1105 {
1106 	struct window_copy_mode_data	*data = wp->modedata;
1107 	struct screen			*s = &data->screen;
1108 	u_int				 ox, oy, px, py;
1109 
1110 	oy = screen_hsize(&wp->base) + data->cy - data->oy;
1111 	ox = window_copy_find_length(wp, oy);
1112 	if (ox != 0) {
1113 		data->lastcx = data->cx;
1114 		data->lastsx = ox;
1115 	}
1116 
1117 	data->cx = data->lastcx;
1118 	if (scroll_only || data->cy == screen_size_y(s) - 1) {
1119 		window_copy_scroll_up(wp, 1);
1120 		if (scroll_only && data->cy > 0)
1121 			window_copy_redraw_lines(wp, data->cy - 1, 2);
1122 	} else {
1123 		window_copy_update_cursor(wp, data->cx, data->cy + 1);
1124 		if (window_copy_update_selection(wp))
1125 			window_copy_redraw_lines(wp, data->cy - 1, 2);
1126 	}
1127 
1128 	py = screen_hsize(&wp->base) + data->cy - data->oy;
1129 	px = window_copy_find_length(wp, py);
1130 	if (data->cx >= data->lastsx || data->cx > px)
1131 		window_copy_cursor_end_of_line(wp);
1132 }
1133 
1134 void
1135 window_copy_cursor_next_word(struct window_pane *wp)
1136 {
1137 	struct window_copy_mode_data	*data = wp->modedata;
1138 	struct screen			*s = &data->screen;
1139 	u_int				 px, py, xx, skip;
1140 
1141 	px = data->cx;
1142 	py = screen_hsize(&wp->base) + data->cy - data->oy;
1143 	xx = window_copy_find_length(wp, py);
1144 
1145 	skip = 1;
1146 	if (px < xx) {
1147 		/* If currently on a space, skip space. */
1148 		if (window_copy_is_space(wp, px, py))
1149 			skip = 0;
1150 	} else
1151 		skip = 0;
1152 	for (;;) {
1153 		if (px >= xx) {
1154 			if (skip) {
1155 				px = xx;
1156 				break;
1157 			}
1158 
1159 			while (px >= xx) {
1160 				if (data->cy == screen_size_y(s) - 1) {
1161 					if (data->oy == 0)
1162 						goto out;
1163 				}
1164 
1165 				px = 0;
1166 				window_copy_cursor_down(wp, 0);
1167 
1168 				py =screen_hsize(
1169 				    &wp->base) + data->cy - data->oy;
1170 				xx = window_copy_find_length(wp, py);
1171 			}
1172 		}
1173 
1174 		if (skip) {
1175 			/* Currently skipping non-space (until space). */
1176 			if (window_copy_is_space(wp, px, py))
1177 				break;
1178 		} else {
1179 			/* Currently skipping space (until non-space). */
1180 			if (!window_copy_is_space(wp, px, py))
1181 				skip = 1;
1182 		}
1183 
1184 		px++;
1185 	}
1186 
1187 out:
1188 	window_copy_update_cursor(wp, px, data->cy);
1189 	if (window_copy_update_selection(wp))
1190 		window_copy_redraw_lines(wp, data->cy, 1);
1191 }
1192 
1193 /* Move to the previous place where a word begins. */
1194 void
1195 window_copy_cursor_previous_word(struct window_pane *wp)
1196 {
1197 	struct window_copy_mode_data	*data = wp->modedata;
1198 	u_int				 px, py;
1199 
1200 	px = data->cx;
1201 	py = screen_hsize(&wp->base) + data->cy - data->oy;
1202 
1203 	/* Move back to the previous word character. */
1204 	for (;;) {
1205 		if (px > 0) {
1206 			px--;
1207 			if (!window_copy_is_space(wp, px, py))
1208 				break;
1209 		} else {
1210 			if (data->cy == 0 &&
1211 			    (screen_hsize(&wp->base) == 0 ||
1212 			    data->oy >= screen_hsize(&wp->base) - 1))
1213 				goto out;
1214 			window_copy_cursor_up(wp, 0);
1215 
1216 			py = screen_hsize(
1217 			    &wp->base) + data->cy - data->oy;
1218 			px = window_copy_find_length(wp, py);
1219 		}
1220 	}
1221 
1222 	/* Move back to the beginning of this word. */
1223 	while (px > 0 && !window_copy_is_space(wp, px - 1, py))
1224 		px--;
1225 
1226 out:
1227 	window_copy_update_cursor(wp, px, data->cy);
1228 	if (window_copy_update_selection(wp))
1229 		window_copy_redraw_lines(wp, data->cy, 1);
1230 }
1231 
1232 void
1233 window_copy_scroll_up(struct window_pane *wp, u_int ny)
1234 {
1235 	struct window_copy_mode_data	*data = wp->modedata;
1236 	struct screen			*s = &data->screen;
1237 	struct screen_write_ctx		 ctx;
1238 
1239 	if (data->oy < ny)
1240 		ny = data->oy;
1241 	if (ny == 0)
1242 		return;
1243 	data->oy -= ny;
1244 	window_copy_update_selection(wp);
1245 
1246 	screen_write_start(&ctx, wp, NULL);
1247 	screen_write_cursormove(&ctx, 0, 0);
1248 	screen_write_deleteline(&ctx, ny);
1249 	window_copy_write_lines(wp, &ctx, screen_size_y(s) - ny, ny);
1250 	window_copy_write_line(wp, &ctx, 0);
1251 	if (screen_size_y(s) > 1)
1252 		window_copy_write_line(wp, &ctx, 1);
1253 	if (screen_size_y(s) > 3)
1254 		window_copy_write_line(wp, &ctx, screen_size_y(s) - 2);
1255 	if (s->sel.flag && screen_size_y(s) > ny)
1256 		window_copy_write_line(wp, &ctx, screen_size_y(s) - ny - 1);
1257 	screen_write_cursormove(&ctx, data->cx, data->cy);
1258 	screen_write_stop(&ctx);
1259 }
1260 
1261 void
1262 window_copy_scroll_down(struct window_pane *wp, u_int ny)
1263 {
1264 	struct window_copy_mode_data	*data = wp->modedata;
1265 	struct screen			*s = &data->screen;
1266 	struct screen_write_ctx		 ctx;
1267 
1268 	if (ny > screen_hsize(&wp->base))
1269 		return;
1270 
1271 	if (data->oy > screen_hsize(&wp->base) - ny)
1272 		ny = screen_hsize(&wp->base) - data->oy;
1273 	if (ny == 0)
1274 		return;
1275 	data->oy += ny;
1276 	window_copy_update_selection(wp);
1277 
1278 	screen_write_start(&ctx, wp, NULL);
1279 	screen_write_cursormove(&ctx, 0, 0);
1280 	screen_write_insertline(&ctx, ny);
1281 	window_copy_write_lines(wp, &ctx, 0, ny);
1282 	if (s->sel.flag && screen_size_y(s) > ny)
1283 		window_copy_write_line(wp, &ctx, ny);
1284 	else if (ny == 1) /* nuke position */
1285 		window_copy_write_line(wp, &ctx, 1);
1286 	screen_write_cursormove(&ctx, data->cx, data->cy);
1287 	screen_write_stop(&ctx);
1288 }
1289