GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/tmux/window-copy.c Lines: 0 1157 0.0 %
Date: 2016-12-06 Branches: 0 791 0.0 %

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