xref: /openbsd-src/usr.bin/tmux/window-copy.c (revision 43003dfe3ad45d1698bed8a37f2b0f5b14f20d4f)
1 /* $OpenBSD: window-copy.c,v 1.29 2009/10/11 07:01:10 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_STARTSELECTION:
282  		window_copy_start_selection(wp);
283 		window_copy_redraw_screen(wp);
284 		break;
285 	case MODEKEYCOPY_CLEARSELECTION:
286 		screen_clear_selection(&data->screen);
287 		window_copy_redraw_screen(wp);
288 		break;
289 	case MODEKEYCOPY_COPYSELECTION:
290 		if (c != NULL && c->session != NULL) {
291 			window_copy_copy_selection(wp, c);
292 			window_pane_reset_mode(wp);
293 		}
294 		break;
295 	case MODEKEYCOPY_STARTOFLINE:
296 		window_copy_cursor_start_of_line(wp);
297 		break;
298 	case MODEKEYCOPY_BACKTOINDENTATION:
299 		window_copy_cursor_back_to_indentation(wp);
300 		break;
301 	case MODEKEYCOPY_ENDOFLINE:
302 		window_copy_cursor_end_of_line(wp);
303 		break;
304 	case MODEKEYCOPY_NEXTWORD:
305 		window_copy_cursor_next_word(wp);
306 		break;
307 	case MODEKEYCOPY_PREVIOUSWORD:
308 		window_copy_cursor_previous_word(wp);
309 		break;
310 	case MODEKEYCOPY_SEARCHUP:
311 		data->inputtype = WINDOW_COPY_SEARCHUP;
312 		data->inputprompt = "Search Up";
313 		goto input_on;
314 	case MODEKEYCOPY_SEARCHDOWN:
315 		data->inputtype = WINDOW_COPY_SEARCHDOWN;
316 		data->inputprompt = "Search Down";
317 		goto input_on;
318 	case MODEKEYCOPY_SEARCHAGAIN:
319 		switch (data->searchtype) {
320 		case WINDOW_COPY_OFF:
321 		case WINDOW_COPY_GOTOLINE:
322 			break;
323 		case WINDOW_COPY_SEARCHUP:
324 			window_copy_search_up(wp, data->searchstr);
325 			break;
326 		case WINDOW_COPY_SEARCHDOWN:
327 			window_copy_search_down(wp, data->searchstr);
328 			break;
329 		}
330 		break;
331 	case MODEKEYCOPY_GOTOLINE:
332 		data->inputtype = WINDOW_COPY_GOTOLINE;
333 		data->inputprompt = "Goto Line";
334 		*data->inputstr = '\0';
335 		goto input_on;
336 	default:
337 		break;
338 	}
339 
340 	return;
341 
342 input_on:
343 	keys = options_get_number(&wp->window->options, "mode-keys");
344 	if (keys == MODEKEY_EMACS)
345 		mode_key_init(&data->mdata, &mode_key_tree_emacs_edit);
346 	else
347 		mode_key_init(&data->mdata, &mode_key_tree_vi_edit);
348 
349 	window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
350 	return;
351 
352 input_off:
353 	keys = options_get_number(&wp->window->options, "mode-keys");
354 	if (keys == MODEKEY_EMACS)
355 		mode_key_init(&data->mdata, &mode_key_tree_emacs_copy);
356 	else
357 		mode_key_init(&data->mdata, &mode_key_tree_vi_copy);
358 
359 	data->inputtype = WINDOW_COPY_OFF;
360 	data->inputprompt = NULL;
361 
362 	window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
363 }
364 
365 int
366 window_copy_key_input(struct window_pane *wp, int key)
367 {
368 	struct window_copy_mode_data	*data = wp->modedata;
369 	struct screen			*s = &data->screen;
370 	size_t				 inputlen;
371 
372 	switch (mode_key_lookup(&data->mdata, key)) {
373 	case MODEKEYEDIT_CANCEL:
374 		return (-1);
375 	case MODEKEYEDIT_BACKSPACE:
376 		inputlen = strlen(data->inputstr);
377 		if (inputlen > 0)
378 			data->inputstr[inputlen - 1] = '\0';
379 		break;
380 	case MODEKEYEDIT_DELETELINE:
381 		*data->inputstr = '\0';
382 		break;
383 	case MODEKEYEDIT_ENTER:
384 		switch (data->inputtype) {
385 		case WINDOW_COPY_OFF:
386 			break;
387 		case WINDOW_COPY_SEARCHUP:
388 			window_copy_search_up(wp, data->inputstr);
389 			data->searchtype = data->inputtype;
390 			data->searchstr = xstrdup(data->inputstr);
391 			break;
392 		case WINDOW_COPY_SEARCHDOWN:
393 			window_copy_search_down(wp, data->inputstr);
394 			data->searchtype = data->inputtype;
395 			data->searchstr = xstrdup(data->inputstr);
396 			break;
397 		case WINDOW_COPY_GOTOLINE:
398 			window_copy_goto_line(wp, data->inputstr);
399 			*data->inputstr = '\0';
400 			break;
401 		}
402 		return (1);
403 	case MODEKEY_OTHER:
404 		if (key < 32 || key > 126)
405 			break;
406 		inputlen = strlen(data->inputstr) + 2;
407 
408 		data->inputstr = xrealloc(data->inputstr, 1, inputlen);
409 		data->inputstr[inputlen - 2] = key;
410 		data->inputstr[inputlen - 1] = '\0';
411 		break;
412 	default:
413 		break;
414 	}
415 
416 	window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
417 	return (0);
418 }
419 
420 void
421 window_copy_mouse(
422     struct window_pane *wp, unused struct client *c, struct mouse_event *m)
423 {
424 	struct window_copy_mode_data	*data = wp->modedata;
425 	struct screen			*s = &data->screen;
426 
427 	if ((m->b & 3) == 3)
428 		return;
429 	if (m->x >= screen_size_x(s))
430 		return;
431 	if (m->y >= screen_size_y(s))
432 		return;
433 
434 	window_copy_update_cursor(wp, m->x, m->y);
435 	if (window_copy_update_selection(wp))
436  		window_copy_redraw_screen(wp);
437 }
438 
439 void
440 window_copy_scroll_to(struct window_pane *wp, u_int px, u_int py)
441 {
442 	struct window_copy_mode_data	*data = wp->modedata;
443 	struct screen			*s = &wp->base;
444 	struct grid			*gd = s->grid;
445 	u_int				 offset, gap;
446 
447 	data->cx = px;
448 
449 	gap = gd->sy / 4;
450 	if (py < gd->sy) {
451 		offset = 0;
452 		data->cy = py;
453 	} else if (py > gd->hsize + gd->sy - gap) {
454 		offset = gd->hsize;
455 		data->cy = py - gd->hsize;
456 	} else {
457 		offset = py + gap - gd->sy;
458 		data->cy = py - offset;
459 	}
460  	data->oy = gd->hsize - offset;
461 
462 	window_copy_redraw_screen(wp);
463 }
464 
465 int
466 window_copy_search_compare(
467     struct grid *gd, u_int px, u_int py, struct grid *sgd, u_int spx)
468 {
469 	const struct grid_cell	*gc, *sgc;
470 	const struct grid_utf8	*gu, *sgu;
471 
472 	gc = grid_peek_cell(gd, px, py);
473 	sgc = grid_peek_cell(sgd, spx, 0);
474 
475 	if ((gc->flags & GRID_FLAG_UTF8) != (sgc->flags & GRID_FLAG_UTF8))
476 		return (0);
477 
478 	if (gc->flags & GRID_FLAG_UTF8) {
479 		gu = grid_peek_utf8(gd, px, py);
480 		sgu = grid_peek_utf8(sgd, spx, 0);
481 		if (memcmp(gu->data, sgu->data, UTF8_SIZE) == 0)
482 			return (1);
483 	} else {
484 		if (gc->data == sgc->data)
485 			return (1);
486 	}
487 	return (0);
488 }
489 
490 int
491 window_copy_search_lr(struct grid *gd,
492     struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last)
493 {
494 	u_int	ax, bx, px;
495 
496 	for (ax = first; ax < last; ax++) {
497 		if (ax + sgd->sx >= gd->sx)
498 			break;
499 		for (bx = 0; bx < sgd->sx; bx++) {
500 			px = ax + bx;
501 			if (!window_copy_search_compare(gd, px, py, sgd, bx))
502 				break;
503 		}
504 		if (bx == sgd->sx) {
505 			*ppx = ax;
506 			return (1);
507 		}
508 	}
509 	return (0);
510 }
511 
512 int
513 window_copy_search_rl(struct grid *gd,
514     struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last)
515 {
516 	u_int	ax, bx, px;
517 
518 	for (ax = last + 1; ax > first; ax--) {
519 		if (gd->sx - (ax - 1) < sgd->sx)
520 			continue;
521 		for (bx = 0; bx < sgd->sx; bx++) {
522 			px = ax - 1 + bx;
523 			if (!window_copy_search_compare(gd, px, py, sgd, bx))
524 				break;
525 		}
526 		if (bx == sgd->sx) {
527 			*ppx = ax - 1;
528 			return (1);
529 		}
530 	}
531 	return (0);
532 }
533 
534 void
535 window_copy_search_up(struct window_pane *wp, const char *searchstr)
536 {
537 	struct window_copy_mode_data	*data = wp->modedata;
538 	struct screen			*s = &wp->base, ss;
539 	struct screen_write_ctx		 ctx;
540 	struct grid			*gd = s->grid, *sgd;
541 	struct grid_cell	 	 gc;
542 	size_t				 searchlen;
543 	u_int				 i, last, fx, fy, px;
544 	int				 utf8flag, n, wrapped;
545 
546 	if (*searchstr == '\0')
547 		return;
548 	utf8flag = options_get_number(&wp->window->options, "utf8");
549 	searchlen = screen_write_strlen(utf8flag, "%s", searchstr);
550 
551 	screen_init(&ss, searchlen, 1, 0);
552 	screen_write_start(&ctx, NULL, &ss);
553 	memcpy(&gc, &grid_default_cell, sizeof gc);
554 	screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr);
555 	screen_write_stop(&ctx);
556 
557 	fx = data->cx;
558 	fy = gd->hsize - data->oy + data->cy;
559 
560 	if (fx == 0) {
561 		if (fy == 0)
562 			return;
563 		fx = gd->sx - 1;
564 		fy--;
565 	} else
566 		fx--;
567 	n = wrapped = 0;
568 
569 retry:
570 	sgd = ss.grid;
571 	for (i = fy + 1; i > 0; i--) {
572 		last = screen_size_x(s);
573 		if (i == fy + 1)
574 			last = fx;
575 		n = window_copy_search_rl(gd, sgd, &px, i - 1, 0, last);
576 		if (n) {
577 			window_copy_scroll_to(wp, px, i - 1);
578 			break;
579 		}
580 	}
581 	if (!n && !wrapped) {
582 		fx = gd->sx - 1;
583 		fy = gd->hsize + gd->sy - 1;
584 		wrapped = 1;
585 		goto retry;
586 	}
587 
588 	screen_free(&ss);
589 }
590 
591 void
592 window_copy_search_down(struct window_pane *wp, const char *searchstr)
593 {
594 	struct window_copy_mode_data	*data = wp->modedata;
595 	struct screen			*s = &wp->base, ss;
596 	struct screen_write_ctx		 ctx;
597 	struct grid			*gd = s->grid, *sgd;
598 	struct grid_cell	 	 gc;
599 	size_t				 searchlen;
600 	u_int				 i, first, fx, fy, px;
601 	int				 utf8flag, n, wrapped;
602 
603 	if (*searchstr == '\0')
604 		return;
605 	utf8flag = options_get_number(&wp->window->options, "utf8");
606 	searchlen = screen_write_strlen(utf8flag, "%s", searchstr);
607 
608 	screen_init(&ss, searchlen, 1, 0);
609 	screen_write_start(&ctx, NULL, &ss);
610 	memcpy(&gc, &grid_default_cell, sizeof gc);
611 	screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr);
612 	screen_write_stop(&ctx);
613 
614 	fx = data->cx;
615 	fy = gd->hsize - data->oy + data->cy;
616 
617 	if (fx == gd->sx - 1) {
618 		if (fy == gd->hsize + gd->sy)
619 			return;
620 		fx = 0;
621 		fy++;
622 	} else
623 		fx++;
624 	n = wrapped = 0;
625 
626 retry:
627 	sgd = ss.grid;
628 	for (i = fy + 1; i < gd->hsize + gd->sy; i++) {
629 		first = 0;
630 		if (i == fy + 1)
631 			first = fx;
632 		n = window_copy_search_lr(gd, sgd, &px, i - 1, first, gd->sx);
633 		if (n) {
634 			window_copy_scroll_to(wp, px, i - 1);
635 			break;
636 		}
637 	}
638 	if (!n && !wrapped) {
639 		fx = 0;
640 		fy = 0;
641 		wrapped = 1;
642 		goto retry;
643 	}
644 
645 	screen_free(&ss);
646 }
647 
648 void
649 window_copy_goto_line(struct window_pane *wp, const char *linestr)
650 {
651 	struct window_copy_mode_data	*data = wp->modedata;
652 	const char			*errstr;
653 	u_int				 lineno;
654 
655 	lineno = strtonum(linestr, 0, screen_hsize(&wp->base), &errstr);
656 	if (errstr != NULL)
657 		return;
658 
659 	data->oy = lineno;
660 	window_copy_redraw_screen(wp);
661 }
662 
663 void
664 window_copy_write_line(
665     struct window_pane *wp, struct screen_write_ctx *ctx, u_int py)
666 {
667 	struct window_copy_mode_data	*data = wp->modedata;
668 	struct screen			*s = &data->screen;
669 	struct options			*oo = &wp->window->options;
670 	struct grid_cell		 gc;
671 	char				 hdr[32];
672 	size_t	 			 last, xoff = 0, size = 0;
673 
674 	memcpy(&gc, &grid_default_cell, sizeof gc);
675 	colour_set_fg(&gc, options_get_number(oo, "mode-fg"));
676 	colour_set_bg(&gc, options_get_number(oo, "mode-bg"));
677 	gc.attr |= options_get_number(oo, "mode-attr");
678 
679 	last = screen_size_y(s) - 1;
680 	if (py == 0) {
681 		size = xsnprintf(hdr, sizeof hdr,
682 		    "[%u/%u]", data->oy, screen_hsize(&wp->base));
683 		screen_write_cursormove(ctx, screen_size_x(s) - size, 0);
684 		screen_write_puts(ctx, &gc, "%s", hdr);
685 	} else if (py == last && data->inputtype != WINDOW_COPY_OFF) {
686 		xoff = size = xsnprintf(hdr, sizeof hdr,
687 		    "%s: %s", data->inputprompt, data->inputstr);
688 		screen_write_cursormove(ctx, 0, last);
689 		screen_write_puts(ctx, &gc, "%s", hdr);
690 	} else
691 		size = 0;
692 
693 	screen_write_cursormove(ctx, xoff, py);
694 	screen_write_copy(ctx, &wp->base, xoff, (screen_hsize(&wp->base) -
695 	    data->oy) + py, screen_size_x(s) - size, 1);
696 
697 	if (py == data->cy && data->cx == screen_size_x(s)) {
698 		memcpy(&gc, &grid_default_cell, sizeof gc);
699 		screen_write_cursormove(ctx, screen_size_x(s) - 1, py);
700 		screen_write_putc(ctx, &gc, '$');
701 	}
702 }
703 
704 void
705 window_copy_write_lines(
706     struct window_pane *wp, struct screen_write_ctx *ctx, u_int py, u_int ny)
707 {
708 	u_int	yy;
709 
710 	for (yy = py; yy < py + ny; yy++)
711 		window_copy_write_line(wp, ctx, py);
712 }
713 
714 void
715 window_copy_redraw_lines(struct window_pane *wp, u_int py, u_int ny)
716 {
717 	struct window_copy_mode_data	*data = wp->modedata;
718 	struct screen_write_ctx	 	 ctx;
719 	u_int				 i;
720 
721 	screen_write_start(&ctx, wp, NULL);
722 	for (i = py; i < py + ny; i++)
723 		window_copy_write_line(wp, &ctx, i);
724 	screen_write_cursormove(&ctx, data->cx, data->cy);
725 	screen_write_stop(&ctx);
726 }
727 
728 void
729 window_copy_redraw_screen(struct window_pane *wp)
730 {
731 	struct window_copy_mode_data	*data = wp->modedata;
732 
733 	window_copy_redraw_lines(wp, 0, screen_size_y(&data->screen));
734 }
735 
736 void
737 window_copy_update_cursor(struct window_pane *wp, u_int cx, u_int cy)
738 {
739 	struct window_copy_mode_data	*data = wp->modedata;
740 	struct screen			*s = &data->screen;
741 	struct screen_write_ctx		 ctx;
742 	u_int				 old_cx, old_cy;
743 
744 	old_cx = data->cx; old_cy = data->cy;
745 	data->cx = cx; data->cy = cy;
746 	if (old_cx == screen_size_x(s))
747 		window_copy_redraw_lines(wp, old_cy, 1);
748 	if (data->cx == screen_size_x(s))
749 		window_copy_redraw_lines(wp, data->cy, 1);
750 	else {
751 		screen_write_start(&ctx, wp, NULL);
752 		screen_write_cursormove(&ctx, data->cx, data->cy);
753 		screen_write_stop(&ctx);
754 	}
755 }
756 
757 void
758 window_copy_start_selection(struct window_pane *wp)
759 {
760 	struct window_copy_mode_data	*data = wp->modedata;
761 	struct screen			*s = &data->screen;
762 
763 	data->selx = data->cx;
764 	data->sely = screen_hsize(&wp->base) + data->cy - data->oy;
765 
766 	s->sel.flag = 1;
767 	window_copy_update_selection(wp);
768 }
769 
770 int
771 window_copy_update_selection(struct window_pane *wp)
772 {
773 	struct window_copy_mode_data	*data = wp->modedata;
774 	struct screen			*s = &data->screen;
775 	struct options			*oo = &wp->window->options;
776 	struct grid_cell		 gc;
777 	u_int				 sx, sy, ty;
778 
779 	if (!s->sel.flag)
780 		return (0);
781 
782 	/* Set colours. */
783 	memcpy(&gc, &grid_default_cell, sizeof gc);
784 	colour_set_fg(&gc, options_get_number(oo, "mode-fg"));
785 	colour_set_bg(&gc, options_get_number(oo, "mode-bg"));
786 	gc.attr |= options_get_number(oo, "mode-attr");
787 
788 	/* Find top of screen. */
789 	ty = screen_hsize(&wp->base) - data->oy;
790 
791 	/* Adjust the selection. */
792 	sx = data->selx;
793 	sy = data->sely;
794 	if (sy < ty) {					/* above screen */
795 		sx = 0;
796 		sy = 0;
797 	} else if (sy > ty + screen_size_y(s) - 1) {	/* below screen */
798 		sx = screen_size_x(s) - 1;
799 		sy = screen_size_y(s) - 1;
800 	} else
801 		sy -= ty;
802 	sy = screen_hsize(s) + sy;
803 
804 	screen_set_selection(
805 	    s, sx, sy, data->cx, screen_hsize(s) + data->cy, &gc);
806 	return (1);
807 }
808 
809 void
810 window_copy_copy_selection(struct window_pane *wp, struct client *c)
811 {
812 	struct window_copy_mode_data	*data = wp->modedata;
813 	struct screen			*s = &data->screen;
814 	char				*buf;
815 	size_t	 			 off;
816 	u_int	 			 i, xx, yy, sx, sy, ex, ey, limit;
817 
818 	if (!s->sel.flag)
819 		return;
820 
821 	buf = xmalloc(1);
822 	off = 0;
823 
824 	*buf = '\0';
825 
826 	/*
827 	 * The selection extends from selx,sely to (adjusted) cx,cy on
828 	 * the base screen.
829 	 */
830 
831 	/* Find start and end. */
832 	xx = data->cx;
833 	yy = screen_hsize(&wp->base) + data->cy - data->oy;
834 	if (yy < data->sely || (yy == data->sely && xx < data->selx)) {
835 		sx = xx; sy = yy;
836 		ex = data->selx; ey = data->sely;
837 	} else {
838 		sx = data->selx; sy = data->sely;
839 		ex = xx; ey = yy;
840 	}
841 
842 	/* Trim ex to end of line. */
843 	xx = window_copy_find_length(wp, ey);
844 	if (ex > xx)
845 		ex = xx;
846 
847 	/* Copy the lines. */
848 	if (sy == ey)
849 		window_copy_copy_line(wp, &buf, &off, sy, sx, ex);
850 	else {
851 		xx = screen_size_x(s);
852 		window_copy_copy_line(wp, &buf, &off, sy, sx, xx);
853 		if (ey - sy > 1) {
854 			for (i = sy + 1; i < ey; i++)
855 				window_copy_copy_line(wp, &buf, &off, i, 0, xx);
856 		}
857 		window_copy_copy_line(wp, &buf, &off, ey, 0, ex);
858 	}
859 
860 	/* Don't bother if no data. */
861 	if (off == 0) {
862 		xfree(buf);
863 		return;
864 	}
865 	off--;	/* remove final \n */
866 
867 	/* Add the buffer to the stack. */
868 	limit = options_get_number(&c->session->options, "buffer-limit");
869 	paste_add(&c->session->buffers, buf, off, limit);
870 }
871 
872 void
873 window_copy_copy_line(struct window_pane *wp,
874     char **buf, size_t *off, u_int sy, u_int sx, u_int ex)
875 {
876 	struct grid		*gd = wp->base.grid;
877  	const struct grid_cell	*gc;
878  	const struct grid_utf8	*gu;
879 	struct grid_line	*gl;
880 	u_int			 i, j, xx, wrapped = 0;
881 
882 	if (sx > ex)
883 		return;
884 
885 	/*
886 	 * Work out if the line was wrapped at the screen edge and all of it is
887 	 * on screen.
888 	 */
889 	gl = &gd->linedata[sy];
890  	if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx)
891 		wrapped = 1;
892 
893 	/* If the line was wrapped, don't strip spaces (use the full length). */
894 	if (wrapped)
895 		xx = gl->cellsize;
896 	else
897 		xx = window_copy_find_length(wp, sy);
898 	if (ex > xx)
899 		ex = xx;
900 	if (sx > xx)
901 		sx = xx;
902 
903 	if (sx < ex) {
904 		for (i = sx; i < ex; i++) {
905 			gc = grid_peek_cell(gd, i, sy);
906 			if (gc->flags & GRID_FLAG_PADDING)
907 				continue;
908 			if (!(gc->flags & GRID_FLAG_UTF8)) {
909 				*buf = xrealloc(*buf, 1, (*off) + 1);
910 				(*buf)[(*off)++] = gc->data;
911 			} else {
912 				gu = grid_peek_utf8(gd, i, sy);
913 				*buf = xrealloc(*buf, 1, (*off) + UTF8_SIZE);
914 				for (j = 0; j < UTF8_SIZE; j++) {
915 					if (gu->data[j] == 0xff)
916 						break;
917 					(*buf)[(*off)++] = gu->data[j];
918 				}
919 			}
920 		}
921 	}
922 
923 	/* Only add a newline if the line wasn't wrapped. */
924  	if (!wrapped) {
925 		*buf = xrealloc(*buf, 1, (*off) + 1);
926 		(*buf)[(*off)++] = '\n';
927 	}
928 }
929 
930 int
931 window_copy_is_space(struct window_pane *wp, u_int px, u_int py)
932 {
933 	const struct grid_cell	*gc;
934 	const char     		*spaces = " -_@";
935 
936 	gc = grid_peek_cell(wp->base.grid, px, py);
937 	if (gc->flags & (GRID_FLAG_PADDING|GRID_FLAG_UTF8))
938 		return (0);
939 	if (gc->data == 0x00 || gc->data == 0x7f)
940 		return (0);
941 	return (strchr(spaces, gc->data) != NULL);
942 }
943 
944 u_int
945 window_copy_find_length(struct window_pane *wp, u_int py)
946 {
947 	const struct grid_cell	*gc;
948 	u_int			 px;
949 
950 	/*
951 	 * If the pane has been resized, its grid can contain old overlong
952 	 * lines. grid_peek_cell does not allow accessing cells beyond the
953 	 * width of the grid, and screen_write_copy treats them as spaces, so
954 	 * ignore them here too.
955 	 */
956 	px = wp->base.grid->linedata[py].cellsize;
957 	if (px > screen_size_x(&wp->base))
958 		px = screen_size_x(&wp->base);
959 	while (px > 0) {
960 		gc = grid_peek_cell(wp->base.grid, px - 1, py);
961 		if (gc->flags & GRID_FLAG_UTF8)
962 			break;
963 		if (gc->data != ' ')
964 			break;
965 		px--;
966 	}
967 	return (px);
968 }
969 
970 void
971 window_copy_cursor_start_of_line(struct window_pane *wp)
972 {
973 	struct window_copy_mode_data	*data = wp->modedata;
974 
975 	window_copy_update_cursor(wp, 0, data->cy);
976 	if (window_copy_update_selection(wp))
977 		window_copy_redraw_lines(wp, data->cy, 1);
978 }
979 
980 void
981 window_copy_cursor_back_to_indentation(struct window_pane *wp)
982 {
983 	struct window_copy_mode_data	*data = wp->modedata;
984 	u_int				 px, py, xx;
985 	const struct grid_cell		*gc;
986 
987 	px = 0;
988 	py = screen_hsize(&wp->base) + data->cy - data->oy;
989 	xx = window_copy_find_length(wp, py);
990 
991 	/*
992 	 * Don't use window_copy_is_space because that treats some word
993 	 * delimiters as spaces.
994 	 */
995 	while (px < xx) {
996 		gc = grid_peek_cell(wp->base.grid, px, py);
997 		if (gc->flags & GRID_FLAG_UTF8)
998 			break;
999 		if (gc->data != ' ')
1000 			break;
1001 		px++;
1002 	}
1003 
1004 	window_copy_update_cursor(wp, px, data->cy);
1005 	if (window_copy_update_selection(wp))
1006 		window_copy_redraw_lines(wp, data->cy, 1);
1007 }
1008 
1009 void
1010 window_copy_cursor_end_of_line(struct window_pane *wp)
1011 {
1012 	struct window_copy_mode_data	*data = wp->modedata;
1013 	u_int				 px, py;
1014 
1015 	py = screen_hsize(&wp->base) + data->cy - data->oy;
1016 	px = window_copy_find_length(wp, py);
1017 
1018 	window_copy_update_cursor(wp, px, data->cy);
1019 	if (window_copy_update_selection(wp))
1020 		window_copy_redraw_lines(wp, data->cy, 1);
1021 }
1022 
1023 void
1024 window_copy_cursor_left(struct window_pane *wp)
1025 {
1026 	struct window_copy_mode_data	*data = wp->modedata;
1027 
1028 	if (data->cx == 0) {
1029 		window_copy_cursor_up(wp, 0);
1030 		window_copy_cursor_end_of_line(wp);
1031 	} else {
1032 		window_copy_update_cursor(wp, data->cx - 1, data->cy);
1033 		if (window_copy_update_selection(wp))
1034 			window_copy_redraw_lines(wp, data->cy, 1);
1035 	}
1036 }
1037 
1038 void
1039 window_copy_cursor_right(struct window_pane *wp)
1040 {
1041 	struct window_copy_mode_data	*data = wp->modedata;
1042 	u_int				 px, py;
1043 
1044 	py = screen_hsize(&wp->base) + data->cy - data->oy;
1045 	px = window_copy_find_length(wp, py);
1046 
1047 	if (data->cx >= px) {
1048 		window_copy_cursor_start_of_line(wp);
1049 		window_copy_cursor_down(wp, 0);
1050 	} else {
1051 		window_copy_update_cursor(wp, data->cx + 1, data->cy);
1052 		if (window_copy_update_selection(wp))
1053 			window_copy_redraw_lines(wp, data->cy, 1);
1054 	}
1055 }
1056 
1057 void
1058 window_copy_cursor_up(struct window_pane *wp, int scroll_only)
1059 {
1060 	struct window_copy_mode_data	*data = wp->modedata;
1061 	u_int				 ox, oy, px, py;
1062 
1063 	oy = screen_hsize(&wp->base) + data->cy - data->oy;
1064 	ox = window_copy_find_length(wp, oy);
1065 	if (ox != 0) {
1066 		data->lastcx = data->cx;
1067 		data->lastsx = ox;
1068 	}
1069 
1070 	data->cx = data->lastcx;
1071 	if (scroll_only || data->cy == 0) {
1072 		window_copy_scroll_down(wp, 1);
1073 		if (scroll_only && window_copy_update_selection(wp))
1074 			window_copy_redraw_lines(wp, data->cy, 2);
1075 	} else {
1076 		window_copy_update_cursor(wp, data->cx, data->cy - 1);
1077 		if (window_copy_update_selection(wp))
1078 			window_copy_redraw_lines(wp, data->cy, 2);
1079 	}
1080 
1081 	py = screen_hsize(&wp->base) + data->cy - data->oy;
1082 	px = window_copy_find_length(wp, py);
1083 	if (data->cx >= data->lastsx || data->cx > px)
1084 		window_copy_cursor_end_of_line(wp);
1085 }
1086 
1087 void
1088 window_copy_cursor_down(struct window_pane *wp, int scroll_only)
1089 {
1090 	struct window_copy_mode_data	*data = wp->modedata;
1091 	struct screen			*s = &data->screen;
1092 	u_int				 ox, oy, px, py;
1093 
1094 	oy = screen_hsize(&wp->base) + data->cy - data->oy;
1095 	ox = window_copy_find_length(wp, oy);
1096 	if (ox != 0) {
1097 		data->lastcx = data->cx;
1098 		data->lastsx = ox;
1099 	}
1100 
1101 	data->cx = data->lastcx;
1102 	if (scroll_only || data->cy == screen_size_y(s) - 1) {
1103 		window_copy_scroll_up(wp, 1);
1104 		if (scroll_only && window_copy_update_selection(wp))
1105 			window_copy_redraw_lines(wp, data->cy - 1, 2);
1106 	} else {
1107 		window_copy_update_cursor(wp, data->cx, data->cy + 1);
1108 		if (window_copy_update_selection(wp))
1109 			window_copy_redraw_lines(wp, data->cy - 1, 2);
1110 	}
1111 
1112 	py = screen_hsize(&wp->base) + data->cy - data->oy;
1113 	px = window_copy_find_length(wp, py);
1114 	if (data->cx >= data->lastsx || data->cx > px)
1115 		window_copy_cursor_end_of_line(wp);
1116 }
1117 
1118 void
1119 window_copy_cursor_next_word(struct window_pane *wp)
1120 {
1121 	struct window_copy_mode_data	*data = wp->modedata;
1122 	struct screen			*s = &data->screen;
1123 	u_int				 px, py, xx, skip;
1124 
1125 	px = data->cx;
1126 	py = screen_hsize(&wp->base) + data->cy - data->oy;
1127 	xx = window_copy_find_length(wp, py);
1128 
1129 	skip = 1;
1130 	if (px < xx) {
1131 		/* If currently on a space, skip space. */
1132 		if (window_copy_is_space(wp, px, py))
1133 			skip = 0;
1134 	} else
1135 		skip = 0;
1136 	for (;;) {
1137 		if (px >= xx) {
1138 			if (skip) {
1139 				px = xx;
1140 				break;
1141 			}
1142 
1143 			while (px >= xx) {
1144 				if (data->cy == screen_size_y(s) - 1) {
1145 					if (data->oy == 0)
1146 						goto out;
1147 				}
1148 
1149 				px = 0;
1150 				window_copy_cursor_down(wp, 0);
1151 
1152 				py =screen_hsize(
1153 				    &wp->base) + data->cy - data->oy;
1154 				xx = window_copy_find_length(wp, py);
1155 			}
1156 		}
1157 
1158 		if (skip) {
1159 			/* Currently skipping non-space (until space). */
1160 			if (window_copy_is_space(wp, px, py))
1161 				break;
1162 		} else {
1163 			/* Currently skipping space (until non-space). */
1164 			if (!window_copy_is_space(wp, px, py))
1165 				skip = 1;
1166 		}
1167 
1168 		px++;
1169 	}
1170 
1171 out:
1172 	window_copy_update_cursor(wp, px, data->cy);
1173 	if (window_copy_update_selection(wp))
1174 		window_copy_redraw_lines(wp, data->cy, 1);
1175 }
1176 
1177 /* Move to the previous place where a word begins. */
1178 void
1179 window_copy_cursor_previous_word(struct window_pane *wp)
1180 {
1181 	struct window_copy_mode_data	*data = wp->modedata;
1182 	u_int				 px, py;
1183 
1184 	px = data->cx;
1185 	py = screen_hsize(&wp->base) + data->cy - data->oy;
1186 
1187 	/* Move back to the previous word character. */
1188 	for (;;) {
1189 		if (px > 0) {
1190 			px--;
1191 			if (!window_copy_is_space(wp, px, py))
1192 				break;
1193 		} else {
1194 			if (data->cy == 0 &&
1195 			    (screen_hsize(&wp->base) == 0 ||
1196 			    data->oy >= screen_hsize(&wp->base) - 1))
1197 				goto out;
1198 			window_copy_cursor_up(wp, 0);
1199 
1200 			py = screen_hsize(
1201 			    &wp->base) + data->cy - data->oy;
1202 			px = window_copy_find_length(wp, py);
1203 		}
1204 	}
1205 
1206 	/* Move back to the beginning of this word. */
1207 	while (px > 0 && !window_copy_is_space(wp, px - 1, py))
1208 		px--;
1209 
1210 out:
1211 	window_copy_update_cursor(wp, px, data->cy);
1212 	if (window_copy_update_selection(wp))
1213 		window_copy_redraw_lines(wp, data->cy, 1);
1214 }
1215 
1216 void
1217 window_copy_scroll_up(struct window_pane *wp, u_int ny)
1218 {
1219 	struct window_copy_mode_data	*data = wp->modedata;
1220 	struct screen			*s = &data->screen;
1221 	struct screen_write_ctx		 ctx;
1222 
1223 	if (data->oy < ny)
1224 		ny = data->oy;
1225 	if (ny == 0)
1226 		return;
1227 	data->oy -= ny;
1228 	window_copy_update_selection(wp);
1229 
1230 	screen_write_start(&ctx, wp, NULL);
1231 	screen_write_cursormove(&ctx, 0, 0);
1232 	screen_write_deleteline(&ctx, ny);
1233 	window_copy_write_lines(wp, &ctx, screen_size_y(s) - ny, ny);
1234 	window_copy_write_line(wp, &ctx, 0);
1235 	window_copy_write_line(wp, &ctx, 1);
1236 	if (s->sel.flag && screen_size_y(s) > ny)
1237 		window_copy_write_line(wp, &ctx, screen_size_y(s) - ny - 1);
1238 	screen_write_cursormove(&ctx, data->cx, data->cy);
1239 	screen_write_stop(&ctx);
1240 }
1241 
1242 void
1243 window_copy_scroll_down(struct window_pane *wp, u_int ny)
1244 {
1245 	struct window_copy_mode_data	*data = wp->modedata;
1246 	struct screen			*s = &data->screen;
1247 	struct screen_write_ctx		 ctx;
1248 
1249 	if (ny > screen_hsize(&wp->base))
1250 		return;
1251 
1252 	if (data->oy > screen_hsize(&wp->base) - ny)
1253 		ny = screen_hsize(&wp->base) - data->oy;
1254 	if (ny == 0)
1255 		return;
1256 	data->oy += ny;
1257 	window_copy_update_selection(wp);
1258 
1259 	screen_write_start(&ctx, wp, NULL);
1260 	screen_write_cursormove(&ctx, 0, 0);
1261 	screen_write_insertline(&ctx, ny);
1262 	window_copy_write_lines(wp, &ctx, 0, ny);
1263 	if (s->sel.flag && screen_size_y(s) > ny)
1264 		window_copy_write_line(wp, &ctx, ny);
1265 	else if (ny == 1) /* nuke position */
1266 		window_copy_write_line(wp, &ctx, 1);
1267 	screen_write_cursormove(&ctx, data->cx, data->cy);
1268 	screen_write_stop(&ctx);
1269 }
1270