Only send end guard if begin was sent, from George Nachman.
[tmux-openbsd.git] / window-copy.c
blob61fa382a7647d4425b19ed3bcbb73f98949c846a
1 /* $OpenBSD$ */
3 /*
4 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
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.
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.
19 #include <sys/types.h>
21 #include <stdlib.h>
22 #include <string.h>
24 #include "tmux.h"
26 struct screen *window_copy_init(struct window_pane *);
27 void window_copy_free(struct window_pane *);
28 void window_copy_resize(struct window_pane *, u_int, u_int);
29 void window_copy_key(struct window_pane *, struct session *, int);
30 int window_copy_key_input(struct window_pane *, int);
31 int window_copy_key_numeric_prefix(struct window_pane *, int);
32 void window_copy_mouse(
33 struct window_pane *, struct session *, struct mouse_event *);
35 void window_copy_redraw_lines(struct window_pane *, u_int, u_int);
36 void window_copy_redraw_screen(struct window_pane *);
37 void window_copy_write_line(
38 struct window_pane *, struct screen_write_ctx *, u_int);
39 void window_copy_write_lines(
40 struct window_pane *, struct screen_write_ctx *, u_int, u_int);
42 void window_copy_scroll_to(struct window_pane *, u_int, u_int);
43 int window_copy_search_compare(
44 struct grid *, u_int, u_int, struct grid *, u_int);
45 int window_copy_search_lr(
46 struct grid *, struct grid *, u_int *, u_int, u_int, u_int);
47 int window_copy_search_rl(
48 struct grid *, struct grid *, u_int *, u_int, u_int, u_int);
49 void window_copy_search_up(struct window_pane *, const char *);
50 void window_copy_search_down(struct window_pane *, const char *);
51 void window_copy_goto_line(struct window_pane *, const char *);
52 void window_copy_update_cursor(struct window_pane *, u_int, u_int);
53 void window_copy_start_selection(struct window_pane *);
54 int window_copy_update_selection(struct window_pane *);
55 void *window_copy_get_selection(struct window_pane *, size_t *);
56 void window_copy_copy_buffer(struct window_pane *, int, void *, size_t);
57 void window_copy_copy_pipe(struct window_pane *, int, const char *);
58 void window_copy_copy_selection(struct window_pane *, int);
59 void window_copy_clear_selection(struct window_pane *);
60 void window_copy_copy_line(
61 struct window_pane *, char **, size_t *, u_int, u_int, u_int);
62 int window_copy_in_set(struct window_pane *, u_int, u_int, const char *);
63 u_int window_copy_find_length(struct window_pane *, u_int);
64 void window_copy_cursor_start_of_line(struct window_pane *);
65 void window_copy_cursor_back_to_indentation(struct window_pane *);
66 void window_copy_cursor_end_of_line(struct window_pane *);
67 void window_copy_cursor_left(struct window_pane *);
68 void window_copy_cursor_right(struct window_pane *);
69 void window_copy_cursor_up(struct window_pane *, int);
70 void window_copy_cursor_down(struct window_pane *, int);
71 void window_copy_cursor_jump(struct window_pane *);
72 void window_copy_cursor_jump_back(struct window_pane *);
73 void window_copy_cursor_jump_to(struct window_pane *);
74 void window_copy_cursor_jump_to_back(struct window_pane *);
75 void window_copy_cursor_next_word(struct window_pane *, const char *);
76 void window_copy_cursor_next_word_end(struct window_pane *, const char *);
77 void window_copy_cursor_previous_word(struct window_pane *, const char *);
78 void window_copy_scroll_up(struct window_pane *, u_int);
79 void window_copy_scroll_down(struct window_pane *, u_int);
80 void window_copy_rectangle_toggle(struct window_pane *);
82 const struct window_mode window_copy_mode = {
83 window_copy_init,
84 window_copy_free,
85 window_copy_resize,
86 window_copy_key,
87 window_copy_mouse,
88 NULL,
91 enum window_copy_input_type {
92 WINDOW_COPY_OFF,
93 WINDOW_COPY_NUMERICPREFIX,
94 WINDOW_COPY_SEARCHUP,
95 WINDOW_COPY_SEARCHDOWN,
96 WINDOW_COPY_JUMPFORWARD,
97 WINDOW_COPY_JUMPBACK,
98 WINDOW_COPY_JUMPTOFORWARD,
99 WINDOW_COPY_JUMPTOBACK,
100 WINDOW_COPY_GOTOLINE,
104 * Copy-mode's visible screen (the "screen" field) is filled from one of
105 * two sources: the original contents of the pane (used when we
106 * actually enter via the "copy-mode" command, to copy the contents of
107 * the current pane), or else a series of lines containing the output
108 * from an output-writing tmux command (such as any of the "show-*" or
109 * "list-*" commands).
111 * In either case, the full content of the copy-mode grid is pointed at
112 * by the "backing" field, and is copied into "screen" as needed (that
113 * is, when scrolling occurs). When copy-mode is backed by a pane,
114 * backing points directly at that pane's screen structure (&wp->base);
115 * when backed by a list of output-lines from a command, it points at
116 * a newly-allocated screen structure (which is deallocated when the
117 * mode ends).
119 struct window_copy_mode_data {
120 struct screen screen;
122 struct screen *backing;
123 int backing_written; /* backing display has started */
125 struct mode_key_data mdata;
127 u_int oy;
129 u_int selx;
130 u_int sely;
132 u_int rectflag; /* are we in rectangle copy mode? */
134 u_int cx;
135 u_int cy;
137 u_int lastcx; /* position in last line with content */
138 u_int lastsx; /* size of last line with content */
140 enum window_copy_input_type inputtype;
141 const char *inputprompt;
142 char *inputstr;
144 int numprefix;
146 enum window_copy_input_type searchtype;
147 char *searchstr;
149 enum window_copy_input_type jumptype;
150 char jumpchar;
153 struct screen *
154 window_copy_init(struct window_pane *wp)
156 struct window_copy_mode_data *data;
157 struct screen *s;
158 int keys;
160 wp->modedata = data = xmalloc(sizeof *data);
161 data->oy = 0;
162 data->cx = 0;
163 data->cy = 0;
165 data->lastcx = 0;
166 data->lastsx = 0;
168 data->backing_written = 0;
170 data->rectflag = 0;
172 data->inputtype = WINDOW_COPY_OFF;
173 data->inputprompt = NULL;
174 data->inputstr = xstrdup("");
175 data->numprefix = -1;
177 data->searchtype = WINDOW_COPY_OFF;
178 data->searchstr = NULL;
180 if (wp->fd != -1)
181 bufferevent_disable(wp->event, EV_READ|EV_WRITE);
183 data->jumptype = WINDOW_COPY_OFF;
184 data->jumpchar = '\0';
186 s = &data->screen;
187 screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
188 if (options_get_number(&wp->window->options, "mode-mouse"))
189 s->mode |= MODE_MOUSE_STANDARD;
191 keys = options_get_number(&wp->window->options, "mode-keys");
192 if (keys == MODEKEY_EMACS)
193 mode_key_init(&data->mdata, &mode_key_tree_emacs_copy);
194 else
195 mode_key_init(&data->mdata, &mode_key_tree_vi_copy);
197 data->backing = NULL;
199 return (s);
202 void
203 window_copy_init_from_pane(struct window_pane *wp)
205 struct window_copy_mode_data *data = wp->modedata;
206 struct screen *s = &data->screen;
207 struct screen_write_ctx ctx;
208 u_int i;
210 if (wp->mode != &window_copy_mode)
211 fatalx("not in copy mode");
213 data->backing = &wp->base;
214 data->cx = data->backing->cx;
215 data->cy = data->backing->cy;
217 s->cx = data->cx;
218 s->cy = data->cy;
220 screen_write_start(&ctx, NULL, s);
221 for (i = 0; i < screen_size_y(s); i++)
222 window_copy_write_line(wp, &ctx, i);
223 screen_write_cursormove(&ctx, data->cx, data->cy);
224 screen_write_stop(&ctx);
227 void
228 window_copy_init_for_output(struct window_pane *wp)
230 struct window_copy_mode_data *data = wp->modedata;
232 data->backing = xmalloc(sizeof *data->backing);
233 screen_init(data->backing, screen_size_x(&wp->base),
234 screen_size_y(&wp->base), UINT_MAX);
235 data->backing->mode &= ~MODE_WRAP;
238 void
239 window_copy_free(struct window_pane *wp)
241 struct window_copy_mode_data *data = wp->modedata;
243 if (wp->fd != -1)
244 bufferevent_enable(wp->event, EV_READ|EV_WRITE);
246 free(data->searchstr);
247 free(data->inputstr);
249 if (data->backing != &wp->base) {
250 screen_free(data->backing);
251 free(data->backing);
253 screen_free(&data->screen);
255 free(data);
258 void
259 window_copy_add(struct window_pane *wp, const char *fmt, ...)
261 va_list ap;
263 va_start(ap, fmt);
264 window_copy_vadd(wp, fmt, ap);
265 va_end(ap);
268 void
269 window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap)
271 struct window_copy_mode_data *data = wp->modedata;
272 struct screen *backing = data->backing;
273 struct screen_write_ctx back_ctx, ctx;
274 struct grid_cell gc;
275 int utf8flag;
276 u_int old_hsize;
278 if (backing == &wp->base)
279 return;
281 utf8flag = options_get_number(&wp->window->options, "utf8");
282 memcpy(&gc, &grid_default_cell, sizeof gc);
284 old_hsize = screen_hsize(data->backing);
285 screen_write_start(&back_ctx, NULL, backing);
286 if (data->backing_written) {
288 * On the second or later line, do a CRLF before writing
289 * (so it's on a new line).
291 screen_write_carriagereturn(&back_ctx);
292 screen_write_linefeed(&back_ctx, 0);
293 } else
294 data->backing_written = 1;
295 screen_write_vnputs(&back_ctx, 0, &gc, utf8flag, fmt, ap);
296 screen_write_stop(&back_ctx);
298 data->oy += screen_hsize(data->backing) - old_hsize;
300 screen_write_start(&ctx, wp, &data->screen);
303 * If the history has changed, draw the top line.
304 * (If there's any history at all, it has changed.)
306 if (screen_hsize(data->backing))
307 window_copy_redraw_lines(wp, 0, 1);
309 /* Write the line, if it's visible. */
310 if (backing->cy + data->oy < screen_size_y(backing))
311 window_copy_redraw_lines(wp, backing->cy, 1);
313 screen_write_stop(&ctx);
316 void
317 window_copy_pageup(struct window_pane *wp)
319 struct window_copy_mode_data *data = wp->modedata;
320 struct screen *s = &data->screen;
321 u_int n;
323 n = 1;
324 if (screen_size_y(s) > 2)
325 n = screen_size_y(s) - 2;
326 if (data->oy + n > screen_hsize(data->backing))
327 data->oy = screen_hsize(data->backing);
328 else
329 data->oy += n;
330 window_copy_update_selection(wp);
331 window_copy_redraw_screen(wp);
334 void
335 window_copy_resize(struct window_pane *wp, u_int sx, u_int sy)
337 struct window_copy_mode_data *data = wp->modedata;
338 struct screen *s = &data->screen;
339 struct screen_write_ctx ctx;
341 screen_resize(s, sx, sy, 0);
342 if (data->backing != &wp->base)
343 screen_resize(data->backing, sx, sy, 0);
345 if (data->cy > sy - 1)
346 data->cy = sy - 1;
347 if (data->cx > sx)
348 data->cx = sx;
349 if (data->oy > screen_hsize(data->backing))
350 data->oy = screen_hsize(data->backing);
352 window_copy_clear_selection(wp);
354 screen_write_start(&ctx, NULL, s);
355 window_copy_write_lines(wp, &ctx, 0, screen_size_y(s) - 1);
356 screen_write_stop(&ctx);
358 window_copy_redraw_screen(wp);
361 void
362 window_copy_key(struct window_pane *wp, struct session *sess, int key)
364 const char *word_separators;
365 struct window_copy_mode_data *data = wp->modedata;
366 struct screen *s = &data->screen;
367 u_int n;
368 int np, keys;
369 enum mode_key_cmd cmd;
370 const char *arg;
372 np = data->numprefix;
373 if (np <= 0)
374 np = 1;
376 if (data->inputtype == WINDOW_COPY_JUMPFORWARD ||
377 data->inputtype == WINDOW_COPY_JUMPBACK ||
378 data->inputtype == WINDOW_COPY_JUMPTOFORWARD ||
379 data->inputtype == WINDOW_COPY_JUMPTOBACK) {
380 /* Ignore keys with modifiers. */
381 if ((key & KEYC_MASK_MOD) == 0) {
382 data->jumpchar = key;
383 if (data->inputtype == WINDOW_COPY_JUMPFORWARD) {
384 for (; np != 0; np--)
385 window_copy_cursor_jump(wp);
386 } else if (data->inputtype == WINDOW_COPY_JUMPBACK) {
387 for (; np != 0; np--)
388 window_copy_cursor_jump_back(wp);
389 } else if (data->inputtype == WINDOW_COPY_JUMPTOFORWARD) {
390 for (; np != 0; np--)
391 window_copy_cursor_jump_to(wp);
392 } else if (data->inputtype == WINDOW_COPY_JUMPTOBACK) {
393 for (; np != 0; np--)
394 window_copy_cursor_jump_to_back(wp);
397 data->jumptype = data->inputtype;
398 data->inputtype = WINDOW_COPY_OFF;
399 window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
400 return;
401 } else if (data->inputtype == WINDOW_COPY_NUMERICPREFIX) {
402 if (window_copy_key_numeric_prefix(wp, key) == 0)
403 return;
404 data->inputtype = WINDOW_COPY_OFF;
405 window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
406 } else if (data->inputtype != WINDOW_COPY_OFF) {
407 if (window_copy_key_input(wp, key) != 0)
408 goto input_off;
409 return;
412 cmd = mode_key_lookup(&data->mdata, key, &arg);
413 switch (cmd) {
414 case MODEKEYCOPY_CANCEL:
415 window_pane_reset_mode(wp);
416 return;
417 case MODEKEYCOPY_LEFT:
418 for (; np != 0; np--)
419 window_copy_cursor_left(wp);
420 break;
421 case MODEKEYCOPY_RIGHT:
422 for (; np != 0; np--)
423 window_copy_cursor_right(wp);
424 break;
425 case MODEKEYCOPY_UP:
426 for (; np != 0; np--)
427 window_copy_cursor_up(wp, 0);
428 break;
429 case MODEKEYCOPY_DOWN:
430 for (; np != 0; np--)
431 window_copy_cursor_down(wp, 0);
432 break;
433 case MODEKEYCOPY_SCROLLUP:
434 for (; np != 0; np--)
435 window_copy_cursor_up(wp, 1);
436 break;
437 case MODEKEYCOPY_SCROLLDOWN:
438 for (; np != 0; np--)
439 window_copy_cursor_down(wp, 1);
440 break;
441 case MODEKEYCOPY_PREVIOUSPAGE:
442 for (; np != 0; np--)
443 window_copy_pageup(wp);
444 break;
445 case MODEKEYCOPY_NEXTPAGE:
446 n = 1;
447 if (screen_size_y(s) > 2)
448 n = screen_size_y(s) - 2;
449 for (; np != 0; np--) {
450 if (data->oy < n)
451 data->oy = 0;
452 else
453 data->oy -= n;
455 window_copy_update_selection(wp);
456 window_copy_redraw_screen(wp);
457 break;
458 case MODEKEYCOPY_HALFPAGEUP:
459 n = screen_size_y(s) / 2;
460 for (; np != 0; np--) {
461 if (data->oy + n > screen_hsize(data->backing))
462 data->oy = screen_hsize(data->backing);
463 else
464 data->oy += n;
466 window_copy_update_selection(wp);
467 window_copy_redraw_screen(wp);
468 break;
469 case MODEKEYCOPY_HALFPAGEDOWN:
470 n = screen_size_y(s) / 2;
471 for (; np != 0; np--) {
472 if (data->oy < n)
473 data->oy = 0;
474 else
475 data->oy -= n;
477 window_copy_update_selection(wp);
478 window_copy_redraw_screen(wp);
479 break;
480 case MODEKEYCOPY_TOPLINE:
481 data->cx = 0;
482 data->cy = 0;
483 window_copy_update_selection(wp);
484 window_copy_redraw_screen(wp);
485 break;
486 case MODEKEYCOPY_MIDDLELINE:
487 data->cx = 0;
488 data->cy = (screen_size_y(s) - 1) / 2;
489 window_copy_update_selection(wp);
490 window_copy_redraw_screen(wp);
491 break;
492 case MODEKEYCOPY_BOTTOMLINE:
493 data->cx = 0;
494 data->cy = screen_size_y(s) - 1;
495 window_copy_update_selection(wp);
496 window_copy_redraw_screen(wp);
497 break;
498 case MODEKEYCOPY_HISTORYTOP:
499 data->cx = 0;
500 data->cy = 0;
501 data->oy = screen_hsize(data->backing);
502 window_copy_update_selection(wp);
503 window_copy_redraw_screen(wp);
504 break;
505 case MODEKEYCOPY_HISTORYBOTTOM:
506 data->cx = 0;
507 data->cy = screen_size_y(s) - 1;
508 data->oy = 0;
509 window_copy_update_selection(wp);
510 window_copy_redraw_screen(wp);
511 break;
512 case MODEKEYCOPY_STARTSELECTION:
513 window_copy_start_selection(wp);
514 window_copy_redraw_screen(wp);
515 break;
516 case MODEKEYCOPY_COPYLINE:
517 case MODEKEYCOPY_SELECTLINE:
518 window_copy_cursor_start_of_line(wp);
519 /* FALLTHROUGH */
520 case MODEKEYCOPY_COPYENDOFLINE:
521 window_copy_start_selection(wp);
522 for (; np > 1; np--)
523 window_copy_cursor_down(wp, 0);
524 window_copy_cursor_end_of_line(wp);
525 window_copy_redraw_screen(wp);
527 /* If a copy command then copy the selection and exit. */
528 if (sess != NULL &&
529 (cmd == MODEKEYCOPY_COPYLINE ||
530 cmd == MODEKEYCOPY_COPYENDOFLINE)) {
531 window_copy_copy_selection(wp, -1);
532 window_pane_reset_mode(wp);
533 return;
535 break;
536 case MODEKEYCOPY_CLEARSELECTION:
537 window_copy_clear_selection(wp);
538 window_copy_redraw_screen(wp);
539 break;
540 case MODEKEYCOPY_COPYPIPE:
541 if (sess != NULL) {
542 window_copy_copy_pipe(wp, data->numprefix, arg);
543 window_pane_reset_mode(wp);
544 return;
546 break;
547 case MODEKEYCOPY_COPYSELECTION:
548 if (sess != NULL) {
549 window_copy_copy_selection(wp, data->numprefix);
550 window_pane_reset_mode(wp);
551 return;
553 break;
554 case MODEKEYCOPY_STARTOFLINE:
555 window_copy_cursor_start_of_line(wp);
556 break;
557 case MODEKEYCOPY_BACKTOINDENTATION:
558 window_copy_cursor_back_to_indentation(wp);
559 break;
560 case MODEKEYCOPY_ENDOFLINE:
561 window_copy_cursor_end_of_line(wp);
562 break;
563 case MODEKEYCOPY_NEXTSPACE:
564 for (; np != 0; np--)
565 window_copy_cursor_next_word(wp, " ");
566 break;
567 case MODEKEYCOPY_NEXTSPACEEND:
568 for (; np != 0; np--)
569 window_copy_cursor_next_word_end(wp, " ");
570 break;
571 case MODEKEYCOPY_NEXTWORD:
572 word_separators =
573 options_get_string(&sess->options, "word-separators");
574 for (; np != 0; np--)
575 window_copy_cursor_next_word(wp, word_separators);
576 break;
577 case MODEKEYCOPY_NEXTWORDEND:
578 word_separators =
579 options_get_string(&sess->options, "word-separators");
580 for (; np != 0; np--)
581 window_copy_cursor_next_word_end(wp, word_separators);
582 break;
583 case MODEKEYCOPY_PREVIOUSSPACE:
584 for (; np != 0; np--)
585 window_copy_cursor_previous_word(wp, " ");
586 break;
587 case MODEKEYCOPY_PREVIOUSWORD:
588 word_separators =
589 options_get_string(&sess->options, "word-separators");
590 for (; np != 0; np--)
591 window_copy_cursor_previous_word(wp, word_separators);
592 break;
593 case MODEKEYCOPY_JUMP:
594 data->inputtype = WINDOW_COPY_JUMPFORWARD;
595 data->inputprompt = "Jump Forward";
596 *data->inputstr = '\0';
597 window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
598 return; /* skip numprefix reset */
599 case MODEKEYCOPY_JUMPAGAIN:
600 if (data->jumptype == WINDOW_COPY_JUMPFORWARD) {
601 for (; np != 0; np--)
602 window_copy_cursor_jump(wp);
603 } else if (data->jumptype == WINDOW_COPY_JUMPBACK) {
604 for (; np != 0; np--)
605 window_copy_cursor_jump_back(wp);
606 } else if (data->jumptype == WINDOW_COPY_JUMPTOFORWARD) {
607 for (; np != 0; np--)
608 window_copy_cursor_jump_to(wp);
609 } else if (data->jumptype == WINDOW_COPY_JUMPTOBACK) {
610 for (; np != 0; np--)
611 window_copy_cursor_jump_to_back(wp);
613 break;
614 case MODEKEYCOPY_JUMPREVERSE:
615 if (data->jumptype == WINDOW_COPY_JUMPFORWARD) {
616 for (; np != 0; np--)
617 window_copy_cursor_jump_back(wp);
618 } else if (data->jumptype == WINDOW_COPY_JUMPBACK) {
619 for (; np != 0; np--)
620 window_copy_cursor_jump(wp);
621 } else if (data->jumptype == WINDOW_COPY_JUMPTOFORWARD) {
622 for (; np != 0; np--)
623 window_copy_cursor_jump_to_back(wp);
624 } else if (data->jumptype == WINDOW_COPY_JUMPTOBACK) {
625 for (; np != 0; np--)
626 window_copy_cursor_jump_to(wp);
628 break;
629 case MODEKEYCOPY_JUMPBACK:
630 data->inputtype = WINDOW_COPY_JUMPBACK;
631 data->inputprompt = "Jump Back";
632 *data->inputstr = '\0';
633 window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
634 return; /* skip numprefix reset */
635 case MODEKEYCOPY_JUMPTO:
636 data->inputtype = WINDOW_COPY_JUMPTOFORWARD;
637 data->inputprompt = "Jump To";
638 *data->inputstr = '\0';
639 window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
640 return; /* skip numprefix reset */
641 case MODEKEYCOPY_JUMPTOBACK:
642 data->inputtype = WINDOW_COPY_JUMPTOBACK;
643 data->inputprompt = "Jump To Back";
644 *data->inputstr = '\0';
645 window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
646 return; /* skip numprefix reset */
647 case MODEKEYCOPY_SEARCHUP:
648 data->inputtype = WINDOW_COPY_SEARCHUP;
649 data->inputprompt = "Search Up";
650 goto input_on;
651 case MODEKEYCOPY_SEARCHDOWN:
652 data->inputtype = WINDOW_COPY_SEARCHDOWN;
653 data->inputprompt = "Search Down";
654 goto input_on;
655 case MODEKEYCOPY_SEARCHAGAIN:
656 case MODEKEYCOPY_SEARCHREVERSE:
657 switch (data->searchtype) {
658 case WINDOW_COPY_OFF:
659 case WINDOW_COPY_GOTOLINE:
660 case WINDOW_COPY_JUMPFORWARD:
661 case WINDOW_COPY_JUMPBACK:
662 case WINDOW_COPY_JUMPTOFORWARD:
663 case WINDOW_COPY_JUMPTOBACK:
664 case WINDOW_COPY_NUMERICPREFIX:
665 break;
666 case WINDOW_COPY_SEARCHUP:
667 if (cmd == MODEKEYCOPY_SEARCHAGAIN) {
668 for (; np != 0; np--) {
669 window_copy_search_up(
670 wp, data->searchstr);
672 } else {
673 for (; np != 0; np--) {
674 window_copy_search_down(
675 wp, data->searchstr);
678 break;
679 case WINDOW_COPY_SEARCHDOWN:
680 if (cmd == MODEKEYCOPY_SEARCHAGAIN) {
681 for (; np != 0; np--) {
682 window_copy_search_down(
683 wp, data->searchstr);
685 } else {
686 for (; np != 0; np--) {
687 window_copy_search_up(
688 wp, data->searchstr);
691 break;
693 break;
694 case MODEKEYCOPY_GOTOLINE:
695 data->inputtype = WINDOW_COPY_GOTOLINE;
696 data->inputprompt = "Goto Line";
697 *data->inputstr = '\0';
698 goto input_on;
699 case MODEKEYCOPY_STARTNUMBERPREFIX:
700 key &= KEYC_MASK_KEY;
701 if (key >= '0' && key <= '9') {
702 data->inputtype = WINDOW_COPY_NUMERICPREFIX;
703 data->numprefix = 0;
704 window_copy_key_numeric_prefix(wp, key);
705 return;
707 break;
708 case MODEKEYCOPY_RECTANGLETOGGLE:
709 window_copy_rectangle_toggle(wp);
710 break;
711 default:
712 break;
715 data->numprefix = -1;
716 return;
718 input_on:
719 keys = options_get_number(&wp->window->options, "mode-keys");
720 if (keys == MODEKEY_EMACS)
721 mode_key_init(&data->mdata, &mode_key_tree_emacs_edit);
722 else
723 mode_key_init(&data->mdata, &mode_key_tree_vi_edit);
725 window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
726 return;
728 input_off:
729 keys = options_get_number(&wp->window->options, "mode-keys");
730 if (keys == MODEKEY_EMACS)
731 mode_key_init(&data->mdata, &mode_key_tree_emacs_copy);
732 else
733 mode_key_init(&data->mdata, &mode_key_tree_vi_copy);
735 data->inputtype = WINDOW_COPY_OFF;
736 data->inputprompt = NULL;
738 window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
742 window_copy_key_input(struct window_pane *wp, int key)
744 struct window_copy_mode_data *data = wp->modedata;
745 struct screen *s = &data->screen;
746 size_t inputlen;
747 int np;
749 switch (mode_key_lookup(&data->mdata, key, NULL)) {
750 case MODEKEYEDIT_CANCEL:
751 data->numprefix = -1;
752 return (-1);
753 case MODEKEYEDIT_BACKSPACE:
754 inputlen = strlen(data->inputstr);
755 if (inputlen > 0)
756 data->inputstr[inputlen - 1] = '\0';
757 break;
758 case MODEKEYEDIT_DELETELINE:
759 *data->inputstr = '\0';
760 break;
761 case MODEKEYEDIT_ENTER:
762 np = data->numprefix;
763 if (np <= 0)
764 np = 1;
766 switch (data->inputtype) {
767 case WINDOW_COPY_OFF:
768 case WINDOW_COPY_JUMPFORWARD:
769 case WINDOW_COPY_JUMPBACK:
770 case WINDOW_COPY_JUMPTOFORWARD:
771 case WINDOW_COPY_JUMPTOBACK:
772 case WINDOW_COPY_NUMERICPREFIX:
773 break;
774 case WINDOW_COPY_SEARCHUP:
775 for (; np != 0; np--)
776 window_copy_search_up(wp, data->inputstr);
777 data->searchtype = data->inputtype;
778 data->searchstr = xstrdup(data->inputstr);
779 break;
780 case WINDOW_COPY_SEARCHDOWN:
781 for (; np != 0; np--)
782 window_copy_search_down(wp, data->inputstr);
783 data->searchtype = data->inputtype;
784 data->searchstr = xstrdup(data->inputstr);
785 break;
786 case WINDOW_COPY_GOTOLINE:
787 window_copy_goto_line(wp, data->inputstr);
788 *data->inputstr = '\0';
789 break;
791 data->numprefix = -1;
792 return (1);
793 case MODEKEY_OTHER:
794 if (key < 32 || key > 126)
795 break;
796 inputlen = strlen(data->inputstr) + 2;
798 data->inputstr = xrealloc(data->inputstr, 1, inputlen);
799 data->inputstr[inputlen - 2] = key;
800 data->inputstr[inputlen - 1] = '\0';
801 break;
802 default:
803 break;
806 window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
807 return (0);
811 window_copy_key_numeric_prefix(struct window_pane *wp, int key)
813 struct window_copy_mode_data *data = wp->modedata;
814 struct screen *s = &data->screen;
816 key &= KEYC_MASK_KEY;
817 if (key < '0' || key > '9')
818 return (1);
820 if (data->numprefix >= 100) /* no more than three digits */
821 return (0);
822 data->numprefix = data->numprefix * 10 + key - '0';
824 window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
825 return (0);
828 void
829 window_copy_mouse(
830 struct window_pane *wp, struct session *sess, struct mouse_event *m)
832 struct window_copy_mode_data *data = wp->modedata;
833 struct screen *s = &data->screen;
834 u_int i;
836 if (m->x >= screen_size_x(s))
837 return;
838 if (m->y >= screen_size_y(s))
839 return;
841 /* If mouse wheel (buttons 4 and 5), scroll. */
842 if (m->event == MOUSE_EVENT_WHEEL) {
843 if (m->wheel == MOUSE_WHEEL_UP) {
844 for (i = 0; i < 5; i++)
845 window_copy_cursor_up(wp, 1);
846 } else if (m->wheel == MOUSE_WHEEL_DOWN) {
847 for (i = 0; i < 5; i++)
848 window_copy_cursor_down(wp, 1);
849 if (data->oy == 0)
850 goto reset_mode;
852 return;
856 * If already reading motion, move the cursor while buttons are still
857 * pressed, or stop the selection on their release.
859 if (s->mode & MODE_MOUSE_BUTTON) {
860 if (~m->event & MOUSE_EVENT_UP) {
861 window_copy_update_cursor(wp, m->x, m->y);
862 if (window_copy_update_selection(wp))
863 window_copy_redraw_screen(wp);
864 return;
866 goto reset_mode;
869 /* Otherwise if other buttons pressed, start selection and motion. */
870 if (~m->event & MOUSE_EVENT_UP) {
871 s->mode &= ~MODE_MOUSE_STANDARD;
872 s->mode |= MODE_MOUSE_BUTTON;
874 window_copy_update_cursor(wp, m->x, m->y);
875 window_copy_start_selection(wp);
876 window_copy_redraw_screen(wp);
879 return;
881 reset_mode:
882 s->mode &= ~MODE_MOUSE_BUTTON;
883 s->mode |= MODE_MOUSE_STANDARD;
884 if (sess != NULL) {
885 window_copy_copy_selection(wp, -1);
886 window_pane_reset_mode(wp);
890 void
891 window_copy_scroll_to(struct window_pane *wp, u_int px, u_int py)
893 struct window_copy_mode_data *data = wp->modedata;
894 struct grid *gd = data->backing->grid;
895 u_int offset, gap;
897 data->cx = px;
899 gap = gd->sy / 4;
900 if (py < gd->sy) {
901 offset = 0;
902 data->cy = py;
903 } else if (py > gd->hsize + gd->sy - gap) {
904 offset = gd->hsize;
905 data->cy = py - gd->hsize;
906 } else {
907 offset = py + gap - gd->sy;
908 data->cy = py - offset;
910 data->oy = gd->hsize - offset;
912 window_copy_update_selection(wp);
913 window_copy_redraw_screen(wp);
917 window_copy_search_compare(
918 struct grid *gd, u_int px, u_int py, struct grid *sgd, u_int spx)
920 const struct grid_cell *gc, *sgc;
921 struct utf8_data ud, sud;
923 gc = grid_peek_cell(gd, px, py);
924 grid_cell_get(gc, &ud);
925 sgc = grid_peek_cell(sgd, spx, 0);
926 grid_cell_get(sgc, &sud);
928 if (ud.size != sud.size || ud.width != sud.width)
929 return (0);
930 return (memcmp(ud.data, sud.data, ud.size) == 0);
934 window_copy_search_lr(struct grid *gd,
935 struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last)
937 u_int ax, bx, px;
939 for (ax = first; ax < last; ax++) {
940 if (ax + sgd->sx >= gd->sx)
941 break;
942 for (bx = 0; bx < sgd->sx; bx++) {
943 px = ax + bx;
944 if (!window_copy_search_compare(gd, px, py, sgd, bx))
945 break;
947 if (bx == sgd->sx) {
948 *ppx = ax;
949 return (1);
952 return (0);
956 window_copy_search_rl(struct grid *gd,
957 struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last)
959 u_int ax, bx, px;
961 for (ax = last + 1; ax > first; ax--) {
962 if (gd->sx - (ax - 1) < sgd->sx)
963 continue;
964 for (bx = 0; bx < sgd->sx; bx++) {
965 px = ax - 1 + bx;
966 if (!window_copy_search_compare(gd, px, py, sgd, bx))
967 break;
969 if (bx == sgd->sx) {
970 *ppx = ax - 1;
971 return (1);
974 return (0);
977 void
978 window_copy_search_up(struct window_pane *wp, const char *searchstr)
980 struct window_copy_mode_data *data = wp->modedata;
981 struct screen *s = data->backing, ss;
982 struct screen_write_ctx ctx;
983 struct grid *gd = s->grid, *sgd;
984 struct grid_cell gc;
985 size_t searchlen;
986 u_int i, last, fx, fy, px;
987 int utf8flag, n, wrapped, wrapflag;
989 if (*searchstr == '\0')
990 return;
991 utf8flag = options_get_number(&wp->window->options, "utf8");
992 wrapflag = options_get_number(&wp->window->options, "wrap-search");
993 searchlen = screen_write_strlen(utf8flag, "%s", searchstr);
995 screen_init(&ss, searchlen, 1, 0);
996 screen_write_start(&ctx, NULL, &ss);
997 memcpy(&gc, &grid_default_cell, sizeof gc);
998 screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr);
999 screen_write_stop(&ctx);
1001 fx = data->cx;
1002 fy = gd->hsize - data->oy + data->cy;
1004 if (fx == 0) {
1005 if (fy == 0)
1006 return;
1007 fx = gd->sx - 1;
1008 fy--;
1009 } else
1010 fx--;
1011 n = wrapped = 0;
1013 retry:
1014 sgd = ss.grid;
1015 for (i = fy + 1; i > 0; i--) {
1016 last = screen_size_x(s);
1017 if (i == fy + 1)
1018 last = fx;
1019 n = window_copy_search_rl(gd, sgd, &px, i - 1, 0, last);
1020 if (n) {
1021 window_copy_scroll_to(wp, px, i - 1);
1022 break;
1025 if (wrapflag && !n && !wrapped) {
1026 fx = gd->sx - 1;
1027 fy = gd->hsize + gd->sy - 1;
1028 wrapped = 1;
1029 goto retry;
1032 screen_free(&ss);
1035 void
1036 window_copy_search_down(struct window_pane *wp, const char *searchstr)
1038 struct window_copy_mode_data *data = wp->modedata;
1039 struct screen *s = data->backing, ss;
1040 struct screen_write_ctx ctx;
1041 struct grid *gd = s->grid, *sgd;
1042 struct grid_cell gc;
1043 size_t searchlen;
1044 u_int i, first, fx, fy, px;
1045 int utf8flag, n, wrapped, wrapflag;
1047 if (*searchstr == '\0')
1048 return;
1049 utf8flag = options_get_number(&wp->window->options, "utf8");
1050 wrapflag = options_get_number(&wp->window->options, "wrap-search");
1051 searchlen = screen_write_strlen(utf8flag, "%s", searchstr);
1053 screen_init(&ss, searchlen, 1, 0);
1054 screen_write_start(&ctx, NULL, &ss);
1055 memcpy(&gc, &grid_default_cell, sizeof gc);
1056 screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr);
1057 screen_write_stop(&ctx);
1059 fx = data->cx;
1060 fy = gd->hsize - data->oy + data->cy;
1062 if (fx == gd->sx - 1) {
1063 if (fy == gd->hsize + gd->sy)
1064 return;
1065 fx = 0;
1066 fy++;
1067 } else
1068 fx++;
1069 n = wrapped = 0;
1071 retry:
1072 sgd = ss.grid;
1073 for (i = fy + 1; i < gd->hsize + gd->sy + 1; i++) {
1074 first = 0;
1075 if (i == fy + 1)
1076 first = fx;
1077 n = window_copy_search_lr(gd, sgd, &px, i - 1, first, gd->sx);
1078 if (n) {
1079 window_copy_scroll_to(wp, px, i - 1);
1080 break;
1083 if (wrapflag && !n && !wrapped) {
1084 fx = 0;
1085 fy = 0;
1086 wrapped = 1;
1087 goto retry;
1090 screen_free(&ss);
1093 void
1094 window_copy_goto_line(struct window_pane *wp, const char *linestr)
1096 struct window_copy_mode_data *data = wp->modedata;
1097 const char *errstr;
1098 u_int lineno;
1100 lineno = strtonum(linestr, 0, screen_hsize(data->backing), &errstr);
1101 if (errstr != NULL)
1102 return;
1104 data->oy = lineno;
1105 window_copy_update_selection(wp);
1106 window_copy_redraw_screen(wp);
1109 void
1110 window_copy_write_line(
1111 struct window_pane *wp, struct screen_write_ctx *ctx, u_int py)
1113 struct window_copy_mode_data *data = wp->modedata;
1114 struct screen *s = &data->screen;
1115 struct options *oo = &wp->window->options;
1116 struct grid_cell gc;
1117 char hdr[32];
1118 size_t last, xoff = 0, size = 0;
1120 window_mode_attrs(&gc, oo);
1122 last = screen_size_y(s) - 1;
1123 if (py == 0) {
1124 size = xsnprintf(hdr, sizeof hdr,
1125 "[%u/%u]", data->oy, screen_hsize(data->backing));
1126 if (size > screen_size_x(s))
1127 size = screen_size_x(s);
1128 screen_write_cursormove(ctx, screen_size_x(s) - size, 0);
1129 screen_write_puts(ctx, &gc, "%s", hdr);
1130 } else if (py == last && data->inputtype != WINDOW_COPY_OFF) {
1131 if (data->inputtype == WINDOW_COPY_NUMERICPREFIX) {
1132 xoff = size = xsnprintf(hdr, sizeof hdr,
1133 "Repeat: %u", data->numprefix);
1134 } else {
1135 xoff = size = xsnprintf(hdr, sizeof hdr,
1136 "%s: %s", data->inputprompt, data->inputstr);
1138 screen_write_cursormove(ctx, 0, last);
1139 screen_write_puts(ctx, &gc, "%s", hdr);
1140 } else
1141 size = 0;
1143 screen_write_cursormove(ctx, xoff, py);
1144 screen_write_copy(ctx, data->backing, xoff,
1145 (screen_hsize(data->backing) - data->oy) + py,
1146 screen_size_x(s) - size, 1);
1148 if (py == data->cy && data->cx == screen_size_x(s)) {
1149 memcpy(&gc, &grid_default_cell, sizeof gc);
1150 screen_write_cursormove(ctx, screen_size_x(s) - 1, py);
1151 screen_write_putc(ctx, &gc, '$');
1155 void
1156 window_copy_write_lines(
1157 struct window_pane *wp, struct screen_write_ctx *ctx, u_int py, u_int ny)
1159 u_int yy;
1161 for (yy = py; yy < py + ny; yy++)
1162 window_copy_write_line(wp, ctx, py);
1165 void
1166 window_copy_redraw_lines(struct window_pane *wp, u_int py, u_int ny)
1168 struct window_copy_mode_data *data = wp->modedata;
1169 struct screen_write_ctx ctx;
1170 u_int i;
1172 screen_write_start(&ctx, wp, NULL);
1173 for (i = py; i < py + ny; i++)
1174 window_copy_write_line(wp, &ctx, i);
1175 screen_write_cursormove(&ctx, data->cx, data->cy);
1176 screen_write_stop(&ctx);
1179 void
1180 window_copy_redraw_screen(struct window_pane *wp)
1182 struct window_copy_mode_data *data = wp->modedata;
1184 window_copy_redraw_lines(wp, 0, screen_size_y(&data->screen));
1187 void
1188 window_copy_update_cursor(struct window_pane *wp, u_int cx, u_int cy)
1190 struct window_copy_mode_data *data = wp->modedata;
1191 struct screen *s = &data->screen;
1192 struct screen_write_ctx ctx;
1193 u_int old_cx, old_cy;
1195 old_cx = data->cx; old_cy = data->cy;
1196 data->cx = cx; data->cy = cy;
1197 if (old_cx == screen_size_x(s))
1198 window_copy_redraw_lines(wp, old_cy, 1);
1199 if (data->cx == screen_size_x(s))
1200 window_copy_redraw_lines(wp, data->cy, 1);
1201 else {
1202 screen_write_start(&ctx, wp, NULL);
1203 screen_write_cursormove(&ctx, data->cx, data->cy);
1204 screen_write_stop(&ctx);
1208 void
1209 window_copy_start_selection(struct window_pane *wp)
1211 struct window_copy_mode_data *data = wp->modedata;
1212 struct screen *s = &data->screen;
1214 data->selx = data->cx;
1215 data->sely = screen_hsize(data->backing) + data->cy - data->oy;
1217 s->sel.flag = 1;
1218 window_copy_update_selection(wp);
1222 window_copy_update_selection(struct window_pane *wp)
1224 struct window_copy_mode_data *data = wp->modedata;
1225 struct screen *s = &data->screen;
1226 struct options *oo = &wp->window->options;
1227 struct grid_cell gc;
1228 u_int sx, sy, ty, cy;
1230 if (!s->sel.flag)
1231 return (0);
1233 /* Set colours. */
1234 window_mode_attrs(&gc, oo);
1236 /* Find top of screen. */
1237 ty = screen_hsize(data->backing) - data->oy;
1239 /* Adjust the selection. */
1240 sx = data->selx;
1241 sy = data->sely;
1242 if (sy < ty) { /* above screen */
1243 if (!data->rectflag)
1244 sx = 0;
1245 sy = 0;
1246 } else if (sy > ty + screen_size_y(s) - 1) { /* below screen */
1247 if (!data->rectflag)
1248 sx = screen_size_x(s) - 1;
1249 sy = screen_size_y(s) - 1;
1250 } else
1251 sy -= ty;
1252 sy = screen_hsize(s) + sy;
1254 screen_set_selection(s,
1255 sx, sy, data->cx, screen_hsize(s) + data->cy, data->rectflag, &gc);
1257 if (data->rectflag) {
1259 * Can't rely on the caller to redraw the right lines for
1260 * rectangle selection - find the highest line and the number
1261 * of lines, and redraw just past that in both directions
1263 cy = data->cy;
1264 if (sy < cy)
1265 window_copy_redraw_lines(wp, sy, cy - sy + 1);
1266 else
1267 window_copy_redraw_lines(wp, cy, sy - cy + 1);
1270 return (1);
1273 void *
1274 window_copy_get_selection(struct window_pane *wp, size_t *len)
1276 struct window_copy_mode_data *data = wp->modedata;
1277 struct screen *s = &data->screen;
1278 char *buf;
1279 size_t off;
1280 u_int i, xx, yy, sx, sy, ex, ey;
1281 u_int firstsx, lastex, restex, restsx;
1282 int keys;
1284 if (!s->sel.flag)
1285 return (NULL);
1287 buf = xmalloc(1);
1288 off = 0;
1290 *buf = '\0';
1293 * The selection extends from selx,sely to (adjusted) cx,cy on
1294 * the base screen.
1297 /* Find start and end. */
1298 xx = data->cx;
1299 yy = screen_hsize(data->backing) + data->cy - data->oy;
1300 if (yy < data->sely || (yy == data->sely && xx < data->selx)) {
1301 sx = xx; sy = yy;
1302 ex = data->selx; ey = data->sely;
1303 } else {
1304 sx = data->selx; sy = data->sely;
1305 ex = xx; ey = yy;
1308 /* Trim ex to end of line. */
1309 xx = window_copy_find_length(wp, ey);
1310 if (ex > xx)
1311 ex = xx;
1314 * Deal with rectangle-copy if necessary; four situations: start of
1315 * first line (firstsx), end of last line (lastex), start (restsx) and
1316 * end (restex) of all other lines.
1318 xx = screen_size_x(s);
1321 * Behave according to mode-keys. If it is emacs, copy like emacs,
1322 * keeping the top-left-most character, and dropping the
1323 * bottom-right-most, regardless of copy direction. If it is vi, also
1324 * keep bottom-right-most character.
1326 keys = options_get_number(&wp->window->options, "mode-keys");
1327 if (data->rectflag) {
1329 * Need to ignore the column with the cursor in it, which for
1330 * rectangular copy means knowing which side the cursor is on.
1332 if (data->selx < data->cx) {
1333 /* Selection start is on the left. */
1334 if (keys == MODEKEY_EMACS) {
1335 lastex = data->cx;
1336 restex = data->cx;
1338 else {
1339 lastex = data->cx + 1;
1340 restex = data->cx + 1;
1342 firstsx = data->selx;
1343 restsx = data->selx;
1344 } else {
1345 /* Cursor is on the left. */
1346 lastex = data->selx + 1;
1347 restex = data->selx + 1;
1348 firstsx = data->cx;
1349 restsx = data->cx;
1351 } else {
1352 if (keys == MODEKEY_EMACS)
1353 lastex = ex;
1354 else
1355 lastex = ex + 1;
1356 restex = xx;
1357 firstsx = sx;
1358 restsx = 0;
1361 /* Copy the lines. */
1362 if (sy == ey)
1363 window_copy_copy_line(wp, &buf, &off, sy, firstsx, lastex);
1364 else {
1365 window_copy_copy_line(wp, &buf, &off, sy, firstsx, restex);
1366 if (ey - sy > 1) {
1367 for (i = sy + 1; i < ey; i++) {
1368 window_copy_copy_line(
1369 wp, &buf, &off, i, restsx, restex);
1372 window_copy_copy_line(wp, &buf, &off, ey, restsx, lastex);
1375 /* Don't bother if no data. */
1376 if (off == 0) {
1377 free(buf);
1378 return (NULL);
1380 *len = off - 1; /* remove final \n */
1381 return (buf);
1384 void
1385 window_copy_copy_buffer(struct window_pane *wp, int idx, void *buf, size_t len)
1387 u_int limit;
1389 if (options_get_number(&global_options, "set-clipboard"))
1390 screen_write_setselection(&wp->ictx.ctx, buf, len);
1392 if (idx == -1) {
1393 limit = options_get_number(&global_options, "buffer-limit");
1394 paste_add(&global_buffers, buf, len, limit);
1395 } else
1396 paste_replace(&global_buffers, idx, buf, len);
1399 void
1400 window_copy_copy_pipe(struct window_pane *wp, int idx, const char *arg)
1402 void* buf;
1403 size_t len;
1404 FILE* f;
1406 buf = window_copy_get_selection(wp, &len);
1407 if (buf == NULL)
1408 return;
1410 f = popen(arg, "w");
1411 if (f != NULL) {
1412 fwrite(buf, 1, len, f);
1413 pclose(f);
1416 window_copy_copy_buffer(wp, idx, buf, len);
1419 void
1420 window_copy_copy_selection(struct window_pane *wp, int idx)
1422 void* buf;
1423 size_t len;
1425 buf = window_copy_get_selection(wp, &len);
1426 if (buf == NULL)
1427 return;
1429 window_copy_copy_buffer(wp, idx, buf, len);
1432 void
1433 window_copy_copy_line(struct window_pane *wp,
1434 char **buf, size_t *off, u_int sy, u_int sx, u_int ex)
1436 struct window_copy_mode_data *data = wp->modedata;
1437 struct grid *gd = data->backing->grid;
1438 const struct grid_cell *gc;
1439 struct grid_line *gl;
1440 struct utf8_data ud;
1441 u_int i, xx, wrapped = 0;
1443 if (sx > ex)
1444 return;
1447 * Work out if the line was wrapped at the screen edge and all of it is
1448 * on screen.
1450 gl = &gd->linedata[sy];
1451 if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx)
1452 wrapped = 1;
1454 /* If the line was wrapped, don't strip spaces (use the full length). */
1455 if (wrapped)
1456 xx = gl->cellsize;
1457 else
1458 xx = window_copy_find_length(wp, sy);
1459 if (ex > xx)
1460 ex = xx;
1461 if (sx > xx)
1462 sx = xx;
1464 if (sx < ex) {
1465 for (i = sx; i < ex; i++) {
1466 gc = grid_peek_cell(gd, i, sy);
1467 if (gc->flags & GRID_FLAG_PADDING)
1468 continue;
1469 grid_cell_get(gc, &ud);
1471 *buf = xrealloc(*buf, 1, (*off) + ud.size);
1472 memcpy(*buf + *off, ud.data, ud.size);
1473 *off += ud.size;
1477 /* Only add a newline if the line wasn't wrapped. */
1478 if (!wrapped || ex != xx) {
1479 *buf = xrealloc(*buf, 1, (*off) + 1);
1480 (*buf)[(*off)++] = '\n';
1484 void
1485 window_copy_clear_selection(struct window_pane *wp)
1487 struct window_copy_mode_data *data = wp->modedata;
1488 u_int px, py;
1490 screen_clear_selection(&data->screen);
1492 py = screen_hsize(data->backing) + data->cy - data->oy;
1493 px = window_copy_find_length(wp, py);
1494 if (data->cx > px)
1495 window_copy_update_cursor(wp, px, data->cy);
1499 window_copy_in_set(struct window_pane *wp, u_int px, u_int py, const char *set)
1501 struct window_copy_mode_data *data = wp->modedata;
1502 const struct grid_cell *gc;
1503 struct utf8_data ud;
1505 gc = grid_peek_cell(data->backing->grid, px, py);
1506 grid_cell_get(gc, &ud);
1507 if (ud.size != 1 || gc->flags & GRID_FLAG_PADDING)
1508 return (0);
1509 if (*ud.data == 0x00 || *ud.data == 0x7f)
1510 return (0);
1511 return (strchr(set, *ud.data) != NULL);
1514 u_int
1515 window_copy_find_length(struct window_pane *wp, u_int py)
1517 struct window_copy_mode_data *data = wp->modedata;
1518 struct screen *s = data->backing;
1519 const struct grid_cell *gc;
1520 struct utf8_data ud;
1521 u_int px;
1524 * If the pane has been resized, its grid can contain old overlong
1525 * lines. grid_peek_cell does not allow accessing cells beyond the
1526 * width of the grid, and screen_write_copy treats them as spaces, so
1527 * ignore them here too.
1529 px = s->grid->linedata[py].cellsize;
1530 if (px > screen_size_x(s))
1531 px = screen_size_x(s);
1532 while (px > 0) {
1533 gc = grid_peek_cell(s->grid, px - 1, py);
1534 grid_cell_get(gc, &ud);
1535 if (ud.size != 1 || *ud.data != ' ')
1536 break;
1537 px--;
1539 return (px);
1542 void
1543 window_copy_cursor_start_of_line(struct window_pane *wp)
1545 struct window_copy_mode_data *data = wp->modedata;
1546 struct screen *back_s = data->backing;
1547 struct grid *gd = back_s->grid;
1548 u_int py;
1550 if (data->cx == 0) {
1551 py = screen_hsize(back_s) + data->cy - data->oy;
1552 while (py > 0 && gd->linedata[py-1].flags & GRID_LINE_WRAPPED) {
1553 window_copy_cursor_up(wp, 0);
1554 py = screen_hsize(back_s) + data->cy - data->oy;
1557 window_copy_update_cursor(wp, 0, data->cy);
1558 if (window_copy_update_selection(wp))
1559 window_copy_redraw_lines(wp, data->cy, 1);
1562 void
1563 window_copy_cursor_back_to_indentation(struct window_pane *wp)
1565 struct window_copy_mode_data *data = wp->modedata;
1566 u_int px, py, xx;
1567 const struct grid_cell *gc;
1568 struct utf8_data ud;
1570 px = 0;
1571 py = screen_hsize(data->backing) + data->cy - data->oy;
1572 xx = window_copy_find_length(wp, py);
1574 while (px < xx) {
1575 gc = grid_peek_cell(data->backing->grid, px, py);
1576 grid_cell_get(gc, &ud);
1577 if (ud.size != 1 || *ud.data != ' ')
1578 break;
1579 px++;
1582 window_copy_update_cursor(wp, px, data->cy);
1583 if (window_copy_update_selection(wp))
1584 window_copy_redraw_lines(wp, data->cy, 1);
1587 void
1588 window_copy_cursor_end_of_line(struct window_pane *wp)
1590 struct window_copy_mode_data *data = wp->modedata;
1591 struct screen *back_s = data->backing;
1592 struct grid *gd = back_s->grid;
1593 u_int px, py;
1595 py = screen_hsize(back_s) + data->cy - data->oy;
1596 px = window_copy_find_length(wp, py);
1598 if (data->cx == px) {
1599 if (data->screen.sel.flag && data->rectflag)
1600 px = screen_size_x(back_s);
1601 if (gd->linedata[py].flags & GRID_LINE_WRAPPED) {
1602 while (py < gd->sy + gd->hsize &&
1603 gd->linedata[py].flags & GRID_LINE_WRAPPED) {
1604 window_copy_cursor_down(wp, 0);
1605 py = screen_hsize(back_s)
1606 + data->cy - data->oy;
1608 px = window_copy_find_length(wp, py);
1611 window_copy_update_cursor(wp, px, data->cy);
1613 if (window_copy_update_selection(wp))
1614 window_copy_redraw_lines(wp, data->cy, 1);
1617 void
1618 window_copy_cursor_left(struct window_pane *wp)
1620 struct window_copy_mode_data *data = wp->modedata;
1622 if (data->cx == 0) {
1623 window_copy_cursor_up(wp, 0);
1624 window_copy_cursor_end_of_line(wp);
1625 } else {
1626 window_copy_update_cursor(wp, data->cx - 1, data->cy);
1627 if (window_copy_update_selection(wp))
1628 window_copy_redraw_lines(wp, data->cy, 1);
1632 void
1633 window_copy_cursor_right(struct window_pane *wp)
1635 struct window_copy_mode_data *data = wp->modedata;
1636 u_int px, py;
1638 if (data->screen.sel.flag && data->rectflag)
1639 px = screen_size_x(&data->screen);
1640 else {
1641 py = screen_hsize(data->backing) + data->cy - data->oy;
1642 px = window_copy_find_length(wp, py);
1645 if (data->cx >= px) {
1646 window_copy_cursor_start_of_line(wp);
1647 window_copy_cursor_down(wp, 0);
1648 } else {
1649 window_copy_update_cursor(wp, data->cx + 1, data->cy);
1650 if (window_copy_update_selection(wp))
1651 window_copy_redraw_lines(wp, data->cy, 1);
1655 void
1656 window_copy_cursor_up(struct window_pane *wp, int scroll_only)
1658 struct window_copy_mode_data *data = wp->modedata;
1659 struct screen *s = &data->screen;
1660 u_int ox, oy, px, py;
1662 oy = screen_hsize(data->backing) + data->cy - data->oy;
1663 ox = window_copy_find_length(wp, oy);
1664 if (data->cx != ox) {
1665 data->lastcx = data->cx;
1666 data->lastsx = ox;
1669 data->cx = data->lastcx;
1670 if (scroll_only || data->cy == 0) {
1671 window_copy_scroll_down(wp, 1);
1672 if (scroll_only) {
1673 if (data->cy == screen_size_y(s) - 1)
1674 window_copy_redraw_lines(wp, data->cy, 1);
1675 else
1676 window_copy_redraw_lines(wp, data->cy, 2);
1678 } else {
1679 window_copy_update_cursor(wp, data->cx, data->cy - 1);
1680 if (window_copy_update_selection(wp)) {
1681 if (data->cy == screen_size_y(s) - 1)
1682 window_copy_redraw_lines(wp, data->cy, 1);
1683 else
1684 window_copy_redraw_lines(wp, data->cy, 2);
1688 if (!data->screen.sel.flag || !data->rectflag) {
1689 py = screen_hsize(data->backing) + data->cy - data->oy;
1690 px = window_copy_find_length(wp, py);
1691 if ((data->cx >= data->lastsx && data->cx != px) ||
1692 data->cx > px)
1693 window_copy_cursor_end_of_line(wp);
1697 void
1698 window_copy_cursor_down(struct window_pane *wp, int scroll_only)
1700 struct window_copy_mode_data *data = wp->modedata;
1701 struct screen *s = &data->screen;
1702 u_int ox, oy, px, py;
1704 oy = screen_hsize(data->backing) + data->cy - data->oy;
1705 ox = window_copy_find_length(wp, oy);
1706 if (data->cx != ox) {
1707 data->lastcx = data->cx;
1708 data->lastsx = ox;
1711 data->cx = data->lastcx;
1712 if (scroll_only || data->cy == screen_size_y(s) - 1) {
1713 window_copy_scroll_up(wp, 1);
1714 if (scroll_only && data->cy > 0)
1715 window_copy_redraw_lines(wp, data->cy - 1, 2);
1716 } else {
1717 window_copy_update_cursor(wp, data->cx, data->cy + 1);
1718 if (window_copy_update_selection(wp))
1719 window_copy_redraw_lines(wp, data->cy - 1, 2);
1722 if (!data->screen.sel.flag || !data->rectflag) {
1723 py = screen_hsize(data->backing) + data->cy - data->oy;
1724 px = window_copy_find_length(wp, py);
1725 if ((data->cx >= data->lastsx && data->cx != px) ||
1726 data->cx > px)
1727 window_copy_cursor_end_of_line(wp);
1731 void
1732 window_copy_cursor_jump(struct window_pane *wp)
1734 struct window_copy_mode_data *data = wp->modedata;
1735 struct screen *back_s = data->backing;
1736 const struct grid_cell *gc;
1737 struct utf8_data ud;
1738 u_int px, py, xx;
1740 px = data->cx + 1;
1741 py = screen_hsize(back_s) + data->cy - data->oy;
1742 xx = window_copy_find_length(wp, py);
1744 while (px < xx) {
1745 gc = grid_peek_cell(back_s->grid, px, py);
1746 grid_cell_get(gc, &ud);
1747 if (!(gc->flags & GRID_FLAG_PADDING) &&
1748 ud.size == 1 && *ud.data == data->jumpchar) {
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 return;
1754 px++;
1758 void
1759 window_copy_cursor_jump_back(struct window_pane *wp)
1761 struct window_copy_mode_data *data = wp->modedata;
1762 struct screen *back_s = data->backing;
1763 const struct grid_cell *gc;
1764 struct utf8_data ud;
1765 u_int px, py;
1767 px = data->cx;
1768 py = screen_hsize(back_s) + data->cy - data->oy;
1770 if (px > 0)
1771 px--;
1773 for (;;) {
1774 gc = grid_peek_cell(back_s->grid, px, py);
1775 grid_cell_get(gc, &ud);
1776 if (!(gc->flags & GRID_FLAG_PADDING) &&
1777 ud.size == 1 && *ud.data == data->jumpchar) {
1778 window_copy_update_cursor(wp, px, data->cy);
1779 if (window_copy_update_selection(wp))
1780 window_copy_redraw_lines(wp, data->cy, 1);
1781 return;
1783 if (px == 0)
1784 break;
1785 px--;
1789 void
1790 window_copy_cursor_jump_to(struct window_pane *wp)
1792 struct window_copy_mode_data *data = wp->modedata;
1793 struct screen *back_s = data->backing;
1794 const struct grid_cell *gc;
1795 struct utf8_data ud;
1796 u_int px, py, xx;
1798 px = data->cx + 1;
1799 py = screen_hsize(back_s) + data->cy - data->oy;
1800 xx = window_copy_find_length(wp, py);
1802 while (px < xx) {
1803 gc = grid_peek_cell(back_s->grid, px, py);
1804 grid_cell_get(gc, &ud);
1805 if (!(gc->flags & GRID_FLAG_PADDING) &&
1806 ud.size == 1 && *ud.data == data->jumpchar) {
1807 window_copy_update_cursor(wp, px - 1, data->cy);
1808 if (window_copy_update_selection(wp))
1809 window_copy_redraw_lines(wp, data->cy, 1);
1810 return;
1812 px++;
1816 void
1817 window_copy_cursor_jump_to_back(struct window_pane *wp)
1819 struct window_copy_mode_data *data = wp->modedata;
1820 struct screen *back_s = data->backing;
1821 const struct grid_cell *gc;
1822 struct utf8_data ud;
1823 u_int px, py;
1825 px = data->cx;
1826 py = screen_hsize(back_s) + data->cy - data->oy;
1828 if (px > 0)
1829 px--;
1831 for (;;) {
1832 gc = grid_peek_cell(back_s->grid, px, py);
1833 grid_cell_get(gc, &ud);
1834 if (!(gc->flags & GRID_FLAG_PADDING) &&
1835 ud.size == 1 && *ud.data == data->jumpchar) {
1836 window_copy_update_cursor(wp, px + 1, data->cy);
1837 if (window_copy_update_selection(wp))
1838 window_copy_redraw_lines(wp, data->cy, 1);
1839 return;
1841 if (px == 0)
1842 break;
1843 px--;
1847 void
1848 window_copy_cursor_next_word(struct window_pane *wp, const char *separators)
1850 struct window_copy_mode_data *data = wp->modedata;
1851 struct screen *back_s = data->backing;
1852 u_int px, py, xx, yy;
1853 int expected = 0;
1855 px = data->cx;
1856 py = screen_hsize(back_s) + data->cy - data->oy;
1857 xx = window_copy_find_length(wp, py);
1858 yy = screen_hsize(back_s) + screen_size_y(back_s) - 1;
1861 * First skip past any nonword characters and then any word characters.
1863 * expected is initially set to 0 for the former and then 1 for the
1864 * latter.
1866 do {
1867 while (px > xx ||
1868 window_copy_in_set(wp, px, py, separators) == expected) {
1869 /* Move down if we're past the end of the line. */
1870 if (px > xx) {
1871 if (py == yy)
1872 return;
1873 window_copy_cursor_down(wp, 0);
1874 px = 0;
1876 py = screen_hsize(back_s) + data->cy - data->oy;
1877 xx = window_copy_find_length(wp, py);
1878 } else
1879 px++;
1881 expected = !expected;
1882 } while (expected == 1);
1884 window_copy_update_cursor(wp, px, data->cy);
1885 if (window_copy_update_selection(wp))
1886 window_copy_redraw_lines(wp, data->cy, 1);
1889 void
1890 window_copy_cursor_next_word_end(struct window_pane *wp, const char *separators)
1892 struct window_copy_mode_data *data = wp->modedata;
1893 struct screen *back_s = data->backing;
1894 u_int px, py, xx, yy;
1895 int expected = 1;
1897 px = data->cx;
1898 py = screen_hsize(back_s) + data->cy - data->oy;
1899 xx = window_copy_find_length(wp, py);
1900 yy = screen_hsize(back_s) + screen_size_y(back_s) - 1;
1903 * First skip past any word characters, then any nonword characters.
1905 * expected is initially set to 1 for the former and then 0 for the
1906 * latter.
1908 do {
1909 while (px > xx ||
1910 window_copy_in_set(wp, px, py, separators) == expected) {
1911 /* Move down if we're past the end of the line. */
1912 if (px > xx) {
1913 if (py == yy)
1914 return;
1915 window_copy_cursor_down(wp, 0);
1916 px = 0;
1918 py = screen_hsize(back_s) + data->cy - data->oy;
1919 xx = window_copy_find_length(wp, py);
1920 } else
1921 px++;
1923 expected = !expected;
1924 } while (expected == 0);
1926 window_copy_update_cursor(wp, px, data->cy);
1927 if (window_copy_update_selection(wp))
1928 window_copy_redraw_lines(wp, data->cy, 1);
1931 /* Move to the previous place where a word begins. */
1932 void
1933 window_copy_cursor_previous_word(struct window_pane *wp, const char *separators)
1935 struct window_copy_mode_data *data = wp->modedata;
1936 u_int px, py;
1938 px = data->cx;
1939 py = screen_hsize(data->backing) + data->cy - data->oy;
1941 /* Move back to the previous word character. */
1942 for (;;) {
1943 if (px > 0) {
1944 px--;
1945 if (!window_copy_in_set(wp, px, py, separators))
1946 break;
1947 } else {
1948 if (data->cy == 0 &&
1949 (screen_hsize(data->backing) == 0 ||
1950 data->oy >= screen_hsize(data->backing) - 1))
1951 goto out;
1952 window_copy_cursor_up(wp, 0);
1954 py = screen_hsize(data->backing) + data->cy - data->oy;
1955 px = window_copy_find_length(wp, py);
1959 /* Move back to the beginning of this word. */
1960 while (px > 0 && !window_copy_in_set(wp, px - 1, py, separators))
1961 px--;
1963 out:
1964 window_copy_update_cursor(wp, px, data->cy);
1965 if (window_copy_update_selection(wp))
1966 window_copy_redraw_lines(wp, data->cy, 1);
1969 void
1970 window_copy_scroll_up(struct window_pane *wp, u_int ny)
1972 struct window_copy_mode_data *data = wp->modedata;
1973 struct screen *s = &data->screen;
1974 struct screen_write_ctx ctx;
1976 if (data->oy < ny)
1977 ny = data->oy;
1978 if (ny == 0)
1979 return;
1980 data->oy -= ny;
1982 screen_write_start(&ctx, wp, NULL);
1983 screen_write_cursormove(&ctx, 0, 0);
1984 screen_write_deleteline(&ctx, ny);
1985 window_copy_write_lines(wp, &ctx, screen_size_y(s) - ny, ny);
1986 window_copy_write_line(wp, &ctx, 0);
1987 if (screen_size_y(s) > 1)
1988 window_copy_write_line(wp, &ctx, 1);
1989 if (screen_size_y(s) > 3)
1990 window_copy_write_line(wp, &ctx, screen_size_y(s) - 2);
1991 if (s->sel.flag && screen_size_y(s) > ny) {
1992 window_copy_update_selection(wp);
1993 window_copy_write_line(wp, &ctx, screen_size_y(s) - ny - 1);
1995 screen_write_cursormove(&ctx, data->cx, data->cy);
1996 window_copy_update_selection(wp);
1997 screen_write_stop(&ctx);
2000 void
2001 window_copy_scroll_down(struct window_pane *wp, u_int ny)
2003 struct window_copy_mode_data *data = wp->modedata;
2004 struct screen *s = &data->screen;
2005 struct screen_write_ctx ctx;
2007 if (ny > screen_hsize(data->backing))
2008 return;
2010 if (data->oy > screen_hsize(data->backing) - ny)
2011 ny = screen_hsize(data->backing) - data->oy;
2012 if (ny == 0)
2013 return;
2014 data->oy += ny;
2016 screen_write_start(&ctx, wp, NULL);
2017 screen_write_cursormove(&ctx, 0, 0);
2018 screen_write_insertline(&ctx, ny);
2019 window_copy_write_lines(wp, &ctx, 0, ny);
2020 if (s->sel.flag && screen_size_y(s) > ny) {
2021 window_copy_update_selection(wp);
2022 window_copy_write_line(wp, &ctx, ny);
2023 } else if (ny == 1) /* nuke position */
2024 window_copy_write_line(wp, &ctx, 1);
2025 screen_write_cursormove(&ctx, data->cx, data->cy);
2026 window_copy_update_selection(wp);
2027 screen_write_stop(&ctx);
2030 void
2031 window_copy_rectangle_toggle(struct window_pane *wp)
2033 struct window_copy_mode_data *data = wp->modedata;
2034 u_int px, py;
2036 data->rectflag = !data->rectflag;
2038 py = screen_hsize(data->backing) + data->cy - data->oy;
2039 px = window_copy_find_length(wp, py);
2040 if (data->cx > px)
2041 window_copy_update_cursor(wp, px, data->cy);
2043 window_copy_update_selection(wp);
2044 window_copy_redraw_screen(wp);