Accept some emacs control keys in vi normal mode, from Alexis
[tmux-openbsd.git] / window-copy.c
blobec6a2f4e8927bb0940de0adabff7d77c17d97b96
1 /* $OpenBSD$ */
3 /*
4 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
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 <ctype.h>
22 #include <regex.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <time.h>
27 #include "tmux.h"
29 struct window_copy_mode_data;
31 static const char *window_copy_key_table(struct window_mode_entry *);
32 static void window_copy_command(struct window_mode_entry *, struct client *,
33 struct session *, struct winlink *, struct args *,
34 struct mouse_event *);
35 static struct screen *window_copy_init(struct window_mode_entry *,
36 struct cmd_find_state *, struct args *);
37 static struct screen *window_copy_view_init(struct window_mode_entry *,
38 struct cmd_find_state *, struct args *);
39 static void window_copy_free(struct window_mode_entry *);
40 static void window_copy_resize(struct window_mode_entry *, u_int, u_int);
41 static void window_copy_formats(struct window_mode_entry *,
42 struct format_tree *);
43 static void window_copy_pageup1(struct window_mode_entry *, int);
44 static int window_copy_pagedown(struct window_mode_entry *, int, int);
45 static void window_copy_next_paragraph(struct window_mode_entry *);
46 static void window_copy_previous_paragraph(struct window_mode_entry *);
47 static void window_copy_redraw_selection(struct window_mode_entry *, u_int);
48 static void window_copy_redraw_lines(struct window_mode_entry *, u_int,
49 u_int);
50 static void window_copy_redraw_screen(struct window_mode_entry *);
51 static void window_copy_write_line(struct window_mode_entry *,
52 struct screen_write_ctx *, u_int);
53 static void window_copy_write_lines(struct window_mode_entry *,
54 struct screen_write_ctx *, u_int, u_int);
55 static char *window_copy_match_at_cursor(struct window_copy_mode_data *);
56 static void window_copy_scroll_to(struct window_mode_entry *, u_int, u_int,
57 int);
58 static int window_copy_search_compare(struct grid *, u_int, u_int,
59 struct grid *, u_int, int);
60 static int window_copy_search_lr(struct grid *, struct grid *, u_int *,
61 u_int, u_int, u_int, int);
62 static int window_copy_search_rl(struct grid *, struct grid *, u_int *,
63 u_int, u_int, u_int, int);
64 static int window_copy_last_regex(struct grid *, u_int, u_int, u_int,
65 u_int, u_int *, u_int *, const char *, const regex_t *,
66 int);
67 static int window_copy_search_mark_at(struct window_copy_mode_data *,
68 u_int, u_int, u_int *);
69 static char *window_copy_stringify(struct grid *, u_int, u_int, u_int,
70 char *, u_int *);
71 static void window_copy_cstrtocellpos(struct grid *, u_int, u_int *,
72 u_int *, const char *);
73 static int window_copy_search_marks(struct window_mode_entry *,
74 struct screen *, int, int);
75 static void window_copy_clear_marks(struct window_mode_entry *);
76 static int window_copy_is_lowercase(const char *);
77 static void window_copy_search_back_overlap(struct grid *, regex_t *,
78 u_int *, u_int *, u_int *, u_int);
79 static int window_copy_search_jump(struct window_mode_entry *,
80 struct grid *, struct grid *, u_int, u_int, u_int, int, int,
81 int, int);
82 static int window_copy_search(struct window_mode_entry *, int, int);
83 static int window_copy_search_up(struct window_mode_entry *, int);
84 static int window_copy_search_down(struct window_mode_entry *, int);
85 static void window_copy_goto_line(struct window_mode_entry *, const char *);
86 static void window_copy_update_cursor(struct window_mode_entry *, u_int,
87 u_int);
88 static void window_copy_start_selection(struct window_mode_entry *);
89 static int window_copy_adjust_selection(struct window_mode_entry *,
90 u_int *, u_int *);
91 static int window_copy_set_selection(struct window_mode_entry *, int, int);
92 static int window_copy_update_selection(struct window_mode_entry *, int,
93 int);
94 static void window_copy_synchronize_cursor(struct window_mode_entry *, int);
95 static void *window_copy_get_selection(struct window_mode_entry *, size_t *);
96 static void window_copy_copy_buffer(struct window_mode_entry *,
97 const char *, void *, size_t);
98 static void window_copy_pipe(struct window_mode_entry *,
99 struct session *, const char *);
100 static void window_copy_copy_pipe(struct window_mode_entry *,
101 struct session *, const char *, const char *);
102 static void window_copy_copy_selection(struct window_mode_entry *,
103 const char *);
104 static void window_copy_append_selection(struct window_mode_entry *);
105 static void window_copy_clear_selection(struct window_mode_entry *);
106 static void window_copy_copy_line(struct window_mode_entry *, char **,
107 size_t *, u_int, u_int, u_int);
108 static int window_copy_in_set(struct window_mode_entry *, u_int, u_int,
109 const char *);
110 static u_int window_copy_find_length(struct window_mode_entry *, u_int);
111 static void window_copy_cursor_start_of_line(struct window_mode_entry *);
112 static void window_copy_cursor_back_to_indentation(
113 struct window_mode_entry *);
114 static void window_copy_cursor_end_of_line(struct window_mode_entry *);
115 static void window_copy_other_end(struct window_mode_entry *);
116 static void window_copy_cursor_left(struct window_mode_entry *);
117 static void window_copy_cursor_right(struct window_mode_entry *, int);
118 static void window_copy_cursor_up(struct window_mode_entry *, int);
119 static void window_copy_cursor_down(struct window_mode_entry *, int);
120 static void window_copy_cursor_jump(struct window_mode_entry *);
121 static void window_copy_cursor_jump_back(struct window_mode_entry *);
122 static void window_copy_cursor_jump_to(struct window_mode_entry *);
123 static void window_copy_cursor_jump_to_back(struct window_mode_entry *);
124 static void window_copy_cursor_next_word(struct window_mode_entry *,
125 const char *);
126 static void window_copy_cursor_next_word_end_pos(struct window_mode_entry *,
127 const char *, u_int *, u_int *);
128 static void window_copy_cursor_next_word_end(struct window_mode_entry *,
129 const char *, int);
130 static void window_copy_cursor_previous_word_pos(struct window_mode_entry *,
131 const char *, u_int *, u_int *);
132 static void window_copy_cursor_previous_word(struct window_mode_entry *,
133 const char *, int);
134 static void window_copy_scroll_up(struct window_mode_entry *, u_int);
135 static void window_copy_scroll_down(struct window_mode_entry *, u_int);
136 static void window_copy_rectangle_set(struct window_mode_entry *, int);
137 static void window_copy_move_mouse(struct mouse_event *);
138 static void window_copy_drag_update(struct client *, struct mouse_event *);
139 static void window_copy_drag_release(struct client *, struct mouse_event *);
140 static void window_copy_jump_to_mark(struct window_mode_entry *);
141 static void window_copy_acquire_cursor_up(struct window_mode_entry *,
142 u_int, u_int, u_int, u_int, u_int);
143 static void window_copy_acquire_cursor_down(struct window_mode_entry *,
144 u_int, u_int, u_int, u_int, u_int, u_int, int);
146 const struct window_mode window_copy_mode = {
147 .name = "copy-mode",
149 .init = window_copy_init,
150 .free = window_copy_free,
151 .resize = window_copy_resize,
152 .key_table = window_copy_key_table,
153 .command = window_copy_command,
154 .formats = window_copy_formats,
157 const struct window_mode window_view_mode = {
158 .name = "view-mode",
160 .init = window_copy_view_init,
161 .free = window_copy_free,
162 .resize = window_copy_resize,
163 .key_table = window_copy_key_table,
164 .command = window_copy_command,
165 .formats = window_copy_formats,
168 enum {
169 WINDOW_COPY_OFF,
170 WINDOW_COPY_SEARCHUP,
171 WINDOW_COPY_SEARCHDOWN,
172 WINDOW_COPY_JUMPFORWARD,
173 WINDOW_COPY_JUMPBACKWARD,
174 WINDOW_COPY_JUMPTOFORWARD,
175 WINDOW_COPY_JUMPTOBACKWARD,
178 enum {
179 WINDOW_COPY_REL_POS_ABOVE,
180 WINDOW_COPY_REL_POS_ON_SCREEN,
181 WINDOW_COPY_REL_POS_BELOW,
184 enum window_copy_cmd_action {
185 WINDOW_COPY_CMD_NOTHING,
186 WINDOW_COPY_CMD_REDRAW,
187 WINDOW_COPY_CMD_CANCEL,
190 enum window_copy_cmd_clear {
191 WINDOW_COPY_CMD_CLEAR_ALWAYS,
192 WINDOW_COPY_CMD_CLEAR_NEVER,
193 WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
196 struct window_copy_cmd_state {
197 struct window_mode_entry *wme;
198 struct args *args;
199 struct mouse_event *m;
201 struct client *c;
202 struct session *s;
203 struct winlink *wl;
207 * Copy mode's visible screen (the "screen" field) is filled from one of two
208 * sources: the original contents of the pane (used when we actually enter via
209 * the "copy-mode" command, to copy the contents of the current pane), or else
210 * a series of lines containing the output from an output-writing tmux command
211 * (such as any of the "show-*" or "list-*" commands).
213 * In either case, the full content of the copy-mode grid is pointed at by the
214 * "backing" field, and is copied into "screen" as needed (that is, when
215 * scrolling occurs). When copy-mode is backed by a pane, backing points
216 * directly at that pane's screen structure (&wp->base); when backed by a list
217 * of output-lines from a command, it points at a newly-allocated screen
218 * structure (which is deallocated when the mode ends).
220 struct window_copy_mode_data {
221 struct screen screen;
223 struct screen *backing;
224 int backing_written; /* backing display started */
226 int viewmode; /* view mode entered */
228 u_int oy; /* number of lines scrolled up */
230 u_int selx; /* beginning of selection */
231 u_int sely;
233 u_int endselx; /* end of selection */
234 u_int endsely;
236 enum {
237 CURSORDRAG_NONE, /* selection is independent of cursor */
238 CURSORDRAG_ENDSEL, /* end is synchronized with cursor */
239 CURSORDRAG_SEL, /* start is synchronized with cursor */
240 } cursordrag;
242 int modekeys;
243 enum {
244 LINE_SEL_NONE,
245 LINE_SEL_LEFT_RIGHT,
246 LINE_SEL_RIGHT_LEFT,
247 } lineflag; /* line selection mode */
248 int rectflag; /* in rectangle copy mode? */
249 int scroll_exit; /* exit on scroll to end? */
250 int hide_position; /* hide position marker */
252 enum {
253 SEL_CHAR, /* select one char at a time */
254 SEL_WORD, /* select one word at a time */
255 SEL_LINE, /* select one line at a time */
256 } selflag;
258 const char *separators; /* word separators */
260 u_int dx; /* drag start position */
261 u_int dy;
263 u_int selrx; /* selection reset positions */
264 u_int selry;
265 u_int endselrx;
266 u_int endselry;
268 u_int cx;
269 u_int cy;
271 u_int lastcx; /* position in last line w/ content */
272 u_int lastsx; /* size of last line w/ content */
274 u_int mx; /* mark position */
275 u_int my;
276 int showmark;
278 int searchtype;
279 int searchdirection;
280 int searchregex;
281 char *searchstr;
282 u_char *searchmark;
283 int searchcount;
284 int searchmore;
285 int searchall;
286 int searchx;
287 int searchy;
288 int searcho;
289 u_char searchgen;
291 int timeout; /* search has timed out */
292 #define WINDOW_COPY_SEARCH_TIMEOUT 10000
293 #define WINDOW_COPY_SEARCH_ALL_TIMEOUT 200
295 int jumptype;
296 struct utf8_data *jumpchar;
298 struct event dragtimer;
299 #define WINDOW_COPY_DRAG_REPEAT_TIME 50000
302 static void
303 window_copy_scroll_timer(__unused int fd, __unused short events, void *arg)
305 struct window_mode_entry *wme = arg;
306 struct window_pane *wp = wme->wp;
307 struct window_copy_mode_data *data = wme->data;
308 struct timeval tv = {
309 .tv_usec = WINDOW_COPY_DRAG_REPEAT_TIME
312 evtimer_del(&data->dragtimer);
314 if (TAILQ_FIRST(&wp->modes) != wme)
315 return;
317 if (data->cy == 0) {
318 evtimer_add(&data->dragtimer, &tv);
319 window_copy_cursor_up(wme, 1);
320 } else if (data->cy == screen_size_y(&data->screen) - 1) {
321 evtimer_add(&data->dragtimer, &tv);
322 window_copy_cursor_down(wme, 1);
326 static struct screen *
327 window_copy_clone_screen(struct screen *src, struct screen *hint, u_int *cx,
328 u_int *cy, int trim)
330 struct screen *dst;
331 const struct grid_line *gl;
332 u_int sy, wx, wy;
333 int reflow;
335 dst = xcalloc(1, sizeof *dst);
337 sy = screen_hsize(src) + screen_size_y(src);
338 if (trim) {
339 while (sy > screen_hsize(src)) {
340 gl = grid_peek_line(src->grid, sy - 1);
341 if (gl->cellused != 0)
342 break;
343 sy--;
346 log_debug("%s: target screen is %ux%u, source %ux%u", __func__,
347 screen_size_x(src), sy, screen_size_x(hint),
348 screen_hsize(src) + screen_size_y(src));
349 screen_init(dst, screen_size_x(src), sy, screen_hlimit(src));
352 * Ensure history is on for the backing grid so lines are not deleted
353 * during resizing.
355 dst->grid->flags |= GRID_HISTORY;
356 grid_duplicate_lines(dst->grid, 0, src->grid, 0, sy);
358 dst->grid->sy = sy - screen_hsize(src);
359 dst->grid->hsize = screen_hsize(src);
360 dst->grid->hscrolled = src->grid->hscrolled;
361 if (src->cy > dst->grid->sy - 1) {
362 dst->cx = 0;
363 dst->cy = dst->grid->sy - 1;
364 } else {
365 dst->cx = src->cx;
366 dst->cy = src->cy;
369 if (cx != NULL && cy != NULL) {
370 *cx = dst->cx;
371 *cy = screen_hsize(dst) + dst->cy;
372 reflow = (screen_size_x(hint) != screen_size_x(dst));
374 else
375 reflow = 0;
376 if (reflow)
377 grid_wrap_position(dst->grid, *cx, *cy, &wx, &wy);
378 screen_resize_cursor(dst, screen_size_x(hint), screen_size_y(hint), 1,
379 0, 0);
380 if (reflow)
381 grid_unwrap_position(dst->grid, cx, cy, wx, wy);
383 return (dst);
386 static struct window_copy_mode_data *
387 window_copy_common_init(struct window_mode_entry *wme)
389 struct window_pane *wp = wme->wp;
390 struct window_copy_mode_data *data;
391 struct screen *base = &wp->base;
393 wme->data = data = xcalloc(1, sizeof *data);
395 data->cursordrag = CURSORDRAG_NONE;
396 data->lineflag = LINE_SEL_NONE;
397 data->selflag = SEL_CHAR;
399 if (wp->searchstr != NULL) {
400 data->searchtype = WINDOW_COPY_SEARCHUP;
401 data->searchregex = wp->searchregex;
402 data->searchstr = xstrdup(wp->searchstr);
403 } else {
404 data->searchtype = WINDOW_COPY_OFF;
405 data->searchregex = 0;
406 data->searchstr = NULL;
408 data->searchx = data->searchy = data->searcho = -1;
409 data->searchall = 1;
411 data->jumptype = WINDOW_COPY_OFF;
412 data->jumpchar = NULL;
414 screen_init(&data->screen, screen_size_x(base), screen_size_y(base), 0);
415 data->modekeys = options_get_number(wp->window->options, "mode-keys");
417 evtimer_set(&data->dragtimer, window_copy_scroll_timer, wme);
419 return (data);
422 static struct screen *
423 window_copy_init(struct window_mode_entry *wme,
424 __unused struct cmd_find_state *fs, struct args *args)
426 struct window_pane *wp = wme->swp;
427 struct window_copy_mode_data *data;
428 struct screen *base = &wp->base;
429 struct screen_write_ctx ctx;
430 u_int i, cx, cy;
432 data = window_copy_common_init(wme);
433 data->backing = window_copy_clone_screen(base, &data->screen, &cx, &cy,
434 wme->swp != wme->wp);
436 data->cx = cx;
437 if (cy < screen_hsize(data->backing)) {
438 data->cy = 0;
439 data->oy = screen_hsize(data->backing) - cy;
440 } else {
441 data->cy = cy - screen_hsize(data->backing);
442 data->oy = 0;
445 data->scroll_exit = args_has(args, 'e');
446 data->hide_position = args_has(args, 'H');
448 data->screen.cx = data->cx;
449 data->screen.cy = data->cy;
450 data->mx = data->cx;
451 data->my = screen_hsize(data->backing) + data->cy - data->oy;
452 data->showmark = 0;
454 screen_write_start(&ctx, &data->screen);
455 for (i = 0; i < screen_size_y(&data->screen); i++)
456 window_copy_write_line(wme, &ctx, i);
457 screen_write_cursormove(&ctx, data->cx, data->cy, 0);
458 screen_write_stop(&ctx);
460 return (&data->screen);
463 static struct screen *
464 window_copy_view_init(struct window_mode_entry *wme,
465 __unused struct cmd_find_state *fs, __unused struct args *args)
467 struct window_pane *wp = wme->wp;
468 struct window_copy_mode_data *data;
469 struct screen *base = &wp->base;
470 struct screen *s;
472 data = window_copy_common_init(wme);
473 data->viewmode = 1;
475 data->backing = s = xmalloc(sizeof *data->backing);
476 screen_init(s, screen_size_x(base), screen_size_y(base), UINT_MAX);
477 data->mx = data->cx;
478 data->my = screen_hsize(data->backing) + data->cy - data->oy;
479 data->showmark = 0;
481 return (&data->screen);
484 static void
485 window_copy_free(struct window_mode_entry *wme)
487 struct window_copy_mode_data *data = wme->data;
489 evtimer_del(&data->dragtimer);
491 free(data->searchmark);
492 free(data->searchstr);
493 free(data->jumpchar);
495 screen_free(data->backing);
496 free(data->backing);
498 screen_free(&data->screen);
499 free(data);
502 void
503 window_copy_add(struct window_pane *wp, const char *fmt, ...)
505 va_list ap;
507 va_start(ap, fmt);
508 window_copy_vadd(wp, fmt, ap);
509 va_end(ap);
512 void
513 window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap)
515 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
516 struct window_copy_mode_data *data = wme->data;
517 struct screen *backing = data->backing;
518 struct screen_write_ctx back_ctx, ctx;
519 struct grid_cell gc;
520 u_int old_hsize, old_cy;
522 memcpy(&gc, &grid_default_cell, sizeof gc);
524 old_hsize = screen_hsize(data->backing);
525 screen_write_start(&back_ctx, backing);
526 if (data->backing_written) {
528 * On the second or later line, do a CRLF before writing
529 * (so it's on a new line).
531 screen_write_carriagereturn(&back_ctx);
532 screen_write_linefeed(&back_ctx, 0, 8);
533 } else
534 data->backing_written = 1;
535 old_cy = backing->cy;
536 screen_write_vnputs(&back_ctx, 0, &gc, fmt, ap);
537 screen_write_stop(&back_ctx);
539 data->oy += screen_hsize(data->backing) - old_hsize;
541 screen_write_start_pane(&ctx, wp, &data->screen);
544 * If the history has changed, draw the top line.
545 * (If there's any history at all, it has changed.)
547 if (screen_hsize(data->backing))
548 window_copy_redraw_lines(wme, 0, 1);
550 /* Write the new lines. */
551 window_copy_redraw_lines(wme, old_cy, backing->cy - old_cy + 1);
553 screen_write_stop(&ctx);
556 void
557 window_copy_pageup(struct window_pane *wp, int half_page)
559 window_copy_pageup1(TAILQ_FIRST(&wp->modes), half_page);
562 static void
563 window_copy_pageup1(struct window_mode_entry *wme, int half_page)
565 struct window_copy_mode_data *data = wme->data;
566 struct screen *s = &data->screen;
567 u_int n, ox, oy, px, py;
569 oy = screen_hsize(data->backing) + data->cy - data->oy;
570 ox = window_copy_find_length(wme, oy);
572 if (data->cx != ox) {
573 data->lastcx = data->cx;
574 data->lastsx = ox;
576 data->cx = data->lastcx;
578 n = 1;
579 if (screen_size_y(s) > 2) {
580 if (half_page)
581 n = screen_size_y(s) / 2;
582 else
583 n = screen_size_y(s) - 2;
586 if (data->oy + n > screen_hsize(data->backing)) {
587 data->oy = screen_hsize(data->backing);
588 if (data->cy < n)
589 data->cy = 0;
590 else
591 data->cy -= n;
592 } else
593 data->oy += n;
595 if (data->screen.sel == NULL || !data->rectflag) {
596 py = screen_hsize(data->backing) + data->cy - data->oy;
597 px = window_copy_find_length(wme, py);
598 if ((data->cx >= data->lastsx && data->cx != px) ||
599 data->cx > px)
600 window_copy_cursor_end_of_line(wme);
603 if (data->searchmark != NULL && !data->timeout)
604 window_copy_search_marks(wme, NULL, data->searchregex, 1);
605 window_copy_update_selection(wme, 1, 0);
606 window_copy_redraw_screen(wme);
609 static int
610 window_copy_pagedown(struct window_mode_entry *wme, int half_page,
611 int scroll_exit)
613 struct window_copy_mode_data *data = wme->data;
614 struct screen *s = &data->screen;
615 u_int n, ox, oy, px, py;
617 oy = screen_hsize(data->backing) + data->cy - data->oy;
618 ox = window_copy_find_length(wme, oy);
620 if (data->cx != ox) {
621 data->lastcx = data->cx;
622 data->lastsx = ox;
624 data->cx = data->lastcx;
626 n = 1;
627 if (screen_size_y(s) > 2) {
628 if (half_page)
629 n = screen_size_y(s) / 2;
630 else
631 n = screen_size_y(s) - 2;
634 if (data->oy < n) {
635 data->oy = 0;
636 if (data->cy + (n - data->oy) >= screen_size_y(data->backing))
637 data->cy = screen_size_y(data->backing) - 1;
638 else
639 data->cy += n - data->oy;
640 } else
641 data->oy -= n;
643 if (data->screen.sel == NULL || !data->rectflag) {
644 py = screen_hsize(data->backing) + data->cy - data->oy;
645 px = window_copy_find_length(wme, py);
646 if ((data->cx >= data->lastsx && data->cx != px) ||
647 data->cx > px)
648 window_copy_cursor_end_of_line(wme);
651 if (scroll_exit && data->oy == 0)
652 return (1);
653 if (data->searchmark != NULL && !data->timeout)
654 window_copy_search_marks(wme, NULL, data->searchregex, 1);
655 window_copy_update_selection(wme, 1, 0);
656 window_copy_redraw_screen(wme);
657 return (0);
660 static void
661 window_copy_previous_paragraph(struct window_mode_entry *wme)
663 struct window_copy_mode_data *data = wme->data;
664 u_int oy;
666 oy = screen_hsize(data->backing) + data->cy - data->oy;
668 while (oy > 0 && window_copy_find_length(wme, oy) == 0)
669 oy--;
671 while (oy > 0 && window_copy_find_length(wme, oy) > 0)
672 oy--;
674 window_copy_scroll_to(wme, 0, oy, 0);
677 static void
678 window_copy_next_paragraph(struct window_mode_entry *wme)
680 struct window_copy_mode_data *data = wme->data;
681 struct screen *s = &data->screen;
682 u_int maxy, ox, oy;
684 oy = screen_hsize(data->backing) + data->cy - data->oy;
685 maxy = screen_hsize(data->backing) + screen_size_y(s) - 1;
687 while (oy < maxy && window_copy_find_length(wme, oy) == 0)
688 oy++;
690 while (oy < maxy && window_copy_find_length(wme, oy) > 0)
691 oy++;
693 ox = window_copy_find_length(wme, oy);
694 window_copy_scroll_to(wme, ox, oy, 0);
697 char *
698 window_copy_get_word(struct window_pane *wp, u_int x, u_int y)
700 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
701 struct window_copy_mode_data *data = wme->data;
702 struct grid *gd = data->screen.grid;
704 return (format_grid_word(gd, x, gd->hsize + y));
707 char *
708 window_copy_get_line(struct window_pane *wp, u_int y)
710 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
711 struct window_copy_mode_data *data = wme->data;
712 struct grid *gd = data->screen.grid;
714 return (format_grid_line(gd, gd->hsize + y));
717 static void *
718 window_copy_cursor_word_cb(struct format_tree *ft)
720 struct window_pane *wp = format_get_pane(ft);
721 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
722 struct window_copy_mode_data *data = wme->data;
724 return (window_copy_get_word(wp, data->cx, data->cy));
727 static void *
728 window_copy_cursor_line_cb(struct format_tree *ft)
730 struct window_pane *wp = format_get_pane(ft);
731 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
732 struct window_copy_mode_data *data = wme->data;
734 return (window_copy_get_line(wp, data->cy));
737 static void *
738 window_copy_search_match_cb(struct format_tree *ft)
740 struct window_pane *wp = format_get_pane(ft);
741 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
742 struct window_copy_mode_data *data = wme->data;
744 return (window_copy_match_at_cursor(data));
747 static void
748 window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft)
750 struct window_copy_mode_data *data = wme->data;
752 format_add(ft, "scroll_position", "%d", data->oy);
753 format_add(ft, "rectangle_toggle", "%d", data->rectflag);
755 format_add(ft, "copy_cursor_x", "%d", data->cx);
756 format_add(ft, "copy_cursor_y", "%d", data->cy);
758 format_add(ft, "selection_present", "%d", data->screen.sel != NULL);
759 if (data->screen.sel != NULL) {
760 format_add(ft, "selection_start_x", "%d", data->selx);
761 format_add(ft, "selection_start_y", "%d", data->sely);
762 format_add(ft, "selection_end_x", "%d", data->endselx);
763 format_add(ft, "selection_end_y", "%d", data->endsely);
764 format_add(ft, "selection_active", "%d",
765 data->cursordrag != CURSORDRAG_NONE);
766 } else
767 format_add(ft, "selection_active", "%d", 0);
769 format_add(ft, "search_present", "%d", data->searchmark != NULL);
770 format_add_cb(ft, "search_match", window_copy_search_match_cb);
772 format_add_cb(ft, "copy_cursor_word", window_copy_cursor_word_cb);
773 format_add_cb(ft, "copy_cursor_line", window_copy_cursor_line_cb);
776 static void
777 window_copy_size_changed(struct window_mode_entry *wme)
779 struct window_copy_mode_data *data = wme->data;
780 struct screen *s = &data->screen;
781 struct screen_write_ctx ctx;
782 int search = (data->searchmark != NULL);
784 window_copy_clear_selection(wme);
785 window_copy_clear_marks(wme);
787 screen_write_start(&ctx, s);
788 window_copy_write_lines(wme, &ctx, 0, screen_size_y(s));
789 screen_write_stop(&ctx);
791 if (search && !data->timeout)
792 window_copy_search_marks(wme, NULL, data->searchregex, 0);
793 data->searchx = data->cx;
794 data->searchy = data->cy;
795 data->searcho = data->oy;
798 static void
799 window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
801 struct window_copy_mode_data *data = wme->data;
802 struct screen *s = &data->screen;
803 struct grid *gd = data->backing->grid;
804 u_int cx, cy, wx, wy;
805 int reflow;
807 screen_resize(s, sx, sy, 0);
808 cx = data->cx;
809 cy = gd->hsize + data->cy - data->oy;
810 reflow = (gd->sx != sx);
811 if (reflow)
812 grid_wrap_position(gd, cx, cy, &wx, &wy);
813 screen_resize_cursor(data->backing, sx, sy, 1, 0, 0);
814 if (reflow)
815 grid_unwrap_position(gd, &cx, &cy, wx, wy);
817 data->cx = cx;
818 if (cy < gd->hsize) {
819 data->cy = 0;
820 data->oy = gd->hsize - cy;
821 } else {
822 data->cy = cy - gd->hsize;
823 data->oy = 0;
826 window_copy_size_changed(wme);
827 window_copy_redraw_screen(wme);
830 static const char *
831 window_copy_key_table(struct window_mode_entry *wme)
833 struct window_pane *wp = wme->wp;
835 if (options_get_number(wp->window->options, "mode-keys") == MODEKEY_VI)
836 return ("copy-mode-vi");
837 return ("copy-mode");
840 static int
841 window_copy_expand_search_string(struct window_copy_cmd_state *cs)
843 struct window_mode_entry *wme = cs->wme;
844 struct window_copy_mode_data *data = wme->data;
845 const char *ss = args_string(cs->args, 1);
846 char *expanded;
848 if (ss == NULL || *ss == '\0')
849 return (0);
851 if (args_has(cs->args, 'F')) {
852 expanded = format_single(NULL, ss, NULL, NULL, NULL, wme->wp);
853 if (*expanded == '\0') {
854 free(expanded);
855 return (0);
857 free(data->searchstr);
858 data->searchstr = expanded;
859 } else {
860 free(data->searchstr);
861 data->searchstr = xstrdup(ss);
863 return (1);
866 static enum window_copy_cmd_action
867 window_copy_cmd_append_selection(struct window_copy_cmd_state *cs)
869 struct window_mode_entry *wme = cs->wme;
870 struct session *s = cs->s;
872 if (s != NULL)
873 window_copy_append_selection(wme);
874 window_copy_clear_selection(wme);
875 return (WINDOW_COPY_CMD_REDRAW);
878 static enum window_copy_cmd_action
879 window_copy_cmd_append_selection_and_cancel(struct window_copy_cmd_state *cs)
881 struct window_mode_entry *wme = cs->wme;
882 struct session *s = cs->s;
884 if (s != NULL)
885 window_copy_append_selection(wme);
886 window_copy_clear_selection(wme);
887 return (WINDOW_COPY_CMD_CANCEL);
890 static enum window_copy_cmd_action
891 window_copy_cmd_back_to_indentation(struct window_copy_cmd_state *cs)
893 struct window_mode_entry *wme = cs->wme;
895 window_copy_cursor_back_to_indentation(wme);
896 return (WINDOW_COPY_CMD_NOTHING);
899 static enum window_copy_cmd_action
900 window_copy_cmd_begin_selection(struct window_copy_cmd_state *cs)
902 struct window_mode_entry *wme = cs->wme;
903 struct client *c = cs->c;
904 struct mouse_event *m = cs->m;
905 struct window_copy_mode_data *data = wme->data;
907 if (m != NULL) {
908 window_copy_start_drag(c, m);
909 return (WINDOW_COPY_CMD_NOTHING);
912 data->lineflag = LINE_SEL_NONE;
913 data->selflag = SEL_CHAR;
914 window_copy_start_selection(wme);
915 return (WINDOW_COPY_CMD_REDRAW);
918 static enum window_copy_cmd_action
919 window_copy_cmd_stop_selection(struct window_copy_cmd_state *cs)
921 struct window_mode_entry *wme = cs->wme;
922 struct window_copy_mode_data *data = wme->data;
924 data->cursordrag = CURSORDRAG_NONE;
925 data->lineflag = LINE_SEL_NONE;
926 data->selflag = SEL_CHAR;
927 return (WINDOW_COPY_CMD_NOTHING);
930 static enum window_copy_cmd_action
931 window_copy_cmd_bottom_line(struct window_copy_cmd_state *cs)
933 struct window_mode_entry *wme = cs->wme;
934 struct window_copy_mode_data *data = wme->data;
936 data->cx = 0;
937 data->cy = screen_size_y(&data->screen) - 1;
939 window_copy_update_selection(wme, 1, 0);
940 return (WINDOW_COPY_CMD_REDRAW);
943 static enum window_copy_cmd_action
944 window_copy_cmd_cancel(__unused struct window_copy_cmd_state *cs)
946 return (WINDOW_COPY_CMD_CANCEL);
949 static enum window_copy_cmd_action
950 window_copy_cmd_clear_selection(struct window_copy_cmd_state *cs)
952 struct window_mode_entry *wme = cs->wme;
954 window_copy_clear_selection(wme);
955 return (WINDOW_COPY_CMD_REDRAW);
958 static enum window_copy_cmd_action
959 window_copy_do_copy_end_of_line(struct window_copy_cmd_state *cs, int pipe,
960 int cancel)
962 struct window_mode_entry *wme = cs->wme;
963 struct client *c = cs->c;
964 struct session *s = cs->s;
965 struct winlink *wl = cs->wl;
966 struct window_pane *wp = wme->wp;
967 u_int count = args_count(cs->args);
968 u_int np = wme->prefix, ocx, ocy, ooy;
969 struct window_copy_mode_data *data = wme->data;
970 char *prefix = NULL, *command = NULL;
971 const char *arg1 = args_string(cs->args, 1);
972 const char *arg2 = args_string(cs->args, 2);
974 if (pipe) {
975 if (count == 3)
976 prefix = format_single(NULL, arg2, c, s, wl, wp);
977 if (s != NULL && count > 1 && *arg1 != '\0')
978 command = format_single(NULL, arg1, c, s, wl, wp);
979 } else {
980 if (count == 2)
981 prefix = format_single(NULL, arg1, c, s, wl, wp);
984 ocx = data->cx;
985 ocy = data->cy;
986 ooy = data->oy;
988 window_copy_start_selection(wme);
989 for (; np > 1; np--)
990 window_copy_cursor_down(wme, 0);
991 window_copy_cursor_end_of_line(wme);
993 if (s != NULL) {
994 if (pipe)
995 window_copy_copy_pipe(wme, s, prefix, command);
996 else
997 window_copy_copy_selection(wme, prefix);
999 if (cancel) {
1000 free(prefix);
1001 free(command);
1002 return (WINDOW_COPY_CMD_CANCEL);
1005 window_copy_clear_selection(wme);
1007 data->cx = ocx;
1008 data->cy = ocy;
1009 data->oy = ooy;
1011 free(prefix);
1012 free(command);
1013 return (WINDOW_COPY_CMD_REDRAW);
1016 static enum window_copy_cmd_action
1017 window_copy_cmd_copy_end_of_line(struct window_copy_cmd_state *cs)
1019 return (window_copy_do_copy_end_of_line(cs, 0, 0));
1022 static enum window_copy_cmd_action
1023 window_copy_cmd_copy_end_of_line_and_cancel(struct window_copy_cmd_state *cs)
1025 return (window_copy_do_copy_end_of_line(cs, 0, 1));
1028 static enum window_copy_cmd_action
1029 window_copy_cmd_copy_pipe_end_of_line(struct window_copy_cmd_state *cs)
1031 return (window_copy_do_copy_end_of_line(cs, 1, 0));
1034 static enum window_copy_cmd_action
1035 window_copy_cmd_copy_pipe_end_of_line_and_cancel(
1036 struct window_copy_cmd_state *cs)
1038 return (window_copy_do_copy_end_of_line(cs, 1, 1));
1041 static enum window_copy_cmd_action
1042 window_copy_do_copy_line(struct window_copy_cmd_state *cs, int pipe, int cancel)
1044 struct window_mode_entry *wme = cs->wme;
1045 struct client *c = cs->c;
1046 struct session *s = cs->s;
1047 struct winlink *wl = cs->wl;
1048 struct window_pane *wp = wme->wp;
1049 struct window_copy_mode_data *data = wme->data;
1050 u_int count = args_count(cs->args);
1051 u_int np = wme->prefix, ocx, ocy, ooy;
1052 char *prefix = NULL, *command = NULL;
1053 const char *arg1 = args_string(cs->args, 1);
1054 const char *arg2 = args_string(cs->args, 2);
1056 if (pipe) {
1057 if (count == 3)
1058 prefix = format_single(NULL, arg2, c, s, wl, wp);
1059 if (s != NULL && count > 1 && *arg1 != '\0')
1060 command = format_single(NULL, arg1, c, s, wl, wp);
1061 } else {
1062 if (count == 2)
1063 prefix = format_single(NULL, arg1, c, s, wl, wp);
1066 ocx = data->cx;
1067 ocy = data->cy;
1068 ooy = data->oy;
1070 data->selflag = SEL_CHAR;
1071 window_copy_cursor_start_of_line(wme);
1072 window_copy_start_selection(wme);
1073 for (; np > 1; np--)
1074 window_copy_cursor_down(wme, 0);
1075 window_copy_cursor_end_of_line(wme);
1077 if (s != NULL) {
1078 if (pipe)
1079 window_copy_copy_pipe(wme, s, prefix, command);
1080 else
1081 window_copy_copy_selection(wme, prefix);
1083 if (cancel) {
1084 free(prefix);
1085 free(command);
1086 return (WINDOW_COPY_CMD_CANCEL);
1089 window_copy_clear_selection(wme);
1091 data->cx = ocx;
1092 data->cy = ocy;
1093 data->oy = ooy;
1095 free(prefix);
1096 free(command);
1097 return (WINDOW_COPY_CMD_REDRAW);
1100 static enum window_copy_cmd_action
1101 window_copy_cmd_copy_line(struct window_copy_cmd_state *cs)
1103 return (window_copy_do_copy_line(cs, 0, 0));
1106 static enum window_copy_cmd_action
1107 window_copy_cmd_copy_line_and_cancel(struct window_copy_cmd_state *cs)
1109 return (window_copy_do_copy_line(cs, 0, 1));
1112 static enum window_copy_cmd_action
1113 window_copy_cmd_copy_pipe_line(struct window_copy_cmd_state *cs)
1115 return (window_copy_do_copy_line(cs, 1, 0));
1118 static enum window_copy_cmd_action
1119 window_copy_cmd_copy_pipe_line_and_cancel(struct window_copy_cmd_state *cs)
1121 return (window_copy_do_copy_line(cs, 1, 1));
1124 static enum window_copy_cmd_action
1125 window_copy_cmd_copy_selection_no_clear(struct window_copy_cmd_state *cs)
1127 struct window_mode_entry *wme = cs->wme;
1128 struct client *c = cs->c;
1129 struct session *s = cs->s;
1130 struct winlink *wl = cs->wl;
1131 struct window_pane *wp = wme->wp;
1132 char *prefix = NULL;
1133 const char *arg1 = args_string(cs->args, 1);
1135 if (arg1 != NULL)
1136 prefix = format_single(NULL, arg1, c, s, wl, wp);
1138 if (s != NULL)
1139 window_copy_copy_selection(wme, prefix);
1141 free(prefix);
1142 return (WINDOW_COPY_CMD_NOTHING);
1145 static enum window_copy_cmd_action
1146 window_copy_cmd_copy_selection(struct window_copy_cmd_state *cs)
1148 struct window_mode_entry *wme = cs->wme;
1150 window_copy_cmd_copy_selection_no_clear(cs);
1151 window_copy_clear_selection(wme);
1152 return (WINDOW_COPY_CMD_REDRAW);
1155 static enum window_copy_cmd_action
1156 window_copy_cmd_copy_selection_and_cancel(struct window_copy_cmd_state *cs)
1158 struct window_mode_entry *wme = cs->wme;
1160 window_copy_cmd_copy_selection_no_clear(cs);
1161 window_copy_clear_selection(wme);
1162 return (WINDOW_COPY_CMD_CANCEL);
1165 static enum window_copy_cmd_action
1166 window_copy_cmd_cursor_down(struct window_copy_cmd_state *cs)
1168 struct window_mode_entry *wme = cs->wme;
1169 u_int np = wme->prefix;
1171 for (; np != 0; np--)
1172 window_copy_cursor_down(wme, 0);
1173 return (WINDOW_COPY_CMD_NOTHING);
1176 static enum window_copy_cmd_action
1177 window_copy_cmd_cursor_down_and_cancel(struct window_copy_cmd_state *cs)
1179 struct window_mode_entry *wme = cs->wme;
1180 struct window_copy_mode_data *data = wme->data;
1181 u_int np = wme->prefix, cy;
1183 cy = data->cy;
1184 for (; np != 0; np--)
1185 window_copy_cursor_down(wme, 0);
1186 if (cy == data->cy && data->oy == 0)
1187 return (WINDOW_COPY_CMD_CANCEL);
1188 return (WINDOW_COPY_CMD_NOTHING);
1191 static enum window_copy_cmd_action
1192 window_copy_cmd_cursor_left(struct window_copy_cmd_state *cs)
1194 struct window_mode_entry *wme = cs->wme;
1195 u_int np = wme->prefix;
1197 for (; np != 0; np--)
1198 window_copy_cursor_left(wme);
1199 return (WINDOW_COPY_CMD_NOTHING);
1202 static enum window_copy_cmd_action
1203 window_copy_cmd_cursor_right(struct window_copy_cmd_state *cs)
1205 struct window_mode_entry *wme = cs->wme;
1206 struct window_copy_mode_data *data = wme->data;
1207 u_int np = wme->prefix;
1209 for (; np != 0; np--) {
1210 window_copy_cursor_right(wme, data->screen.sel != NULL &&
1211 data->rectflag);
1213 return (WINDOW_COPY_CMD_NOTHING);
1216 static enum window_copy_cmd_action
1217 window_copy_cmd_cursor_up(struct window_copy_cmd_state *cs)
1219 struct window_mode_entry *wme = cs->wme;
1220 u_int np = wme->prefix;
1222 for (; np != 0; np--)
1223 window_copy_cursor_up(wme, 0);
1224 return (WINDOW_COPY_CMD_NOTHING);
1227 static enum window_copy_cmd_action
1228 window_copy_cmd_end_of_line(struct window_copy_cmd_state *cs)
1230 struct window_mode_entry *wme = cs->wme;
1232 window_copy_cursor_end_of_line(wme);
1233 return (WINDOW_COPY_CMD_NOTHING);
1236 static enum window_copy_cmd_action
1237 window_copy_cmd_halfpage_down(struct window_copy_cmd_state *cs)
1239 struct window_mode_entry *wme = cs->wme;
1240 struct window_copy_mode_data *data = wme->data;
1241 u_int np = wme->prefix;
1243 for (; np != 0; np--) {
1244 if (window_copy_pagedown(wme, 1, data->scroll_exit))
1245 return (WINDOW_COPY_CMD_CANCEL);
1247 return (WINDOW_COPY_CMD_NOTHING);
1250 static enum window_copy_cmd_action
1251 window_copy_cmd_halfpage_down_and_cancel(struct window_copy_cmd_state *cs)
1254 struct window_mode_entry *wme = cs->wme;
1255 u_int np = wme->prefix;
1257 for (; np != 0; np--) {
1258 if (window_copy_pagedown(wme, 1, 1))
1259 return (WINDOW_COPY_CMD_CANCEL);
1261 return (WINDOW_COPY_CMD_NOTHING);
1264 static enum window_copy_cmd_action
1265 window_copy_cmd_halfpage_up(struct window_copy_cmd_state *cs)
1267 struct window_mode_entry *wme = cs->wme;
1268 u_int np = wme->prefix;
1270 for (; np != 0; np--)
1271 window_copy_pageup1(wme, 1);
1272 return (WINDOW_COPY_CMD_NOTHING);
1275 static enum window_copy_cmd_action
1276 window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs)
1278 struct window_mode_entry *wme = cs->wme;
1279 struct window_copy_mode_data *data = wme->data;
1280 struct screen *s = data->backing;
1281 u_int oy;
1283 oy = screen_hsize(s) + data->cy - data->oy;
1284 if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely)
1285 window_copy_other_end(wme);
1287 data->cy = screen_size_y(&data->screen) - 1;
1288 data->cx = window_copy_find_length(wme, screen_hsize(s) + data->cy);
1289 data->oy = 0;
1291 if (data->searchmark != NULL && !data->timeout)
1292 window_copy_search_marks(wme, NULL, data->searchregex, 1);
1293 window_copy_update_selection(wme, 1, 0);
1294 return (WINDOW_COPY_CMD_REDRAW);
1297 static enum window_copy_cmd_action
1298 window_copy_cmd_history_top(struct window_copy_cmd_state *cs)
1300 struct window_mode_entry *wme = cs->wme;
1301 struct window_copy_mode_data *data = wme->data;
1302 u_int oy;
1304 oy = screen_hsize(data->backing) + data->cy - data->oy;
1305 if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely)
1306 window_copy_other_end(wme);
1308 data->cy = 0;
1309 data->cx = 0;
1310 data->oy = screen_hsize(data->backing);
1312 if (data->searchmark != NULL && !data->timeout)
1313 window_copy_search_marks(wme, NULL, data->searchregex, 1);
1314 window_copy_update_selection(wme, 1, 0);
1315 return (WINDOW_COPY_CMD_REDRAW);
1318 static enum window_copy_cmd_action
1319 window_copy_cmd_jump_again(struct window_copy_cmd_state *cs)
1321 struct window_mode_entry *wme = cs->wme;
1322 struct window_copy_mode_data *data = wme->data;
1323 u_int np = wme->prefix;
1325 switch (data->jumptype) {
1326 case WINDOW_COPY_JUMPFORWARD:
1327 for (; np != 0; np--)
1328 window_copy_cursor_jump(wme);
1329 break;
1330 case WINDOW_COPY_JUMPBACKWARD:
1331 for (; np != 0; np--)
1332 window_copy_cursor_jump_back(wme);
1333 break;
1334 case WINDOW_COPY_JUMPTOFORWARD:
1335 for (; np != 0; np--)
1336 window_copy_cursor_jump_to(wme);
1337 break;
1338 case WINDOW_COPY_JUMPTOBACKWARD:
1339 for (; np != 0; np--)
1340 window_copy_cursor_jump_to_back(wme);
1341 break;
1343 return (WINDOW_COPY_CMD_NOTHING);
1346 static enum window_copy_cmd_action
1347 window_copy_cmd_jump_reverse(struct window_copy_cmd_state *cs)
1349 struct window_mode_entry *wme = cs->wme;
1350 struct window_copy_mode_data *data = wme->data;
1351 u_int np = wme->prefix;
1353 switch (data->jumptype) {
1354 case WINDOW_COPY_JUMPFORWARD:
1355 for (; np != 0; np--)
1356 window_copy_cursor_jump_back(wme);
1357 break;
1358 case WINDOW_COPY_JUMPBACKWARD:
1359 for (; np != 0; np--)
1360 window_copy_cursor_jump(wme);
1361 break;
1362 case WINDOW_COPY_JUMPTOFORWARD:
1363 for (; np != 0; np--)
1364 window_copy_cursor_jump_to_back(wme);
1365 break;
1366 case WINDOW_COPY_JUMPTOBACKWARD:
1367 for (; np != 0; np--)
1368 window_copy_cursor_jump_to(wme);
1369 break;
1371 return (WINDOW_COPY_CMD_NOTHING);
1374 static enum window_copy_cmd_action
1375 window_copy_cmd_middle_line(struct window_copy_cmd_state *cs)
1377 struct window_mode_entry *wme = cs->wme;
1378 struct window_copy_mode_data *data = wme->data;
1380 data->cx = 0;
1381 data->cy = (screen_size_y(&data->screen) - 1) / 2;
1383 window_copy_update_selection(wme, 1, 0);
1384 return (WINDOW_COPY_CMD_REDRAW);
1387 static enum window_copy_cmd_action
1388 window_copy_cmd_previous_matching_bracket(struct window_copy_cmd_state *cs)
1390 struct window_mode_entry *wme = cs->wme;
1391 u_int np = wme->prefix;
1392 struct window_copy_mode_data *data = wme->data;
1393 struct screen *s = data->backing;
1394 char open[] = "{[(", close[] = "}])";
1395 char tried, found, start, *cp;
1396 u_int px, py, xx, n;
1397 struct grid_cell gc;
1398 int failed;
1400 for (; np != 0; np--) {
1401 /* Get cursor position and line length. */
1402 px = data->cx;
1403 py = screen_hsize(s) + data->cy - data->oy;
1404 xx = window_copy_find_length(wme, py);
1405 if (xx == 0)
1406 break;
1409 * Get the current character. If not on a bracket, try the
1410 * previous. If still not, then behave like previous-word.
1412 tried = 0;
1413 retry:
1414 grid_get_cell(s->grid, px, py, &gc);
1415 if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING))
1416 cp = NULL;
1417 else {
1418 found = *gc.data.data;
1419 cp = strchr(close, found);
1421 if (cp == NULL) {
1422 if (data->modekeys == MODEKEY_EMACS) {
1423 if (!tried && px > 0) {
1424 px--;
1425 tried = 1;
1426 goto retry;
1428 window_copy_cursor_previous_word(wme, close, 1);
1430 continue;
1432 start = open[cp - close];
1434 /* Walk backward until the matching bracket is reached. */
1435 n = 1;
1436 failed = 0;
1437 do {
1438 if (px == 0) {
1439 if (py == 0) {
1440 failed = 1;
1441 break;
1443 do {
1444 py--;
1445 xx = window_copy_find_length(wme, py);
1446 } while (xx == 0 && py > 0);
1447 if (xx == 0 && py == 0) {
1448 failed = 1;
1449 break;
1451 px = xx - 1;
1452 } else
1453 px--;
1455 grid_get_cell(s->grid, px, py, &gc);
1456 if (gc.data.size == 1 &&
1457 (~gc.flags & GRID_FLAG_PADDING)) {
1458 if (*gc.data.data == found)
1459 n++;
1460 else if (*gc.data.data == start)
1461 n--;
1463 } while (n != 0);
1465 /* Move the cursor to the found location if any. */
1466 if (!failed)
1467 window_copy_scroll_to(wme, px, py, 0);
1470 return (WINDOW_COPY_CMD_NOTHING);
1473 static enum window_copy_cmd_action
1474 window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state *cs)
1476 struct window_mode_entry *wme = cs->wme;
1477 u_int np = wme->prefix;
1478 struct window_copy_mode_data *data = wme->data;
1479 struct screen *s = data->backing;
1480 char open[] = "{[(", close[] = "}])";
1481 char tried, found, end, *cp;
1482 u_int px, py, xx, yy, sx, sy, n;
1483 struct grid_cell gc;
1484 int failed;
1485 struct grid_line *gl;
1487 for (; np != 0; np--) {
1488 /* Get cursor position and line length. */
1489 px = data->cx;
1490 py = screen_hsize(s) + data->cy - data->oy;
1491 xx = window_copy_find_length(wme, py);
1492 yy = screen_hsize(s) + screen_size_y(s) - 1;
1493 if (xx == 0)
1494 break;
1497 * Get the current character. If not on a bracket, try the
1498 * next. If still not, then behave like next-word.
1500 tried = 0;
1501 retry:
1502 grid_get_cell(s->grid, px, py, &gc);
1503 if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING))
1504 cp = NULL;
1505 else {
1506 found = *gc.data.data;
1509 * In vi mode, attempt to move to previous bracket if a
1510 * closing bracket is found first. If this fails,
1511 * return to the original cursor position.
1513 cp = strchr(close, found);
1514 if (cp != NULL && data->modekeys == MODEKEY_VI) {
1515 sx = data->cx;
1516 sy = screen_hsize(s) + data->cy - data->oy;
1518 window_copy_scroll_to(wme, px, py, 0);
1519 window_copy_cmd_previous_matching_bracket(cs);
1521 px = data->cx;
1522 py = screen_hsize(s) + data->cy - data->oy;
1523 grid_get_cell(s->grid, px, py, &gc);
1524 if (gc.data.size == 1 &&
1525 (~gc.flags & GRID_FLAG_PADDING) &&
1526 strchr(close, *gc.data.data) != NULL)
1527 window_copy_scroll_to(wme, sx, sy, 0);
1528 break;
1531 cp = strchr(open, found);
1533 if (cp == NULL) {
1534 if (data->modekeys == MODEKEY_EMACS) {
1535 if (!tried && px <= xx) {
1536 px++;
1537 tried = 1;
1538 goto retry;
1540 window_copy_cursor_next_word_end(wme, open, 0);
1541 continue;
1543 /* For vi, continue searching for bracket until EOL. */
1544 if (px > xx) {
1545 if (py == yy)
1546 continue;
1547 gl = grid_get_line(s->grid, py);
1548 if (~gl->flags & GRID_LINE_WRAPPED)
1549 continue;
1550 if (gl->cellsize > s->grid->sx)
1551 continue;
1552 px = 0;
1553 py++;
1554 xx = window_copy_find_length(wme, py);
1555 } else
1556 px++;
1557 goto retry;
1559 end = close[cp - open];
1561 /* Walk forward until the matching bracket is reached. */
1562 n = 1;
1563 failed = 0;
1564 do {
1565 if (px > xx) {
1566 if (py == yy) {
1567 failed = 1;
1568 break;
1570 px = 0;
1571 py++;
1572 xx = window_copy_find_length(wme, py);
1573 } else
1574 px++;
1576 grid_get_cell(s->grid, px, py, &gc);
1577 if (gc.data.size == 1 &&
1578 (~gc.flags & GRID_FLAG_PADDING)) {
1579 if (*gc.data.data == found)
1580 n++;
1581 else if (*gc.data.data == end)
1582 n--;
1584 } while (n != 0);
1586 /* Move the cursor to the found location if any. */
1587 if (!failed)
1588 window_copy_scroll_to(wme, px, py, 0);
1591 return (WINDOW_COPY_CMD_NOTHING);
1594 static enum window_copy_cmd_action
1595 window_copy_cmd_next_paragraph(struct window_copy_cmd_state *cs)
1597 struct window_mode_entry *wme = cs->wme;
1598 u_int np = wme->prefix;
1600 for (; np != 0; np--)
1601 window_copy_next_paragraph(wme);
1602 return (WINDOW_COPY_CMD_NOTHING);
1605 static enum window_copy_cmd_action
1606 window_copy_cmd_next_space(struct window_copy_cmd_state *cs)
1608 struct window_mode_entry *wme = cs->wme;
1609 u_int np = wme->prefix;
1611 for (; np != 0; np--)
1612 window_copy_cursor_next_word(wme, "");
1613 return (WINDOW_COPY_CMD_NOTHING);
1616 static enum window_copy_cmd_action
1617 window_copy_cmd_next_space_end(struct window_copy_cmd_state *cs)
1619 struct window_mode_entry *wme = cs->wme;
1620 u_int np = wme->prefix;
1622 for (; np != 0; np--)
1623 window_copy_cursor_next_word_end(wme, "", 0);
1624 return (WINDOW_COPY_CMD_NOTHING);
1627 static enum window_copy_cmd_action
1628 window_copy_cmd_next_word(struct window_copy_cmd_state *cs)
1630 struct window_mode_entry *wme = cs->wme;
1631 u_int np = wme->prefix;
1632 const char *separators;
1634 separators = options_get_string(cs->s->options, "word-separators");
1636 for (; np != 0; np--)
1637 window_copy_cursor_next_word(wme, separators);
1638 return (WINDOW_COPY_CMD_NOTHING);
1641 static enum window_copy_cmd_action
1642 window_copy_cmd_next_word_end(struct window_copy_cmd_state *cs)
1644 struct window_mode_entry *wme = cs->wme;
1645 u_int np = wme->prefix;
1646 const char *separators;
1648 separators = options_get_string(cs->s->options, "word-separators");
1650 for (; np != 0; np--)
1651 window_copy_cursor_next_word_end(wme, separators, 0);
1652 return (WINDOW_COPY_CMD_NOTHING);
1655 static enum window_copy_cmd_action
1656 window_copy_cmd_other_end(struct window_copy_cmd_state *cs)
1658 struct window_mode_entry *wme = cs->wme;
1659 u_int np = wme->prefix;
1660 struct window_copy_mode_data *data = wme->data;
1662 data->selflag = SEL_CHAR;
1663 if ((np % 2) != 0)
1664 window_copy_other_end(wme);
1665 return (WINDOW_COPY_CMD_NOTHING);
1668 static enum window_copy_cmd_action
1669 window_copy_cmd_page_down(struct window_copy_cmd_state *cs)
1671 struct window_mode_entry *wme = cs->wme;
1672 struct window_copy_mode_data *data = wme->data;
1673 u_int np = wme->prefix;
1675 for (; np != 0; np--) {
1676 if (window_copy_pagedown(wme, 0, data->scroll_exit))
1677 return (WINDOW_COPY_CMD_CANCEL);
1679 return (WINDOW_COPY_CMD_NOTHING);
1682 static enum window_copy_cmd_action
1683 window_copy_cmd_page_down_and_cancel(struct window_copy_cmd_state *cs)
1685 struct window_mode_entry *wme = cs->wme;
1686 u_int np = wme->prefix;
1688 for (; np != 0; np--) {
1689 if (window_copy_pagedown(wme, 0, 1))
1690 return (WINDOW_COPY_CMD_CANCEL);
1692 return (WINDOW_COPY_CMD_NOTHING);
1695 static enum window_copy_cmd_action
1696 window_copy_cmd_page_up(struct window_copy_cmd_state *cs)
1698 struct window_mode_entry *wme = cs->wme;
1699 u_int np = wme->prefix;
1701 for (; np != 0; np--)
1702 window_copy_pageup1(wme, 0);
1703 return (WINDOW_COPY_CMD_NOTHING);
1706 static enum window_copy_cmd_action
1707 window_copy_cmd_previous_paragraph(struct window_copy_cmd_state *cs)
1709 struct window_mode_entry *wme = cs->wme;
1710 u_int np = wme->prefix;
1712 for (; np != 0; np--)
1713 window_copy_previous_paragraph(wme);
1714 return (WINDOW_COPY_CMD_NOTHING);
1717 static enum window_copy_cmd_action
1718 window_copy_cmd_previous_space(struct window_copy_cmd_state *cs)
1720 struct window_mode_entry *wme = cs->wme;
1721 u_int np = wme->prefix;
1723 for (; np != 0; np--)
1724 window_copy_cursor_previous_word(wme, "", 1);
1725 return (WINDOW_COPY_CMD_NOTHING);
1728 static enum window_copy_cmd_action
1729 window_copy_cmd_previous_word(struct window_copy_cmd_state *cs)
1731 struct window_mode_entry *wme = cs->wme;
1732 u_int np = wme->prefix;
1733 const char *separators;
1735 separators = options_get_string(cs->s->options, "word-separators");
1737 for (; np != 0; np--)
1738 window_copy_cursor_previous_word(wme, separators, 1);
1739 return (WINDOW_COPY_CMD_NOTHING);
1742 static enum window_copy_cmd_action
1743 window_copy_cmd_rectangle_on(struct window_copy_cmd_state *cs)
1745 struct window_mode_entry *wme = cs->wme;
1746 struct window_copy_mode_data *data = wme->data;
1748 data->lineflag = LINE_SEL_NONE;
1749 window_copy_rectangle_set(wme, 1);
1751 return (WINDOW_COPY_CMD_NOTHING);
1754 static enum window_copy_cmd_action
1755 window_copy_cmd_rectangle_off(struct window_copy_cmd_state *cs)
1757 struct window_mode_entry *wme = cs->wme;
1758 struct window_copy_mode_data *data = wme->data;
1760 data->lineflag = LINE_SEL_NONE;
1761 window_copy_rectangle_set(wme, 0);
1763 return (WINDOW_COPY_CMD_NOTHING);
1766 static enum window_copy_cmd_action
1767 window_copy_cmd_rectangle_toggle(struct window_copy_cmd_state *cs)
1769 struct window_mode_entry *wme = cs->wme;
1770 struct window_copy_mode_data *data = wme->data;
1772 data->lineflag = LINE_SEL_NONE;
1773 window_copy_rectangle_set(wme, !data->rectflag);
1775 return (WINDOW_COPY_CMD_NOTHING);
1778 static enum window_copy_cmd_action
1779 window_copy_cmd_scroll_down(struct window_copy_cmd_state *cs)
1781 struct window_mode_entry *wme = cs->wme;
1782 struct window_copy_mode_data *data = wme->data;
1783 u_int np = wme->prefix;
1785 for (; np != 0; np--)
1786 window_copy_cursor_down(wme, 1);
1787 if (data->scroll_exit && data->oy == 0)
1788 return (WINDOW_COPY_CMD_CANCEL);
1789 return (WINDOW_COPY_CMD_NOTHING);
1792 static enum window_copy_cmd_action
1793 window_copy_cmd_scroll_down_and_cancel(struct window_copy_cmd_state *cs)
1795 struct window_mode_entry *wme = cs->wme;
1796 struct window_copy_mode_data *data = wme->data;
1797 u_int np = wme->prefix;
1799 for (; np != 0; np--)
1800 window_copy_cursor_down(wme, 1);
1801 if (data->oy == 0)
1802 return (WINDOW_COPY_CMD_CANCEL);
1803 return (WINDOW_COPY_CMD_NOTHING);
1806 static enum window_copy_cmd_action
1807 window_copy_cmd_scroll_up(struct window_copy_cmd_state *cs)
1809 struct window_mode_entry *wme = cs->wme;
1810 u_int np = wme->prefix;
1812 for (; np != 0; np--)
1813 window_copy_cursor_up(wme, 1);
1814 return (WINDOW_COPY_CMD_NOTHING);
1817 static enum window_copy_cmd_action
1818 window_copy_cmd_search_again(struct window_copy_cmd_state *cs)
1820 struct window_mode_entry *wme = cs->wme;
1821 struct window_copy_mode_data *data = wme->data;
1822 u_int np = wme->prefix;
1824 if (data->searchtype == WINDOW_COPY_SEARCHUP) {
1825 for (; np != 0; np--)
1826 window_copy_search_up(wme, data->searchregex);
1827 } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
1828 for (; np != 0; np--)
1829 window_copy_search_down(wme, data->searchregex);
1831 return (WINDOW_COPY_CMD_NOTHING);
1834 static enum window_copy_cmd_action
1835 window_copy_cmd_search_reverse(struct window_copy_cmd_state *cs)
1837 struct window_mode_entry *wme = cs->wme;
1838 struct window_copy_mode_data *data = wme->data;
1839 u_int np = wme->prefix;
1841 if (data->searchtype == WINDOW_COPY_SEARCHUP) {
1842 for (; np != 0; np--)
1843 window_copy_search_down(wme, data->searchregex);
1844 } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
1845 for (; np != 0; np--)
1846 window_copy_search_up(wme, data->searchregex);
1848 return (WINDOW_COPY_CMD_NOTHING);
1851 static enum window_copy_cmd_action
1852 window_copy_cmd_select_line(struct window_copy_cmd_state *cs)
1854 struct window_mode_entry *wme = cs->wme;
1855 struct window_copy_mode_data *data = wme->data;
1856 u_int np = wme->prefix;
1858 data->lineflag = LINE_SEL_LEFT_RIGHT;
1859 data->rectflag = 0;
1860 data->selflag = SEL_LINE;
1861 data->dx = data->cx;
1862 data->dy = screen_hsize(data->backing) + data->cy - data->oy;
1864 window_copy_cursor_start_of_line(wme);
1865 data->selrx = data->cx;
1866 data->selry = screen_hsize(data->backing) + data->cy - data->oy;
1867 data->endselry = data->selry;
1868 window_copy_start_selection(wme);
1869 window_copy_cursor_end_of_line(wme);
1870 data->endselry = screen_hsize(data->backing) + data->cy - data->oy;
1871 data->endselrx = window_copy_find_length(wme, data->endselry);
1872 for (; np > 1; np--) {
1873 window_copy_cursor_down(wme, 0);
1874 window_copy_cursor_end_of_line(wme);
1877 return (WINDOW_COPY_CMD_REDRAW);
1880 static enum window_copy_cmd_action
1881 window_copy_cmd_select_word(struct window_copy_cmd_state *cs)
1883 struct window_mode_entry *wme = cs->wme;
1884 struct options *session_options = cs->s->options;
1885 struct window_copy_mode_data *data = wme->data;
1886 u_int px, py, nextx, nexty;
1888 data->lineflag = LINE_SEL_LEFT_RIGHT;
1889 data->rectflag = 0;
1890 data->selflag = SEL_WORD;
1891 data->dx = data->cx;
1892 data->dy = screen_hsize(data->backing) + data->cy - data->oy;
1894 data->separators = options_get_string(session_options,
1895 "word-separators");
1896 window_copy_cursor_previous_word(wme, data->separators, 0);
1897 px = data->cx;
1898 py = screen_hsize(data->backing) + data->cy - data->oy;
1899 data->selrx = px;
1900 data->selry = py;
1901 window_copy_start_selection(wme);
1903 /* Handle single character words. */
1904 nextx = px + 1;
1905 nexty = py;
1906 if (grid_get_line(data->backing->grid, nexty)->flags &
1907 GRID_LINE_WRAPPED && nextx > screen_size_x(data->backing) - 1) {
1908 nextx = 0;
1909 nexty++;
1911 if (px >= window_copy_find_length(wme, py) ||
1912 !window_copy_in_set(wme, nextx, nexty, WHITESPACE))
1913 window_copy_cursor_next_word_end(wme, data->separators, 1);
1914 else {
1915 window_copy_update_cursor(wme, px, data->cy);
1916 if (window_copy_update_selection(wme, 1, 1))
1917 window_copy_redraw_lines(wme, data->cy, 1);
1919 data->endselrx = data->cx;
1920 data->endselry = screen_hsize(data->backing) + data->cy - data->oy;
1921 if (data->dy > data->endselry) {
1922 data->dy = data->endselry;
1923 data->dx = data->endselrx;
1924 } else if (data->dx > data->endselrx)
1925 data->dx = data->endselrx;
1927 return (WINDOW_COPY_CMD_REDRAW);
1930 static enum window_copy_cmd_action
1931 window_copy_cmd_set_mark(struct window_copy_cmd_state *cs)
1933 struct window_copy_mode_data *data = cs->wme->data;
1935 data->mx = data->cx;
1936 data->my = screen_hsize(data->backing) + data->cy - data->oy;
1937 data->showmark = 1;
1938 return (WINDOW_COPY_CMD_REDRAW);
1941 static enum window_copy_cmd_action
1942 window_copy_cmd_start_of_line(struct window_copy_cmd_state *cs)
1944 struct window_mode_entry *wme = cs->wme;
1946 window_copy_cursor_start_of_line(wme);
1947 return (WINDOW_COPY_CMD_NOTHING);
1950 static enum window_copy_cmd_action
1951 window_copy_cmd_top_line(struct window_copy_cmd_state *cs)
1953 struct window_mode_entry *wme = cs->wme;
1954 struct window_copy_mode_data *data = wme->data;
1956 data->cx = 0;
1957 data->cy = 0;
1959 window_copy_update_selection(wme, 1, 0);
1960 return (WINDOW_COPY_CMD_REDRAW);
1963 static enum window_copy_cmd_action
1964 window_copy_cmd_copy_pipe_no_clear(struct window_copy_cmd_state *cs)
1966 struct window_mode_entry *wme = cs->wme;
1967 struct client *c = cs->c;
1968 struct session *s = cs->s;
1969 struct winlink *wl = cs->wl;
1970 struct window_pane *wp = wme->wp;
1971 char *command = NULL, *prefix = NULL;
1972 const char *arg1 = args_string(cs->args, 1);
1973 const char *arg2 = args_string(cs->args, 2);
1975 if (arg2 != NULL)
1976 prefix = format_single(NULL, arg2, c, s, wl, wp);
1978 if (s != NULL && arg1 != NULL && *arg1 != '\0')
1979 command = format_single(NULL, arg1, c, s, wl, wp);
1980 window_copy_copy_pipe(wme, s, prefix, command);
1981 free(command);
1983 free(prefix);
1984 return (WINDOW_COPY_CMD_NOTHING);
1987 static enum window_copy_cmd_action
1988 window_copy_cmd_copy_pipe(struct window_copy_cmd_state *cs)
1990 struct window_mode_entry *wme = cs->wme;
1992 window_copy_cmd_copy_pipe_no_clear(cs);
1993 window_copy_clear_selection(wme);
1994 return (WINDOW_COPY_CMD_REDRAW);
1997 static enum window_copy_cmd_action
1998 window_copy_cmd_copy_pipe_and_cancel(struct window_copy_cmd_state *cs)
2000 struct window_mode_entry *wme = cs->wme;
2002 window_copy_cmd_copy_pipe_no_clear(cs);
2003 window_copy_clear_selection(wme);
2004 return (WINDOW_COPY_CMD_CANCEL);
2007 static enum window_copy_cmd_action
2008 window_copy_cmd_pipe_no_clear(struct window_copy_cmd_state *cs)
2010 struct window_mode_entry *wme = cs->wme;
2011 struct client *c = cs->c;
2012 struct session *s = cs->s;
2013 struct winlink *wl = cs->wl;
2014 struct window_pane *wp = wme->wp;
2015 char *command = NULL;
2016 const char *arg1 = args_string(cs->args, 1);
2018 if (s != NULL && arg1 != NULL && *arg1 != '\0')
2019 command = format_single(NULL, arg1, c, s, wl, wp);
2020 window_copy_pipe(wme, s, command);
2021 free(command);
2023 return (WINDOW_COPY_CMD_NOTHING);
2026 static enum window_copy_cmd_action
2027 window_copy_cmd_pipe(struct window_copy_cmd_state *cs)
2029 struct window_mode_entry *wme = cs->wme;
2031 window_copy_cmd_pipe_no_clear(cs);
2032 window_copy_clear_selection(wme);
2033 return (WINDOW_COPY_CMD_REDRAW);
2036 static enum window_copy_cmd_action
2037 window_copy_cmd_pipe_and_cancel(struct window_copy_cmd_state *cs)
2039 struct window_mode_entry *wme = cs->wme;
2041 window_copy_cmd_pipe_no_clear(cs);
2042 window_copy_clear_selection(wme);
2043 return (WINDOW_COPY_CMD_CANCEL);
2046 static enum window_copy_cmd_action
2047 window_copy_cmd_goto_line(struct window_copy_cmd_state *cs)
2049 struct window_mode_entry *wme = cs->wme;
2050 const char *arg1 = args_string(cs->args, 1);
2052 if (*arg1 != '\0')
2053 window_copy_goto_line(wme, arg1);
2054 return (WINDOW_COPY_CMD_NOTHING);
2057 static enum window_copy_cmd_action
2058 window_copy_cmd_jump_backward(struct window_copy_cmd_state *cs)
2060 struct window_mode_entry *wme = cs->wme;
2061 struct window_copy_mode_data *data = wme->data;
2062 u_int np = wme->prefix;
2063 const char *arg1 = args_string(cs->args, 1);
2065 if (*arg1 != '\0') {
2066 data->jumptype = WINDOW_COPY_JUMPBACKWARD;
2067 free(data->jumpchar);
2068 data->jumpchar = utf8_fromcstr(arg1);
2069 for (; np != 0; np--)
2070 window_copy_cursor_jump_back(wme);
2072 return (WINDOW_COPY_CMD_NOTHING);
2075 static enum window_copy_cmd_action
2076 window_copy_cmd_jump_forward(struct window_copy_cmd_state *cs)
2078 struct window_mode_entry *wme = cs->wme;
2079 struct window_copy_mode_data *data = wme->data;
2080 u_int np = wme->prefix;
2081 const char *arg1 = args_string(cs->args, 1);
2083 if (*arg1 != '\0') {
2084 data->jumptype = WINDOW_COPY_JUMPFORWARD;
2085 free(data->jumpchar);
2086 data->jumpchar = utf8_fromcstr(arg1);
2087 for (; np != 0; np--)
2088 window_copy_cursor_jump(wme);
2090 return (WINDOW_COPY_CMD_NOTHING);
2093 static enum window_copy_cmd_action
2094 window_copy_cmd_jump_to_backward(struct window_copy_cmd_state *cs)
2096 struct window_mode_entry *wme = cs->wme;
2097 struct window_copy_mode_data *data = wme->data;
2098 u_int np = wme->prefix;
2099 const char *arg1 = args_string(cs->args, 1);
2101 if (*arg1 != '\0') {
2102 data->jumptype = WINDOW_COPY_JUMPTOBACKWARD;
2103 free(data->jumpchar);
2104 data->jumpchar = utf8_fromcstr(arg1);
2105 for (; np != 0; np--)
2106 window_copy_cursor_jump_to_back(wme);
2108 return (WINDOW_COPY_CMD_NOTHING);
2111 static enum window_copy_cmd_action
2112 window_copy_cmd_jump_to_forward(struct window_copy_cmd_state *cs)
2114 struct window_mode_entry *wme = cs->wme;
2115 struct window_copy_mode_data *data = wme->data;
2116 u_int np = wme->prefix;
2117 const char *arg1 = args_string(cs->args, 1);
2119 if (*arg1 != '\0') {
2120 data->jumptype = WINDOW_COPY_JUMPTOFORWARD;
2121 free(data->jumpchar);
2122 data->jumpchar = utf8_fromcstr(arg1);
2123 for (; np != 0; np--)
2124 window_copy_cursor_jump_to(wme);
2126 return (WINDOW_COPY_CMD_NOTHING);
2129 static enum window_copy_cmd_action
2130 window_copy_cmd_jump_to_mark(struct window_copy_cmd_state *cs)
2132 struct window_mode_entry *wme = cs->wme;
2134 window_copy_jump_to_mark(wme);
2135 return (WINDOW_COPY_CMD_NOTHING);
2138 static enum window_copy_cmd_action
2139 window_copy_cmd_search_backward(struct window_copy_cmd_state *cs)
2141 struct window_mode_entry *wme = cs->wme;
2142 struct window_copy_mode_data *data = wme->data;
2143 u_int np = wme->prefix;
2145 if (!window_copy_expand_search_string(cs))
2146 return (WINDOW_COPY_CMD_NOTHING);
2148 if (data->searchstr != NULL) {
2149 data->searchtype = WINDOW_COPY_SEARCHUP;
2150 data->searchregex = 1;
2151 data->timeout = 0;
2152 for (; np != 0; np--)
2153 window_copy_search_up(wme, 1);
2155 return (WINDOW_COPY_CMD_NOTHING);
2158 static enum window_copy_cmd_action
2159 window_copy_cmd_search_backward_text(struct window_copy_cmd_state *cs)
2161 struct window_mode_entry *wme = cs->wme;
2162 struct window_copy_mode_data *data = wme->data;
2163 u_int np = wme->prefix;
2165 if (!window_copy_expand_search_string(cs))
2166 return (WINDOW_COPY_CMD_NOTHING);
2168 if (data->searchstr != NULL) {
2169 data->searchtype = WINDOW_COPY_SEARCHUP;
2170 data->searchregex = 0;
2171 data->timeout = 0;
2172 for (; np != 0; np--)
2173 window_copy_search_up(wme, 0);
2175 return (WINDOW_COPY_CMD_NOTHING);
2178 static enum window_copy_cmd_action
2179 window_copy_cmd_search_forward(struct window_copy_cmd_state *cs)
2181 struct window_mode_entry *wme = cs->wme;
2182 struct window_copy_mode_data *data = wme->data;
2183 u_int np = wme->prefix;
2185 if (!window_copy_expand_search_string(cs))
2186 return (WINDOW_COPY_CMD_NOTHING);
2188 if (data->searchstr != NULL) {
2189 data->searchtype = WINDOW_COPY_SEARCHDOWN;
2190 data->searchregex = 1;
2191 data->timeout = 0;
2192 for (; np != 0; np--)
2193 window_copy_search_down(wme, 1);
2195 return (WINDOW_COPY_CMD_NOTHING);
2198 static enum window_copy_cmd_action
2199 window_copy_cmd_search_forward_text(struct window_copy_cmd_state *cs)
2201 struct window_mode_entry *wme = cs->wme;
2202 struct window_copy_mode_data *data = wme->data;
2203 u_int np = wme->prefix;
2205 if (!window_copy_expand_search_string(cs))
2206 return (WINDOW_COPY_CMD_NOTHING);
2208 if (data->searchstr != NULL) {
2209 data->searchtype = WINDOW_COPY_SEARCHDOWN;
2210 data->searchregex = 0;
2211 data->timeout = 0;
2212 for (; np != 0; np--)
2213 window_copy_search_down(wme, 0);
2215 return (WINDOW_COPY_CMD_NOTHING);
2218 static enum window_copy_cmd_action
2219 window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs)
2221 struct window_mode_entry *wme = cs->wme;
2222 struct window_copy_mode_data *data = wme->data;
2223 const char *arg1 = args_string(cs->args, 1);
2224 const char *ss = data->searchstr;
2225 char prefix;
2226 enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING;
2228 data->timeout = 0;
2230 log_debug("%s: %s", __func__, arg1);
2232 prefix = *arg1++;
2233 if (data->searchx == -1 || data->searchy == -1) {
2234 data->searchx = data->cx;
2235 data->searchy = data->cy;
2236 data->searcho = data->oy;
2237 } else if (ss != NULL && strcmp(arg1, ss) != 0) {
2238 data->cx = data->searchx;
2239 data->cy = data->searchy;
2240 data->oy = data->searcho;
2241 action = WINDOW_COPY_CMD_REDRAW;
2243 if (*arg1 == '\0') {
2244 window_copy_clear_marks(wme);
2245 return (WINDOW_COPY_CMD_REDRAW);
2247 switch (prefix) {
2248 case '=':
2249 case '-':
2250 data->searchtype = WINDOW_COPY_SEARCHUP;
2251 data->searchregex = 0;
2252 free(data->searchstr);
2253 data->searchstr = xstrdup(arg1);
2254 if (!window_copy_search_up(wme, 0)) {
2255 window_copy_clear_marks(wme);
2256 return (WINDOW_COPY_CMD_REDRAW);
2258 break;
2259 case '+':
2260 data->searchtype = WINDOW_COPY_SEARCHDOWN;
2261 data->searchregex = 0;
2262 free(data->searchstr);
2263 data->searchstr = xstrdup(arg1);
2264 if (!window_copy_search_down(wme, 0)) {
2265 window_copy_clear_marks(wme);
2266 return (WINDOW_COPY_CMD_REDRAW);
2268 break;
2270 return (action);
2273 static enum window_copy_cmd_action
2274 window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs)
2276 struct window_mode_entry *wme = cs->wme;
2277 struct window_copy_mode_data *data = wme->data;
2278 const char *arg1 = args_string(cs->args, 1);
2279 const char *ss = data->searchstr;
2280 char prefix;
2281 enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING;
2283 data->timeout = 0;
2285 log_debug("%s: %s", __func__, arg1);
2287 prefix = *arg1++;
2288 if (data->searchx == -1 || data->searchy == -1) {
2289 data->searchx = data->cx;
2290 data->searchy = data->cy;
2291 data->searcho = data->oy;
2292 } else if (ss != NULL && strcmp(arg1, ss) != 0) {
2293 data->cx = data->searchx;
2294 data->cy = data->searchy;
2295 data->oy = data->searcho;
2296 action = WINDOW_COPY_CMD_REDRAW;
2298 if (*arg1 == '\0') {
2299 window_copy_clear_marks(wme);
2300 return (WINDOW_COPY_CMD_REDRAW);
2302 switch (prefix) {
2303 case '=':
2304 case '+':
2305 data->searchtype = WINDOW_COPY_SEARCHDOWN;
2306 data->searchregex = 0;
2307 free(data->searchstr);
2308 data->searchstr = xstrdup(arg1);
2309 if (!window_copy_search_down(wme, 0)) {
2310 window_copy_clear_marks(wme);
2311 return (WINDOW_COPY_CMD_REDRAW);
2313 break;
2314 case '-':
2315 data->searchtype = WINDOW_COPY_SEARCHUP;
2316 data->searchregex = 0;
2317 free(data->searchstr);
2318 data->searchstr = xstrdup(arg1);
2319 if (!window_copy_search_up(wme, 0)) {
2320 window_copy_clear_marks(wme);
2321 return (WINDOW_COPY_CMD_REDRAW);
2324 return (action);
2327 static enum window_copy_cmd_action
2328 window_copy_cmd_refresh_from_pane(struct window_copy_cmd_state *cs)
2330 struct window_mode_entry *wme = cs->wme;
2331 struct window_pane *wp = wme->swp;
2332 struct window_copy_mode_data *data = wme->data;
2334 if (data->viewmode)
2335 return (WINDOW_COPY_CMD_NOTHING);
2337 screen_free(data->backing);
2338 free(data->backing);
2339 data->backing = window_copy_clone_screen(&wp->base, &data->screen, NULL, NULL, wme->swp != wme->wp);
2341 window_copy_size_changed(wme);
2342 return (WINDOW_COPY_CMD_REDRAW);
2345 static const struct {
2346 const char *command;
2347 u_int minargs;
2348 u_int maxargs;
2349 enum window_copy_cmd_clear clear;
2350 enum window_copy_cmd_action (*f)(struct window_copy_cmd_state *);
2351 } window_copy_cmd_table[] = {
2352 { .command = "append-selection",
2353 .minargs = 0,
2354 .maxargs = 0,
2355 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2356 .f = window_copy_cmd_append_selection
2358 { .command = "append-selection-and-cancel",
2359 .minargs = 0,
2360 .maxargs = 0,
2361 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2362 .f = window_copy_cmd_append_selection_and_cancel
2364 { .command = "back-to-indentation",
2365 .minargs = 0,
2366 .maxargs = 0,
2367 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2368 .f = window_copy_cmd_back_to_indentation
2370 { .command = "begin-selection",
2371 .minargs = 0,
2372 .maxargs = 0,
2373 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2374 .f = window_copy_cmd_begin_selection
2376 { .command = "bottom-line",
2377 .minargs = 0,
2378 .maxargs = 0,
2379 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2380 .f = window_copy_cmd_bottom_line
2382 { .command = "cancel",
2383 .minargs = 0,
2384 .maxargs = 0,
2385 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2386 .f = window_copy_cmd_cancel
2388 { .command = "clear-selection",
2389 .minargs = 0,
2390 .maxargs = 0,
2391 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2392 .f = window_copy_cmd_clear_selection
2394 { .command = "copy-end-of-line",
2395 .minargs = 0,
2396 .maxargs = 1,
2397 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2398 .f = window_copy_cmd_copy_end_of_line
2400 { .command = "copy-end-of-line-and-cancel",
2401 .minargs = 0,
2402 .maxargs = 1,
2403 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2404 .f = window_copy_cmd_copy_end_of_line_and_cancel
2406 { .command = "copy-pipe-end-of-line",
2407 .minargs = 0,
2408 .maxargs = 2,
2409 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2410 .f = window_copy_cmd_copy_pipe_end_of_line
2412 { .command = "copy-pipe-end-of-line-and-cancel",
2413 .minargs = 0,
2414 .maxargs = 2,
2415 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2416 .f = window_copy_cmd_copy_pipe_end_of_line_and_cancel
2418 { .command = "copy-line",
2419 .minargs = 0,
2420 .maxargs = 1,
2421 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2422 .f = window_copy_cmd_copy_line
2424 { .command = "copy-line-and-cancel",
2425 .minargs = 0,
2426 .maxargs = 1,
2427 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2428 .f = window_copy_cmd_copy_line_and_cancel
2430 { .command = "copy-pipe-line",
2431 .minargs = 0,
2432 .maxargs = 2,
2433 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2434 .f = window_copy_cmd_copy_pipe_line
2436 { .command = "copy-pipe-line-and-cancel",
2437 .minargs = 0,
2438 .maxargs = 2,
2439 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2440 .f = window_copy_cmd_copy_pipe_line_and_cancel
2442 { .command = "copy-pipe-no-clear",
2443 .minargs = 0,
2444 .maxargs = 2,
2445 .clear = WINDOW_COPY_CMD_CLEAR_NEVER,
2446 .f = window_copy_cmd_copy_pipe_no_clear
2448 { .command = "copy-pipe",
2449 .minargs = 0,
2450 .maxargs = 2,
2451 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2452 .f = window_copy_cmd_copy_pipe
2454 { .command = "copy-pipe-and-cancel",
2455 .minargs = 0,
2456 .maxargs = 2,
2457 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2458 .f = window_copy_cmd_copy_pipe_and_cancel
2460 { .command = "copy-selection-no-clear",
2461 .minargs = 0,
2462 .maxargs = 1,
2463 .clear = WINDOW_COPY_CMD_CLEAR_NEVER,
2464 .f = window_copy_cmd_copy_selection_no_clear
2466 { .command = "copy-selection",
2467 .minargs = 0,
2468 .maxargs = 1,
2469 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2470 .f = window_copy_cmd_copy_selection
2472 { .command = "copy-selection-and-cancel",
2473 .minargs = 0,
2474 .maxargs = 1,
2475 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2476 .f = window_copy_cmd_copy_selection_and_cancel
2478 { .command = "cursor-down",
2479 .minargs = 0,
2480 .maxargs = 0,
2481 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2482 .f = window_copy_cmd_cursor_down
2484 { .command = "cursor-down-and-cancel",
2485 .minargs = 0,
2486 .maxargs = 0,
2487 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2488 .f = window_copy_cmd_cursor_down_and_cancel
2490 { .command = "cursor-left",
2491 .minargs = 0,
2492 .maxargs = 0,
2493 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2494 .f = window_copy_cmd_cursor_left
2496 { .command = "cursor-right",
2497 .minargs = 0,
2498 .maxargs = 0,
2499 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2500 .f = window_copy_cmd_cursor_right
2502 { .command = "cursor-up",
2503 .minargs = 0,
2504 .maxargs = 0,
2505 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2506 .f = window_copy_cmd_cursor_up
2508 { .command = "end-of-line",
2509 .minargs = 0,
2510 .maxargs = 0,
2511 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2512 .f = window_copy_cmd_end_of_line
2514 { .command = "goto-line",
2515 .minargs = 1,
2516 .maxargs = 1,
2517 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2518 .f = window_copy_cmd_goto_line
2520 { .command = "halfpage-down",
2521 .minargs = 0,
2522 .maxargs = 0,
2523 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2524 .f = window_copy_cmd_halfpage_down
2526 { .command = "halfpage-down-and-cancel",
2527 .minargs = 0,
2528 .maxargs = 0,
2529 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2530 .f = window_copy_cmd_halfpage_down_and_cancel
2532 { .command = "halfpage-up",
2533 .minargs = 0,
2534 .maxargs = 0,
2535 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2536 .f = window_copy_cmd_halfpage_up
2538 { .command = "history-bottom",
2539 .minargs = 0,
2540 .maxargs = 0,
2541 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2542 .f = window_copy_cmd_history_bottom
2544 { .command = "history-top",
2545 .minargs = 0,
2546 .maxargs = 0,
2547 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2548 .f = window_copy_cmd_history_top
2550 { .command = "jump-again",
2551 .minargs = 0,
2552 .maxargs = 0,
2553 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2554 .f = window_copy_cmd_jump_again
2556 { .command = "jump-backward",
2557 .minargs = 1,
2558 .maxargs = 1,
2559 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2560 .f = window_copy_cmd_jump_backward
2562 { .command = "jump-forward",
2563 .minargs = 1,
2564 .maxargs = 1,
2565 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2566 .f = window_copy_cmd_jump_forward
2568 { .command = "jump-reverse",
2569 .minargs = 0,
2570 .maxargs = 0,
2571 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2572 .f = window_copy_cmd_jump_reverse
2574 { .command = "jump-to-backward",
2575 .minargs = 1,
2576 .maxargs = 1,
2577 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2578 .f = window_copy_cmd_jump_to_backward
2580 { .command = "jump-to-forward",
2581 .minargs = 1,
2582 .maxargs = 1,
2583 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2584 .f = window_copy_cmd_jump_to_forward
2586 { .command = "jump-to-mark",
2587 .minargs = 0,
2588 .maxargs = 0,
2589 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2590 .f = window_copy_cmd_jump_to_mark
2592 { .command = "middle-line",
2593 .minargs = 0,
2594 .maxargs = 0,
2595 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2596 .f = window_copy_cmd_middle_line
2598 { .command = "next-matching-bracket",
2599 .minargs = 0,
2600 .maxargs = 0,
2601 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2602 .f = window_copy_cmd_next_matching_bracket
2604 { .command = "next-paragraph",
2605 .minargs = 0,
2606 .maxargs = 0,
2607 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2608 .f = window_copy_cmd_next_paragraph
2610 { .command = "next-space",
2611 .minargs = 0,
2612 .maxargs = 0,
2613 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2614 .f = window_copy_cmd_next_space
2616 { .command = "next-space-end",
2617 .minargs = 0,
2618 .maxargs = 0,
2619 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2620 .f = window_copy_cmd_next_space_end
2622 { .command = "next-word",
2623 .minargs = 0,
2624 .maxargs = 0,
2625 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2626 .f = window_copy_cmd_next_word
2628 { .command = "next-word-end",
2629 .minargs = 0,
2630 .maxargs = 0,
2631 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2632 .f = window_copy_cmd_next_word_end
2634 { .command = "other-end",
2635 .minargs = 0,
2636 .maxargs = 0,
2637 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2638 .f = window_copy_cmd_other_end
2640 { .command = "page-down",
2641 .minargs = 0,
2642 .maxargs = 0,
2643 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2644 .f = window_copy_cmd_page_down
2646 { .command = "page-down-and-cancel",
2647 .minargs = 0,
2648 .maxargs = 0,
2649 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2650 .f = window_copy_cmd_page_down_and_cancel
2652 { .command = "page-up",
2653 .minargs = 0,
2654 .maxargs = 0,
2655 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2656 .f = window_copy_cmd_page_up
2658 { .command = "pipe-no-clear",
2659 .minargs = 0,
2660 .maxargs = 1,
2661 .clear = WINDOW_COPY_CMD_CLEAR_NEVER,
2662 .f = window_copy_cmd_pipe_no_clear
2664 { .command = "pipe",
2665 .minargs = 0,
2666 .maxargs = 1,
2667 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2668 .f = window_copy_cmd_pipe
2670 { .command = "pipe-and-cancel",
2671 .minargs = 0,
2672 .maxargs = 1,
2673 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2674 .f = window_copy_cmd_pipe_and_cancel
2676 { .command = "previous-matching-bracket",
2677 .minargs = 0,
2678 .maxargs = 0,
2679 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2680 .f = window_copy_cmd_previous_matching_bracket
2682 { .command = "previous-paragraph",
2683 .minargs = 0,
2684 .maxargs = 0,
2685 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2686 .f = window_copy_cmd_previous_paragraph
2688 { .command = "previous-space",
2689 .minargs = 0,
2690 .maxargs = 0,
2691 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2692 .f = window_copy_cmd_previous_space
2694 { .command = "previous-word",
2695 .minargs = 0,
2696 .maxargs = 0,
2697 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2698 .f = window_copy_cmd_previous_word
2700 { .command = "rectangle-on",
2701 .minargs = 0,
2702 .maxargs = 0,
2703 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2704 .f = window_copy_cmd_rectangle_on
2706 { .command = "rectangle-off",
2707 .minargs = 0,
2708 .maxargs = 0,
2709 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2710 .f = window_copy_cmd_rectangle_off
2712 { .command = "rectangle-toggle",
2713 .minargs = 0,
2714 .maxargs = 0,
2715 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2716 .f = window_copy_cmd_rectangle_toggle
2718 { .command = "refresh-from-pane",
2719 .minargs = 0,
2720 .maxargs = 0,
2721 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2722 .f = window_copy_cmd_refresh_from_pane
2724 { .command = "scroll-down",
2725 .minargs = 0,
2726 .maxargs = 0,
2727 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2728 .f = window_copy_cmd_scroll_down
2730 { .command = "scroll-down-and-cancel",
2731 .minargs = 0,
2732 .maxargs = 0,
2733 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2734 .f = window_copy_cmd_scroll_down_and_cancel
2736 { .command = "scroll-up",
2737 .minargs = 0,
2738 .maxargs = 0,
2739 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2740 .f = window_copy_cmd_scroll_up
2742 { .command = "search-again",
2743 .minargs = 0,
2744 .maxargs = 0,
2745 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2746 .f = window_copy_cmd_search_again
2748 { .command = "search-backward",
2749 .minargs = 0,
2750 .maxargs = 1,
2751 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2752 .f = window_copy_cmd_search_backward
2754 { .command = "search-backward-text",
2755 .minargs = 0,
2756 .maxargs = 1,
2757 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2758 .f = window_copy_cmd_search_backward_text
2760 { .command = "search-backward-incremental",
2761 .minargs = 1,
2762 .maxargs = 1,
2763 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2764 .f = window_copy_cmd_search_backward_incremental
2766 { .command = "search-forward",
2767 .minargs = 0,
2768 .maxargs = 1,
2769 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2770 .f = window_copy_cmd_search_forward
2772 { .command = "search-forward-text",
2773 .minargs = 0,
2774 .maxargs = 1,
2775 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2776 .f = window_copy_cmd_search_forward_text
2778 { .command = "search-forward-incremental",
2779 .minargs = 1,
2780 .maxargs = 1,
2781 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2782 .f = window_copy_cmd_search_forward_incremental
2784 { .command = "search-reverse",
2785 .minargs = 0,
2786 .maxargs = 0,
2787 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2788 .f = window_copy_cmd_search_reverse
2790 { .command = "select-line",
2791 .minargs = 0,
2792 .maxargs = 0,
2793 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2794 .f = window_copy_cmd_select_line
2796 { .command = "select-word",
2797 .minargs = 0,
2798 .maxargs = 0,
2799 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2800 .f = window_copy_cmd_select_word
2802 { .command = "set-mark",
2803 .minargs = 0,
2804 .maxargs = 0,
2805 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2806 .f = window_copy_cmd_set_mark
2808 { .command = "start-of-line",
2809 .minargs = 0,
2810 .maxargs = 0,
2811 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2812 .f = window_copy_cmd_start_of_line
2814 { .command = "stop-selection",
2815 .minargs = 0,
2816 .maxargs = 0,
2817 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2818 .f = window_copy_cmd_stop_selection
2820 { .command = "top-line",
2821 .minargs = 0,
2822 .maxargs = 0,
2823 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2824 .f = window_copy_cmd_top_line
2828 static void
2829 window_copy_command(struct window_mode_entry *wme, struct client *c,
2830 struct session *s, struct winlink *wl, struct args *args,
2831 struct mouse_event *m)
2833 struct window_copy_mode_data *data = wme->data;
2834 struct window_copy_cmd_state cs;
2835 enum window_copy_cmd_action action;
2836 enum window_copy_cmd_clear clear = WINDOW_COPY_CMD_CLEAR_NEVER;
2837 const char *command;
2838 u_int i, count = args_count(args);
2839 int keys;
2841 if (count == 0)
2842 return;
2843 command = args_string(args, 0);
2845 if (m != NULL && m->valid && !MOUSE_WHEEL(m->b))
2846 window_copy_move_mouse(m);
2848 cs.wme = wme;
2849 cs.args = args;
2850 cs.m = m;
2852 cs.c = c;
2853 cs.s = s;
2854 cs.wl = wl;
2856 action = WINDOW_COPY_CMD_NOTHING;
2857 for (i = 0; i < nitems(window_copy_cmd_table); i++) {
2858 if (strcmp(window_copy_cmd_table[i].command, command) == 0) {
2859 if (count - 1 < window_copy_cmd_table[i].minargs ||
2860 count - 1 > window_copy_cmd_table[i].maxargs)
2861 break;
2862 clear = window_copy_cmd_table[i].clear;
2863 action = window_copy_cmd_table[i].f(&cs);
2864 break;
2868 if (strncmp(command, "search-", 7) != 0 && data->searchmark != NULL) {
2869 keys = options_get_number(wme->wp->window->options, "mode-keys");
2870 if (clear == WINDOW_COPY_CMD_CLEAR_EMACS_ONLY &&
2871 keys == MODEKEY_VI)
2872 clear = WINDOW_COPY_CMD_CLEAR_NEVER;
2873 if (clear != WINDOW_COPY_CMD_CLEAR_NEVER) {
2874 window_copy_clear_marks(wme);
2875 data->searchx = data->searchy = -1;
2877 if (action == WINDOW_COPY_CMD_NOTHING)
2878 action = WINDOW_COPY_CMD_REDRAW;
2880 wme->prefix = 1;
2882 if (action == WINDOW_COPY_CMD_CANCEL)
2883 window_pane_reset_mode(wme->wp);
2884 else if (action == WINDOW_COPY_CMD_REDRAW)
2885 window_copy_redraw_screen(wme);
2888 static void
2889 window_copy_scroll_to(struct window_mode_entry *wme, u_int px, u_int py,
2890 int no_redraw)
2892 struct window_copy_mode_data *data = wme->data;
2893 struct grid *gd = data->backing->grid;
2894 u_int offset, gap;
2896 data->cx = px;
2898 if (py >= gd->hsize - data->oy && py < gd->hsize - data->oy + gd->sy)
2899 data->cy = py - (gd->hsize - data->oy);
2900 else {
2901 gap = gd->sy / 4;
2902 if (py < gd->sy) {
2903 offset = 0;
2904 data->cy = py;
2905 } else if (py > gd->hsize + gd->sy - gap) {
2906 offset = gd->hsize;
2907 data->cy = py - gd->hsize;
2908 } else {
2909 offset = py + gap - gd->sy;
2910 data->cy = py - offset;
2912 data->oy = gd->hsize - offset;
2915 if (!no_redraw && data->searchmark != NULL && !data->timeout)
2916 window_copy_search_marks(wme, NULL, data->searchregex, 1);
2917 window_copy_update_selection(wme, 1, 0);
2918 if (!no_redraw)
2919 window_copy_redraw_screen(wme);
2922 static int
2923 window_copy_search_compare(struct grid *gd, u_int px, u_int py,
2924 struct grid *sgd, u_int spx, int cis)
2926 struct grid_cell gc, sgc;
2927 const struct utf8_data *ud, *sud;
2929 grid_get_cell(gd, px, py, &gc);
2930 ud = &gc.data;
2931 grid_get_cell(sgd, spx, 0, &sgc);
2932 sud = &sgc.data;
2934 if (ud->size != sud->size || ud->width != sud->width)
2935 return (0);
2937 if (cis && ud->size == 1)
2938 return (tolower(ud->data[0]) == sud->data[0]);
2940 return (memcmp(ud->data, sud->data, ud->size) == 0);
2943 static int
2944 window_copy_search_lr(struct grid *gd, struct grid *sgd, u_int *ppx, u_int py,
2945 u_int first, u_int last, int cis)
2947 u_int ax, bx, px, pywrap, endline;
2948 int matched;
2949 struct grid_line *gl;
2951 endline = gd->hsize + gd->sy - 1;
2952 for (ax = first; ax < last; ax++) {
2953 for (bx = 0; bx < sgd->sx; bx++) {
2954 px = ax + bx;
2955 pywrap = py;
2956 /* Wrap line. */
2957 while (px >= gd->sx && pywrap < endline) {
2958 gl = grid_get_line(gd, pywrap);
2959 if (~gl->flags & GRID_LINE_WRAPPED)
2960 break;
2961 px -= gd->sx;
2962 pywrap++;
2964 /* We have run off the end of the grid. */
2965 if (px >= gd->sx)
2966 break;
2967 matched = window_copy_search_compare(gd, px, pywrap,
2968 sgd, bx, cis);
2969 if (!matched)
2970 break;
2972 if (bx == sgd->sx) {
2973 *ppx = ax;
2974 return (1);
2977 return (0);
2980 static int
2981 window_copy_search_rl(struct grid *gd,
2982 struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis)
2984 u_int ax, bx, px, pywrap, endline;
2985 int matched;
2986 struct grid_line *gl;
2988 endline = gd->hsize + gd->sy - 1;
2989 for (ax = last; ax > first; ax--) {
2990 for (bx = 0; bx < sgd->sx; bx++) {
2991 px = ax - 1 + bx;
2992 pywrap = py;
2993 /* Wrap line. */
2994 while (px >= gd->sx && pywrap < endline) {
2995 gl = grid_get_line(gd, pywrap);
2996 if (~gl->flags & GRID_LINE_WRAPPED)
2997 break;
2998 px -= gd->sx;
2999 pywrap++;
3001 /* We have run off the end of the grid. */
3002 if (px >= gd->sx)
3003 break;
3004 matched = window_copy_search_compare(gd, px, pywrap,
3005 sgd, bx, cis);
3006 if (!matched)
3007 break;
3009 if (bx == sgd->sx) {
3010 *ppx = ax - 1;
3011 return (1);
3014 return (0);
3017 static int
3018 window_copy_search_lr_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py,
3019 u_int first, u_int last, regex_t *reg)
3021 int eflags = 0;
3022 u_int endline, foundx, foundy, len, pywrap, size = 1;
3023 char *buf;
3024 regmatch_t regmatch;
3025 struct grid_line *gl;
3028 * This can happen during search if the last match was the last
3029 * character on a line.
3031 if (first >= last)
3032 return (0);
3034 /* Set flags for regex search. */
3035 if (first != 0)
3036 eflags |= REG_NOTBOL;
3038 /* Need to look at the entire string. */
3039 buf = xmalloc(size);
3040 buf[0] = '\0';
3041 buf = window_copy_stringify(gd, py, first, gd->sx, buf, &size);
3042 len = gd->sx - first;
3043 endline = gd->hsize + gd->sy - 1;
3044 pywrap = py;
3045 while (buf != NULL && pywrap <= endline) {
3046 gl = grid_get_line(gd, pywrap);
3047 if (~gl->flags & GRID_LINE_WRAPPED)
3048 break;
3049 pywrap++;
3050 buf = window_copy_stringify(gd, pywrap, 0, gd->sx, buf, &size);
3051 len += gd->sx;
3054 if (regexec(reg, buf, 1, &regmatch, eflags) == 0 &&
3055 regmatch.rm_so != regmatch.rm_eo) {
3056 foundx = first;
3057 foundy = py;
3058 window_copy_cstrtocellpos(gd, len, &foundx, &foundy,
3059 buf + regmatch.rm_so);
3060 if (foundy == py && foundx < last) {
3061 *ppx = foundx;
3062 len -= foundx - first;
3063 window_copy_cstrtocellpos(gd, len, &foundx, &foundy,
3064 buf + regmatch.rm_eo);
3065 *psx = foundx;
3066 while (foundy > py) {
3067 *psx += gd->sx;
3068 foundy--;
3070 *psx -= *ppx;
3071 free(buf);
3072 return (1);
3076 free(buf);
3077 *ppx = 0;
3078 *psx = 0;
3079 return (0);
3082 static int
3083 window_copy_search_rl_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py,
3084 u_int first, u_int last, regex_t *reg)
3086 int eflags = 0;
3087 u_int endline, len, pywrap, size = 1;
3088 char *buf;
3089 struct grid_line *gl;
3091 /* Set flags for regex search. */
3092 if (first != 0)
3093 eflags |= REG_NOTBOL;
3095 /* Need to look at the entire string. */
3096 buf = xmalloc(size);
3097 buf[0] = '\0';
3098 buf = window_copy_stringify(gd, py, first, gd->sx, buf, &size);
3099 len = gd->sx - first;
3100 endline = gd->hsize + gd->sy - 1;
3101 pywrap = py;
3102 while (buf != NULL && (pywrap <= endline)) {
3103 gl = grid_get_line(gd, pywrap);
3104 if (~gl->flags & GRID_LINE_WRAPPED)
3105 break;
3106 pywrap++;
3107 buf = window_copy_stringify(gd, pywrap, 0, gd->sx, buf, &size);
3108 len += gd->sx;
3111 if (window_copy_last_regex(gd, py, first, last, len, ppx, psx, buf,
3112 reg, eflags))
3114 free(buf);
3115 return (1);
3118 free(buf);
3119 *ppx = 0;
3120 *psx = 0;
3121 return (0);
3124 static const char *
3125 window_copy_cellstring(const struct grid_line *gl, u_int px, size_t *size,
3126 int *allocated)
3128 static struct utf8_data ud;
3129 struct grid_cell_entry *gce;
3130 char *copy;
3132 if (px >= gl->cellsize) {
3133 *size = 1;
3134 *allocated = 0;
3135 return (" ");
3138 gce = &gl->celldata[px];
3139 if (gce->flags & GRID_FLAG_PADDING) {
3140 *size = 0;
3141 *allocated = 0;
3142 return (NULL);
3144 if (~gce->flags & GRID_FLAG_EXTENDED) {
3145 *size = 1;
3146 *allocated = 0;
3147 return (&gce->data.data);
3150 utf8_to_data(gl->extddata[gce->offset].data, &ud);
3151 *size = ud.size;
3152 *allocated = 1;
3154 copy = xmalloc(ud.size);
3155 memcpy(copy, ud.data, ud.size);
3156 return (copy);
3159 /* Find last match in given range. */
3160 static int
3161 window_copy_last_regex(struct grid *gd, u_int py, u_int first, u_int last,
3162 u_int len, u_int *ppx, u_int *psx, const char *buf, const regex_t *preg,
3163 int eflags)
3165 u_int foundx, foundy, oldx, px = 0, savepx, savesx = 0;
3166 regmatch_t regmatch;
3168 foundx = first;
3169 foundy = py;
3170 oldx = first;
3171 while (regexec(preg, buf + px, 1, &regmatch, eflags) == 0) {
3172 if (regmatch.rm_so == regmatch.rm_eo)
3173 break;
3174 window_copy_cstrtocellpos(gd, len, &foundx, &foundy,
3175 buf + px + regmatch.rm_so);
3176 if (foundy > py || foundx >= last)
3177 break;
3178 len -= foundx - oldx;
3179 savepx = foundx;
3180 window_copy_cstrtocellpos(gd, len, &foundx, &foundy,
3181 buf + px + regmatch.rm_eo);
3182 if (foundy > py || foundx >= last) {
3183 *ppx = savepx;
3184 *psx = foundx;
3185 while (foundy > py) {
3186 *psx += gd->sx;
3187 foundy--;
3189 *psx -= *ppx;
3190 return (1);
3191 } else {
3192 savesx = foundx - savepx;
3193 len -= savesx;
3194 oldx = foundx;
3196 px += regmatch.rm_eo;
3199 if (savesx > 0) {
3200 *ppx = savepx;
3201 *psx = savesx;
3202 return (1);
3203 } else {
3204 *ppx = 0;
3205 *psx = 0;
3206 return (0);
3210 /* Stringify line and append to input buffer. Caller frees. */
3211 static char *
3212 window_copy_stringify(struct grid *gd, u_int py, u_int first, u_int last,
3213 char *buf, u_int *size)
3215 u_int ax, bx, newsize = *size;
3216 const struct grid_line *gl;
3217 const char *d;
3218 size_t bufsize = 1024, dlen;
3219 int allocated;
3221 while (bufsize < newsize)
3222 bufsize *= 2;
3223 buf = xrealloc(buf, bufsize);
3225 gl = grid_peek_line(gd, py);
3226 bx = *size - 1;
3227 for (ax = first; ax < last; ax++) {
3228 d = window_copy_cellstring(gl, ax, &dlen, &allocated);
3229 newsize += dlen;
3230 while (bufsize < newsize) {
3231 bufsize *= 2;
3232 buf = xrealloc(buf, bufsize);
3234 if (dlen == 1)
3235 buf[bx++] = *d;
3236 else {
3237 memcpy(buf + bx, d, dlen);
3238 bx += dlen;
3240 if (allocated)
3241 free((void *)d);
3243 buf[newsize - 1] = '\0';
3245 *size = newsize;
3246 return (buf);
3249 /* Map start of C string containing UTF-8 data to grid cell position. */
3250 static void
3251 window_copy_cstrtocellpos(struct grid *gd, u_int ncells, u_int *ppx, u_int *ppy,
3252 const char *str)
3254 u_int cell, ccell, px, pywrap, pos, len;
3255 int match;
3256 const struct grid_line *gl;
3257 const char *d;
3258 size_t dlen;
3259 struct {
3260 const char *d;
3261 size_t dlen;
3262 int allocated;
3263 } *cells;
3265 /* Populate the array of cell data. */
3266 cells = xreallocarray(NULL, ncells, sizeof cells[0]);
3267 cell = 0;
3268 px = *ppx;
3269 pywrap = *ppy;
3270 gl = grid_peek_line(gd, pywrap);
3271 while (cell < ncells) {
3272 cells[cell].d = window_copy_cellstring(gl, px,
3273 &cells[cell].dlen, &cells[cell].allocated);
3274 cell++;
3275 px++;
3276 if (px == gd->sx) {
3277 px = 0;
3278 pywrap++;
3279 gl = grid_peek_line(gd, pywrap);
3283 /* Locate starting cell. */
3284 cell = 0;
3285 len = strlen(str);
3286 while (cell < ncells) {
3287 ccell = cell;
3288 pos = 0;
3289 match = 1;
3290 while (ccell < ncells) {
3291 if (str[pos] == '\0') {
3292 match = 0;
3293 break;
3295 d = cells[ccell].d;
3296 dlen = cells[ccell].dlen;
3297 if (dlen == 1) {
3298 if (str[pos] != *d) {
3299 match = 0;
3300 break;
3302 pos++;
3303 } else {
3304 if (dlen > len - pos)
3305 dlen = len - pos;
3306 if (memcmp(str + pos, d, dlen) != 0) {
3307 match = 0;
3308 break;
3310 pos += dlen;
3312 ccell++;
3314 if (match)
3315 break;
3316 cell++;
3319 /* If not found this will be one past the end. */
3320 px = *ppx + cell;
3321 pywrap = *ppy;
3322 while (px >= gd->sx) {
3323 px -= gd->sx;
3324 pywrap++;
3327 *ppx = px;
3328 *ppy = pywrap;
3330 /* Free cell data. */
3331 for (cell = 0; cell < ncells; cell++) {
3332 if (cells[cell].allocated)
3333 free((void *)cells[cell].d);
3335 free(cells);
3338 static void
3339 window_copy_move_left(struct screen *s, u_int *fx, u_int *fy, int wrapflag)
3341 if (*fx == 0) { /* left */
3342 if (*fy == 0) { /* top */
3343 if (wrapflag) {
3344 *fx = screen_size_x(s) - 1;
3345 *fy = screen_hsize(s) + screen_size_y(s) - 1;
3347 return;
3349 *fx = screen_size_x(s) - 1;
3350 *fy = *fy - 1;
3351 } else
3352 *fx = *fx - 1;
3355 static void
3356 window_copy_move_right(struct screen *s, u_int *fx, u_int *fy, int wrapflag)
3358 if (*fx == screen_size_x(s) - 1) { /* right */
3359 if (*fy == screen_hsize(s) + screen_size_y(s) - 1) { /* bottom */
3360 if (wrapflag) {
3361 *fx = 0;
3362 *fy = 0;
3364 return;
3366 *fx = 0;
3367 *fy = *fy + 1;
3368 } else
3369 *fx = *fx + 1;
3372 static int
3373 window_copy_is_lowercase(const char *ptr)
3375 while (*ptr != '\0') {
3376 if (*ptr != tolower((u_char)*ptr))
3377 return (0);
3378 ++ptr;
3380 return (1);
3384 * Handle backward wrapped regex searches with overlapping matches. In this case
3385 * find the longest overlapping match from previous wrapped lines.
3387 static void
3388 window_copy_search_back_overlap(struct grid *gd, regex_t *preg, u_int *ppx,
3389 u_int *psx, u_int *ppy, u_int endline)
3391 u_int endx, endy, oldendx, oldendy, px, py, sx;
3392 int found = 1;
3394 oldendx = *ppx + *psx;
3395 oldendy = *ppy - 1;
3396 while (oldendx > gd->sx - 1) {
3397 oldendx -= gd->sx;
3398 oldendy++;
3400 endx = oldendx;
3401 endy = oldendy;
3402 px = *ppx;
3403 py = *ppy;
3404 while (found && px == 0 && py - 1 > endline &&
3405 grid_get_line(gd, py - 2)->flags & GRID_LINE_WRAPPED &&
3406 endx == oldendx && endy == oldendy) {
3407 py--;
3408 found = window_copy_search_rl_regex(gd, &px, &sx, py - 1, 0,
3409 gd->sx, preg);
3410 if (found) {
3411 endx = px + sx;
3412 endy = py - 1;
3413 while (endx > gd->sx - 1) {
3414 endx -= gd->sx;
3415 endy++;
3417 if (endx == oldendx && endy == oldendy) {
3418 *ppx = px;
3419 *ppy = py;
3426 * Search for text stored in sgd starting from position fx,fy up to endline. If
3427 * found, jump to it. If cis then ignore case. The direction is 0 for searching
3428 * up, down otherwise. If wrap then go to begin/end of grid and try again if
3429 * not found.
3431 static int
3432 window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd,
3433 struct grid *sgd, u_int fx, u_int fy, u_int endline, int cis, int wrap,
3434 int direction, int regex)
3436 u_int i, px, sx, ssize = 1;
3437 int found = 0, cflags = REG_EXTENDED;
3438 char *sbuf;
3439 regex_t reg;
3441 if (regex) {
3442 sbuf = xmalloc(ssize);
3443 sbuf[0] = '\0';
3444 sbuf = window_copy_stringify(sgd, 0, 0, sgd->sx, sbuf, &ssize);
3445 if (cis)
3446 cflags |= REG_ICASE;
3447 if (regcomp(&reg, sbuf, cflags) != 0) {
3448 free(sbuf);
3449 return (0);
3451 free(sbuf);
3454 if (direction) {
3455 for (i = fy; i <= endline; i++) {
3456 if (regex) {
3457 found = window_copy_search_lr_regex(gd,
3458 &px, &sx, i, fx, gd->sx, &reg);
3459 } else {
3460 found = window_copy_search_lr(gd, sgd,
3461 &px, i, fx, gd->sx, cis);
3463 if (found)
3464 break;
3465 fx = 0;
3467 } else {
3468 for (i = fy + 1; endline < i; i--) {
3469 if (regex) {
3470 found = window_copy_search_rl_regex(gd,
3471 &px, &sx, i - 1, 0, fx + 1, &reg);
3472 if (found) {
3473 window_copy_search_back_overlap(gd,
3474 &reg, &px, &sx, &i, endline);
3476 } else {
3477 found = window_copy_search_rl(gd, sgd,
3478 &px, i - 1, 0, fx + 1, cis);
3480 if (found) {
3481 i--;
3482 break;
3484 fx = gd->sx - 1;
3487 if (regex)
3488 regfree(&reg);
3490 if (found) {
3491 window_copy_scroll_to(wme, px, i, 1);
3492 return (1);
3494 if (wrap) {
3495 return (window_copy_search_jump(wme, gd, sgd,
3496 direction ? 0 : gd->sx - 1,
3497 direction ? 0 : gd->hsize + gd->sy - 1, fy, cis, 0,
3498 direction, regex));
3500 return (0);
3503 static void
3504 window_copy_move_after_search_mark(struct window_copy_mode_data *data,
3505 u_int *fx, u_int *fy, int wrapflag)
3507 struct screen *s = data->backing;
3508 u_int at, start;
3510 if (window_copy_search_mark_at(data, *fx, *fy, &start) == 0 &&
3511 data->searchmark[start] != 0) {
3512 while (window_copy_search_mark_at(data, *fx, *fy, &at) == 0) {
3513 if (data->searchmark[at] != data->searchmark[start])
3514 break;
3515 /* Stop if not wrapping and at the end of the grid. */
3516 if (!wrapflag &&
3517 *fx == screen_size_x(s) - 1 &&
3518 *fy == screen_hsize(s) + screen_size_y(s) - 1)
3519 break;
3521 window_copy_move_right(s, fx, fy, wrapflag);
3527 * Search in for text searchstr. If direction is 0 then search up, otherwise
3528 * down.
3530 static int
3531 window_copy_search(struct window_mode_entry *wme, int direction, int regex)
3533 struct window_pane *wp = wme->wp;
3534 struct window_copy_mode_data *data = wme->data;
3535 struct screen *s = data->backing, ss;
3536 struct screen_write_ctx ctx;
3537 struct grid *gd = s->grid;
3538 const char *str = data->searchstr;
3539 u_int at, endline, fx, fy, start;
3540 int cis, found, keys, visible_only;
3541 int wrapflag;
3543 if (regex && str[strcspn(str, "^$*+()?[].\\")] == '\0')
3544 regex = 0;
3546 data->searchdirection = direction;
3548 if (data->timeout)
3549 return (0);
3551 if (data->searchall || wp->searchstr == NULL ||
3552 wp->searchregex != regex) {
3553 visible_only = 0;
3554 data->searchall = 0;
3555 } else
3556 visible_only = (strcmp(wp->searchstr, str) == 0);
3557 free(wp->searchstr);
3558 wp->searchstr = xstrdup(str);
3559 wp->searchregex = regex;
3561 fx = data->cx;
3562 fy = screen_hsize(data->backing) - data->oy + data->cy;
3564 screen_init(&ss, screen_write_strlen("%s", str), 1, 0);
3565 screen_write_start(&ctx, &ss);
3566 screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", str);
3567 screen_write_stop(&ctx);
3569 wrapflag = options_get_number(wp->window->options, "wrap-search");
3570 cis = window_copy_is_lowercase(str);
3572 keys = options_get_number(wp->window->options, "mode-keys");
3574 if (direction) {
3576 * Behave according to mode-keys. If it is emacs, search forward
3577 * leaves the cursor after the match. If it is vi, the cursor
3578 * remains at the beginning of the match, regardless of
3579 * direction, which means that we need to start the next search
3580 * after the term the cursor is currently on when searching
3581 * forward.
3583 if (keys == MODEKEY_VI) {
3584 if (data->searchmark != NULL)
3585 window_copy_move_after_search_mark(data, &fx,
3586 &fy, wrapflag);
3587 else {
3589 * When there are no search marks, start the
3590 * search after the current cursor position.
3592 window_copy_move_right(s, &fx, &fy, wrapflag);
3595 endline = gd->hsize + gd->sy - 1;
3597 else {
3598 window_copy_move_left(s, &fx, &fy, wrapflag);
3599 endline = 0;
3602 found = window_copy_search_jump(wme, gd, ss.grid, fx, fy, endline, cis,
3603 wrapflag, direction, regex);
3604 if (found) {
3605 window_copy_search_marks(wme, &ss, regex, visible_only);
3606 fx = data->cx;
3607 fy = screen_hsize(data->backing) - data->oy + data->cy;
3610 * When searching forward, if the cursor is not at the beginning
3611 * of the mark, search again.
3613 if (direction &&
3614 window_copy_search_mark_at(data, fx, fy, &at) == 0 &&
3615 at > 0 &&
3616 data->searchmark[at] == data->searchmark[at - 1]) {
3617 window_copy_move_after_search_mark(data, &fx, &fy,
3618 wrapflag);
3619 window_copy_search_jump(wme, gd, ss.grid, fx,
3620 fy, endline, cis, wrapflag, direction,
3621 regex);
3622 fx = data->cx;
3623 fy = screen_hsize(data->backing) - data->oy + data->cy;
3626 if (direction) {
3628 * When in Emacs mode, position the cursor just after
3629 * the mark.
3631 if (keys == MODEKEY_EMACS) {
3632 window_copy_move_after_search_mark(data, &fx,
3633 &fy, wrapflag);
3634 data->cx = fx;
3635 data->cy = fy - screen_hsize(data->backing) +
3636 data-> oy;
3639 else {
3641 * When searching backward, position the cursor at the
3642 * beginning of the mark.
3644 if (window_copy_search_mark_at(data, fx, fy,
3645 &start) == 0) {
3646 while (window_copy_search_mark_at(data, fx, fy,
3647 &at) == 0 &&
3648 data->searchmark[at] ==
3649 data->searchmark[start]) {
3650 data->cx = fx;
3651 data->cy = fy -
3652 screen_hsize(data->backing) +
3653 data-> oy;
3654 if (at == 0)
3655 break;
3657 window_copy_move_left(s, &fx, &fy, 0);
3662 window_copy_redraw_screen(wme);
3664 screen_free(&ss);
3665 return (found);
3668 static void
3669 window_copy_visible_lines(struct window_copy_mode_data *data, u_int *start,
3670 u_int *end)
3672 struct grid *gd = data->backing->grid;
3673 const struct grid_line *gl;
3675 for (*start = gd->hsize - data->oy; *start > 0; (*start)--) {
3676 gl = grid_peek_line(gd, (*start) - 1);
3677 if (~gl->flags & GRID_LINE_WRAPPED)
3678 break;
3680 *end = gd->hsize - data->oy + gd->sy;
3683 static int
3684 window_copy_search_mark_at(struct window_copy_mode_data *data, u_int px,
3685 u_int py, u_int *at)
3687 struct screen *s = data->backing;
3688 struct grid *gd = s->grid;
3690 if (py < gd->hsize - data->oy)
3691 return (-1);
3692 if (py > gd->hsize - data->oy + gd->sy - 1)
3693 return (-1);
3694 *at = ((py - (gd->hsize - data->oy)) * gd->sx) + px;
3695 return (0);
3698 static int
3699 window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp,
3700 int regex, int visible_only)
3702 struct window_copy_mode_data *data = wme->data;
3703 struct screen *s = data->backing, ss;
3704 struct screen_write_ctx ctx;
3705 struct grid *gd = s->grid;
3706 int found, cis, stopped = 0;
3707 int cflags = REG_EXTENDED;
3708 u_int px, py, i, b, nfound = 0, width;
3709 u_int ssize = 1, start, end;
3710 char *sbuf;
3711 regex_t reg;
3712 uint64_t stop = 0, tstart, t;
3714 if (ssp == NULL) {
3715 width = screen_write_strlen("%s", data->searchstr);
3716 screen_init(&ss, width, 1, 0);
3717 screen_write_start(&ctx, &ss);
3718 screen_write_nputs(&ctx, -1, &grid_default_cell, "%s",
3719 data->searchstr);
3720 screen_write_stop(&ctx);
3721 ssp = &ss;
3722 } else
3723 width = screen_size_x(ssp);
3725 cis = window_copy_is_lowercase(data->searchstr);
3727 if (regex) {
3728 sbuf = xmalloc(ssize);
3729 sbuf[0] = '\0';
3730 sbuf = window_copy_stringify(ssp->grid, 0, 0, ssp->grid->sx,
3731 sbuf, &ssize);
3732 if (cis)
3733 cflags |= REG_ICASE;
3734 if (regcomp(&reg, sbuf, cflags) != 0) {
3735 free(sbuf);
3736 return (0);
3738 free(sbuf);
3740 tstart = get_timer();
3742 if (visible_only)
3743 window_copy_visible_lines(data, &start, &end);
3744 else {
3745 start = 0;
3746 end = gd->hsize + gd->sy;
3747 stop = get_timer() + WINDOW_COPY_SEARCH_ALL_TIMEOUT;
3750 again:
3751 free(data->searchmark);
3752 data->searchmark = xcalloc(gd->sx, gd->sy);
3753 data->searchgen = 1;
3755 for (py = start; py < end; py++) {
3756 px = 0;
3757 for (;;) {
3758 if (regex) {
3759 found = window_copy_search_lr_regex(gd,
3760 &px, &width, py, px, gd->sx, &reg);
3761 if (!found)
3762 break;
3763 } else {
3764 found = window_copy_search_lr(gd, ssp->grid,
3765 &px, py, px, gd->sx, cis);
3766 if (!found)
3767 break;
3769 nfound++;
3771 if (window_copy_search_mark_at(data, px, py, &b) == 0) {
3772 if (b + width > gd->sx * gd->sy)
3773 width = (gd->sx * gd->sy) - b;
3774 for (i = b; i < b + width; i++) {
3775 if (data->searchmark[i] != 0)
3776 continue;
3777 data->searchmark[i] = data->searchgen;
3779 if (data->searchgen == UCHAR_MAX)
3780 data->searchgen = 1;
3781 else
3782 data->searchgen++;
3784 px += width;
3787 t = get_timer();
3788 if (t - tstart > WINDOW_COPY_SEARCH_TIMEOUT) {
3789 data->timeout = 1;
3790 break;
3792 if (stop != 0 && t > stop) {
3793 stopped = 1;
3794 break;
3797 if (data->timeout) {
3798 window_copy_clear_marks(wme);
3799 goto out;
3802 if (stopped && stop != 0) {
3803 /* Try again but just the visible context. */
3804 window_copy_visible_lines(data, &start, &end);
3805 stop = 0;
3806 goto again;
3809 if (!visible_only) {
3810 if (stopped) {
3811 if (nfound > 1000)
3812 data->searchcount = 1000;
3813 else if (nfound > 100)
3814 data->searchcount = 100;
3815 else if (nfound > 10)
3816 data->searchcount = 10;
3817 else
3818 data->searchcount = -1;
3819 data->searchmore = 1;
3820 } else {
3821 data->searchcount = nfound;
3822 data->searchmore = 0;
3826 out:
3827 if (ssp == &ss)
3828 screen_free(&ss);
3829 if (regex)
3830 regfree(&reg);
3831 return (1);
3834 static void
3835 window_copy_clear_marks(struct window_mode_entry *wme)
3837 struct window_copy_mode_data *data = wme->data;
3839 free(data->searchmark);
3840 data->searchmark = NULL;
3843 static int
3844 window_copy_search_up(struct window_mode_entry *wme, int regex)
3846 return (window_copy_search(wme, 0, regex));
3849 static int
3850 window_copy_search_down(struct window_mode_entry *wme, int regex)
3852 return (window_copy_search(wme, 1, regex));
3855 static void
3856 window_copy_goto_line(struct window_mode_entry *wme, const char *linestr)
3858 struct window_copy_mode_data *data = wme->data;
3859 const char *errstr;
3860 int lineno;
3862 lineno = strtonum(linestr, -1, INT_MAX, &errstr);
3863 if (errstr != NULL)
3864 return;
3865 if (lineno < 0 || (u_int)lineno > screen_hsize(data->backing))
3866 lineno = screen_hsize(data->backing);
3868 data->oy = lineno;
3869 window_copy_update_selection(wme, 1, 0);
3870 window_copy_redraw_screen(wme);
3873 static void
3874 window_copy_match_start_end(struct window_copy_mode_data *data, u_int at,
3875 u_int *start, u_int *end)
3877 struct grid *gd = data->backing->grid;
3878 u_int last = (gd->sy * gd->sx) - 1;
3879 u_char mark = data->searchmark[at];
3881 *start = *end = at;
3882 while (*start != 0 && data->searchmark[*start] == mark)
3883 (*start)--;
3884 if (data->searchmark[*start] != mark)
3885 (*start)++;
3886 while (*end != last && data->searchmark[*end] == mark)
3887 (*end)++;
3888 if (data->searchmark[*end] != mark)
3889 (*end)--;
3892 static char *
3893 window_copy_match_at_cursor(struct window_copy_mode_data *data)
3895 struct grid *gd = data->backing->grid;
3896 struct grid_cell gc;
3897 u_int at, start, end, cy, px, py;
3898 u_int sx = screen_size_x(data->backing);
3899 char *buf = NULL;
3900 size_t len = 0;
3902 if (data->searchmark == NULL)
3903 return (NULL);
3905 cy = screen_hsize(data->backing) - data->oy + data->cy;
3906 if (window_copy_search_mark_at(data, data->cx, cy, &at) != 0)
3907 return (NULL);
3908 if (data->searchmark[at] == 0) {
3909 /* Allow one position after the match. */
3910 if (at == 0 || data->searchmark[--at] == 0)
3911 return (NULL);
3913 window_copy_match_start_end(data, at, &start, &end);
3916 * Cells will not be set in the marked array unless they are valid text
3917 * and wrapping will be taken care of, so we can just copy.
3919 for (at = start; at <= end; at++) {
3920 py = at / sx;
3921 px = at - (py * sx);
3923 grid_get_cell(gd, px, gd->hsize + py - data->oy, &gc);
3924 buf = xrealloc(buf, len + gc.data.size + 1);
3925 memcpy(buf + len, gc.data.data, gc.data.size);
3926 len += gc.data.size;
3928 if (len != 0)
3929 buf[len] = '\0';
3930 return (buf);
3933 static void
3934 window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy,
3935 struct grid_cell *gc, const struct grid_cell *mgc,
3936 const struct grid_cell *cgc, const struct grid_cell *mkgc)
3938 struct window_pane *wp = wme->wp;
3939 struct window_copy_mode_data *data = wme->data;
3940 u_int mark, start, end, cy, cursor, current;
3941 int inv = 0, found = 0;
3942 int keys;
3944 if (data->showmark && fy == data->my) {
3945 gc->attr = mkgc->attr;
3946 if (fx == data->mx)
3947 inv = 1;
3948 if (inv) {
3949 gc->fg = mkgc->bg;
3950 gc->bg = mkgc->fg;
3952 else {
3953 gc->fg = mkgc->fg;
3954 gc->bg = mkgc->bg;
3958 if (data->searchmark == NULL)
3959 return;
3961 if (window_copy_search_mark_at(data, fx, fy, &current) != 0)
3962 return;
3963 mark = data->searchmark[current];
3964 if (mark == 0)
3965 return;
3967 cy = screen_hsize(data->backing) - data->oy + data->cy;
3968 if (window_copy_search_mark_at(data, data->cx, cy, &cursor) == 0) {
3969 keys = options_get_number(wp->window->options, "mode-keys");
3970 if (cursor != 0 &&
3971 keys == MODEKEY_EMACS &&
3972 data->searchdirection) {
3973 if (data->searchmark[cursor - 1] == mark) {
3974 cursor--;
3975 found = 1;
3977 } else if (data->searchmark[cursor] == mark)
3978 found = 1;
3979 if (found) {
3980 window_copy_match_start_end(data, cursor, &start, &end);
3981 if (current >= start && current <= end) {
3982 gc->attr = cgc->attr;
3983 if (inv) {
3984 gc->fg = cgc->bg;
3985 gc->bg = cgc->fg;
3987 else {
3988 gc->fg = cgc->fg;
3989 gc->bg = cgc->bg;
3991 return;
3996 gc->attr = mgc->attr;
3997 if (inv) {
3998 gc->fg = mgc->bg;
3999 gc->bg = mgc->fg;
4001 else {
4002 gc->fg = mgc->fg;
4003 gc->bg = mgc->bg;
4007 static void
4008 window_copy_write_one(struct window_mode_entry *wme,
4009 struct screen_write_ctx *ctx, u_int py, u_int fy, u_int nx,
4010 const struct grid_cell *mgc, const struct grid_cell *cgc,
4011 const struct grid_cell *mkgc)
4013 struct window_copy_mode_data *data = wme->data;
4014 struct grid *gd = data->backing->grid;
4015 struct grid_cell gc;
4016 u_int fx;
4018 screen_write_cursormove(ctx, 0, py, 0);
4019 for (fx = 0; fx < nx; fx++) {
4020 grid_get_cell(gd, fx, fy, &gc);
4021 if (fx + gc.data.width <= nx) {
4022 window_copy_update_style(wme, fx, fy, &gc, mgc, cgc,
4023 mkgc);
4024 screen_write_cell(ctx, &gc);
4029 static void
4030 window_copy_write_line(struct window_mode_entry *wme,
4031 struct screen_write_ctx *ctx, u_int py)
4033 struct window_pane *wp = wme->wp;
4034 struct window_copy_mode_data *data = wme->data;
4035 struct screen *s = &data->screen;
4036 struct options *oo = wp->window->options;
4037 struct grid_cell gc, mgc, cgc, mkgc;
4038 char hdr[512];
4039 size_t size = 0;
4040 u_int hsize = screen_hsize(data->backing);
4042 style_apply(&gc, oo, "mode-style", NULL);
4043 gc.flags |= GRID_FLAG_NOPALETTE;
4044 style_apply(&mgc, oo, "copy-mode-match-style", NULL);
4045 mgc.flags |= GRID_FLAG_NOPALETTE;
4046 style_apply(&cgc, oo, "copy-mode-current-match-style", NULL);
4047 cgc.flags |= GRID_FLAG_NOPALETTE;
4048 style_apply(&mkgc, oo, "copy-mode-mark-style", NULL);
4049 mkgc.flags |= GRID_FLAG_NOPALETTE;
4051 if (py == 0 && s->rupper < s->rlower && !data->hide_position) {
4052 if (data->searchmark == NULL) {
4053 if (data->timeout) {
4054 size = xsnprintf(hdr, sizeof hdr,
4055 "(timed out) [%u/%u]", data->oy, hsize);
4056 } else {
4057 size = xsnprintf(hdr, sizeof hdr,
4058 "[%u/%u]", data->oy, hsize);
4060 } else {
4061 if (data->searchcount == -1) {
4062 size = xsnprintf(hdr, sizeof hdr,
4063 "[%u/%u]", data->oy, hsize);
4064 } else {
4065 size = xsnprintf(hdr, sizeof hdr,
4066 "(%d%s results) [%u/%u]", data->searchcount,
4067 data->searchmore ? "+" : "", data->oy,
4068 hsize);
4071 if (size > screen_size_x(s))
4072 size = screen_size_x(s);
4073 screen_write_cursormove(ctx, screen_size_x(s) - size, 0, 0);
4074 screen_write_puts(ctx, &gc, "%s", hdr);
4075 } else
4076 size = 0;
4078 if (size < screen_size_x(s)) {
4079 window_copy_write_one(wme, ctx, py, hsize - data->oy + py,
4080 screen_size_x(s) - size, &mgc, &cgc, &mkgc);
4083 if (py == data->cy && data->cx == screen_size_x(s)) {
4084 screen_write_cursormove(ctx, screen_size_x(s) - 1, py, 0);
4085 screen_write_putc(ctx, &grid_default_cell, '$');
4089 static void
4090 window_copy_write_lines(struct window_mode_entry *wme,
4091 struct screen_write_ctx *ctx, u_int py, u_int ny)
4093 u_int yy;
4095 for (yy = py; yy < py + ny; yy++)
4096 window_copy_write_line(wme, ctx, py);
4099 static void
4100 window_copy_redraw_selection(struct window_mode_entry *wme, u_int old_y)
4102 struct window_copy_mode_data *data = wme->data;
4103 struct grid *gd = data->backing->grid;
4104 u_int new_y, start, end;
4106 new_y = data->cy;
4107 if (old_y <= new_y) {
4108 start = old_y;
4109 end = new_y;
4110 } else {
4111 start = new_y;
4112 end = old_y;
4116 * In word selection mode the first word on the line below the cursor
4117 * might be selected, so add this line to the redraw area.
4119 if (data->selflag == SEL_WORD) {
4120 /* Last grid line in data coordinates. */
4121 if (end < gd->sy + data->oy - 1)
4122 end++;
4124 window_copy_redraw_lines(wme, start, end - start + 1);
4127 static void
4128 window_copy_redraw_lines(struct window_mode_entry *wme, u_int py, u_int ny)
4130 struct window_pane *wp = wme->wp;
4131 struct window_copy_mode_data *data = wme->data;
4132 struct screen_write_ctx ctx;
4133 u_int i;
4135 screen_write_start_pane(&ctx, wp, NULL);
4136 for (i = py; i < py + ny; i++)
4137 window_copy_write_line(wme, &ctx, i);
4138 screen_write_cursormove(&ctx, data->cx, data->cy, 0);
4139 screen_write_stop(&ctx);
4142 static void
4143 window_copy_redraw_screen(struct window_mode_entry *wme)
4145 struct window_copy_mode_data *data = wme->data;
4147 window_copy_redraw_lines(wme, 0, screen_size_y(&data->screen));
4150 static void
4151 window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin,
4152 int no_reset)
4154 struct window_copy_mode_data *data = wme->data;
4155 u_int xx, yy;
4157 xx = data->cx;
4158 yy = screen_hsize(data->backing) + data->cy - data->oy;
4159 switch (data->selflag) {
4160 case SEL_WORD:
4161 if (no_reset)
4162 break;
4163 begin = 0;
4164 if (data->dy > yy || (data->dy == yy && data->dx > xx)) {
4165 /* Right to left selection. */
4166 window_copy_cursor_previous_word_pos(wme,
4167 data->separators, &xx, &yy);
4168 begin = 1;
4170 /* Reset the end. */
4171 data->endselx = data->endselrx;
4172 data->endsely = data->endselry;
4173 } else {
4174 /* Left to right selection. */
4175 if (xx >= window_copy_find_length(wme, yy) ||
4176 !window_copy_in_set(wme, xx + 1, yy, WHITESPACE)) {
4177 window_copy_cursor_next_word_end_pos(wme,
4178 data->separators, &xx, &yy);
4181 /* Reset the start. */
4182 data->selx = data->selrx;
4183 data->sely = data->selry;
4185 break;
4186 case SEL_LINE:
4187 if (no_reset)
4188 break;
4189 begin = 0;
4190 if (data->dy > yy) {
4191 /* Right to left selection. */
4192 xx = 0;
4193 begin = 1;
4195 /* Reset the end. */
4196 data->endselx = data->endselrx;
4197 data->endsely = data->endselry;
4198 } else {
4199 /* Left to right selection. */
4200 if (yy < data->endselry)
4201 yy = data->endselry;
4202 xx = window_copy_find_length(wme, yy);
4204 /* Reset the start. */
4205 data->selx = data->selrx;
4206 data->sely = data->selry;
4208 break;
4209 case SEL_CHAR:
4210 break;
4212 if (begin) {
4213 data->selx = xx;
4214 data->sely = yy;
4215 } else {
4216 data->endselx = xx;
4217 data->endsely = yy;
4221 static void
4222 window_copy_synchronize_cursor(struct window_mode_entry *wme, int no_reset)
4224 struct window_copy_mode_data *data = wme->data;
4226 switch (data->cursordrag) {
4227 case CURSORDRAG_ENDSEL:
4228 window_copy_synchronize_cursor_end(wme, 0, no_reset);
4229 break;
4230 case CURSORDRAG_SEL:
4231 window_copy_synchronize_cursor_end(wme, 1, no_reset);
4232 break;
4233 case CURSORDRAG_NONE:
4234 break;
4238 static void
4239 window_copy_update_cursor(struct window_mode_entry *wme, u_int cx, u_int cy)
4241 struct window_pane *wp = wme->wp;
4242 struct window_copy_mode_data *data = wme->data;
4243 struct screen *s = &data->screen;
4244 struct screen_write_ctx ctx;
4245 u_int old_cx, old_cy;
4247 old_cx = data->cx; old_cy = data->cy;
4248 data->cx = cx; data->cy = cy;
4249 if (old_cx == screen_size_x(s))
4250 window_copy_redraw_lines(wme, old_cy, 1);
4251 if (data->cx == screen_size_x(s))
4252 window_copy_redraw_lines(wme, data->cy, 1);
4253 else {
4254 screen_write_start_pane(&ctx, wp, NULL);
4255 screen_write_cursormove(&ctx, data->cx, data->cy, 0);
4256 screen_write_stop(&ctx);
4260 static void
4261 window_copy_start_selection(struct window_mode_entry *wme)
4263 struct window_copy_mode_data *data = wme->data;
4265 data->selx = data->cx;
4266 data->sely = screen_hsize(data->backing) + data->cy - data->oy;
4268 data->endselx = data->selx;
4269 data->endsely = data->sely;
4271 data->cursordrag = CURSORDRAG_ENDSEL;
4273 window_copy_set_selection(wme, 1, 0);
4276 static int
4277 window_copy_adjust_selection(struct window_mode_entry *wme, u_int *selx,
4278 u_int *sely)
4280 struct window_copy_mode_data *data = wme->data;
4281 struct screen *s = &data->screen;
4282 u_int sx, sy, ty;
4283 int relpos;
4285 sx = *selx;
4286 sy = *sely;
4288 ty = screen_hsize(data->backing) - data->oy;
4289 if (sy < ty) {
4290 relpos = WINDOW_COPY_REL_POS_ABOVE;
4291 if (!data->rectflag)
4292 sx = 0;
4293 sy = 0;
4294 } else if (sy > ty + screen_size_y(s) - 1) {
4295 relpos = WINDOW_COPY_REL_POS_BELOW;
4296 if (!data->rectflag)
4297 sx = screen_size_x(s) - 1;
4298 sy = screen_size_y(s) - 1;
4299 } else {
4300 relpos = WINDOW_COPY_REL_POS_ON_SCREEN;
4301 sy -= ty;
4304 *selx = sx;
4305 *sely = sy;
4306 return (relpos);
4309 static int
4310 window_copy_update_selection(struct window_mode_entry *wme, int may_redraw,
4311 int no_reset)
4313 struct window_copy_mode_data *data = wme->data;
4314 struct screen *s = &data->screen;
4316 if (s->sel == NULL && data->lineflag == LINE_SEL_NONE)
4317 return (0);
4318 return (window_copy_set_selection(wme, may_redraw, no_reset));
4321 static int
4322 window_copy_set_selection(struct window_mode_entry *wme, int may_redraw,
4323 int no_reset)
4325 struct window_pane *wp = wme->wp;
4326 struct window_copy_mode_data *data = wme->data;
4327 struct screen *s = &data->screen;
4328 struct options *oo = wp->window->options;
4329 struct grid_cell gc;
4330 u_int sx, sy, cy, endsx, endsy;
4331 int startrelpos, endrelpos;
4333 window_copy_synchronize_cursor(wme, no_reset);
4335 /* Adjust the selection. */
4336 sx = data->selx;
4337 sy = data->sely;
4338 startrelpos = window_copy_adjust_selection(wme, &sx, &sy);
4340 /* Adjust the end of selection. */
4341 endsx = data->endselx;
4342 endsy = data->endsely;
4343 endrelpos = window_copy_adjust_selection(wme, &endsx, &endsy);
4345 /* Selection is outside of the current screen */
4346 if (startrelpos == endrelpos &&
4347 startrelpos != WINDOW_COPY_REL_POS_ON_SCREEN) {
4348 screen_hide_selection(s);
4349 return (0);
4352 /* Set colours and selection. */
4353 style_apply(&gc, oo, "mode-style", NULL);
4354 gc.flags |= GRID_FLAG_NOPALETTE;
4355 screen_set_selection(s, sx, sy, endsx, endsy, data->rectflag,
4356 data->modekeys, &gc);
4358 if (data->rectflag && may_redraw) {
4360 * Can't rely on the caller to redraw the right lines for
4361 * rectangle selection - find the highest line and the number
4362 * of lines, and redraw just past that in both directions
4364 cy = data->cy;
4365 if (data->cursordrag == CURSORDRAG_ENDSEL) {
4366 if (sy < cy)
4367 window_copy_redraw_lines(wme, sy, cy - sy + 1);
4368 else
4369 window_copy_redraw_lines(wme, cy, sy - cy + 1);
4370 } else {
4371 if (endsy < cy) {
4372 window_copy_redraw_lines(wme, endsy,
4373 cy - endsy + 1);
4374 } else {
4375 window_copy_redraw_lines(wme, cy,
4376 endsy - cy + 1);
4381 return (1);
4384 static void *
4385 window_copy_get_selection(struct window_mode_entry *wme, size_t *len)
4387 struct window_pane *wp = wme->wp;
4388 struct window_copy_mode_data *data = wme->data;
4389 struct screen *s = &data->screen;
4390 char *buf;
4391 size_t off;
4392 u_int i, xx, yy, sx, sy, ex, ey, ey_last;
4393 u_int firstsx, lastex, restex, restsx, selx;
4394 int keys;
4396 if (data->screen.sel == NULL && data->lineflag == LINE_SEL_NONE) {
4397 buf = window_copy_match_at_cursor(data);
4398 if (buf != NULL)
4399 *len = strlen(buf);
4400 else
4401 *len = 0;
4402 return (buf);
4405 buf = xmalloc(1);
4406 off = 0;
4408 *buf = '\0';
4411 * The selection extends from selx,sely to (adjusted) cx,cy on
4412 * the base screen.
4415 /* Find start and end. */
4416 xx = data->endselx;
4417 yy = data->endsely;
4418 if (yy < data->sely || (yy == data->sely && xx < data->selx)) {
4419 sx = xx; sy = yy;
4420 ex = data->selx; ey = data->sely;
4421 } else {
4422 sx = data->selx; sy = data->sely;
4423 ex = xx; ey = yy;
4426 /* Trim ex to end of line. */
4427 ey_last = window_copy_find_length(wme, ey);
4428 if (ex > ey_last)
4429 ex = ey_last;
4432 * Deal with rectangle-copy if necessary; four situations: start of
4433 * first line (firstsx), end of last line (lastex), start (restsx) and
4434 * end (restex) of all other lines.
4436 xx = screen_size_x(s);
4439 * Behave according to mode-keys. If it is emacs, copy like emacs,
4440 * keeping the top-left-most character, and dropping the
4441 * bottom-right-most, regardless of copy direction. If it is vi, also
4442 * keep bottom-right-most character.
4444 keys = options_get_number(wp->window->options, "mode-keys");
4445 if (data->rectflag) {
4447 * Need to ignore the column with the cursor in it, which for
4448 * rectangular copy means knowing which side the cursor is on.
4450 if (data->cursordrag == CURSORDRAG_ENDSEL)
4451 selx = data->selx;
4452 else
4453 selx = data->endselx;
4454 if (selx < data->cx) {
4455 /* Selection start is on the left. */
4456 if (keys == MODEKEY_EMACS) {
4457 lastex = data->cx;
4458 restex = data->cx;
4460 else {
4461 lastex = data->cx + 1;
4462 restex = data->cx + 1;
4464 firstsx = selx;
4465 restsx = selx;
4466 } else {
4467 /* Cursor is on the left. */
4468 lastex = selx + 1;
4469 restex = selx + 1;
4470 firstsx = data->cx;
4471 restsx = data->cx;
4473 } else {
4474 if (keys == MODEKEY_EMACS)
4475 lastex = ex;
4476 else
4477 lastex = ex + 1;
4478 restex = xx;
4479 firstsx = sx;
4480 restsx = 0;
4483 /* Copy the lines. */
4484 for (i = sy; i <= ey; i++) {
4485 window_copy_copy_line(wme, &buf, &off, i,
4486 (i == sy ? firstsx : restsx),
4487 (i == ey ? lastex : restex));
4490 /* Don't bother if no data. */
4491 if (off == 0) {
4492 free(buf);
4493 *len = 0;
4494 return (NULL);
4496 /* Remove final \n (unless at end in vi mode). */
4497 if (keys == MODEKEY_EMACS || lastex <= ey_last) {
4498 if (~grid_get_line(data->backing->grid, ey)->flags &
4499 GRID_LINE_WRAPPED || lastex != ey_last)
4500 off -= 1;
4502 *len = off;
4503 return (buf);
4506 static void
4507 window_copy_copy_buffer(struct window_mode_entry *wme, const char *prefix,
4508 void *buf, size_t len)
4510 struct window_pane *wp = wme->wp;
4511 struct screen_write_ctx ctx;
4513 if (options_get_number(global_options, "set-clipboard") != 0) {
4514 screen_write_start_pane(&ctx, wp, NULL);
4515 screen_write_setselection(&ctx, buf, len);
4516 screen_write_stop(&ctx);
4517 notify_pane("pane-set-clipboard", wp);
4520 paste_add(prefix, buf, len);
4523 static void *
4524 window_copy_pipe_run(struct window_mode_entry *wme, struct session *s,
4525 const char *cmd, size_t *len)
4527 void *buf;
4528 struct job *job;
4530 buf = window_copy_get_selection(wme, len);
4531 if (cmd == NULL || *cmd == '\0')
4532 cmd = options_get_string(global_options, "copy-command");
4533 if (cmd != NULL && *cmd != '\0') {
4534 job = job_run(cmd, 0, NULL, NULL, s, NULL, NULL, NULL, NULL,
4535 NULL, JOB_NOWAIT, -1, -1);
4536 bufferevent_write(job_get_event(job), buf, *len);
4538 return (buf);
4541 static void
4542 window_copy_pipe(struct window_mode_entry *wme, struct session *s,
4543 const char *cmd)
4545 size_t len;
4547 window_copy_pipe_run(wme, s, cmd, &len);
4550 static void
4551 window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s,
4552 const char *prefix, const char *cmd)
4554 void *buf;
4555 size_t len;
4557 buf = window_copy_pipe_run(wme, s, cmd, &len);
4558 if (buf != NULL)
4559 window_copy_copy_buffer(wme, prefix, buf, len);
4562 static void
4563 window_copy_copy_selection(struct window_mode_entry *wme, const char *prefix)
4565 char *buf;
4566 size_t len;
4568 buf = window_copy_get_selection(wme, &len);
4569 if (buf != NULL)
4570 window_copy_copy_buffer(wme, prefix, buf, len);
4573 static void
4574 window_copy_append_selection(struct window_mode_entry *wme)
4576 struct window_pane *wp = wme->wp;
4577 char *buf;
4578 struct paste_buffer *pb;
4579 const char *bufdata, *bufname = NULL;
4580 size_t len, bufsize;
4581 struct screen_write_ctx ctx;
4583 buf = window_copy_get_selection(wme, &len);
4584 if (buf == NULL)
4585 return;
4587 if (options_get_number(global_options, "set-clipboard") != 0) {
4588 screen_write_start_pane(&ctx, wp, NULL);
4589 screen_write_setselection(&ctx, buf, len);
4590 screen_write_stop(&ctx);
4591 notify_pane("pane-set-clipboard", wp);
4594 pb = paste_get_top(&bufname);
4595 if (pb != NULL) {
4596 bufdata = paste_buffer_data(pb, &bufsize);
4597 buf = xrealloc(buf, len + bufsize);
4598 memmove(buf + bufsize, buf, len);
4599 memcpy(buf, bufdata, bufsize);
4600 len += bufsize;
4602 if (paste_set(buf, len, bufname, NULL) != 0)
4603 free(buf);
4606 static void
4607 window_copy_copy_line(struct window_mode_entry *wme, char **buf, size_t *off,
4608 u_int sy, u_int sx, u_int ex)
4610 struct window_copy_mode_data *data = wme->data;
4611 struct grid *gd = data->backing->grid;
4612 struct grid_cell gc;
4613 struct grid_line *gl;
4614 struct utf8_data ud;
4615 u_int i, xx, wrapped = 0;
4616 const char *s;
4618 if (sx > ex)
4619 return;
4622 * Work out if the line was wrapped at the screen edge and all of it is
4623 * on screen.
4625 gl = grid_get_line(gd, sy);
4626 if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx)
4627 wrapped = 1;
4629 /* If the line was wrapped, don't strip spaces (use the full length). */
4630 if (wrapped)
4631 xx = gl->cellsize;
4632 else
4633 xx = window_copy_find_length(wme, sy);
4634 if (ex > xx)
4635 ex = xx;
4636 if (sx > xx)
4637 sx = xx;
4639 if (sx < ex) {
4640 for (i = sx; i < ex; i++) {
4641 grid_get_cell(gd, i, sy, &gc);
4642 if (gc.flags & GRID_FLAG_PADDING)
4643 continue;
4644 utf8_copy(&ud, &gc.data);
4645 if (ud.size == 1 && (gc.attr & GRID_ATTR_CHARSET)) {
4646 s = tty_acs_get(NULL, ud.data[0]);
4647 if (s != NULL && strlen(s) <= sizeof ud.data) {
4648 ud.size = strlen(s);
4649 memcpy(ud.data, s, ud.size);
4653 *buf = xrealloc(*buf, (*off) + ud.size);
4654 memcpy(*buf + *off, ud.data, ud.size);
4655 *off += ud.size;
4659 /* Only add a newline if the line wasn't wrapped. */
4660 if (!wrapped || ex != xx) {
4661 *buf = xrealloc(*buf, (*off) + 1);
4662 (*buf)[(*off)++] = '\n';
4666 static void
4667 window_copy_clear_selection(struct window_mode_entry *wme)
4669 struct window_copy_mode_data *data = wme->data;
4670 u_int px, py;
4672 screen_clear_selection(&data->screen);
4674 data->cursordrag = CURSORDRAG_NONE;
4675 data->lineflag = LINE_SEL_NONE;
4676 data->selflag = SEL_CHAR;
4678 py = screen_hsize(data->backing) + data->cy - data->oy;
4679 px = window_copy_find_length(wme, py);
4680 if (data->cx > px)
4681 window_copy_update_cursor(wme, px, data->cy);
4684 static int
4685 window_copy_in_set(struct window_mode_entry *wme, u_int px, u_int py,
4686 const char *set)
4688 struct window_copy_mode_data *data = wme->data;
4689 struct grid_cell gc;
4691 grid_get_cell(data->backing->grid, px, py, &gc);
4692 if (gc.flags & GRID_FLAG_PADDING)
4693 return (0);
4694 return (utf8_cstrhas(set, &gc.data));
4697 static u_int
4698 window_copy_find_length(struct window_mode_entry *wme, u_int py)
4700 struct window_copy_mode_data *data = wme->data;
4702 return (grid_line_length(data->backing->grid, py));
4705 static void
4706 window_copy_cursor_start_of_line(struct window_mode_entry *wme)
4708 struct window_copy_mode_data *data = wme->data;
4709 struct screen *back_s = data->backing;
4710 struct grid_reader gr;
4711 u_int px, py, oldy, hsize;
4713 px = data->cx;
4714 hsize = screen_hsize(back_s);
4715 py = hsize + data->cy - data->oy;
4716 oldy = data->cy;
4718 grid_reader_start(&gr, back_s->grid, px, py);
4719 grid_reader_cursor_start_of_line(&gr, 1);
4720 grid_reader_get_cursor(&gr, &px, &py);
4721 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
4724 static void
4725 window_copy_cursor_back_to_indentation(struct window_mode_entry *wme)
4727 struct window_copy_mode_data *data = wme->data;
4728 struct screen *back_s = data->backing;
4729 struct grid_reader gr;
4730 u_int px, py, oldy, hsize;
4732 px = data->cx;
4733 hsize = screen_hsize(back_s);
4734 py = hsize + data->cy - data->oy;
4735 oldy = data->cy;
4737 grid_reader_start(&gr, back_s->grid, px, py);
4738 grid_reader_cursor_back_to_indentation(&gr);
4739 grid_reader_get_cursor(&gr, &px, &py);
4740 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
4743 static void
4744 window_copy_cursor_end_of_line(struct window_mode_entry *wme)
4746 struct window_copy_mode_data *data = wme->data;
4747 struct screen *back_s = data->backing;
4748 struct grid_reader gr;
4749 u_int px, py, oldy, hsize;
4751 px = data->cx;
4752 hsize = screen_hsize(back_s);
4753 py = hsize + data->cy - data->oy;
4754 oldy = data->cy;
4756 grid_reader_start(&gr, back_s->grid, px, py);
4757 if (data->screen.sel != NULL && data->rectflag)
4758 grid_reader_cursor_end_of_line(&gr, 1, 1);
4759 else
4760 grid_reader_cursor_end_of_line(&gr, 1, 0);
4761 grid_reader_get_cursor(&gr, &px, &py);
4762 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
4763 data->oy, oldy, px, py, 0);
4766 static void
4767 window_copy_other_end(struct window_mode_entry *wme)
4769 struct window_copy_mode_data *data = wme->data;
4770 struct screen *s = &data->screen;
4771 u_int selx, sely, cy, yy, hsize;
4773 if (s->sel == NULL && data->lineflag == LINE_SEL_NONE)
4774 return;
4776 if (data->lineflag == LINE_SEL_LEFT_RIGHT)
4777 data->lineflag = LINE_SEL_RIGHT_LEFT;
4778 else if (data->lineflag == LINE_SEL_RIGHT_LEFT)
4779 data->lineflag = LINE_SEL_LEFT_RIGHT;
4781 switch (data->cursordrag) {
4782 case CURSORDRAG_NONE:
4783 case CURSORDRAG_SEL:
4784 data->cursordrag = CURSORDRAG_ENDSEL;
4785 break;
4786 case CURSORDRAG_ENDSEL:
4787 data->cursordrag = CURSORDRAG_SEL;
4788 break;
4791 selx = data->endselx;
4792 sely = data->endsely;
4793 if (data->cursordrag == CURSORDRAG_SEL) {
4794 selx = data->selx;
4795 sely = data->sely;
4798 cy = data->cy;
4799 yy = screen_hsize(data->backing) + data->cy - data->oy;
4801 data->cx = selx;
4803 hsize = screen_hsize(data->backing);
4804 if (sely < hsize - data->oy) { /* above */
4805 data->oy = hsize - sely;
4806 data->cy = 0;
4807 } else if (sely > hsize - data->oy + screen_size_y(s)) { /* below */
4808 data->oy = hsize - sely + screen_size_y(s) - 1;
4809 data->cy = screen_size_y(s) - 1;
4810 } else
4811 data->cy = cy + sely - yy;
4813 window_copy_update_selection(wme, 1, 1);
4814 window_copy_redraw_screen(wme);
4817 static void
4818 window_copy_cursor_left(struct window_mode_entry *wme)
4820 struct window_copy_mode_data *data = wme->data;
4821 struct screen *back_s = data->backing;
4822 struct grid_reader gr;
4823 u_int px, py, oldy, hsize;
4825 px = data->cx;
4826 hsize = screen_hsize(back_s);
4827 py = hsize + data->cy - data->oy;
4828 oldy = data->cy;
4830 grid_reader_start(&gr, back_s->grid, px, py);
4831 grid_reader_cursor_left(&gr, 1);
4832 grid_reader_get_cursor(&gr, &px, &py);
4833 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
4836 static void
4837 window_copy_cursor_right(struct window_mode_entry *wme, int all)
4839 struct window_copy_mode_data *data = wme->data;
4840 struct screen *back_s = data->backing;
4841 struct grid_reader gr;
4842 u_int px, py, oldy, hsize;
4844 px = data->cx;
4845 hsize = screen_hsize(back_s);
4846 py = hsize + data->cy - data->oy;
4847 oldy = data->cy;
4849 grid_reader_start(&gr, back_s->grid, px, py);
4850 grid_reader_cursor_right(&gr, 1, all);
4851 grid_reader_get_cursor(&gr, &px, &py);
4852 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
4853 data->oy, oldy, px, py, 0);
4856 static void
4857 window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only)
4859 struct window_copy_mode_data *data = wme->data;
4860 struct screen *s = &data->screen;
4861 u_int ox, oy, px, py;
4862 int norectsel;
4864 norectsel = data->screen.sel == NULL || !data->rectflag;
4865 oy = screen_hsize(data->backing) + data->cy - data->oy;
4866 ox = window_copy_find_length(wme, oy);
4867 if (norectsel && data->cx != ox) {
4868 data->lastcx = data->cx;
4869 data->lastsx = ox;
4872 if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely)
4873 window_copy_other_end(wme);
4875 if (scroll_only || data->cy == 0) {
4876 if (norectsel)
4877 data->cx = data->lastcx;
4878 window_copy_scroll_down(wme, 1);
4879 if (scroll_only) {
4880 if (data->cy == screen_size_y(s) - 1)
4881 window_copy_redraw_lines(wme, data->cy, 1);
4882 else
4883 window_copy_redraw_lines(wme, data->cy, 2);
4885 } else {
4886 if (norectsel) {
4887 window_copy_update_cursor(wme, data->lastcx,
4888 data->cy - 1);
4889 } else
4890 window_copy_update_cursor(wme, data->cx, data->cy - 1);
4891 if (window_copy_update_selection(wme, 1, 0)) {
4892 if (data->cy == screen_size_y(s) - 1)
4893 window_copy_redraw_lines(wme, data->cy, 1);
4894 else
4895 window_copy_redraw_lines(wme, data->cy, 2);
4899 if (norectsel) {
4900 py = screen_hsize(data->backing) + data->cy - data->oy;
4901 px = window_copy_find_length(wme, py);
4902 if ((data->cx >= data->lastsx && data->cx != px) ||
4903 data->cx > px)
4905 window_copy_update_cursor(wme, px, data->cy);
4906 if (window_copy_update_selection(wme, 1, 0))
4907 window_copy_redraw_lines(wme, data->cy, 1);
4911 if (data->lineflag == LINE_SEL_LEFT_RIGHT)
4913 py = screen_hsize(data->backing) + data->cy - data->oy;
4914 if (data->rectflag)
4915 px = screen_size_x(data->backing);
4916 else
4917 px = window_copy_find_length(wme, py);
4918 window_copy_update_cursor(wme, px, data->cy);
4919 if (window_copy_update_selection(wme, 1, 0))
4920 window_copy_redraw_lines(wme, data->cy, 1);
4922 else if (data->lineflag == LINE_SEL_RIGHT_LEFT)
4924 window_copy_update_cursor(wme, 0, data->cy);
4925 if (window_copy_update_selection(wme, 1, 0))
4926 window_copy_redraw_lines(wme, data->cy, 1);
4930 static void
4931 window_copy_cursor_down(struct window_mode_entry *wme, int scroll_only)
4933 struct window_copy_mode_data *data = wme->data;
4934 struct screen *s = &data->screen;
4935 u_int ox, oy, px, py;
4936 int norectsel;
4938 norectsel = data->screen.sel == NULL || !data->rectflag;
4939 oy = screen_hsize(data->backing) + data->cy - data->oy;
4940 ox = window_copy_find_length(wme, oy);
4941 if (norectsel && data->cx != ox) {
4942 data->lastcx = data->cx;
4943 data->lastsx = ox;
4946 if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely)
4947 window_copy_other_end(wme);
4949 if (scroll_only || data->cy == screen_size_y(s) - 1) {
4950 if (norectsel)
4951 data->cx = data->lastcx;
4952 window_copy_scroll_up(wme, 1);
4953 if (scroll_only && data->cy > 0)
4954 window_copy_redraw_lines(wme, data->cy - 1, 2);
4955 } else {
4956 if (norectsel) {
4957 window_copy_update_cursor(wme, data->lastcx,
4958 data->cy + 1);
4959 } else
4960 window_copy_update_cursor(wme, data->cx, data->cy + 1);
4961 if (window_copy_update_selection(wme, 1, 0))
4962 window_copy_redraw_lines(wme, data->cy - 1, 2);
4965 if (norectsel) {
4966 py = screen_hsize(data->backing) + data->cy - data->oy;
4967 px = window_copy_find_length(wme, py);
4968 if ((data->cx >= data->lastsx && data->cx != px) ||
4969 data->cx > px)
4971 window_copy_update_cursor(wme, px, data->cy);
4972 if (window_copy_update_selection(wme, 1, 0))
4973 window_copy_redraw_lines(wme, data->cy, 1);
4977 if (data->lineflag == LINE_SEL_LEFT_RIGHT)
4979 py = screen_hsize(data->backing) + data->cy - data->oy;
4980 if (data->rectflag)
4981 px = screen_size_x(data->backing);
4982 else
4983 px = window_copy_find_length(wme, py);
4984 window_copy_update_cursor(wme, px, data->cy);
4985 if (window_copy_update_selection(wme, 1, 0))
4986 window_copy_redraw_lines(wme, data->cy, 1);
4988 else if (data->lineflag == LINE_SEL_RIGHT_LEFT)
4990 window_copy_update_cursor(wme, 0, data->cy);
4991 if (window_copy_update_selection(wme, 1, 0))
4992 window_copy_redraw_lines(wme, data->cy, 1);
4996 static void
4997 window_copy_cursor_jump(struct window_mode_entry *wme)
4999 struct window_copy_mode_data *data = wme->data;
5000 struct screen *back_s = data->backing;
5001 struct grid_reader gr;
5002 u_int px, py, oldy, hsize;
5004 px = data->cx + 1;
5005 hsize = screen_hsize(back_s);
5006 py = hsize + data->cy - data->oy;
5007 oldy = data->cy;
5009 grid_reader_start(&gr, back_s->grid, px, py);
5010 if (grid_reader_cursor_jump(&gr, data->jumpchar)) {
5011 grid_reader_get_cursor(&gr, &px, &py);
5012 window_copy_acquire_cursor_down(wme, hsize,
5013 screen_size_y(back_s), data->oy, oldy, px, py, 0);
5017 static void
5018 window_copy_cursor_jump_back(struct window_mode_entry *wme)
5020 struct window_copy_mode_data *data = wme->data;
5021 struct screen *back_s = data->backing;
5022 struct grid_reader gr;
5023 u_int px, py, oldy, hsize;
5025 px = data->cx;
5026 hsize = screen_hsize(back_s);
5027 py = hsize + data->cy - data->oy;
5028 oldy = data->cy;
5030 grid_reader_start(&gr, back_s->grid, px, py);
5031 grid_reader_cursor_left(&gr, 0);
5032 if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) {
5033 grid_reader_get_cursor(&gr, &px, &py);
5034 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px,
5035 py);
5039 static void
5040 window_copy_cursor_jump_to(struct window_mode_entry *wme)
5042 struct window_copy_mode_data *data = wme->data;
5043 struct screen *back_s = data->backing;
5044 struct grid_reader gr;
5045 u_int px, py, oldy, hsize;
5047 px = data->cx + 2;
5048 hsize = screen_hsize(back_s);
5049 py = hsize + data->cy - data->oy;
5050 oldy = data->cy;
5052 grid_reader_start(&gr, back_s->grid, px, py);
5053 if (grid_reader_cursor_jump(&gr, data->jumpchar)) {
5054 grid_reader_cursor_left(&gr, 1);
5055 grid_reader_get_cursor(&gr, &px, &py);
5056 window_copy_acquire_cursor_down(wme, hsize,
5057 screen_size_y(back_s), data->oy, oldy, px, py, 0);
5061 static void
5062 window_copy_cursor_jump_to_back(struct window_mode_entry *wme)
5064 struct window_copy_mode_data *data = wme->data;
5065 struct screen *back_s = data->backing;
5066 struct grid_reader gr;
5067 u_int px, py, oldy, hsize;
5069 px = data->cx;
5070 hsize = screen_hsize(back_s);
5071 py = hsize + data->cy - data->oy;
5072 oldy = data->cy;
5074 grid_reader_start(&gr, back_s->grid, px, py);
5075 grid_reader_cursor_left(&gr, 0);
5076 grid_reader_cursor_left(&gr, 0);
5077 if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) {
5078 grid_reader_cursor_right(&gr, 1, 0);
5079 grid_reader_get_cursor(&gr, &px, &py);
5080 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px,
5081 py);
5085 static void
5086 window_copy_cursor_next_word(struct window_mode_entry *wme,
5087 const char *separators)
5089 struct window_copy_mode_data *data = wme->data;
5090 struct screen *back_s = data->backing;
5091 struct grid_reader gr;
5092 u_int px, py, oldy, hsize;
5094 px = data->cx;
5095 hsize = screen_hsize(back_s);
5096 py = hsize + data->cy - data->oy;
5097 oldy = data->cy;
5099 grid_reader_start(&gr, back_s->grid, px, py);
5100 grid_reader_cursor_next_word(&gr, separators);
5101 grid_reader_get_cursor(&gr, &px, &py);
5102 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
5103 data->oy, oldy, px, py, 0);
5106 /* Compute the next place where a word ends. */
5107 static void
5108 window_copy_cursor_next_word_end_pos(struct window_mode_entry *wme,
5109 const char *separators, u_int *ppx, u_int *ppy)
5111 struct window_pane *wp = wme->wp;
5112 struct window_copy_mode_data *data = wme->data;
5113 struct options *oo = wp->window->options;
5114 struct screen *back_s = data->backing;
5115 struct grid_reader gr;
5116 u_int px, py, hsize;
5118 px = data->cx;
5119 hsize = screen_hsize(back_s);
5120 py = hsize + data->cy - data->oy;
5122 grid_reader_start(&gr, back_s->grid, px, py);
5123 if (options_get_number(oo, "mode-keys") == MODEKEY_VI) {
5124 if (!grid_reader_in_set(&gr, WHITESPACE))
5125 grid_reader_cursor_right(&gr, 0, 0);
5126 grid_reader_cursor_next_word_end(&gr, separators);
5127 grid_reader_cursor_left(&gr, 1);
5128 } else
5129 grid_reader_cursor_next_word_end(&gr, separators);
5130 grid_reader_get_cursor(&gr, &px, &py);
5131 *ppx = px;
5132 *ppy = py;
5135 /* Move to the next place where a word ends. */
5136 static void
5137 window_copy_cursor_next_word_end(struct window_mode_entry *wme,
5138 const char *separators, int no_reset)
5140 struct window_pane *wp = wme->wp;
5141 struct window_copy_mode_data *data = wme->data;
5142 struct options *oo = wp->window->options;
5143 struct screen *back_s = data->backing;
5144 struct grid_reader gr;
5145 u_int px, py, oldy, hsize;
5147 px = data->cx;
5148 hsize = screen_hsize(back_s);
5149 py = hsize + data->cy - data->oy;
5150 oldy = data->cy;
5152 grid_reader_start(&gr, back_s->grid, px, py);
5153 if (options_get_number(oo, "mode-keys") == MODEKEY_VI) {
5154 if (!grid_reader_in_set(&gr, WHITESPACE))
5155 grid_reader_cursor_right(&gr, 0, 0);
5156 grid_reader_cursor_next_word_end(&gr, separators);
5157 grid_reader_cursor_left(&gr, 1);
5158 } else
5159 grid_reader_cursor_next_word_end(&gr, separators);
5160 grid_reader_get_cursor(&gr, &px, &py);
5161 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
5162 data->oy, oldy, px, py, no_reset);
5165 /* Compute the previous place where a word begins. */
5166 static void
5167 window_copy_cursor_previous_word_pos(struct window_mode_entry *wme,
5168 const char *separators, u_int *ppx, u_int *ppy)
5170 struct window_copy_mode_data *data = wme->data;
5171 struct screen *back_s = data->backing;
5172 struct grid_reader gr;
5173 u_int px, py, hsize;
5175 px = data->cx;
5176 hsize = screen_hsize(back_s);
5177 py = hsize + data->cy - data->oy;
5179 grid_reader_start(&gr, back_s->grid, px, py);
5180 grid_reader_cursor_previous_word(&gr, separators, /* already= */ 0,
5181 /* stop_at_eol= */ 1);
5182 grid_reader_get_cursor(&gr, &px, &py);
5183 *ppx = px;
5184 *ppy = py;
5187 /* Move to the previous place where a word begins. */
5188 static void
5189 window_copy_cursor_previous_word(struct window_mode_entry *wme,
5190 const char *separators, int already)
5192 struct window_copy_mode_data *data = wme->data;
5193 struct window *w = wme->wp->window;
5194 struct screen *back_s = data->backing;
5195 struct grid_reader gr;
5196 u_int px, py, oldy, hsize;
5197 int stop_at_eol;
5199 if (options_get_number(w->options, "mode-keys") == MODEKEY_EMACS)
5200 stop_at_eol = 1;
5201 else
5202 stop_at_eol = 0;
5204 px = data->cx;
5205 hsize = screen_hsize(back_s);
5206 py = hsize + data->cy - data->oy;
5207 oldy = data->cy;
5209 grid_reader_start(&gr, back_s->grid, px, py);
5210 grid_reader_cursor_previous_word(&gr, separators, already, stop_at_eol);
5211 grid_reader_get_cursor(&gr, &px, &py);
5212 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
5215 static void
5216 window_copy_scroll_up(struct window_mode_entry *wme, u_int ny)
5218 struct window_pane *wp = wme->wp;
5219 struct window_copy_mode_data *data = wme->data;
5220 struct screen *s = &data->screen;
5221 struct screen_write_ctx ctx;
5223 if (data->oy < ny)
5224 ny = data->oy;
5225 if (ny == 0)
5226 return;
5227 data->oy -= ny;
5229 if (data->searchmark != NULL && !data->timeout)
5230 window_copy_search_marks(wme, NULL, data->searchregex, 1);
5231 window_copy_update_selection(wme, 0, 0);
5233 screen_write_start_pane(&ctx, wp, NULL);
5234 screen_write_cursormove(&ctx, 0, 0, 0);
5235 screen_write_deleteline(&ctx, ny, 8);
5236 window_copy_write_lines(wme, &ctx, screen_size_y(s) - ny, ny);
5237 window_copy_write_line(wme, &ctx, 0);
5238 if (screen_size_y(s) > 1)
5239 window_copy_write_line(wme, &ctx, 1);
5240 if (screen_size_y(s) > 3)
5241 window_copy_write_line(wme, &ctx, screen_size_y(s) - 2);
5242 if (s->sel != NULL && screen_size_y(s) > ny)
5243 window_copy_write_line(wme, &ctx, screen_size_y(s) - ny - 1);
5244 screen_write_cursormove(&ctx, data->cx, data->cy, 0);
5245 screen_write_stop(&ctx);
5248 static void
5249 window_copy_scroll_down(struct window_mode_entry *wme, u_int ny)
5251 struct window_pane *wp = wme->wp;
5252 struct window_copy_mode_data *data = wme->data;
5253 struct screen *s = &data->screen;
5254 struct screen_write_ctx ctx;
5256 if (ny > screen_hsize(data->backing))
5257 return;
5259 if (data->oy > screen_hsize(data->backing) - ny)
5260 ny = screen_hsize(data->backing) - data->oy;
5261 if (ny == 0)
5262 return;
5263 data->oy += ny;
5265 if (data->searchmark != NULL && !data->timeout)
5266 window_copy_search_marks(wme, NULL, data->searchregex, 1);
5267 window_copy_update_selection(wme, 0, 0);
5269 screen_write_start_pane(&ctx, wp, NULL);
5270 screen_write_cursormove(&ctx, 0, 0, 0);
5271 screen_write_insertline(&ctx, ny, 8);
5272 window_copy_write_lines(wme, &ctx, 0, ny);
5273 if (s->sel != NULL && screen_size_y(s) > ny)
5274 window_copy_write_line(wme, &ctx, ny);
5275 else if (ny == 1) /* nuke position */
5276 window_copy_write_line(wme, &ctx, 1);
5277 screen_write_cursormove(&ctx, data->cx, data->cy, 0);
5278 screen_write_stop(&ctx);
5281 static void
5282 window_copy_rectangle_set(struct window_mode_entry *wme, int rectflag)
5284 struct window_copy_mode_data *data = wme->data;
5285 u_int px, py;
5287 data->rectflag = rectflag;
5289 py = screen_hsize(data->backing) + data->cy - data->oy;
5290 px = window_copy_find_length(wme, py);
5291 if (data->cx > px)
5292 window_copy_update_cursor(wme, px, data->cy);
5294 window_copy_update_selection(wme, 1, 0);
5295 window_copy_redraw_screen(wme);
5298 static void
5299 window_copy_move_mouse(struct mouse_event *m)
5301 struct window_pane *wp;
5302 struct window_mode_entry *wme;
5303 u_int x, y;
5305 wp = cmd_mouse_pane(m, NULL, NULL);
5306 if (wp == NULL)
5307 return;
5308 wme = TAILQ_FIRST(&wp->modes);
5309 if (wme == NULL)
5310 return;
5311 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode)
5312 return;
5314 if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
5315 return;
5317 window_copy_update_cursor(wme, x, y);
5320 void
5321 window_copy_start_drag(struct client *c, struct mouse_event *m)
5323 struct window_pane *wp;
5324 struct window_mode_entry *wme;
5325 struct window_copy_mode_data *data;
5326 u_int x, y, yg;
5328 if (c == NULL)
5329 return;
5331 wp = cmd_mouse_pane(m, NULL, NULL);
5332 if (wp == NULL)
5333 return;
5334 wme = TAILQ_FIRST(&wp->modes);
5335 if (wme == NULL)
5336 return;
5337 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode)
5338 return;
5340 if (cmd_mouse_at(wp, m, &x, &y, 1) != 0)
5341 return;
5343 c->tty.mouse_drag_update = window_copy_drag_update;
5344 c->tty.mouse_drag_release = window_copy_drag_release;
5346 data = wme->data;
5347 yg = screen_hsize(data->backing) + y - data->oy;
5348 if (x < data->selrx || x > data->endselrx || yg != data->selry)
5349 data->selflag = SEL_CHAR;
5350 switch (data->selflag) {
5351 case SEL_WORD:
5352 if (data->separators != NULL) {
5353 window_copy_update_cursor(wme, x, y);
5354 window_copy_cursor_previous_word_pos(wme,
5355 data->separators, &x, &y);
5356 y -= screen_hsize(data->backing) - data->oy;
5358 window_copy_update_cursor(wme, x, y);
5359 break;
5360 case SEL_LINE:
5361 window_copy_update_cursor(wme, 0, y);
5362 break;
5363 case SEL_CHAR:
5364 window_copy_update_cursor(wme, x, y);
5365 window_copy_start_selection(wme);
5366 break;
5369 window_copy_redraw_screen(wme);
5370 window_copy_drag_update(c, m);
5373 static void
5374 window_copy_drag_update(struct client *c, struct mouse_event *m)
5376 struct window_pane *wp;
5377 struct window_mode_entry *wme;
5378 struct window_copy_mode_data *data;
5379 u_int x, y, old_cx, old_cy;
5380 struct timeval tv = {
5381 .tv_usec = WINDOW_COPY_DRAG_REPEAT_TIME
5384 if (c == NULL)
5385 return;
5387 wp = cmd_mouse_pane(m, NULL, NULL);
5388 if (wp == NULL)
5389 return;
5390 wme = TAILQ_FIRST(&wp->modes);
5391 if (wme == NULL)
5392 return;
5393 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode)
5394 return;
5396 data = wme->data;
5397 evtimer_del(&data->dragtimer);
5399 if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
5400 return;
5401 old_cx = data->cx;
5402 old_cy = data->cy;
5404 window_copy_update_cursor(wme, x, y);
5405 if (window_copy_update_selection(wme, 1, 0))
5406 window_copy_redraw_selection(wme, old_cy);
5407 if (old_cy != data->cy || old_cx == data->cx) {
5408 if (y == 0) {
5409 evtimer_add(&data->dragtimer, &tv);
5410 window_copy_cursor_up(wme, 1);
5411 } else if (y == screen_size_y(&data->screen) - 1) {
5412 evtimer_add(&data->dragtimer, &tv);
5413 window_copy_cursor_down(wme, 1);
5418 static void
5419 window_copy_drag_release(struct client *c, struct mouse_event *m)
5421 struct window_pane *wp;
5422 struct window_mode_entry *wme;
5423 struct window_copy_mode_data *data;
5425 if (c == NULL)
5426 return;
5428 wp = cmd_mouse_pane(m, NULL, NULL);
5429 if (wp == NULL)
5430 return;
5431 wme = TAILQ_FIRST(&wp->modes);
5432 if (wme == NULL)
5433 return;
5434 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode)
5435 return;
5437 data = wme->data;
5438 evtimer_del(&data->dragtimer);
5441 static void
5442 window_copy_jump_to_mark(struct window_mode_entry *wme)
5444 struct window_copy_mode_data *data = wme->data;
5445 u_int tmx, tmy;
5447 tmx = data->cx;
5448 tmy = screen_hsize(data->backing) + data->cy - data->oy;
5449 data->cx = data->mx;
5450 if (data->my < screen_hsize(data->backing)) {
5451 data->cy = 0;
5452 data->oy = screen_hsize(data->backing) - data->my;
5453 } else {
5454 data->cy = data->my - screen_hsize(data->backing);
5455 data->oy = 0;
5457 data->mx = tmx;
5458 data->my = tmy;
5459 data->showmark = 1;
5460 window_copy_update_selection(wme, 0, 0);
5461 window_copy_redraw_screen(wme);
5464 /* Scroll up if the cursor went off the visible screen. */
5465 static void
5466 window_copy_acquire_cursor_up(struct window_mode_entry *wme, u_int hsize,
5467 u_int oy, u_int oldy, u_int px, u_int py)
5469 u_int cy, yy, ny, nd;
5471 yy = hsize - oy;
5472 if (py < yy) {
5473 ny = yy - py;
5474 cy = 0;
5475 nd = 1;
5476 } else {
5477 ny = 0;
5478 cy = py - yy;
5479 nd = oldy - cy + 1;
5481 while (ny > 0) {
5482 window_copy_cursor_up(wme, 1);
5483 ny--;
5485 window_copy_update_cursor(wme, px, cy);
5486 if (window_copy_update_selection(wme, 1, 0))
5487 window_copy_redraw_lines(wme, cy, nd);
5490 /* Scroll down if the cursor went off the visible screen. */
5491 static void
5492 window_copy_acquire_cursor_down(struct window_mode_entry *wme, u_int hsize,
5493 u_int sy, u_int oy, u_int oldy, u_int px, u_int py, int no_reset)
5495 u_int cy, yy, ny, nd;
5497 cy = py - hsize + oy;
5498 yy = sy - 1;
5499 if (cy > yy) {
5500 ny = cy - yy;
5501 oldy = yy;
5502 nd = 1;
5503 } else {
5504 ny = 0;
5505 nd = cy - oldy + 1;
5507 while (ny > 0) {
5508 window_copy_cursor_down(wme, 1);
5509 ny--;
5511 if (cy > yy)
5512 window_copy_update_cursor(wme, px, yy);
5513 else
5514 window_copy_update_cursor(wme, px, cy);
5515 if (window_copy_update_selection(wme, 1, no_reset))
5516 window_copy_redraw_lines(wme, oldy, nd);