window-copy.c revision 1.2
1/* $Id: window-copy.c,v 1.2 2011/08/17 19:28:36 jmmv 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
26struct screen *window_copy_init(struct window_pane *);
27void	window_copy_free(struct window_pane *);
28void	window_copy_resize(struct window_pane *, u_int, u_int);
29void	window_copy_key(struct window_pane *, struct session *, int);
30int	window_copy_key_input(struct window_pane *, int);
31int	window_copy_key_numeric_prefix(struct window_pane *, int);
32void	window_copy_mouse(
33	    struct window_pane *, struct session *, struct mouse_event *);
34
35void	window_copy_redraw_lines(struct window_pane *, u_int, u_int);
36void	window_copy_redraw_screen(struct window_pane *);
37void	window_copy_write_line(
38	    struct window_pane *, struct screen_write_ctx *, u_int);
39void	window_copy_write_lines(
40	    struct window_pane *, struct screen_write_ctx *, u_int, u_int);
41
42void	window_copy_scroll_to(struct window_pane *, u_int, u_int);
43int	window_copy_search_compare(
44	    struct grid *, u_int, u_int, struct grid *, u_int);
45int	window_copy_search_lr(
46	    struct grid *, struct grid *, u_int *, u_int, u_int, u_int);
47int	window_copy_search_rl(
48	    struct grid *, struct grid *, u_int *, u_int, u_int, u_int);
49void	window_copy_search_up(struct window_pane *, const char *);
50void	window_copy_search_down(struct window_pane *, const char *);
51void	window_copy_goto_line(struct window_pane *, const char *);
52void	window_copy_update_cursor(struct window_pane *, u_int, u_int);
53void	window_copy_start_selection(struct window_pane *);
54int	window_copy_update_selection(struct window_pane *);
55void	window_copy_copy_selection(struct window_pane *);
56void	window_copy_clear_selection(struct window_pane *);
57void	window_copy_copy_line(
58	    struct window_pane *, char **, size_t *, u_int, u_int, u_int);
59int	window_copy_in_set(struct window_pane *, u_int, u_int, const char *);
60u_int	window_copy_find_length(struct window_pane *, u_int);
61void	window_copy_cursor_start_of_line(struct window_pane *);
62void	window_copy_cursor_back_to_indentation(struct window_pane *);
63void	window_copy_cursor_end_of_line(struct window_pane *);
64void	window_copy_cursor_left(struct window_pane *);
65void	window_copy_cursor_right(struct window_pane *);
66void	window_copy_cursor_up(struct window_pane *, int);
67void	window_copy_cursor_down(struct window_pane *, int);
68void	window_copy_cursor_jump(struct window_pane *);
69void	window_copy_cursor_jump_back(struct window_pane *);
70void	window_copy_cursor_next_word(struct window_pane *, const char *);
71void	window_copy_cursor_next_word_end(struct window_pane *, const char *);
72void	window_copy_cursor_previous_word(struct window_pane *, const char *);
73void	window_copy_scroll_up(struct window_pane *, u_int);
74void	window_copy_scroll_down(struct window_pane *, u_int);
75void	window_copy_rectangle_toggle(struct window_pane *);
76
77const struct window_mode window_copy_mode = {
78	window_copy_init,
79	window_copy_free,
80	window_copy_resize,
81	window_copy_key,
82	window_copy_mouse,
83	NULL,
84};
85
86enum window_copy_input_type {
87	WINDOW_COPY_OFF,
88	WINDOW_COPY_NUMERICPREFIX,
89	WINDOW_COPY_SEARCHUP,
90	WINDOW_COPY_SEARCHDOWN,
91	WINDOW_COPY_JUMPFORWARD,
92	WINDOW_COPY_JUMPBACK,
93	WINDOW_COPY_GOTOLINE,
94};
95
96/*
97 * Copy-mode's visible screen (the "screen" field) is filled from one of
98 * two sources: the original contents of the pane (used when we
99 * actually enter via the "copy-mode" command, to copy the contents of
100 * the current pane), or else a series of lines containing the output
101 * from an output-writing tmux command (such as any of the "show-*" or
102 * "list-*" commands).
103 *
104 * In either case, the full content of the copy-mode grid is pointed at
105 * by the "backing" field, and is copied into "screen" as needed (that
106 * is, when scrolling occurs). When copy-mode is backed by a pane,
107 * backing points directly at that pane's screen structure (&wp->base);
108 * when backed by a list of output-lines from a command, it points at
109 * a newly-allocated screen structure (which is deallocated when the
110 * mode ends).
111 */
112struct window_copy_mode_data {
113	struct screen	screen;
114
115	struct screen  *backing;
116	int		backing_written; /* backing display has started */
117
118	struct mode_key_data mdata;
119
120	u_int		oy;
121
122	u_int		selx;
123	u_int		sely;
124
125	u_int		rectflag; /* are we in rectangle copy mode? */
126
127	u_int		cx;
128	u_int		cy;
129
130	u_int		lastcx; /* position in last line with content */
131	u_int		lastsx; /* size of last line with content */
132
133	enum window_copy_input_type inputtype;
134	const char     *inputprompt;
135	char   	       *inputstr;
136
137	u_int		numprefix;
138
139	enum window_copy_input_type searchtype;
140	char	       *searchstr;
141
142	enum window_copy_input_type jumptype;
143	char		jumpchar;
144};
145
146struct screen *
147window_copy_init(struct window_pane *wp)
148{
149	struct window_copy_mode_data	*data;
150	struct screen			*s;
151	int				 keys;
152
153	wp->modedata = data = xmalloc(sizeof *data);
154	data->oy = 0;
155	data->cx = 0;
156	data->cy = 0;
157
158	data->lastcx = 0;
159	data->lastsx = 0;
160
161	data->backing_written = 0;
162
163	data->rectflag = 0;
164
165	data->inputtype = WINDOW_COPY_OFF;
166	data->inputprompt = NULL;
167	data->inputstr = xstrdup("");
168	data->numprefix = 0;
169
170	data->searchtype = WINDOW_COPY_OFF;
171	data->searchstr = NULL;
172
173	if (wp->fd != -1)
174		bufferevent_disable(wp->event, EV_READ|EV_WRITE);
175
176	data->jumptype = WINDOW_COPY_OFF;
177	data->jumpchar = '\0';
178
179	s = &data->screen;
180	screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
181	if (options_get_number(&wp->window->options, "mode-mouse"))
182		s->mode |= MODE_MOUSE_STANDARD;
183
184	keys = options_get_number(&wp->window->options, "mode-keys");
185	if (keys == MODEKEY_EMACS)
186		mode_key_init(&data->mdata, &mode_key_tree_emacs_copy);
187	else
188		mode_key_init(&data->mdata, &mode_key_tree_vi_copy);
189
190	data->backing = NULL;
191
192	return (s);
193}
194
195void
196window_copy_init_from_pane(struct window_pane *wp)
197{
198	struct window_copy_mode_data	*data = wp->modedata;
199	struct screen			*s = &data->screen;
200	struct screen_write_ctx	 	 ctx;
201	u_int				 i;
202
203	if (wp->mode != &window_copy_mode)
204		fatalx("not in copy mode");
205
206	data->backing = &wp->base;
207	data->cx = data->backing->cx;
208	data->cy = data->backing->cy;
209
210	s->cx = data->cx;
211	s->cy = data->cy;
212
213	screen_write_start(&ctx, NULL, s);
214	for (i = 0; i < screen_size_y(s); i++)
215		window_copy_write_line(wp, &ctx, i);
216	screen_write_cursormove(&ctx, data->cx, data->cy);
217	screen_write_stop(&ctx);
218}
219
220void
221window_copy_init_for_output(struct window_pane *wp)
222{
223	struct window_copy_mode_data	*data = wp->modedata;
224
225	data->backing = xmalloc(sizeof *data->backing);
226	screen_init(data->backing, screen_size_x(&wp->base),
227	    screen_size_y(&wp->base), UINT_MAX);
228	data->backing->mode &= ~MODE_WRAP;
229}
230
231void
232window_copy_free(struct window_pane *wp)
233{
234	struct window_copy_mode_data	*data = wp->modedata;
235
236	if (wp->fd != -1)
237		bufferevent_enable(wp->event, EV_READ|EV_WRITE);
238
239	if (data->searchstr != NULL)
240		xfree(data->searchstr);
241	xfree(data->inputstr);
242
243	if (data->backing != &wp->base) {
244		screen_free(data->backing);
245		xfree(data->backing);
246	}
247	screen_free(&data->screen);
248
249	xfree(data);
250}
251
252void
253window_copy_add(struct window_pane *wp, const char *fmt, ...)
254{
255	va_list	ap;
256
257	va_start(ap, fmt);
258	window_copy_vadd(wp, fmt, ap);
259	va_end(ap);
260}
261
262void
263window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap)
264{
265	struct window_copy_mode_data	*data = wp->modedata;
266	struct screen			*backing = data->backing;
267	struct screen_write_ctx	 	 back_ctx, ctx;
268	struct grid_cell		 gc;
269	int				 utf8flag;
270	u_int				 old_hsize;
271
272	if (backing == &wp->base)
273		return;
274
275	utf8flag = options_get_number(&wp->window->options, "utf8");
276	memcpy(&gc, &grid_default_cell, sizeof gc);
277
278	old_hsize = screen_hsize(data->backing);
279	screen_write_start(&back_ctx, NULL, backing);
280	if (data->backing_written) {
281		/*
282		 * On the second or later line, do a CRLF before writing
283		 * (so it's on a new line).
284		 */
285		screen_write_carriagereturn(&back_ctx);
286		screen_write_linefeed(&back_ctx, 0);
287	} else
288		data->backing_written = 1;
289	screen_write_vnputs(&back_ctx, 0, &gc, utf8flag, fmt, ap);
290	screen_write_stop(&back_ctx);
291
292	data->oy += screen_hsize(data->backing) - old_hsize;
293
294	screen_write_start(&ctx, wp, &data->screen);
295
296	/*
297	 * If the history has changed, draw the top line.
298	 * (If there's any history at all, it has changed.)
299	 */
300	if (screen_hsize(data->backing))
301		window_copy_redraw_lines(wp, 0, 1);
302
303	/* Write the line, if it's visible. */
304	if (backing->cy + data->oy < screen_size_y(backing))
305		window_copy_redraw_lines(wp, backing->cy, 1);
306
307	screen_write_stop(&ctx);
308}
309
310void
311window_copy_pageup(struct window_pane *wp)
312{
313	struct window_copy_mode_data	*data = wp->modedata;
314	struct screen			*s = &data->screen;
315	u_int				 n;
316
317	n = 1;
318	if (screen_size_y(s) > 2)
319		n = screen_size_y(s) - 2;
320	if (data->oy + n > screen_hsize(data->backing))
321		data->oy = screen_hsize(data->backing);
322	else
323		data->oy += n;
324	window_copy_update_selection(wp);
325	window_copy_redraw_screen(wp);
326}
327
328void
329window_copy_resize(struct window_pane *wp, u_int sx, u_int sy)
330{
331	struct window_copy_mode_data	*data = wp->modedata;
332	struct screen			*s = &data->screen;
333	struct screen_write_ctx	 	 ctx;
334
335	screen_resize(s, sx, sy);
336	if (data->backing != &wp->base)
337		screen_resize(data->backing, sx, sy);
338
339	if (data->cy > sy - 1)
340		data->cy = sy - 1;
341	if (data->cx > sx)
342		data->cx = sx;
343	if (data->oy > screen_hsize(data->backing))
344		data->oy = screen_hsize(data->backing);
345
346	window_copy_clear_selection(wp);
347
348	screen_write_start(&ctx, NULL, s);
349	window_copy_write_lines(wp, &ctx, 0, screen_size_y(s) - 1);
350	screen_write_stop(&ctx);
351
352	window_copy_redraw_screen(wp);
353}
354
355void
356window_copy_key(struct window_pane *wp, struct session *sess, int key)
357{
358	const char			*word_separators;
359	struct window_copy_mode_data	*data = wp->modedata;
360	struct screen			*s = &data->screen;
361	u_int				 n, np;
362	int				 keys;
363	enum mode_key_cmd		 cmd;
364
365	np = data->numprefix;
366	if (np == 0)
367		np = 1;
368
369	if (data->inputtype == WINDOW_COPY_JUMPFORWARD ||
370	    data->inputtype == WINDOW_COPY_JUMPBACK) {
371		/* Ignore keys with modifiers. */
372		if ((key & KEYC_MASK_MOD) == 0) {
373			data->jumpchar = key;
374			if (data->inputtype == WINDOW_COPY_JUMPFORWARD) {
375				for (; np != 0; np--)
376					window_copy_cursor_jump(wp);
377			}  else {
378				for (; np != 0; np--)
379					window_copy_cursor_jump_back(wp);
380			}
381		}
382		data->jumptype = data->inputtype;
383		data->inputtype = WINDOW_COPY_OFF;
384		window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
385		return;
386	} else if (data->inputtype == WINDOW_COPY_NUMERICPREFIX) {
387		if (window_copy_key_numeric_prefix(wp, key) == 0)
388			return;
389		data->inputtype = WINDOW_COPY_OFF;
390		window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
391	} else if (data->inputtype != WINDOW_COPY_OFF) {
392		if (window_copy_key_input(wp, key) != 0)
393			goto input_off;
394		return;
395	}
396
397	cmd = mode_key_lookup(&data->mdata, key);
398	switch (cmd) {
399	case MODEKEYCOPY_CANCEL:
400		window_pane_reset_mode(wp);
401		return;
402	case MODEKEYCOPY_LEFT:
403		for (; np != 0; np--)
404			window_copy_cursor_left(wp);
405		break;
406	case MODEKEYCOPY_RIGHT:
407		for (; np != 0; np--)
408			window_copy_cursor_right(wp);
409		break;
410	case MODEKEYCOPY_UP:
411		for (; np != 0; np--)
412			window_copy_cursor_up(wp, 0);
413		break;
414	case MODEKEYCOPY_DOWN:
415		for (; np != 0; np--)
416			window_copy_cursor_down(wp, 0);
417		break;
418	case MODEKEYCOPY_SCROLLUP:
419		for (; np != 0; np--)
420			window_copy_cursor_up(wp, 1);
421		break;
422	case MODEKEYCOPY_SCROLLDOWN:
423		for (; np != 0; np--)
424			window_copy_cursor_down(wp, 1);
425		break;
426	case MODEKEYCOPY_PREVIOUSPAGE:
427		for (; np != 0; np--)
428			window_copy_pageup(wp);
429		break;
430	case MODEKEYCOPY_NEXTPAGE:
431		n = 1;
432		if (screen_size_y(s) > 2)
433			n = screen_size_y(s) - 2;
434		for (; np != 0; np--) {
435			if (data->oy < n)
436				data->oy = 0;
437			else
438				data->oy -= n;
439		}
440		window_copy_update_selection(wp);
441		window_copy_redraw_screen(wp);
442		break;
443	case MODEKEYCOPY_HALFPAGEUP:
444		n = screen_size_y(s) / 2;
445		for (; np != 0; np--) {
446			if (data->oy + n > screen_hsize(data->backing))
447				data->oy = screen_hsize(data->backing);
448			else
449				data->oy += n;
450		}
451		window_copy_update_selection(wp);
452		window_copy_redraw_screen(wp);
453		break;
454	case MODEKEYCOPY_HALFPAGEDOWN:
455		n = screen_size_y(s) / 2;
456		for (; np != 0; np--) {
457			if (data->oy < n)
458				data->oy = 0;
459			else
460				data->oy -= n;
461		}
462		window_copy_update_selection(wp);
463		window_copy_redraw_screen(wp);
464		break;
465	case MODEKEYCOPY_TOPLINE:
466		data->cx = 0;
467		data->cy = 0;
468		window_copy_update_selection(wp);
469		window_copy_redraw_screen(wp);
470		break;
471	case MODEKEYCOPY_MIDDLELINE:
472		data->cx = 0;
473		data->cy = (screen_size_y(s) - 1) / 2;
474		window_copy_update_selection(wp);
475		window_copy_redraw_screen(wp);
476		break;
477	case MODEKEYCOPY_BOTTOMLINE:
478		data->cx = 0;
479		data->cy = screen_size_y(s) - 1;
480		window_copy_update_selection(wp);
481		window_copy_redraw_screen(wp);
482		break;
483	case MODEKEYCOPY_HISTORYTOP:
484		data->cx = 0;
485		data->cy = 0;
486		data->oy = screen_hsize(data->backing);
487		window_copy_update_selection(wp);
488		window_copy_redraw_screen(wp);
489		break;
490	case MODEKEYCOPY_HISTORYBOTTOM:
491		data->cx = 0;
492		data->cy = screen_size_y(s) - 1;
493		data->oy = 0;
494		window_copy_update_selection(wp);
495		window_copy_redraw_screen(wp);
496		break;
497	case MODEKEYCOPY_STARTSELECTION:
498		window_copy_start_selection(wp);
499		window_copy_redraw_screen(wp);
500		break;
501	case MODEKEYCOPY_COPYLINE:
502	case MODEKEYCOPY_SELECTLINE:
503		window_copy_cursor_start_of_line(wp);
504		/* FALLTHROUGH */
505	case MODEKEYCOPY_COPYENDOFLINE:
506		window_copy_start_selection(wp);
507		for (; np > 1; np--)
508			window_copy_cursor_down(wp, 0);
509		window_copy_cursor_end_of_line(wp);
510		window_copy_redraw_screen(wp);
511
512		/* If a copy command then copy the selection and exit. */
513		if (sess != NULL &&
514		    (cmd == MODEKEYCOPY_COPYLINE ||
515		    cmd == MODEKEYCOPY_COPYENDOFLINE)) {
516			window_copy_copy_selection(wp);
517			window_pane_reset_mode(wp);
518			return;
519		}
520		break;
521	case MODEKEYCOPY_CLEARSELECTION:
522		window_copy_clear_selection(wp);
523		window_copy_redraw_screen(wp);
524		break;
525	case MODEKEYCOPY_COPYSELECTION:
526		if (sess != NULL) {
527			window_copy_copy_selection(wp);
528			window_pane_reset_mode(wp);
529			return;
530		}
531		break;
532	case MODEKEYCOPY_STARTOFLINE:
533		window_copy_cursor_start_of_line(wp);
534		break;
535	case MODEKEYCOPY_BACKTOINDENTATION:
536		window_copy_cursor_back_to_indentation(wp);
537		break;
538	case MODEKEYCOPY_ENDOFLINE:
539		window_copy_cursor_end_of_line(wp);
540		break;
541	case MODEKEYCOPY_NEXTSPACE:
542		for (; np != 0; np--)
543			window_copy_cursor_next_word(wp, " ");
544		break;
545	case MODEKEYCOPY_NEXTSPACEEND:
546		for (; np != 0; np--)
547			window_copy_cursor_next_word_end(wp, " ");
548		break;
549	case MODEKEYCOPY_NEXTWORD:
550		word_separators =
551		    options_get_string(&wp->window->options, "word-separators");
552		for (; np != 0; np--)
553			window_copy_cursor_next_word(wp, word_separators);
554		break;
555	case MODEKEYCOPY_NEXTWORDEND:
556		word_separators =
557		    options_get_string(&wp->window->options, "word-separators");
558		for (; np != 0; np--)
559			window_copy_cursor_next_word_end(wp, word_separators);
560		break;
561	case MODEKEYCOPY_PREVIOUSSPACE:
562		for (; np != 0; np--)
563			window_copy_cursor_previous_word(wp, " ");
564		break;
565	case MODEKEYCOPY_PREVIOUSWORD:
566		word_separators =
567		    options_get_string(&wp->window->options, "word-separators");
568		for (; np != 0; np--)
569			window_copy_cursor_previous_word(wp, word_separators);
570		break;
571	case MODEKEYCOPY_JUMP:
572		data->inputtype = WINDOW_COPY_JUMPFORWARD;
573		data->inputprompt = "Jump Forward";
574		*data->inputstr = '\0';
575		window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
576		return; /* skip numprefix reset */
577	case MODEKEYCOPY_JUMPAGAIN:
578		if (data->jumptype == WINDOW_COPY_JUMPFORWARD) {
579			for (; np != 0; np--)
580				window_copy_cursor_jump(wp);
581		} else if (data->jumptype == WINDOW_COPY_JUMPBACK) {
582			for (; np != 0; np--)
583				window_copy_cursor_jump_back(wp);
584		}
585		break;
586	case MODEKEYCOPY_JUMPREVERSE:
587		if (data->jumptype == WINDOW_COPY_JUMPFORWARD) {
588			for (; np != 0; np--)
589				window_copy_cursor_jump_back(wp);
590		} else if (data->jumptype == WINDOW_COPY_JUMPBACK) {
591			for (; np != 0; np--)
592				window_copy_cursor_jump(wp);
593		}
594		break;
595	case MODEKEYCOPY_JUMPBACK:
596		data->inputtype = WINDOW_COPY_JUMPBACK;
597		data->inputprompt = "Jump Back";
598		*data->inputstr = '\0';
599		window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
600		return; /* skip numprefix reset */
601	case MODEKEYCOPY_SEARCHUP:
602		data->inputtype = WINDOW_COPY_SEARCHUP;
603		data->inputprompt = "Search Up";
604		goto input_on;
605	case MODEKEYCOPY_SEARCHDOWN:
606		data->inputtype = WINDOW_COPY_SEARCHDOWN;
607		data->inputprompt = "Search Down";
608		goto input_on;
609	case MODEKEYCOPY_SEARCHAGAIN:
610	case MODEKEYCOPY_SEARCHREVERSE:
611		switch (data->searchtype) {
612		case WINDOW_COPY_OFF:
613		case WINDOW_COPY_GOTOLINE:
614		case WINDOW_COPY_JUMPFORWARD:
615		case WINDOW_COPY_JUMPBACK:
616		case WINDOW_COPY_NUMERICPREFIX:
617			break;
618		case WINDOW_COPY_SEARCHUP:
619			if (cmd == MODEKEYCOPY_SEARCHAGAIN) {
620				for (; np != 0; np--) {
621					window_copy_search_up(
622					    wp, data->searchstr);
623				}
624			} else {
625				for (; np != 0; np--) {
626					window_copy_search_down(
627					    wp, data->searchstr);
628				}
629			}
630			break;
631		case WINDOW_COPY_SEARCHDOWN:
632			if (cmd == MODEKEYCOPY_SEARCHAGAIN) {
633				for (; np != 0; np--) {
634					window_copy_search_down(
635					    wp, data->searchstr);
636				}
637			} else {
638				for (; np != 0; np--) {
639					window_copy_search_up(
640					    wp, data->searchstr);
641				}
642			}
643			break;
644		}
645		break;
646	case MODEKEYCOPY_GOTOLINE:
647		data->inputtype = WINDOW_COPY_GOTOLINE;
648		data->inputprompt = "Goto Line";
649		*data->inputstr = '\0';
650		goto input_on;
651	case MODEKEYCOPY_STARTNUMBERPREFIX:
652		key &= KEYC_MASK_KEY;
653		if (key >= '0' && key <= '9') {
654			data->inputtype = WINDOW_COPY_NUMERICPREFIX;
655			data->numprefix = 0;
656			window_copy_key_numeric_prefix(wp, key);
657			return;
658		}
659		break;
660	case MODEKEYCOPY_RECTANGLETOGGLE:
661		window_copy_rectangle_toggle(wp);
662		break;
663	default:
664		break;
665	}
666
667	data->numprefix = 0;
668	return;
669
670input_on:
671	keys = options_get_number(&wp->window->options, "mode-keys");
672	if (keys == MODEKEY_EMACS)
673		mode_key_init(&data->mdata, &mode_key_tree_emacs_edit);
674	else
675		mode_key_init(&data->mdata, &mode_key_tree_vi_edit);
676
677	window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
678	return;
679
680input_off:
681	keys = options_get_number(&wp->window->options, "mode-keys");
682	if (keys == MODEKEY_EMACS)
683		mode_key_init(&data->mdata, &mode_key_tree_emacs_copy);
684	else
685		mode_key_init(&data->mdata, &mode_key_tree_vi_copy);
686
687	data->inputtype = WINDOW_COPY_OFF;
688	data->inputprompt = NULL;
689
690	window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
691}
692
693int
694window_copy_key_input(struct window_pane *wp, int key)
695{
696	struct window_copy_mode_data	*data = wp->modedata;
697	struct screen			*s = &data->screen;
698	size_t				 inputlen;
699	u_int				 np;
700
701	switch (mode_key_lookup(&data->mdata, key)) {
702	case MODEKEYEDIT_CANCEL:
703		data->numprefix = 0;
704		return (-1);
705	case MODEKEYEDIT_BACKSPACE:
706		inputlen = strlen(data->inputstr);
707		if (inputlen > 0)
708			data->inputstr[inputlen - 1] = '\0';
709		break;
710	case MODEKEYEDIT_DELETELINE:
711		*data->inputstr = '\0';
712		break;
713	case MODEKEYEDIT_ENTER:
714		np = data->numprefix;
715		if (np == 0)
716			np = 1;
717
718		switch (data->inputtype) {
719		case WINDOW_COPY_OFF:
720		case WINDOW_COPY_JUMPFORWARD:
721		case WINDOW_COPY_JUMPBACK:
722		case WINDOW_COPY_NUMERICPREFIX:
723			break;
724		case WINDOW_COPY_SEARCHUP:
725			for (; np != 0; np--)
726				window_copy_search_up(wp, data->inputstr);
727			data->searchtype = data->inputtype;
728			data->searchstr = xstrdup(data->inputstr);
729			break;
730		case WINDOW_COPY_SEARCHDOWN:
731			for (; np != 0; np--)
732				window_copy_search_down(wp, data->inputstr);
733			data->searchtype = data->inputtype;
734			data->searchstr = xstrdup(data->inputstr);
735			break;
736		case WINDOW_COPY_GOTOLINE:
737			window_copy_goto_line(wp, data->inputstr);
738			*data->inputstr = '\0';
739			break;
740		}
741		data->numprefix = 0;
742		return (1);
743	case MODEKEY_OTHER:
744		if (key < 32 || key > 126)
745			break;
746		inputlen = strlen(data->inputstr) + 2;
747
748		data->inputstr = xrealloc(data->inputstr, 1, inputlen);
749		data->inputstr[inputlen - 2] = key;
750		data->inputstr[inputlen - 1] = '\0';
751		break;
752	default:
753		break;
754	}
755
756	window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
757	return (0);
758}
759
760int
761window_copy_key_numeric_prefix(struct window_pane *wp, int key)
762{
763	struct window_copy_mode_data	*data = wp->modedata;
764	struct screen			*s = &data->screen;
765
766	key &= KEYC_MASK_KEY;
767	if (key < '0' || key > '9')
768		return 1;
769
770	if (data->numprefix >= 100)	/* no more than three digits */
771		return 0;
772	data->numprefix = data->numprefix * 10 + key - '0';
773
774	window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
775	return 0;
776}
777
778/* ARGSUSED */
779void
780window_copy_mouse(
781    struct window_pane *wp, struct session *sess, struct mouse_event *m)
782{
783	struct window_copy_mode_data	*data = wp->modedata;
784	struct screen			*s = &data->screen;
785	u_int				 i, old_cy;
786
787	if (m->x >= screen_size_x(s))
788		return;
789	if (m->y >= screen_size_y(s))
790		return;
791
792	/* If mouse wheel (buttons 4 and 5), scroll. */
793	if ((m->b & MOUSE_45)) {
794		if ((m->b & MOUSE_BUTTON) == MOUSE_1) {
795			for (i = 0; i < 5; i++)
796				window_copy_cursor_up(wp, 0);
797		} else if ((m->b & MOUSE_BUTTON) == MOUSE_2) {
798			old_cy = data->cy;
799			for (i = 0; i < 5; i++)
800				window_copy_cursor_down(wp, 0);
801			if (old_cy == data->cy)
802				goto reset_mode;
803		}
804		return;
805	}
806
807	/*
808	 * If already reading motion, move the cursor while buttons are still
809	 * pressed, or stop the selection on their release.
810	 */
811	if (s->mode & MODE_MOUSE_BUTTON) {
812		if ((m->b & MOUSE_BUTTON) != MOUSE_UP) {
813			window_copy_update_cursor(wp, m->x, m->y);
814			if (window_copy_update_selection(wp))
815				window_copy_redraw_screen(wp);
816			return;
817		}
818		goto reset_mode;
819	}
820
821	/* Otherwise if other buttons pressed, start selection and motion. */
822	if ((m->b & MOUSE_BUTTON) != MOUSE_UP) {
823		s->mode &= ~MODE_MOUSE_STANDARD;
824		s->mode |= MODE_MOUSE_BUTTON;
825
826		window_copy_update_cursor(wp, m->x, m->y);
827		window_copy_start_selection(wp);
828		window_copy_redraw_screen(wp);
829	}
830
831	return;
832
833reset_mode:
834	s->mode &= ~MODE_MOUSE_BUTTON;
835	s->mode |= MODE_MOUSE_STANDARD;
836	if (sess != NULL) {
837		window_copy_copy_selection(wp);
838		window_pane_reset_mode(wp);
839	}
840}
841
842void
843window_copy_scroll_to(struct window_pane *wp, u_int px, u_int py)
844{
845	struct window_copy_mode_data	*data = wp->modedata;
846	struct grid			*gd = data->backing->grid;
847	u_int				 offset, gap;
848
849	data->cx = px;
850
851	gap = gd->sy / 4;
852	if (py < gd->sy) {
853		offset = 0;
854		data->cy = py;
855	} else if (py > gd->hsize + gd->sy - gap) {
856		offset = gd->hsize;
857		data->cy = py - gd->hsize;
858	} else {
859		offset = py + gap - gd->sy;
860		data->cy = py - offset;
861	}
862	data->oy = gd->hsize - offset;
863
864	window_copy_update_selection(wp);
865	window_copy_redraw_screen(wp);
866}
867
868int
869window_copy_search_compare(
870    struct grid *gd, u_int px, u_int py, struct grid *sgd, u_int spx)
871{
872	const struct grid_cell	*gc, *sgc;
873	const struct grid_utf8	*gu, *sgu;
874
875	gc = grid_peek_cell(gd, px, py);
876	sgc = grid_peek_cell(sgd, spx, 0);
877
878	if ((gc->flags & GRID_FLAG_UTF8) != (sgc->flags & GRID_FLAG_UTF8))
879		return (0);
880
881	if (gc->flags & GRID_FLAG_UTF8) {
882		gu = grid_peek_utf8(gd, px, py);
883		sgu = grid_peek_utf8(sgd, spx, 0);
884		if (grid_utf8_compare(gu, sgu))
885			return (1);
886	} else {
887		if (gc->data == sgc->data)
888			return (1);
889	}
890	return (0);
891}
892
893int
894window_copy_search_lr(struct grid *gd,
895    struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last)
896{
897	u_int	ax, bx, px;
898
899	for (ax = first; ax < last; ax++) {
900		if (ax + sgd->sx >= gd->sx)
901			break;
902		for (bx = 0; bx < sgd->sx; bx++) {
903			px = ax + bx;
904			if (!window_copy_search_compare(gd, px, py, sgd, bx))
905				break;
906		}
907		if (bx == sgd->sx) {
908			*ppx = ax;
909			return (1);
910		}
911	}
912	return (0);
913}
914
915int
916window_copy_search_rl(struct grid *gd,
917    struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last)
918{
919	u_int	ax, bx, px;
920
921	for (ax = last + 1; ax > first; ax--) {
922		if (gd->sx - (ax - 1) < sgd->sx)
923			continue;
924		for (bx = 0; bx < sgd->sx; bx++) {
925			px = ax - 1 + bx;
926			if (!window_copy_search_compare(gd, px, py, sgd, bx))
927				break;
928		}
929		if (bx == sgd->sx) {
930			*ppx = ax - 1;
931			return (1);
932		}
933	}
934	return (0);
935}
936
937void
938window_copy_search_up(struct window_pane *wp, const char *searchstr)
939{
940	struct window_copy_mode_data	*data = wp->modedata;
941	struct screen			*s = data->backing, ss;
942	struct screen_write_ctx		 ctx;
943	struct grid			*gd = s->grid, *sgd;
944	struct grid_cell	 	 gc;
945	size_t				 searchlen;
946	u_int				 i, last, fx, fy, px;
947	int				 utf8flag, n, wrapped;
948
949	if (*searchstr == '\0')
950		return;
951	utf8flag = options_get_number(&wp->window->options, "utf8");
952	searchlen = screen_write_strlen(utf8flag, "%s", searchstr);
953
954	screen_init(&ss, searchlen, 1, 0);
955	screen_write_start(&ctx, NULL, &ss);
956	memcpy(&gc, &grid_default_cell, sizeof gc);
957	screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr);
958	screen_write_stop(&ctx);
959
960	fx = data->cx;
961	fy = gd->hsize - data->oy + data->cy;
962
963	if (fx == 0) {
964		if (fy == 0)
965			return;
966		fx = gd->sx - 1;
967		fy--;
968	} else
969		fx--;
970	n = wrapped = 0;
971
972retry:
973	sgd = ss.grid;
974	for (i = fy + 1; i > 0; i--) {
975		last = screen_size_x(s);
976		if (i == fy + 1)
977			last = fx;
978		n = window_copy_search_rl(gd, sgd, &px, i - 1, 0, last);
979		if (n) {
980			window_copy_scroll_to(wp, px, i - 1);
981			break;
982		}
983	}
984	if (!n && !wrapped) {
985		fx = gd->sx - 1;
986		fy = gd->hsize + gd->sy - 1;
987		wrapped = 1;
988		goto retry;
989	}
990
991	screen_free(&ss);
992}
993
994void
995window_copy_search_down(struct window_pane *wp, const char *searchstr)
996{
997	struct window_copy_mode_data	*data = wp->modedata;
998	struct screen			*s = data->backing, ss;
999	struct screen_write_ctx		 ctx;
1000	struct grid			*gd = s->grid, *sgd;
1001	struct grid_cell	 	 gc;
1002	size_t				 searchlen;
1003	u_int				 i, first, fx, fy, px;
1004	int				 utf8flag, n, wrapped;
1005
1006	if (*searchstr == '\0')
1007		return;
1008	utf8flag = options_get_number(&wp->window->options, "utf8");
1009	searchlen = screen_write_strlen(utf8flag, "%s", searchstr);
1010
1011	screen_init(&ss, searchlen, 1, 0);
1012	screen_write_start(&ctx, NULL, &ss);
1013	memcpy(&gc, &grid_default_cell, sizeof gc);
1014	screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr);
1015	screen_write_stop(&ctx);
1016
1017	fx = data->cx;
1018	fy = gd->hsize - data->oy + data->cy;
1019
1020	if (fx == gd->sx - 1) {
1021		if (fy == gd->hsize + gd->sy)
1022			return;
1023		fx = 0;
1024		fy++;
1025	} else
1026		fx++;
1027	n = wrapped = 0;
1028
1029retry:
1030	sgd = ss.grid;
1031	for (i = fy + 1; i < gd->hsize + gd->sy; i++) {
1032		first = 0;
1033		if (i == fy + 1)
1034			first = fx;
1035		n = window_copy_search_lr(gd, sgd, &px, i - 1, first, gd->sx);
1036		if (n) {
1037			window_copy_scroll_to(wp, px, i - 1);
1038			break;
1039		}
1040	}
1041	if (!n && !wrapped) {
1042		fx = 0;
1043		fy = 0;
1044		wrapped = 1;
1045		goto retry;
1046	}
1047
1048	screen_free(&ss);
1049}
1050
1051void
1052window_copy_goto_line(struct window_pane *wp, const char *linestr)
1053{
1054	struct window_copy_mode_data	*data = wp->modedata;
1055	const char			*errstr;
1056	u_int				 lineno;
1057
1058	lineno = strtonum(linestr, 0, screen_hsize(data->backing), &errstr);
1059	if (errstr != NULL)
1060		return;
1061
1062	data->oy = lineno;
1063	window_copy_update_selection(wp);
1064	window_copy_redraw_screen(wp);
1065}
1066
1067void
1068window_copy_write_line(
1069    struct window_pane *wp, struct screen_write_ctx *ctx, u_int py)
1070{
1071	struct window_copy_mode_data	*data = wp->modedata;
1072	struct screen			*s = &data->screen;
1073	struct options			*oo = &wp->window->options;
1074	struct grid_cell		 gc;
1075	char				 hdr[32];
1076	size_t	 			 last, xoff = 0, size = 0;
1077
1078	memcpy(&gc, &grid_default_cell, sizeof gc);
1079	colour_set_fg(&gc, options_get_number(oo, "mode-fg"));
1080	colour_set_bg(&gc, options_get_number(oo, "mode-bg"));
1081	gc.attr |= options_get_number(oo, "mode-attr");
1082
1083	last = screen_size_y(s) - 1;
1084	if (py == 0) {
1085		size = xsnprintf(hdr, sizeof hdr,
1086		    "[%u/%u]", data->oy, screen_hsize(data->backing));
1087		if (size > screen_size_x(s))
1088			size = screen_size_x(s);
1089		screen_write_cursormove(ctx, screen_size_x(s) - size, 0);
1090		screen_write_puts(ctx, &gc, "%s", hdr);
1091	} else if (py == last && data->inputtype != WINDOW_COPY_OFF) {
1092		if (data->inputtype == WINDOW_COPY_NUMERICPREFIX) {
1093			xoff = size = xsnprintf(hdr, sizeof hdr,
1094			    "Repeat: %u", data->numprefix);
1095		} else {
1096			xoff = size = xsnprintf(hdr, sizeof hdr,
1097			    "%s: %s", data->inputprompt, data->inputstr);
1098		}
1099		screen_write_cursormove(ctx, 0, last);
1100		screen_write_puts(ctx, &gc, "%s", hdr);
1101	} else
1102		size = 0;
1103
1104	screen_write_cursormove(ctx, xoff, py);
1105	screen_write_copy(ctx, data->backing, xoff,
1106	    (screen_hsize(data->backing) - data->oy) + py,
1107	    screen_size_x(s) - size, 1);
1108
1109	if (py == data->cy && data->cx == screen_size_x(s)) {
1110		memcpy(&gc, &grid_default_cell, sizeof gc);
1111		screen_write_cursormove(ctx, screen_size_x(s) - 1, py);
1112		screen_write_putc(ctx, &gc, '$');
1113	}
1114}
1115
1116void
1117window_copy_write_lines(
1118    struct window_pane *wp, struct screen_write_ctx *ctx, u_int py, u_int ny)
1119{
1120	u_int	yy;
1121
1122	for (yy = py; yy < py + ny; yy++)
1123		window_copy_write_line(wp, ctx, py);
1124}
1125
1126void
1127window_copy_redraw_lines(struct window_pane *wp, u_int py, u_int ny)
1128{
1129	struct window_copy_mode_data	*data = wp->modedata;
1130	struct screen_write_ctx	 	 ctx;
1131	u_int				 i;
1132
1133	screen_write_start(&ctx, wp, NULL);
1134	for (i = py; i < py + ny; i++)
1135		window_copy_write_line(wp, &ctx, i);
1136	screen_write_cursormove(&ctx, data->cx, data->cy);
1137	screen_write_stop(&ctx);
1138}
1139
1140void
1141window_copy_redraw_screen(struct window_pane *wp)
1142{
1143	struct window_copy_mode_data	*data = wp->modedata;
1144
1145	window_copy_redraw_lines(wp, 0, screen_size_y(&data->screen));
1146}
1147
1148void
1149window_copy_update_cursor(struct window_pane *wp, u_int cx, u_int cy)
1150{
1151	struct window_copy_mode_data	*data = wp->modedata;
1152	struct screen			*s = &data->screen;
1153	struct screen_write_ctx		 ctx;
1154	u_int				 old_cx, old_cy;
1155
1156	old_cx = data->cx; old_cy = data->cy;
1157	data->cx = cx; data->cy = cy;
1158	if (old_cx == screen_size_x(s))
1159		window_copy_redraw_lines(wp, old_cy, 1);
1160	if (data->cx == screen_size_x(s))
1161		window_copy_redraw_lines(wp, data->cy, 1);
1162	else {
1163		screen_write_start(&ctx, wp, NULL);
1164		screen_write_cursormove(&ctx, data->cx, data->cy);
1165		screen_write_stop(&ctx);
1166	}
1167}
1168
1169void
1170window_copy_start_selection(struct window_pane *wp)
1171{
1172	struct window_copy_mode_data	*data = wp->modedata;
1173	struct screen			*s = &data->screen;
1174
1175	data->selx = data->cx;
1176	data->sely = screen_hsize(data->backing) + data->cy - data->oy;
1177
1178	s->sel.flag = 1;
1179	window_copy_update_selection(wp);
1180}
1181
1182int
1183window_copy_update_selection(struct window_pane *wp)
1184{
1185	struct window_copy_mode_data	*data = wp->modedata;
1186	struct screen			*s = &data->screen;
1187	struct options			*oo = &wp->window->options;
1188	struct grid_cell		 gc;
1189	u_int				 sx, sy, ty, cy;
1190
1191	if (!s->sel.flag)
1192		return (0);
1193
1194	/* Set colours. */
1195	memcpy(&gc, &grid_default_cell, sizeof gc);
1196	colour_set_fg(&gc, options_get_number(oo, "mode-fg"));
1197	colour_set_bg(&gc, options_get_number(oo, "mode-bg"));
1198	gc.attr |= options_get_number(oo, "mode-attr");
1199
1200	/* Find top of screen. */
1201	ty = screen_hsize(data->backing) - data->oy;
1202
1203	/* Adjust the selection. */
1204	sx = data->selx;
1205	sy = data->sely;
1206	if (sy < ty) {					/* above screen */
1207		if (!data->rectflag)
1208			sx = 0;
1209		sy = 0;
1210	} else if (sy > ty + screen_size_y(s) - 1) {	/* below screen */
1211		if (!data->rectflag)
1212			sx = screen_size_x(s) - 1;
1213		sy = screen_size_y(s) - 1;
1214	} else
1215		sy -= ty;
1216	sy = screen_hsize(s) + sy;
1217
1218	screen_set_selection(s,
1219	    sx, sy, data->cx, screen_hsize(s) + data->cy, data->rectflag, &gc);
1220
1221	if (data->rectflag) {
1222		/*
1223		 * Can't rely on the caller to redraw the right lines for
1224		 * rectangle selection - find the highest line and the number
1225		 * of lines, and redraw just past that in both directions
1226		 */
1227		cy = data->cy;
1228		if (sy < cy)
1229			window_copy_redraw_lines(wp, sy, cy - sy + 1);
1230		else
1231			window_copy_redraw_lines(wp, cy, sy - cy + 1);
1232	}
1233
1234	return (1);
1235}
1236
1237void
1238window_copy_copy_selection(struct window_pane *wp)
1239{
1240	struct window_copy_mode_data	*data = wp->modedata;
1241	struct screen			*s = &data->screen;
1242	char				*buf;
1243	size_t				 off;
1244	u_int				 i, xx, yy, sx, sy, ex, ey, limit;
1245	u_int				 firstsx, lastex, restex, restsx;
1246	int				 keys;
1247
1248	if (!s->sel.flag)
1249		return;
1250
1251	buf = xmalloc(1);
1252	off = 0;
1253
1254	*buf = '\0';
1255
1256	/*
1257	 * The selection extends from selx,sely to (adjusted) cx,cy on
1258	 * the base screen.
1259	 */
1260
1261	/* Find start and end. */
1262	xx = data->cx;
1263	yy = screen_hsize(data->backing) + data->cy - data->oy;
1264	if (yy < data->sely || (yy == data->sely && xx < data->selx)) {
1265		sx = xx; sy = yy;
1266		ex = data->selx; ey = data->sely;
1267	} else {
1268		sx = data->selx; sy = data->sely;
1269		ex = xx; ey = yy;
1270	}
1271
1272	/* Trim ex to end of line. */
1273	xx = window_copy_find_length(wp, ey);
1274	if (ex > xx)
1275		ex = xx;
1276
1277	/*
1278	 * Deal with rectangle-copy if necessary; four situations: start of
1279	 * first line (firstsx), end of last line (lastex), start (restsx) and
1280	 * end (restex) of all other lines.
1281	 */
1282	xx = screen_size_x(s);
1283
1284	/*
1285	 * Behave according to mode-keys. If it is emacs, copy like emacs,
1286	 * keeping the top-left-most character, and dropping the
1287	 * bottom-right-most, regardless of copy direction. If it is vi, also
1288	 * keep bottom-right-most character.
1289	 */
1290	keys = options_get_number(&wp->window->options, "mode-keys");
1291	if (data->rectflag) {
1292		/*
1293		 * Need to ignore the column with the cursor in it, which for
1294		 * rectangular copy means knowing which side the cursor is on.
1295		 */
1296		if (data->selx < data->cx) {
1297			/* Selection start is on the left. */
1298			if (keys == MODEKEY_EMACS) {
1299				lastex = data->cx;
1300				restex = data->cx;
1301			}
1302			else {
1303				lastex = data->cx + 1;
1304				restex = data->cx + 1;
1305			}
1306			firstsx = data->selx;
1307			restsx = data->selx;
1308		} else {
1309			/* Cursor is on the left. */
1310			lastex = data->selx + 1;
1311			restex = data->selx + 1;
1312			firstsx = data->cx;
1313			restsx = data->cx;
1314		}
1315	} else {
1316		if (keys == MODEKEY_EMACS)
1317			lastex = ex;
1318		else
1319			lastex = ex + 1;
1320		restex = xx;
1321		firstsx = sx;
1322		restsx = 0;
1323	}
1324
1325	/* Copy the lines. */
1326	if (sy == ey)
1327		window_copy_copy_line(wp, &buf, &off, sy, firstsx, lastex);
1328	else {
1329		window_copy_copy_line(wp, &buf, &off, sy, firstsx, restex);
1330		if (ey - sy > 1) {
1331			for (i = sy + 1; i < ey; i++) {
1332				window_copy_copy_line(
1333				    wp, &buf, &off, i, restsx, restex);
1334			}
1335		}
1336		window_copy_copy_line(wp, &buf, &off, ey, restsx, lastex);
1337	}
1338
1339	/* Don't bother if no data. */
1340	if (off == 0) {
1341		xfree(buf);
1342		return;
1343	}
1344	off--;	/* remove final \n */
1345
1346	if (options_get_number(&global_options, "set-clipboard"))
1347		screen_write_setselection(&wp->ictx.ctx, (u_char *)buf, off);
1348
1349	/* Add the buffer to the stack. */
1350	limit = options_get_number(&global_options, "buffer-limit");
1351	paste_add(&global_buffers, buf, off, limit);
1352}
1353
1354void
1355window_copy_copy_line(struct window_pane *wp,
1356    char **buf, size_t *off, u_int sy, u_int sx, u_int ex)
1357{
1358	struct window_copy_mode_data	*data = wp->modedata;
1359	struct grid			*gd = data->backing->grid;
1360	const struct grid_cell		*gc;
1361	const struct grid_utf8		*gu;
1362	struct grid_line		*gl;
1363	u_int				 i, xx, wrapped = 0;
1364	size_t				 size;
1365
1366	if (sx > ex)
1367		return;
1368
1369	/*
1370	 * Work out if the line was wrapped at the screen edge and all of it is
1371	 * on screen.
1372	 */
1373	gl = &gd->linedata[sy];
1374	if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx)
1375		wrapped = 1;
1376
1377	/* If the line was wrapped, don't strip spaces (use the full length). */
1378	if (wrapped)
1379		xx = gl->cellsize;
1380	else
1381		xx = window_copy_find_length(wp, sy);
1382	if (ex > xx)
1383		ex = xx;
1384	if (sx > xx)
1385		sx = xx;
1386
1387	if (sx < ex) {
1388		for (i = sx; i < ex; i++) {
1389			gc = grid_peek_cell(gd, i, sy);
1390			if (gc->flags & GRID_FLAG_PADDING)
1391				continue;
1392			if (!(gc->flags & GRID_FLAG_UTF8)) {
1393				*buf = xrealloc(*buf, 1, (*off) + 1);
1394				(*buf)[(*off)++] = gc->data;
1395			} else {
1396				gu = grid_peek_utf8(gd, i, sy);
1397				size = grid_utf8_size(gu);
1398				*buf = xrealloc(*buf, 1, (*off) + size);
1399				*off += grid_utf8_copy(gu, *buf + *off, size);
1400			}
1401		}
1402	}
1403
1404	/* Only add a newline if the line wasn't wrapped. */
1405	if (!wrapped || ex != xx) {
1406		*buf = xrealloc(*buf, 1, (*off) + 1);
1407		(*buf)[(*off)++] = '\n';
1408	}
1409}
1410
1411void
1412window_copy_clear_selection(struct window_pane *wp)
1413{
1414	struct window_copy_mode_data   *data = wp->modedata;
1415	u_int				px, py;
1416
1417	screen_clear_selection(&data->screen);
1418
1419	py = screen_hsize(data->backing) + data->cy - data->oy;
1420	px = window_copy_find_length(wp, py);
1421	if (data->cx > px)
1422		window_copy_update_cursor(wp, px, data->cy);
1423}
1424
1425int
1426window_copy_in_set(struct window_pane *wp, u_int px, u_int py, const char *set)
1427{
1428	struct window_copy_mode_data	*data = wp->modedata;
1429	const struct grid_cell		*gc;
1430
1431	gc = grid_peek_cell(data->backing->grid, px, py);
1432	if (gc->flags & (GRID_FLAG_PADDING|GRID_FLAG_UTF8))
1433		return (0);
1434	if (gc->data == 0x00 || gc->data == 0x7f)
1435		return (0);
1436	return (strchr(set, gc->data) != NULL);
1437}
1438
1439u_int
1440window_copy_find_length(struct window_pane *wp, u_int py)
1441{
1442	struct window_copy_mode_data	*data = wp->modedata;
1443	struct screen			*s = data->backing;
1444	const struct grid_cell		*gc;
1445	u_int				 px;
1446
1447	/*
1448	 * If the pane has been resized, its grid can contain old overlong
1449	 * lines. grid_peek_cell does not allow accessing cells beyond the
1450	 * width of the grid, and screen_write_copy treats them as spaces, so
1451	 * ignore them here too.
1452	 */
1453	px = s->grid->linedata[py].cellsize;
1454	if (px > screen_size_x(s))
1455		px = screen_size_x(s);
1456	while (px > 0) {
1457		gc = grid_peek_cell(s->grid, px - 1, py);
1458		if (gc->flags & GRID_FLAG_UTF8)
1459			break;
1460		if (gc->data != ' ')
1461			break;
1462		px--;
1463	}
1464	return (px);
1465}
1466
1467void
1468window_copy_cursor_start_of_line(struct window_pane *wp)
1469{
1470	struct window_copy_mode_data	*data = wp->modedata;
1471	struct screen			*back_s = data->backing;
1472	struct grid			*gd = back_s->grid;
1473	u_int				 py;
1474
1475	if (data->cx == 0) {
1476		py = screen_hsize(back_s) + data->cy - data->oy;
1477		while (py > 0 && gd->linedata[py-1].flags & GRID_LINE_WRAPPED) {
1478			window_copy_cursor_up(wp, 0);
1479			py = screen_hsize(back_s) + data->cy - data->oy;
1480		}
1481	}
1482	window_copy_update_cursor(wp, 0, data->cy);
1483	if (window_copy_update_selection(wp))
1484		window_copy_redraw_lines(wp, data->cy, 1);
1485}
1486
1487void
1488window_copy_cursor_back_to_indentation(struct window_pane *wp)
1489{
1490	struct window_copy_mode_data	*data = wp->modedata;
1491	u_int				 px, py, xx;
1492	const struct grid_cell		*gc;
1493
1494	px = 0;
1495	py = screen_hsize(data->backing) + data->cy - data->oy;
1496	xx = window_copy_find_length(wp, py);
1497
1498	while (px < xx) {
1499		gc = grid_peek_cell(data->backing->grid, px, py);
1500		if (gc->flags & GRID_FLAG_UTF8)
1501			break;
1502		if (gc->data != ' ')
1503			break;
1504		px++;
1505	}
1506
1507	window_copy_update_cursor(wp, px, data->cy);
1508	if (window_copy_update_selection(wp))
1509		window_copy_redraw_lines(wp, data->cy, 1);
1510}
1511
1512void
1513window_copy_cursor_end_of_line(struct window_pane *wp)
1514{
1515	struct window_copy_mode_data	*data = wp->modedata;
1516	struct screen			*back_s = data->backing;
1517	struct grid			*gd = back_s->grid;
1518	u_int				 px, py;
1519
1520	py = screen_hsize(back_s) + data->cy - data->oy;
1521	px = window_copy_find_length(wp, py);
1522
1523	if (data->cx == px) {
1524		if (data->screen.sel.flag && data->rectflag)
1525			px = screen_size_x(back_s);
1526		if (gd->linedata[py].flags & GRID_LINE_WRAPPED) {
1527			while (py < gd->sy + gd->hsize &&
1528			    gd->linedata[py].flags & GRID_LINE_WRAPPED) {
1529				window_copy_cursor_down(wp, 0);
1530				py = screen_hsize(back_s)
1531				     + data->cy - data->oy;
1532			}
1533			px = window_copy_find_length(wp, py);
1534		}
1535	}
1536	window_copy_update_cursor(wp, px, data->cy);
1537
1538	if (window_copy_update_selection(wp))
1539		window_copy_redraw_lines(wp, data->cy, 1);
1540}
1541
1542void
1543window_copy_cursor_left(struct window_pane *wp)
1544{
1545	struct window_copy_mode_data	*data = wp->modedata;
1546
1547	if (data->cx == 0) {
1548		window_copy_cursor_up(wp, 0);
1549		window_copy_cursor_end_of_line(wp);
1550	} else {
1551		window_copy_update_cursor(wp, data->cx - 1, data->cy);
1552		if (window_copy_update_selection(wp))
1553			window_copy_redraw_lines(wp, data->cy, 1);
1554	}
1555}
1556
1557void
1558window_copy_cursor_right(struct window_pane *wp)
1559{
1560	struct window_copy_mode_data	*data = wp->modedata;
1561	u_int				 px, py;
1562
1563	if (data->screen.sel.flag && data->rectflag)
1564		px = screen_size_x(&data->screen);
1565	else {
1566		py = screen_hsize(data->backing) + data->cy - data->oy;
1567		px = window_copy_find_length(wp, py);
1568	}
1569
1570	if (data->cx >= px) {
1571		window_copy_cursor_start_of_line(wp);
1572		window_copy_cursor_down(wp, 0);
1573	} else {
1574		window_copy_update_cursor(wp, data->cx + 1, data->cy);
1575		if (window_copy_update_selection(wp))
1576			window_copy_redraw_lines(wp, data->cy, 1);
1577	}
1578}
1579
1580void
1581window_copy_cursor_up(struct window_pane *wp, int scroll_only)
1582{
1583	struct window_copy_mode_data	*data = wp->modedata;
1584	struct screen			*s = &data->screen;
1585	u_int				 ox, oy, px, py;
1586
1587	oy = screen_hsize(data->backing) + data->cy - data->oy;
1588	ox = window_copy_find_length(wp, oy);
1589	if (ox != 0) {
1590		data->lastcx = data->cx;
1591		data->lastsx = ox;
1592	}
1593
1594	data->cx = data->lastcx;
1595	if (scroll_only || data->cy == 0) {
1596		window_copy_scroll_down(wp, 1);
1597		if (scroll_only) {
1598			if (data->cy == screen_size_y(s) - 1)
1599				window_copy_redraw_lines(wp, data->cy, 1);
1600			else
1601				window_copy_redraw_lines(wp, data->cy, 2);
1602		}
1603	} else {
1604		window_copy_update_cursor(wp, data->cx, data->cy - 1);
1605		if (window_copy_update_selection(wp)) {
1606			if (data->cy == screen_size_y(s) - 1)
1607				window_copy_redraw_lines(wp, data->cy, 1);
1608			else
1609				window_copy_redraw_lines(wp, data->cy, 2);
1610		}
1611	}
1612
1613	if (!data->screen.sel.flag || !data->rectflag) {
1614		py = screen_hsize(data->backing) + data->cy - data->oy;
1615		px = window_copy_find_length(wp, py);
1616		if ((data->cx >= data->lastsx && data->cx != px) ||
1617		    data->cx > px)
1618			window_copy_cursor_end_of_line(wp);
1619	}
1620}
1621
1622void
1623window_copy_cursor_down(struct window_pane *wp, int scroll_only)
1624{
1625	struct window_copy_mode_data	*data = wp->modedata;
1626	struct screen			*s = &data->screen;
1627	u_int				 ox, oy, px, py;
1628
1629	oy = screen_hsize(data->backing) + data->cy - data->oy;
1630	ox = window_copy_find_length(wp, oy);
1631	if (ox != 0) {
1632		data->lastcx = data->cx;
1633		data->lastsx = ox;
1634	}
1635
1636	data->cx = data->lastcx;
1637	if (scroll_only || data->cy == screen_size_y(s) - 1) {
1638		window_copy_scroll_up(wp, 1);
1639		if (scroll_only && data->cy > 0)
1640			window_copy_redraw_lines(wp, data->cy - 1, 2);
1641	} else {
1642		window_copy_update_cursor(wp, data->cx, data->cy + 1);
1643		if (window_copy_update_selection(wp))
1644			window_copy_redraw_lines(wp, data->cy - 1, 2);
1645	}
1646
1647	if (!data->screen.sel.flag || !data->rectflag) {
1648		py = screen_hsize(data->backing) + data->cy - data->oy;
1649		px = window_copy_find_length(wp, py);
1650		if ((data->cx >= data->lastsx && data->cx != px) ||
1651		    data->cx > px)
1652			window_copy_cursor_end_of_line(wp);
1653	}
1654}
1655
1656void
1657window_copy_cursor_jump(struct window_pane *wp)
1658{
1659	struct window_copy_mode_data	*data = wp->modedata;
1660	struct screen			*back_s = data->backing;
1661	const struct grid_cell		*gc;
1662	u_int				 px, py, xx;
1663
1664	px = data->cx + 1;
1665	py = screen_hsize(back_s) + data->cy - data->oy;
1666	xx = window_copy_find_length(wp, py);
1667
1668	while (px < xx) {
1669		gc = grid_peek_cell(back_s->grid, px, py);
1670		if ((gc->flags & (GRID_FLAG_PADDING|GRID_FLAG_UTF8)) == 0
1671		    && gc->data == data->jumpchar) {
1672
1673			window_copy_update_cursor(wp, px, data->cy);
1674			if (window_copy_update_selection(wp))
1675				window_copy_redraw_lines(wp, data->cy, 1);
1676			return;
1677		}
1678		px++;
1679	}
1680}
1681
1682void
1683window_copy_cursor_jump_back(struct window_pane *wp)
1684{
1685	struct window_copy_mode_data	*data = wp->modedata;
1686	struct screen			*back_s = data->backing;
1687	const struct grid_cell		*gc;
1688	u_int				 px, py;
1689
1690	px = data->cx;
1691	py = screen_hsize(back_s) + data->cy - data->oy;
1692
1693	if (px > 0)
1694		px--;
1695
1696	for (;;) {
1697		gc = grid_peek_cell(back_s->grid, px, py);
1698		if ((gc->flags & (GRID_FLAG_PADDING|GRID_FLAG_UTF8)) == 0
1699		    && gc->data == data->jumpchar) {
1700
1701			window_copy_update_cursor(wp, px, data->cy);
1702			if (window_copy_update_selection(wp))
1703				window_copy_redraw_lines(wp, data->cy, 1);
1704			return;
1705		}
1706		if (px == 0)
1707			break;
1708		px--;
1709	}
1710}
1711
1712void
1713window_copy_cursor_next_word(struct window_pane *wp, const char *separators)
1714{
1715	struct window_copy_mode_data	*data = wp->modedata;
1716	struct screen			*back_s = data->backing;
1717	u_int				 px, py, xx, yy;
1718	int				 expected = 0;
1719
1720	px = data->cx;
1721	py = screen_hsize(back_s) + data->cy - data->oy;
1722	xx = window_copy_find_length(wp, py);
1723	yy = screen_hsize(back_s) + screen_size_y(back_s) - 1;
1724
1725	/*
1726	 * First skip past any nonword characters and then any word characters.
1727	 *
1728	 * expected is initially set to 0 for the former and then 1 for the
1729	 * latter.
1730	 */
1731	do {
1732		while (px > xx ||
1733		    window_copy_in_set(wp, px, py, separators) == expected) {
1734			/* Move down if we're past the end of the line. */
1735			if (px > xx) {
1736				if (py == yy)
1737					return;
1738				window_copy_cursor_down(wp, 0);
1739				px = 0;
1740
1741				py = screen_hsize(back_s) + data->cy - data->oy;
1742				xx = window_copy_find_length(wp, py);
1743			} else
1744				px++;
1745		}
1746		expected = !expected;
1747	} while (expected == 1);
1748
1749	window_copy_update_cursor(wp, px, data->cy);
1750	if (window_copy_update_selection(wp))
1751		window_copy_redraw_lines(wp, data->cy, 1);
1752}
1753
1754void
1755window_copy_cursor_next_word_end(struct window_pane *wp, const char *separators)
1756{
1757	struct window_copy_mode_data	*data = wp->modedata;
1758	struct screen			*back_s = data->backing;
1759	u_int				 px, py, xx, yy;
1760	int				 expected = 1;
1761
1762	px = data->cx;
1763	py = screen_hsize(back_s) + data->cy - data->oy;
1764	xx = window_copy_find_length(wp, py);
1765	yy = screen_hsize(back_s) + screen_size_y(back_s) - 1;
1766
1767	/*
1768	 * First skip past any word characters, then any nonword characters.
1769	 *
1770	 * expected is initially set to 1 for the former and then 0 for the
1771	 * latter.
1772	 */
1773	do {
1774		while (px > xx ||
1775		    window_copy_in_set(wp, px, py, separators) == expected) {
1776			/* Move down if we're past the end of the line. */
1777			if (px > xx) {
1778				if (py == yy)
1779					return;
1780				window_copy_cursor_down(wp, 0);
1781				px = 0;
1782
1783				py = screen_hsize(back_s) + data->cy - data->oy;
1784				xx = window_copy_find_length(wp, py);
1785			} else
1786				px++;
1787		}
1788		expected = !expected;
1789	} while (expected == 0);
1790
1791	window_copy_update_cursor(wp, px, data->cy);
1792	if (window_copy_update_selection(wp))
1793		window_copy_redraw_lines(wp, data->cy, 1);
1794}
1795
1796/* Move to the previous place where a word begins. */
1797void
1798window_copy_cursor_previous_word(struct window_pane *wp, const char *separators)
1799{
1800	struct window_copy_mode_data	*data = wp->modedata;
1801	u_int				 px, py;
1802
1803	px = data->cx;
1804	py = screen_hsize(data->backing) + data->cy - data->oy;
1805
1806	/* Move back to the previous word character. */
1807	for (;;) {
1808		if (px > 0) {
1809			px--;
1810			if (!window_copy_in_set(wp, px, py, separators))
1811				break;
1812		} else {
1813			if (data->cy == 0 &&
1814			    (screen_hsize(data->backing) == 0 ||
1815			    data->oy >= screen_hsize(data->backing) - 1))
1816				goto out;
1817			window_copy_cursor_up(wp, 0);
1818
1819			py = screen_hsize(data->backing) + data->cy - data->oy;
1820			px = window_copy_find_length(wp, py);
1821		}
1822	}
1823
1824	/* Move back to the beginning of this word. */
1825	while (px > 0 && !window_copy_in_set(wp, px - 1, py, separators))
1826		px--;
1827
1828out:
1829	window_copy_update_cursor(wp, px, data->cy);
1830	if (window_copy_update_selection(wp))
1831		window_copy_redraw_lines(wp, data->cy, 1);
1832}
1833
1834void
1835window_copy_scroll_up(struct window_pane *wp, u_int ny)
1836{
1837	struct window_copy_mode_data	*data = wp->modedata;
1838	struct screen			*s = &data->screen;
1839	struct screen_write_ctx		 ctx;
1840
1841	if (data->oy < ny)
1842		ny = data->oy;
1843	if (ny == 0)
1844		return;
1845	data->oy -= ny;
1846
1847	screen_write_start(&ctx, wp, NULL);
1848	screen_write_cursormove(&ctx, 0, 0);
1849	screen_write_deleteline(&ctx, ny);
1850	window_copy_write_lines(wp, &ctx, screen_size_y(s) - ny, ny);
1851	window_copy_write_line(wp, &ctx, 0);
1852	if (screen_size_y(s) > 1)
1853		window_copy_write_line(wp, &ctx, 1);
1854	if (screen_size_y(s) > 3)
1855		window_copy_write_line(wp, &ctx, screen_size_y(s) - 2);
1856	if (s->sel.flag && screen_size_y(s) > ny) {
1857		window_copy_update_selection(wp);
1858		window_copy_write_line(wp, &ctx, screen_size_y(s) - ny - 1);
1859	}
1860	screen_write_cursormove(&ctx, data->cx, data->cy);
1861	window_copy_update_selection(wp);
1862	screen_write_stop(&ctx);
1863}
1864
1865void
1866window_copy_scroll_down(struct window_pane *wp, u_int ny)
1867{
1868	struct window_copy_mode_data	*data = wp->modedata;
1869	struct screen			*s = &data->screen;
1870	struct screen_write_ctx		 ctx;
1871
1872	if (ny > screen_hsize(data->backing))
1873		return;
1874
1875	if (data->oy > screen_hsize(data->backing) - ny)
1876		ny = screen_hsize(data->backing) - data->oy;
1877	if (ny == 0)
1878		return;
1879	data->oy += ny;
1880
1881	screen_write_start(&ctx, wp, NULL);
1882	screen_write_cursormove(&ctx, 0, 0);
1883	screen_write_insertline(&ctx, ny);
1884	window_copy_write_lines(wp, &ctx, 0, ny);
1885	if (s->sel.flag && screen_size_y(s) > ny) {
1886		window_copy_update_selection(wp);
1887		window_copy_write_line(wp, &ctx, ny);
1888	} else if (ny == 1) /* nuke position */
1889		window_copy_write_line(wp, &ctx, 1);
1890	screen_write_cursormove(&ctx, data->cx, data->cy);
1891	window_copy_update_selection(wp);
1892	screen_write_stop(&ctx);
1893}
1894
1895void
1896window_copy_rectangle_toggle(struct window_pane *wp)
1897{
1898	struct window_copy_mode_data	*data = wp->modedata;
1899	u_int				 px, py;
1900
1901	data->rectflag = !data->rectflag;
1902
1903	py = screen_hsize(data->backing) + data->cy - data->oy;
1904	px = window_copy_find_length(wp, py);
1905	if (data->cx > px)
1906		window_copy_update_cursor(wp, px, data->cy);
1907
1908	window_copy_update_selection(wp);
1909	window_copy_redraw_screen(wp);
1910}
1911