Add remain-on-exit-format to set text shown when pane is dead.
[tmux-openbsd.git] / window-copy.c
blob7c55b0f23a4247d56168e0160c7b7e5ee05e663e
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_toggle_position(struct window_copy_cmd_state *cs)
1278 struct window_mode_entry *wme = cs->wme;
1279 struct window_copy_mode_data *data = wme->data;
1281 data->hide_position = !data->hide_position;
1282 return (WINDOW_COPY_CMD_REDRAW);
1285 static enum window_copy_cmd_action
1286 window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs)
1288 struct window_mode_entry *wme = cs->wme;
1289 struct window_copy_mode_data *data = wme->data;
1290 struct screen *s = data->backing;
1291 u_int oy;
1293 oy = screen_hsize(s) + data->cy - data->oy;
1294 if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely)
1295 window_copy_other_end(wme);
1297 data->cy = screen_size_y(&data->screen) - 1;
1298 data->cx = window_copy_find_length(wme, screen_hsize(s) + data->cy);
1299 data->oy = 0;
1301 if (data->searchmark != NULL && !data->timeout)
1302 window_copy_search_marks(wme, NULL, data->searchregex, 1);
1303 window_copy_update_selection(wme, 1, 0);
1304 return (WINDOW_COPY_CMD_REDRAW);
1307 static enum window_copy_cmd_action
1308 window_copy_cmd_history_top(struct window_copy_cmd_state *cs)
1310 struct window_mode_entry *wme = cs->wme;
1311 struct window_copy_mode_data *data = wme->data;
1312 u_int oy;
1314 oy = screen_hsize(data->backing) + data->cy - data->oy;
1315 if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely)
1316 window_copy_other_end(wme);
1318 data->cy = 0;
1319 data->cx = 0;
1320 data->oy = screen_hsize(data->backing);
1322 if (data->searchmark != NULL && !data->timeout)
1323 window_copy_search_marks(wme, NULL, data->searchregex, 1);
1324 window_copy_update_selection(wme, 1, 0);
1325 return (WINDOW_COPY_CMD_REDRAW);
1328 static enum window_copy_cmd_action
1329 window_copy_cmd_jump_again(struct window_copy_cmd_state *cs)
1331 struct window_mode_entry *wme = cs->wme;
1332 struct window_copy_mode_data *data = wme->data;
1333 u_int np = wme->prefix;
1335 switch (data->jumptype) {
1336 case WINDOW_COPY_JUMPFORWARD:
1337 for (; np != 0; np--)
1338 window_copy_cursor_jump(wme);
1339 break;
1340 case WINDOW_COPY_JUMPBACKWARD:
1341 for (; np != 0; np--)
1342 window_copy_cursor_jump_back(wme);
1343 break;
1344 case WINDOW_COPY_JUMPTOFORWARD:
1345 for (; np != 0; np--)
1346 window_copy_cursor_jump_to(wme);
1347 break;
1348 case WINDOW_COPY_JUMPTOBACKWARD:
1349 for (; np != 0; np--)
1350 window_copy_cursor_jump_to_back(wme);
1351 break;
1353 return (WINDOW_COPY_CMD_NOTHING);
1356 static enum window_copy_cmd_action
1357 window_copy_cmd_jump_reverse(struct window_copy_cmd_state *cs)
1359 struct window_mode_entry *wme = cs->wme;
1360 struct window_copy_mode_data *data = wme->data;
1361 u_int np = wme->prefix;
1363 switch (data->jumptype) {
1364 case WINDOW_COPY_JUMPFORWARD:
1365 for (; np != 0; np--)
1366 window_copy_cursor_jump_back(wme);
1367 break;
1368 case WINDOW_COPY_JUMPBACKWARD:
1369 for (; np != 0; np--)
1370 window_copy_cursor_jump(wme);
1371 break;
1372 case WINDOW_COPY_JUMPTOFORWARD:
1373 for (; np != 0; np--)
1374 window_copy_cursor_jump_to_back(wme);
1375 break;
1376 case WINDOW_COPY_JUMPTOBACKWARD:
1377 for (; np != 0; np--)
1378 window_copy_cursor_jump_to(wme);
1379 break;
1381 return (WINDOW_COPY_CMD_NOTHING);
1384 static enum window_copy_cmd_action
1385 window_copy_cmd_middle_line(struct window_copy_cmd_state *cs)
1387 struct window_mode_entry *wme = cs->wme;
1388 struct window_copy_mode_data *data = wme->data;
1390 data->cx = 0;
1391 data->cy = (screen_size_y(&data->screen) - 1) / 2;
1393 window_copy_update_selection(wme, 1, 0);
1394 return (WINDOW_COPY_CMD_REDRAW);
1397 static enum window_copy_cmd_action
1398 window_copy_cmd_previous_matching_bracket(struct window_copy_cmd_state *cs)
1400 struct window_mode_entry *wme = cs->wme;
1401 u_int np = wme->prefix;
1402 struct window_copy_mode_data *data = wme->data;
1403 struct screen *s = data->backing;
1404 char open[] = "{[(", close[] = "}])";
1405 char tried, found, start, *cp;
1406 u_int px, py, xx, n;
1407 struct grid_cell gc;
1408 int failed;
1410 for (; np != 0; np--) {
1411 /* Get cursor position and line length. */
1412 px = data->cx;
1413 py = screen_hsize(s) + data->cy - data->oy;
1414 xx = window_copy_find_length(wme, py);
1415 if (xx == 0)
1416 break;
1419 * Get the current character. If not on a bracket, try the
1420 * previous. If still not, then behave like previous-word.
1422 tried = 0;
1423 retry:
1424 grid_get_cell(s->grid, px, py, &gc);
1425 if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING))
1426 cp = NULL;
1427 else {
1428 found = *gc.data.data;
1429 cp = strchr(close, found);
1431 if (cp == NULL) {
1432 if (data->modekeys == MODEKEY_EMACS) {
1433 if (!tried && px > 0) {
1434 px--;
1435 tried = 1;
1436 goto retry;
1438 window_copy_cursor_previous_word(wme, close, 1);
1440 continue;
1442 start = open[cp - close];
1444 /* Walk backward until the matching bracket is reached. */
1445 n = 1;
1446 failed = 0;
1447 do {
1448 if (px == 0) {
1449 if (py == 0) {
1450 failed = 1;
1451 break;
1453 do {
1454 py--;
1455 xx = window_copy_find_length(wme, py);
1456 } while (xx == 0 && py > 0);
1457 if (xx == 0 && py == 0) {
1458 failed = 1;
1459 break;
1461 px = xx - 1;
1462 } else
1463 px--;
1465 grid_get_cell(s->grid, px, py, &gc);
1466 if (gc.data.size == 1 &&
1467 (~gc.flags & GRID_FLAG_PADDING)) {
1468 if (*gc.data.data == found)
1469 n++;
1470 else if (*gc.data.data == start)
1471 n--;
1473 } while (n != 0);
1475 /* Move the cursor to the found location if any. */
1476 if (!failed)
1477 window_copy_scroll_to(wme, px, py, 0);
1480 return (WINDOW_COPY_CMD_NOTHING);
1483 static enum window_copy_cmd_action
1484 window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state *cs)
1486 struct window_mode_entry *wme = cs->wme;
1487 u_int np = wme->prefix;
1488 struct window_copy_mode_data *data = wme->data;
1489 struct screen *s = data->backing;
1490 char open[] = "{[(", close[] = "}])";
1491 char tried, found, end, *cp;
1492 u_int px, py, xx, yy, sx, sy, n;
1493 struct grid_cell gc;
1494 int failed;
1495 struct grid_line *gl;
1497 for (; np != 0; np--) {
1498 /* Get cursor position and line length. */
1499 px = data->cx;
1500 py = screen_hsize(s) + data->cy - data->oy;
1501 xx = window_copy_find_length(wme, py);
1502 yy = screen_hsize(s) + screen_size_y(s) - 1;
1503 if (xx == 0)
1504 break;
1507 * Get the current character. If not on a bracket, try the
1508 * next. If still not, then behave like next-word.
1510 tried = 0;
1511 retry:
1512 grid_get_cell(s->grid, px, py, &gc);
1513 if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING))
1514 cp = NULL;
1515 else {
1516 found = *gc.data.data;
1519 * In vi mode, attempt to move to previous bracket if a
1520 * closing bracket is found first. If this fails,
1521 * return to the original cursor position.
1523 cp = strchr(close, found);
1524 if (cp != NULL && data->modekeys == MODEKEY_VI) {
1525 sx = data->cx;
1526 sy = screen_hsize(s) + data->cy - data->oy;
1528 window_copy_scroll_to(wme, px, py, 0);
1529 window_copy_cmd_previous_matching_bracket(cs);
1531 px = data->cx;
1532 py = screen_hsize(s) + data->cy - data->oy;
1533 grid_get_cell(s->grid, px, py, &gc);
1534 if (gc.data.size == 1 &&
1535 (~gc.flags & GRID_FLAG_PADDING) &&
1536 strchr(close, *gc.data.data) != NULL)
1537 window_copy_scroll_to(wme, sx, sy, 0);
1538 break;
1541 cp = strchr(open, found);
1543 if (cp == NULL) {
1544 if (data->modekeys == MODEKEY_EMACS) {
1545 if (!tried && px <= xx) {
1546 px++;
1547 tried = 1;
1548 goto retry;
1550 window_copy_cursor_next_word_end(wme, open, 0);
1551 continue;
1553 /* For vi, continue searching for bracket until EOL. */
1554 if (px > xx) {
1555 if (py == yy)
1556 continue;
1557 gl = grid_get_line(s->grid, py);
1558 if (~gl->flags & GRID_LINE_WRAPPED)
1559 continue;
1560 if (gl->cellsize > s->grid->sx)
1561 continue;
1562 px = 0;
1563 py++;
1564 xx = window_copy_find_length(wme, py);
1565 } else
1566 px++;
1567 goto retry;
1569 end = close[cp - open];
1571 /* Walk forward until the matching bracket is reached. */
1572 n = 1;
1573 failed = 0;
1574 do {
1575 if (px > xx) {
1576 if (py == yy) {
1577 failed = 1;
1578 break;
1580 px = 0;
1581 py++;
1582 xx = window_copy_find_length(wme, py);
1583 } else
1584 px++;
1586 grid_get_cell(s->grid, px, py, &gc);
1587 if (gc.data.size == 1 &&
1588 (~gc.flags & GRID_FLAG_PADDING)) {
1589 if (*gc.data.data == found)
1590 n++;
1591 else if (*gc.data.data == end)
1592 n--;
1594 } while (n != 0);
1596 /* Move the cursor to the found location if any. */
1597 if (!failed)
1598 window_copy_scroll_to(wme, px, py, 0);
1601 return (WINDOW_COPY_CMD_NOTHING);
1604 static enum window_copy_cmd_action
1605 window_copy_cmd_next_paragraph(struct window_copy_cmd_state *cs)
1607 struct window_mode_entry *wme = cs->wme;
1608 u_int np = wme->prefix;
1610 for (; np != 0; np--)
1611 window_copy_next_paragraph(wme);
1612 return (WINDOW_COPY_CMD_NOTHING);
1615 static enum window_copy_cmd_action
1616 window_copy_cmd_next_space(struct window_copy_cmd_state *cs)
1618 struct window_mode_entry *wme = cs->wme;
1619 u_int np = wme->prefix;
1621 for (; np != 0; np--)
1622 window_copy_cursor_next_word(wme, "");
1623 return (WINDOW_COPY_CMD_NOTHING);
1626 static enum window_copy_cmd_action
1627 window_copy_cmd_next_space_end(struct window_copy_cmd_state *cs)
1629 struct window_mode_entry *wme = cs->wme;
1630 u_int np = wme->prefix;
1632 for (; np != 0; np--)
1633 window_copy_cursor_next_word_end(wme, "", 0);
1634 return (WINDOW_COPY_CMD_NOTHING);
1637 static enum window_copy_cmd_action
1638 window_copy_cmd_next_word(struct window_copy_cmd_state *cs)
1640 struct window_mode_entry *wme = cs->wme;
1641 u_int np = wme->prefix;
1642 const char *separators;
1644 separators = options_get_string(cs->s->options, "word-separators");
1646 for (; np != 0; np--)
1647 window_copy_cursor_next_word(wme, separators);
1648 return (WINDOW_COPY_CMD_NOTHING);
1651 static enum window_copy_cmd_action
1652 window_copy_cmd_next_word_end(struct window_copy_cmd_state *cs)
1654 struct window_mode_entry *wme = cs->wme;
1655 u_int np = wme->prefix;
1656 const char *separators;
1658 separators = options_get_string(cs->s->options, "word-separators");
1660 for (; np != 0; np--)
1661 window_copy_cursor_next_word_end(wme, separators, 0);
1662 return (WINDOW_COPY_CMD_NOTHING);
1665 static enum window_copy_cmd_action
1666 window_copy_cmd_other_end(struct window_copy_cmd_state *cs)
1668 struct window_mode_entry *wme = cs->wme;
1669 u_int np = wme->prefix;
1670 struct window_copy_mode_data *data = wme->data;
1672 data->selflag = SEL_CHAR;
1673 if ((np % 2) != 0)
1674 window_copy_other_end(wme);
1675 return (WINDOW_COPY_CMD_NOTHING);
1678 static enum window_copy_cmd_action
1679 window_copy_cmd_page_down(struct window_copy_cmd_state *cs)
1681 struct window_mode_entry *wme = cs->wme;
1682 struct window_copy_mode_data *data = wme->data;
1683 u_int np = wme->prefix;
1685 for (; np != 0; np--) {
1686 if (window_copy_pagedown(wme, 0, data->scroll_exit))
1687 return (WINDOW_COPY_CMD_CANCEL);
1689 return (WINDOW_COPY_CMD_NOTHING);
1692 static enum window_copy_cmd_action
1693 window_copy_cmd_page_down_and_cancel(struct window_copy_cmd_state *cs)
1695 struct window_mode_entry *wme = cs->wme;
1696 u_int np = wme->prefix;
1698 for (; np != 0; np--) {
1699 if (window_copy_pagedown(wme, 0, 1))
1700 return (WINDOW_COPY_CMD_CANCEL);
1702 return (WINDOW_COPY_CMD_NOTHING);
1705 static enum window_copy_cmd_action
1706 window_copy_cmd_page_up(struct window_copy_cmd_state *cs)
1708 struct window_mode_entry *wme = cs->wme;
1709 u_int np = wme->prefix;
1711 for (; np != 0; np--)
1712 window_copy_pageup1(wme, 0);
1713 return (WINDOW_COPY_CMD_NOTHING);
1716 static enum window_copy_cmd_action
1717 window_copy_cmd_previous_paragraph(struct window_copy_cmd_state *cs)
1719 struct window_mode_entry *wme = cs->wme;
1720 u_int np = wme->prefix;
1722 for (; np != 0; np--)
1723 window_copy_previous_paragraph(wme);
1724 return (WINDOW_COPY_CMD_NOTHING);
1727 static enum window_copy_cmd_action
1728 window_copy_cmd_previous_space(struct window_copy_cmd_state *cs)
1730 struct window_mode_entry *wme = cs->wme;
1731 u_int np = wme->prefix;
1733 for (; np != 0; np--)
1734 window_copy_cursor_previous_word(wme, "", 1);
1735 return (WINDOW_COPY_CMD_NOTHING);
1738 static enum window_copy_cmd_action
1739 window_copy_cmd_previous_word(struct window_copy_cmd_state *cs)
1741 struct window_mode_entry *wme = cs->wme;
1742 u_int np = wme->prefix;
1743 const char *separators;
1745 separators = options_get_string(cs->s->options, "word-separators");
1747 for (; np != 0; np--)
1748 window_copy_cursor_previous_word(wme, separators, 1);
1749 return (WINDOW_COPY_CMD_NOTHING);
1752 static enum window_copy_cmd_action
1753 window_copy_cmd_rectangle_on(struct window_copy_cmd_state *cs)
1755 struct window_mode_entry *wme = cs->wme;
1756 struct window_copy_mode_data *data = wme->data;
1758 data->lineflag = LINE_SEL_NONE;
1759 window_copy_rectangle_set(wme, 1);
1761 return (WINDOW_COPY_CMD_NOTHING);
1764 static enum window_copy_cmd_action
1765 window_copy_cmd_rectangle_off(struct window_copy_cmd_state *cs)
1767 struct window_mode_entry *wme = cs->wme;
1768 struct window_copy_mode_data *data = wme->data;
1770 data->lineflag = LINE_SEL_NONE;
1771 window_copy_rectangle_set(wme, 0);
1773 return (WINDOW_COPY_CMD_NOTHING);
1776 static enum window_copy_cmd_action
1777 window_copy_cmd_rectangle_toggle(struct window_copy_cmd_state *cs)
1779 struct window_mode_entry *wme = cs->wme;
1780 struct window_copy_mode_data *data = wme->data;
1782 data->lineflag = LINE_SEL_NONE;
1783 window_copy_rectangle_set(wme, !data->rectflag);
1785 return (WINDOW_COPY_CMD_NOTHING);
1788 static enum window_copy_cmd_action
1789 window_copy_cmd_scroll_down(struct window_copy_cmd_state *cs)
1791 struct window_mode_entry *wme = cs->wme;
1792 struct window_copy_mode_data *data = wme->data;
1793 u_int np = wme->prefix;
1795 for (; np != 0; np--)
1796 window_copy_cursor_down(wme, 1);
1797 if (data->scroll_exit && data->oy == 0)
1798 return (WINDOW_COPY_CMD_CANCEL);
1799 return (WINDOW_COPY_CMD_NOTHING);
1802 static enum window_copy_cmd_action
1803 window_copy_cmd_scroll_down_and_cancel(struct window_copy_cmd_state *cs)
1805 struct window_mode_entry *wme = cs->wme;
1806 struct window_copy_mode_data *data = wme->data;
1807 u_int np = wme->prefix;
1809 for (; np != 0; np--)
1810 window_copy_cursor_down(wme, 1);
1811 if (data->oy == 0)
1812 return (WINDOW_COPY_CMD_CANCEL);
1813 return (WINDOW_COPY_CMD_NOTHING);
1816 static enum window_copy_cmd_action
1817 window_copy_cmd_scroll_up(struct window_copy_cmd_state *cs)
1819 struct window_mode_entry *wme = cs->wme;
1820 u_int np = wme->prefix;
1822 for (; np != 0; np--)
1823 window_copy_cursor_up(wme, 1);
1824 return (WINDOW_COPY_CMD_NOTHING);
1827 static enum window_copy_cmd_action
1828 window_copy_cmd_search_again(struct window_copy_cmd_state *cs)
1830 struct window_mode_entry *wme = cs->wme;
1831 struct window_copy_mode_data *data = wme->data;
1832 u_int np = wme->prefix;
1834 if (data->searchtype == WINDOW_COPY_SEARCHUP) {
1835 for (; np != 0; np--)
1836 window_copy_search_up(wme, data->searchregex);
1837 } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
1838 for (; np != 0; np--)
1839 window_copy_search_down(wme, data->searchregex);
1841 return (WINDOW_COPY_CMD_NOTHING);
1844 static enum window_copy_cmd_action
1845 window_copy_cmd_search_reverse(struct window_copy_cmd_state *cs)
1847 struct window_mode_entry *wme = cs->wme;
1848 struct window_copy_mode_data *data = wme->data;
1849 u_int np = wme->prefix;
1851 if (data->searchtype == WINDOW_COPY_SEARCHUP) {
1852 for (; np != 0; np--)
1853 window_copy_search_down(wme, data->searchregex);
1854 } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
1855 for (; np != 0; np--)
1856 window_copy_search_up(wme, data->searchregex);
1858 return (WINDOW_COPY_CMD_NOTHING);
1861 static enum window_copy_cmd_action
1862 window_copy_cmd_select_line(struct window_copy_cmd_state *cs)
1864 struct window_mode_entry *wme = cs->wme;
1865 struct window_copy_mode_data *data = wme->data;
1866 u_int np = wme->prefix;
1868 data->lineflag = LINE_SEL_LEFT_RIGHT;
1869 data->rectflag = 0;
1870 data->selflag = SEL_LINE;
1871 data->dx = data->cx;
1872 data->dy = screen_hsize(data->backing) + data->cy - data->oy;
1874 window_copy_cursor_start_of_line(wme);
1875 data->selrx = data->cx;
1876 data->selry = screen_hsize(data->backing) + data->cy - data->oy;
1877 data->endselry = data->selry;
1878 window_copy_start_selection(wme);
1879 window_copy_cursor_end_of_line(wme);
1880 data->endselry = screen_hsize(data->backing) + data->cy - data->oy;
1881 data->endselrx = window_copy_find_length(wme, data->endselry);
1882 for (; np > 1; np--) {
1883 window_copy_cursor_down(wme, 0);
1884 window_copy_cursor_end_of_line(wme);
1887 return (WINDOW_COPY_CMD_REDRAW);
1890 static enum window_copy_cmd_action
1891 window_copy_cmd_select_word(struct window_copy_cmd_state *cs)
1893 struct window_mode_entry *wme = cs->wme;
1894 struct options *session_options = cs->s->options;
1895 struct window_copy_mode_data *data = wme->data;
1896 u_int px, py, nextx, nexty;
1898 data->lineflag = LINE_SEL_LEFT_RIGHT;
1899 data->rectflag = 0;
1900 data->selflag = SEL_WORD;
1901 data->dx = data->cx;
1902 data->dy = screen_hsize(data->backing) + data->cy - data->oy;
1904 data->separators = options_get_string(session_options,
1905 "word-separators");
1906 window_copy_cursor_previous_word(wme, data->separators, 0);
1907 px = data->cx;
1908 py = screen_hsize(data->backing) + data->cy - data->oy;
1909 data->selrx = px;
1910 data->selry = py;
1911 window_copy_start_selection(wme);
1913 /* Handle single character words. */
1914 nextx = px + 1;
1915 nexty = py;
1916 if (grid_get_line(data->backing->grid, nexty)->flags &
1917 GRID_LINE_WRAPPED && nextx > screen_size_x(data->backing) - 1) {
1918 nextx = 0;
1919 nexty++;
1921 if (px >= window_copy_find_length(wme, py) ||
1922 !window_copy_in_set(wme, nextx, nexty, WHITESPACE))
1923 window_copy_cursor_next_word_end(wme, data->separators, 1);
1924 else {
1925 window_copy_update_cursor(wme, px, data->cy);
1926 if (window_copy_update_selection(wme, 1, 1))
1927 window_copy_redraw_lines(wme, data->cy, 1);
1929 data->endselrx = data->cx;
1930 data->endselry = screen_hsize(data->backing) + data->cy - data->oy;
1931 if (data->dy > data->endselry) {
1932 data->dy = data->endselry;
1933 data->dx = data->endselrx;
1934 } else if (data->dx > data->endselrx)
1935 data->dx = data->endselrx;
1937 return (WINDOW_COPY_CMD_REDRAW);
1940 static enum window_copy_cmd_action
1941 window_copy_cmd_set_mark(struct window_copy_cmd_state *cs)
1943 struct window_copy_mode_data *data = cs->wme->data;
1945 data->mx = data->cx;
1946 data->my = screen_hsize(data->backing) + data->cy - data->oy;
1947 data->showmark = 1;
1948 return (WINDOW_COPY_CMD_REDRAW);
1951 static enum window_copy_cmd_action
1952 window_copy_cmd_start_of_line(struct window_copy_cmd_state *cs)
1954 struct window_mode_entry *wme = cs->wme;
1956 window_copy_cursor_start_of_line(wme);
1957 return (WINDOW_COPY_CMD_NOTHING);
1960 static enum window_copy_cmd_action
1961 window_copy_cmd_top_line(struct window_copy_cmd_state *cs)
1963 struct window_mode_entry *wme = cs->wme;
1964 struct window_copy_mode_data *data = wme->data;
1966 data->cx = 0;
1967 data->cy = 0;
1969 window_copy_update_selection(wme, 1, 0);
1970 return (WINDOW_COPY_CMD_REDRAW);
1973 static enum window_copy_cmd_action
1974 window_copy_cmd_copy_pipe_no_clear(struct window_copy_cmd_state *cs)
1976 struct window_mode_entry *wme = cs->wme;
1977 struct client *c = cs->c;
1978 struct session *s = cs->s;
1979 struct winlink *wl = cs->wl;
1980 struct window_pane *wp = wme->wp;
1981 char *command = NULL, *prefix = NULL;
1982 const char *arg1 = args_string(cs->args, 1);
1983 const char *arg2 = args_string(cs->args, 2);
1985 if (arg2 != NULL)
1986 prefix = format_single(NULL, arg2, c, s, wl, wp);
1988 if (s != NULL && arg1 != NULL && *arg1 != '\0')
1989 command = format_single(NULL, arg1, c, s, wl, wp);
1990 window_copy_copy_pipe(wme, s, prefix, command);
1991 free(command);
1993 free(prefix);
1994 return (WINDOW_COPY_CMD_NOTHING);
1997 static enum window_copy_cmd_action
1998 window_copy_cmd_copy_pipe(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_REDRAW);
2007 static enum window_copy_cmd_action
2008 window_copy_cmd_copy_pipe_and_cancel(struct window_copy_cmd_state *cs)
2010 struct window_mode_entry *wme = cs->wme;
2012 window_copy_cmd_copy_pipe_no_clear(cs);
2013 window_copy_clear_selection(wme);
2014 return (WINDOW_COPY_CMD_CANCEL);
2017 static enum window_copy_cmd_action
2018 window_copy_cmd_pipe_no_clear(struct window_copy_cmd_state *cs)
2020 struct window_mode_entry *wme = cs->wme;
2021 struct client *c = cs->c;
2022 struct session *s = cs->s;
2023 struct winlink *wl = cs->wl;
2024 struct window_pane *wp = wme->wp;
2025 char *command = NULL;
2026 const char *arg1 = args_string(cs->args, 1);
2028 if (s != NULL && arg1 != NULL && *arg1 != '\0')
2029 command = format_single(NULL, arg1, c, s, wl, wp);
2030 window_copy_pipe(wme, s, command);
2031 free(command);
2033 return (WINDOW_COPY_CMD_NOTHING);
2036 static enum window_copy_cmd_action
2037 window_copy_cmd_pipe(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_REDRAW);
2046 static enum window_copy_cmd_action
2047 window_copy_cmd_pipe_and_cancel(struct window_copy_cmd_state *cs)
2049 struct window_mode_entry *wme = cs->wme;
2051 window_copy_cmd_pipe_no_clear(cs);
2052 window_copy_clear_selection(wme);
2053 return (WINDOW_COPY_CMD_CANCEL);
2056 static enum window_copy_cmd_action
2057 window_copy_cmd_goto_line(struct window_copy_cmd_state *cs)
2059 struct window_mode_entry *wme = cs->wme;
2060 const char *arg1 = args_string(cs->args, 1);
2062 if (*arg1 != '\0')
2063 window_copy_goto_line(wme, arg1);
2064 return (WINDOW_COPY_CMD_NOTHING);
2067 static enum window_copy_cmd_action
2068 window_copy_cmd_jump_backward(struct window_copy_cmd_state *cs)
2070 struct window_mode_entry *wme = cs->wme;
2071 struct window_copy_mode_data *data = wme->data;
2072 u_int np = wme->prefix;
2073 const char *arg1 = args_string(cs->args, 1);
2075 if (*arg1 != '\0') {
2076 data->jumptype = WINDOW_COPY_JUMPBACKWARD;
2077 free(data->jumpchar);
2078 data->jumpchar = utf8_fromcstr(arg1);
2079 for (; np != 0; np--)
2080 window_copy_cursor_jump_back(wme);
2082 return (WINDOW_COPY_CMD_NOTHING);
2085 static enum window_copy_cmd_action
2086 window_copy_cmd_jump_forward(struct window_copy_cmd_state *cs)
2088 struct window_mode_entry *wme = cs->wme;
2089 struct window_copy_mode_data *data = wme->data;
2090 u_int np = wme->prefix;
2091 const char *arg1 = args_string(cs->args, 1);
2093 if (*arg1 != '\0') {
2094 data->jumptype = WINDOW_COPY_JUMPFORWARD;
2095 free(data->jumpchar);
2096 data->jumpchar = utf8_fromcstr(arg1);
2097 for (; np != 0; np--)
2098 window_copy_cursor_jump(wme);
2100 return (WINDOW_COPY_CMD_NOTHING);
2103 static enum window_copy_cmd_action
2104 window_copy_cmd_jump_to_backward(struct window_copy_cmd_state *cs)
2106 struct window_mode_entry *wme = cs->wme;
2107 struct window_copy_mode_data *data = wme->data;
2108 u_int np = wme->prefix;
2109 const char *arg1 = args_string(cs->args, 1);
2111 if (*arg1 != '\0') {
2112 data->jumptype = WINDOW_COPY_JUMPTOBACKWARD;
2113 free(data->jumpchar);
2114 data->jumpchar = utf8_fromcstr(arg1);
2115 for (; np != 0; np--)
2116 window_copy_cursor_jump_to_back(wme);
2118 return (WINDOW_COPY_CMD_NOTHING);
2121 static enum window_copy_cmd_action
2122 window_copy_cmd_jump_to_forward(struct window_copy_cmd_state *cs)
2124 struct window_mode_entry *wme = cs->wme;
2125 struct window_copy_mode_data *data = wme->data;
2126 u_int np = wme->prefix;
2127 const char *arg1 = args_string(cs->args, 1);
2129 if (*arg1 != '\0') {
2130 data->jumptype = WINDOW_COPY_JUMPTOFORWARD;
2131 free(data->jumpchar);
2132 data->jumpchar = utf8_fromcstr(arg1);
2133 for (; np != 0; np--)
2134 window_copy_cursor_jump_to(wme);
2136 return (WINDOW_COPY_CMD_NOTHING);
2139 static enum window_copy_cmd_action
2140 window_copy_cmd_jump_to_mark(struct window_copy_cmd_state *cs)
2142 struct window_mode_entry *wme = cs->wme;
2144 window_copy_jump_to_mark(wme);
2145 return (WINDOW_COPY_CMD_NOTHING);
2148 static enum window_copy_cmd_action
2149 window_copy_cmd_search_backward(struct window_copy_cmd_state *cs)
2151 struct window_mode_entry *wme = cs->wme;
2152 struct window_copy_mode_data *data = wme->data;
2153 u_int np = wme->prefix;
2155 if (!window_copy_expand_search_string(cs))
2156 return (WINDOW_COPY_CMD_NOTHING);
2158 if (data->searchstr != NULL) {
2159 data->searchtype = WINDOW_COPY_SEARCHUP;
2160 data->searchregex = 1;
2161 data->timeout = 0;
2162 for (; np != 0; np--)
2163 window_copy_search_up(wme, 1);
2165 return (WINDOW_COPY_CMD_NOTHING);
2168 static enum window_copy_cmd_action
2169 window_copy_cmd_search_backward_text(struct window_copy_cmd_state *cs)
2171 struct window_mode_entry *wme = cs->wme;
2172 struct window_copy_mode_data *data = wme->data;
2173 u_int np = wme->prefix;
2175 if (!window_copy_expand_search_string(cs))
2176 return (WINDOW_COPY_CMD_NOTHING);
2178 if (data->searchstr != NULL) {
2179 data->searchtype = WINDOW_COPY_SEARCHUP;
2180 data->searchregex = 0;
2181 data->timeout = 0;
2182 for (; np != 0; np--)
2183 window_copy_search_up(wme, 0);
2185 return (WINDOW_COPY_CMD_NOTHING);
2188 static enum window_copy_cmd_action
2189 window_copy_cmd_search_forward(struct window_copy_cmd_state *cs)
2191 struct window_mode_entry *wme = cs->wme;
2192 struct window_copy_mode_data *data = wme->data;
2193 u_int np = wme->prefix;
2195 if (!window_copy_expand_search_string(cs))
2196 return (WINDOW_COPY_CMD_NOTHING);
2198 if (data->searchstr != NULL) {
2199 data->searchtype = WINDOW_COPY_SEARCHDOWN;
2200 data->searchregex = 1;
2201 data->timeout = 0;
2202 for (; np != 0; np--)
2203 window_copy_search_down(wme, 1);
2205 return (WINDOW_COPY_CMD_NOTHING);
2208 static enum window_copy_cmd_action
2209 window_copy_cmd_search_forward_text(struct window_copy_cmd_state *cs)
2211 struct window_mode_entry *wme = cs->wme;
2212 struct window_copy_mode_data *data = wme->data;
2213 u_int np = wme->prefix;
2215 if (!window_copy_expand_search_string(cs))
2216 return (WINDOW_COPY_CMD_NOTHING);
2218 if (data->searchstr != NULL) {
2219 data->searchtype = WINDOW_COPY_SEARCHDOWN;
2220 data->searchregex = 0;
2221 data->timeout = 0;
2222 for (; np != 0; np--)
2223 window_copy_search_down(wme, 0);
2225 return (WINDOW_COPY_CMD_NOTHING);
2228 static enum window_copy_cmd_action
2229 window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs)
2231 struct window_mode_entry *wme = cs->wme;
2232 struct window_copy_mode_data *data = wme->data;
2233 const char *arg1 = args_string(cs->args, 1);
2234 const char *ss = data->searchstr;
2235 char prefix;
2236 enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING;
2238 data->timeout = 0;
2240 log_debug("%s: %s", __func__, arg1);
2242 prefix = *arg1++;
2243 if (data->searchx == -1 || data->searchy == -1) {
2244 data->searchx = data->cx;
2245 data->searchy = data->cy;
2246 data->searcho = data->oy;
2247 } else if (ss != NULL && strcmp(arg1, ss) != 0) {
2248 data->cx = data->searchx;
2249 data->cy = data->searchy;
2250 data->oy = data->searcho;
2251 action = WINDOW_COPY_CMD_REDRAW;
2253 if (*arg1 == '\0') {
2254 window_copy_clear_marks(wme);
2255 return (WINDOW_COPY_CMD_REDRAW);
2257 switch (prefix) {
2258 case '=':
2259 case '-':
2260 data->searchtype = WINDOW_COPY_SEARCHUP;
2261 data->searchregex = 0;
2262 free(data->searchstr);
2263 data->searchstr = xstrdup(arg1);
2264 if (!window_copy_search_up(wme, 0)) {
2265 window_copy_clear_marks(wme);
2266 return (WINDOW_COPY_CMD_REDRAW);
2268 break;
2269 case '+':
2270 data->searchtype = WINDOW_COPY_SEARCHDOWN;
2271 data->searchregex = 0;
2272 free(data->searchstr);
2273 data->searchstr = xstrdup(arg1);
2274 if (!window_copy_search_down(wme, 0)) {
2275 window_copy_clear_marks(wme);
2276 return (WINDOW_COPY_CMD_REDRAW);
2278 break;
2280 return (action);
2283 static enum window_copy_cmd_action
2284 window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs)
2286 struct window_mode_entry *wme = cs->wme;
2287 struct window_copy_mode_data *data = wme->data;
2288 const char *arg1 = args_string(cs->args, 1);
2289 const char *ss = data->searchstr;
2290 char prefix;
2291 enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING;
2293 data->timeout = 0;
2295 log_debug("%s: %s", __func__, arg1);
2297 prefix = *arg1++;
2298 if (data->searchx == -1 || data->searchy == -1) {
2299 data->searchx = data->cx;
2300 data->searchy = data->cy;
2301 data->searcho = data->oy;
2302 } else if (ss != NULL && strcmp(arg1, ss) != 0) {
2303 data->cx = data->searchx;
2304 data->cy = data->searchy;
2305 data->oy = data->searcho;
2306 action = WINDOW_COPY_CMD_REDRAW;
2308 if (*arg1 == '\0') {
2309 window_copy_clear_marks(wme);
2310 return (WINDOW_COPY_CMD_REDRAW);
2312 switch (prefix) {
2313 case '=':
2314 case '+':
2315 data->searchtype = WINDOW_COPY_SEARCHDOWN;
2316 data->searchregex = 0;
2317 free(data->searchstr);
2318 data->searchstr = xstrdup(arg1);
2319 if (!window_copy_search_down(wme, 0)) {
2320 window_copy_clear_marks(wme);
2321 return (WINDOW_COPY_CMD_REDRAW);
2323 break;
2324 case '-':
2325 data->searchtype = WINDOW_COPY_SEARCHUP;
2326 data->searchregex = 0;
2327 free(data->searchstr);
2328 data->searchstr = xstrdup(arg1);
2329 if (!window_copy_search_up(wme, 0)) {
2330 window_copy_clear_marks(wme);
2331 return (WINDOW_COPY_CMD_REDRAW);
2334 return (action);
2337 static enum window_copy_cmd_action
2338 window_copy_cmd_refresh_from_pane(struct window_copy_cmd_state *cs)
2340 struct window_mode_entry *wme = cs->wme;
2341 struct window_pane *wp = wme->swp;
2342 struct window_copy_mode_data *data = wme->data;
2344 if (data->viewmode)
2345 return (WINDOW_COPY_CMD_NOTHING);
2347 screen_free(data->backing);
2348 free(data->backing);
2349 data->backing = window_copy_clone_screen(&wp->base, &data->screen, NULL, NULL, wme->swp != wme->wp);
2351 window_copy_size_changed(wme);
2352 return (WINDOW_COPY_CMD_REDRAW);
2355 static const struct {
2356 const char *command;
2357 u_int minargs;
2358 u_int maxargs;
2359 enum window_copy_cmd_clear clear;
2360 enum window_copy_cmd_action (*f)(struct window_copy_cmd_state *);
2361 } window_copy_cmd_table[] = {
2362 { .command = "append-selection",
2363 .minargs = 0,
2364 .maxargs = 0,
2365 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2366 .f = window_copy_cmd_append_selection
2368 { .command = "append-selection-and-cancel",
2369 .minargs = 0,
2370 .maxargs = 0,
2371 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2372 .f = window_copy_cmd_append_selection_and_cancel
2374 { .command = "back-to-indentation",
2375 .minargs = 0,
2376 .maxargs = 0,
2377 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2378 .f = window_copy_cmd_back_to_indentation
2380 { .command = "begin-selection",
2381 .minargs = 0,
2382 .maxargs = 0,
2383 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2384 .f = window_copy_cmd_begin_selection
2386 { .command = "bottom-line",
2387 .minargs = 0,
2388 .maxargs = 0,
2389 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2390 .f = window_copy_cmd_bottom_line
2392 { .command = "cancel",
2393 .minargs = 0,
2394 .maxargs = 0,
2395 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2396 .f = window_copy_cmd_cancel
2398 { .command = "clear-selection",
2399 .minargs = 0,
2400 .maxargs = 0,
2401 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2402 .f = window_copy_cmd_clear_selection
2404 { .command = "copy-end-of-line",
2405 .minargs = 0,
2406 .maxargs = 1,
2407 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2408 .f = window_copy_cmd_copy_end_of_line
2410 { .command = "copy-end-of-line-and-cancel",
2411 .minargs = 0,
2412 .maxargs = 1,
2413 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2414 .f = window_copy_cmd_copy_end_of_line_and_cancel
2416 { .command = "copy-pipe-end-of-line",
2417 .minargs = 0,
2418 .maxargs = 2,
2419 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2420 .f = window_copy_cmd_copy_pipe_end_of_line
2422 { .command = "copy-pipe-end-of-line-and-cancel",
2423 .minargs = 0,
2424 .maxargs = 2,
2425 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2426 .f = window_copy_cmd_copy_pipe_end_of_line_and_cancel
2428 { .command = "copy-line",
2429 .minargs = 0,
2430 .maxargs = 1,
2431 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2432 .f = window_copy_cmd_copy_line
2434 { .command = "copy-line-and-cancel",
2435 .minargs = 0,
2436 .maxargs = 1,
2437 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2438 .f = window_copy_cmd_copy_line_and_cancel
2440 { .command = "copy-pipe-line",
2441 .minargs = 0,
2442 .maxargs = 2,
2443 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2444 .f = window_copy_cmd_copy_pipe_line
2446 { .command = "copy-pipe-line-and-cancel",
2447 .minargs = 0,
2448 .maxargs = 2,
2449 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2450 .f = window_copy_cmd_copy_pipe_line_and_cancel
2452 { .command = "copy-pipe-no-clear",
2453 .minargs = 0,
2454 .maxargs = 2,
2455 .clear = WINDOW_COPY_CMD_CLEAR_NEVER,
2456 .f = window_copy_cmd_copy_pipe_no_clear
2458 { .command = "copy-pipe",
2459 .minargs = 0,
2460 .maxargs = 2,
2461 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2462 .f = window_copy_cmd_copy_pipe
2464 { .command = "copy-pipe-and-cancel",
2465 .minargs = 0,
2466 .maxargs = 2,
2467 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2468 .f = window_copy_cmd_copy_pipe_and_cancel
2470 { .command = "copy-selection-no-clear",
2471 .minargs = 0,
2472 .maxargs = 1,
2473 .clear = WINDOW_COPY_CMD_CLEAR_NEVER,
2474 .f = window_copy_cmd_copy_selection_no_clear
2476 { .command = "copy-selection",
2477 .minargs = 0,
2478 .maxargs = 1,
2479 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2480 .f = window_copy_cmd_copy_selection
2482 { .command = "copy-selection-and-cancel",
2483 .minargs = 0,
2484 .maxargs = 1,
2485 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2486 .f = window_copy_cmd_copy_selection_and_cancel
2488 { .command = "cursor-down",
2489 .minargs = 0,
2490 .maxargs = 0,
2491 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2492 .f = window_copy_cmd_cursor_down
2494 { .command = "cursor-down-and-cancel",
2495 .minargs = 0,
2496 .maxargs = 0,
2497 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2498 .f = window_copy_cmd_cursor_down_and_cancel
2500 { .command = "cursor-left",
2501 .minargs = 0,
2502 .maxargs = 0,
2503 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2504 .f = window_copy_cmd_cursor_left
2506 { .command = "cursor-right",
2507 .minargs = 0,
2508 .maxargs = 0,
2509 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2510 .f = window_copy_cmd_cursor_right
2512 { .command = "cursor-up",
2513 .minargs = 0,
2514 .maxargs = 0,
2515 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2516 .f = window_copy_cmd_cursor_up
2518 { .command = "end-of-line",
2519 .minargs = 0,
2520 .maxargs = 0,
2521 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2522 .f = window_copy_cmd_end_of_line
2524 { .command = "goto-line",
2525 .minargs = 1,
2526 .maxargs = 1,
2527 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2528 .f = window_copy_cmd_goto_line
2530 { .command = "halfpage-down",
2531 .minargs = 0,
2532 .maxargs = 0,
2533 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2534 .f = window_copy_cmd_halfpage_down
2536 { .command = "halfpage-down-and-cancel",
2537 .minargs = 0,
2538 .maxargs = 0,
2539 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2540 .f = window_copy_cmd_halfpage_down_and_cancel
2542 { .command = "halfpage-up",
2543 .minargs = 0,
2544 .maxargs = 0,
2545 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2546 .f = window_copy_cmd_halfpage_up
2548 { .command = "history-bottom",
2549 .minargs = 0,
2550 .maxargs = 0,
2551 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2552 .f = window_copy_cmd_history_bottom
2554 { .command = "history-top",
2555 .minargs = 0,
2556 .maxargs = 0,
2557 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2558 .f = window_copy_cmd_history_top
2560 { .command = "jump-again",
2561 .minargs = 0,
2562 .maxargs = 0,
2563 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2564 .f = window_copy_cmd_jump_again
2566 { .command = "jump-backward",
2567 .minargs = 1,
2568 .maxargs = 1,
2569 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2570 .f = window_copy_cmd_jump_backward
2572 { .command = "jump-forward",
2573 .minargs = 1,
2574 .maxargs = 1,
2575 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2576 .f = window_copy_cmd_jump_forward
2578 { .command = "jump-reverse",
2579 .minargs = 0,
2580 .maxargs = 0,
2581 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2582 .f = window_copy_cmd_jump_reverse
2584 { .command = "jump-to-backward",
2585 .minargs = 1,
2586 .maxargs = 1,
2587 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2588 .f = window_copy_cmd_jump_to_backward
2590 { .command = "jump-to-forward",
2591 .minargs = 1,
2592 .maxargs = 1,
2593 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2594 .f = window_copy_cmd_jump_to_forward
2596 { .command = "jump-to-mark",
2597 .minargs = 0,
2598 .maxargs = 0,
2599 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2600 .f = window_copy_cmd_jump_to_mark
2602 { .command = "middle-line",
2603 .minargs = 0,
2604 .maxargs = 0,
2605 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2606 .f = window_copy_cmd_middle_line
2608 { .command = "next-matching-bracket",
2609 .minargs = 0,
2610 .maxargs = 0,
2611 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2612 .f = window_copy_cmd_next_matching_bracket
2614 { .command = "next-paragraph",
2615 .minargs = 0,
2616 .maxargs = 0,
2617 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2618 .f = window_copy_cmd_next_paragraph
2620 { .command = "next-space",
2621 .minargs = 0,
2622 .maxargs = 0,
2623 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2624 .f = window_copy_cmd_next_space
2626 { .command = "next-space-end",
2627 .minargs = 0,
2628 .maxargs = 0,
2629 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2630 .f = window_copy_cmd_next_space_end
2632 { .command = "next-word",
2633 .minargs = 0,
2634 .maxargs = 0,
2635 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2636 .f = window_copy_cmd_next_word
2638 { .command = "next-word-end",
2639 .minargs = 0,
2640 .maxargs = 0,
2641 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2642 .f = window_copy_cmd_next_word_end
2644 { .command = "other-end",
2645 .minargs = 0,
2646 .maxargs = 0,
2647 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2648 .f = window_copy_cmd_other_end
2650 { .command = "page-down",
2651 .minargs = 0,
2652 .maxargs = 0,
2653 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2654 .f = window_copy_cmd_page_down
2656 { .command = "page-down-and-cancel",
2657 .minargs = 0,
2658 .maxargs = 0,
2659 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2660 .f = window_copy_cmd_page_down_and_cancel
2662 { .command = "page-up",
2663 .minargs = 0,
2664 .maxargs = 0,
2665 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2666 .f = window_copy_cmd_page_up
2668 { .command = "pipe-no-clear",
2669 .minargs = 0,
2670 .maxargs = 1,
2671 .clear = WINDOW_COPY_CMD_CLEAR_NEVER,
2672 .f = window_copy_cmd_pipe_no_clear
2674 { .command = "pipe",
2675 .minargs = 0,
2676 .maxargs = 1,
2677 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2678 .f = window_copy_cmd_pipe
2680 { .command = "pipe-and-cancel",
2681 .minargs = 0,
2682 .maxargs = 1,
2683 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2684 .f = window_copy_cmd_pipe_and_cancel
2686 { .command = "previous-matching-bracket",
2687 .minargs = 0,
2688 .maxargs = 0,
2689 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2690 .f = window_copy_cmd_previous_matching_bracket
2692 { .command = "previous-paragraph",
2693 .minargs = 0,
2694 .maxargs = 0,
2695 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2696 .f = window_copy_cmd_previous_paragraph
2698 { .command = "previous-space",
2699 .minargs = 0,
2700 .maxargs = 0,
2701 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2702 .f = window_copy_cmd_previous_space
2704 { .command = "previous-word",
2705 .minargs = 0,
2706 .maxargs = 0,
2707 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2708 .f = window_copy_cmd_previous_word
2710 { .command = "rectangle-on",
2711 .minargs = 0,
2712 .maxargs = 0,
2713 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2714 .f = window_copy_cmd_rectangle_on
2716 { .command = "rectangle-off",
2717 .minargs = 0,
2718 .maxargs = 0,
2719 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2720 .f = window_copy_cmd_rectangle_off
2722 { .command = "rectangle-toggle",
2723 .minargs = 0,
2724 .maxargs = 0,
2725 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2726 .f = window_copy_cmd_rectangle_toggle
2728 { .command = "refresh-from-pane",
2729 .minargs = 0,
2730 .maxargs = 0,
2731 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2732 .f = window_copy_cmd_refresh_from_pane
2734 { .command = "scroll-down",
2735 .minargs = 0,
2736 .maxargs = 0,
2737 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2738 .f = window_copy_cmd_scroll_down
2740 { .command = "scroll-down-and-cancel",
2741 .minargs = 0,
2742 .maxargs = 0,
2743 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2744 .f = window_copy_cmd_scroll_down_and_cancel
2746 { .command = "scroll-up",
2747 .minargs = 0,
2748 .maxargs = 0,
2749 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2750 .f = window_copy_cmd_scroll_up
2752 { .command = "search-again",
2753 .minargs = 0,
2754 .maxargs = 0,
2755 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2756 .f = window_copy_cmd_search_again
2758 { .command = "search-backward",
2759 .minargs = 0,
2760 .maxargs = 1,
2761 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2762 .f = window_copy_cmd_search_backward
2764 { .command = "search-backward-text",
2765 .minargs = 0,
2766 .maxargs = 1,
2767 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2768 .f = window_copy_cmd_search_backward_text
2770 { .command = "search-backward-incremental",
2771 .minargs = 1,
2772 .maxargs = 1,
2773 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2774 .f = window_copy_cmd_search_backward_incremental
2776 { .command = "search-forward",
2777 .minargs = 0,
2778 .maxargs = 1,
2779 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2780 .f = window_copy_cmd_search_forward
2782 { .command = "search-forward-text",
2783 .minargs = 0,
2784 .maxargs = 1,
2785 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2786 .f = window_copy_cmd_search_forward_text
2788 { .command = "search-forward-incremental",
2789 .minargs = 1,
2790 .maxargs = 1,
2791 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2792 .f = window_copy_cmd_search_forward_incremental
2794 { .command = "search-reverse",
2795 .minargs = 0,
2796 .maxargs = 0,
2797 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2798 .f = window_copy_cmd_search_reverse
2800 { .command = "select-line",
2801 .minargs = 0,
2802 .maxargs = 0,
2803 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2804 .f = window_copy_cmd_select_line
2806 { .command = "select-word",
2807 .minargs = 0,
2808 .maxargs = 0,
2809 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2810 .f = window_copy_cmd_select_word
2812 { .command = "set-mark",
2813 .minargs = 0,
2814 .maxargs = 0,
2815 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2816 .f = window_copy_cmd_set_mark
2818 { .command = "start-of-line",
2819 .minargs = 0,
2820 .maxargs = 0,
2821 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2822 .f = window_copy_cmd_start_of_line
2824 { .command = "stop-selection",
2825 .minargs = 0,
2826 .maxargs = 0,
2827 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2828 .f = window_copy_cmd_stop_selection
2830 { .command = "toggle-position",
2831 .minargs = 0,
2832 .maxargs = 0,
2833 .clear = WINDOW_COPY_CMD_CLEAR_NEVER,
2834 .f = window_copy_cmd_toggle_position
2836 { .command = "top-line",
2837 .minargs = 0,
2838 .maxargs = 0,
2839 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2840 .f = window_copy_cmd_top_line
2844 static void
2845 window_copy_command(struct window_mode_entry *wme, struct client *c,
2846 struct session *s, struct winlink *wl, struct args *args,
2847 struct mouse_event *m)
2849 struct window_copy_mode_data *data = wme->data;
2850 struct window_copy_cmd_state cs;
2851 enum window_copy_cmd_action action;
2852 enum window_copy_cmd_clear clear = WINDOW_COPY_CMD_CLEAR_NEVER;
2853 const char *command;
2854 u_int i, count = args_count(args);
2855 int keys;
2857 if (count == 0)
2858 return;
2859 command = args_string(args, 0);
2861 if (m != NULL && m->valid && !MOUSE_WHEEL(m->b))
2862 window_copy_move_mouse(m);
2864 cs.wme = wme;
2865 cs.args = args;
2866 cs.m = m;
2868 cs.c = c;
2869 cs.s = s;
2870 cs.wl = wl;
2872 action = WINDOW_COPY_CMD_NOTHING;
2873 for (i = 0; i < nitems(window_copy_cmd_table); i++) {
2874 if (strcmp(window_copy_cmd_table[i].command, command) == 0) {
2875 if (count - 1 < window_copy_cmd_table[i].minargs ||
2876 count - 1 > window_copy_cmd_table[i].maxargs)
2877 break;
2878 clear = window_copy_cmd_table[i].clear;
2879 action = window_copy_cmd_table[i].f(&cs);
2880 break;
2884 if (strncmp(command, "search-", 7) != 0 && data->searchmark != NULL) {
2885 keys = options_get_number(wme->wp->window->options, "mode-keys");
2886 if (clear == WINDOW_COPY_CMD_CLEAR_EMACS_ONLY &&
2887 keys == MODEKEY_VI)
2888 clear = WINDOW_COPY_CMD_CLEAR_NEVER;
2889 if (clear != WINDOW_COPY_CMD_CLEAR_NEVER) {
2890 window_copy_clear_marks(wme);
2891 data->searchx = data->searchy = -1;
2893 if (action == WINDOW_COPY_CMD_NOTHING)
2894 action = WINDOW_COPY_CMD_REDRAW;
2896 wme->prefix = 1;
2898 if (action == WINDOW_COPY_CMD_CANCEL)
2899 window_pane_reset_mode(wme->wp);
2900 else if (action == WINDOW_COPY_CMD_REDRAW)
2901 window_copy_redraw_screen(wme);
2904 static void
2905 window_copy_scroll_to(struct window_mode_entry *wme, u_int px, u_int py,
2906 int no_redraw)
2908 struct window_copy_mode_data *data = wme->data;
2909 struct grid *gd = data->backing->grid;
2910 u_int offset, gap;
2912 data->cx = px;
2914 if (py >= gd->hsize - data->oy && py < gd->hsize - data->oy + gd->sy)
2915 data->cy = py - (gd->hsize - data->oy);
2916 else {
2917 gap = gd->sy / 4;
2918 if (py < gd->sy) {
2919 offset = 0;
2920 data->cy = py;
2921 } else if (py > gd->hsize + gd->sy - gap) {
2922 offset = gd->hsize;
2923 data->cy = py - gd->hsize;
2924 } else {
2925 offset = py + gap - gd->sy;
2926 data->cy = py - offset;
2928 data->oy = gd->hsize - offset;
2931 if (!no_redraw && data->searchmark != NULL && !data->timeout)
2932 window_copy_search_marks(wme, NULL, data->searchregex, 1);
2933 window_copy_update_selection(wme, 1, 0);
2934 if (!no_redraw)
2935 window_copy_redraw_screen(wme);
2938 static int
2939 window_copy_search_compare(struct grid *gd, u_int px, u_int py,
2940 struct grid *sgd, u_int spx, int cis)
2942 struct grid_cell gc, sgc;
2943 const struct utf8_data *ud, *sud;
2945 grid_get_cell(gd, px, py, &gc);
2946 ud = &gc.data;
2947 grid_get_cell(sgd, spx, 0, &sgc);
2948 sud = &sgc.data;
2950 if (ud->size != sud->size || ud->width != sud->width)
2951 return (0);
2953 if (cis && ud->size == 1)
2954 return (tolower(ud->data[0]) == sud->data[0]);
2956 return (memcmp(ud->data, sud->data, ud->size) == 0);
2959 static int
2960 window_copy_search_lr(struct grid *gd, struct grid *sgd, u_int *ppx, u_int py,
2961 u_int first, u_int last, int cis)
2963 u_int ax, bx, px, pywrap, endline;
2964 int matched;
2965 struct grid_line *gl;
2967 endline = gd->hsize + gd->sy - 1;
2968 for (ax = first; ax < last; ax++) {
2969 for (bx = 0; bx < sgd->sx; bx++) {
2970 px = ax + bx;
2971 pywrap = py;
2972 /* Wrap line. */
2973 while (px >= gd->sx && pywrap < endline) {
2974 gl = grid_get_line(gd, pywrap);
2975 if (~gl->flags & GRID_LINE_WRAPPED)
2976 break;
2977 px -= gd->sx;
2978 pywrap++;
2980 /* We have run off the end of the grid. */
2981 if (px >= gd->sx)
2982 break;
2983 matched = window_copy_search_compare(gd, px, pywrap,
2984 sgd, bx, cis);
2985 if (!matched)
2986 break;
2988 if (bx == sgd->sx) {
2989 *ppx = ax;
2990 return (1);
2993 return (0);
2996 static int
2997 window_copy_search_rl(struct grid *gd,
2998 struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis)
3000 u_int ax, bx, px, pywrap, endline;
3001 int matched;
3002 struct grid_line *gl;
3004 endline = gd->hsize + gd->sy - 1;
3005 for (ax = last; ax > first; ax--) {
3006 for (bx = 0; bx < sgd->sx; bx++) {
3007 px = ax - 1 + bx;
3008 pywrap = py;
3009 /* Wrap line. */
3010 while (px >= gd->sx && pywrap < endline) {
3011 gl = grid_get_line(gd, pywrap);
3012 if (~gl->flags & GRID_LINE_WRAPPED)
3013 break;
3014 px -= gd->sx;
3015 pywrap++;
3017 /* We have run off the end of the grid. */
3018 if (px >= gd->sx)
3019 break;
3020 matched = window_copy_search_compare(gd, px, pywrap,
3021 sgd, bx, cis);
3022 if (!matched)
3023 break;
3025 if (bx == sgd->sx) {
3026 *ppx = ax - 1;
3027 return (1);
3030 return (0);
3033 static int
3034 window_copy_search_lr_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py,
3035 u_int first, u_int last, regex_t *reg)
3037 int eflags = 0;
3038 u_int endline, foundx, foundy, len, pywrap, size = 1;
3039 char *buf;
3040 regmatch_t regmatch;
3041 struct grid_line *gl;
3044 * This can happen during search if the last match was the last
3045 * character on a line.
3047 if (first >= last)
3048 return (0);
3050 /* Set flags for regex search. */
3051 if (first != 0)
3052 eflags |= REG_NOTBOL;
3054 /* Need to look at the entire string. */
3055 buf = xmalloc(size);
3056 buf[0] = '\0';
3057 buf = window_copy_stringify(gd, py, first, gd->sx, buf, &size);
3058 len = gd->sx - first;
3059 endline = gd->hsize + gd->sy - 1;
3060 pywrap = py;
3061 while (buf != NULL && pywrap <= endline) {
3062 gl = grid_get_line(gd, pywrap);
3063 if (~gl->flags & GRID_LINE_WRAPPED)
3064 break;
3065 pywrap++;
3066 buf = window_copy_stringify(gd, pywrap, 0, gd->sx, buf, &size);
3067 len += gd->sx;
3070 if (regexec(reg, buf, 1, &regmatch, eflags) == 0 &&
3071 regmatch.rm_so != regmatch.rm_eo) {
3072 foundx = first;
3073 foundy = py;
3074 window_copy_cstrtocellpos(gd, len, &foundx, &foundy,
3075 buf + regmatch.rm_so);
3076 if (foundy == py && foundx < last) {
3077 *ppx = foundx;
3078 len -= foundx - first;
3079 window_copy_cstrtocellpos(gd, len, &foundx, &foundy,
3080 buf + regmatch.rm_eo);
3081 *psx = foundx;
3082 while (foundy > py) {
3083 *psx += gd->sx;
3084 foundy--;
3086 *psx -= *ppx;
3087 free(buf);
3088 return (1);
3092 free(buf);
3093 *ppx = 0;
3094 *psx = 0;
3095 return (0);
3098 static int
3099 window_copy_search_rl_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py,
3100 u_int first, u_int last, regex_t *reg)
3102 int eflags = 0;
3103 u_int endline, len, pywrap, size = 1;
3104 char *buf;
3105 struct grid_line *gl;
3107 /* Set flags for regex search. */
3108 if (first != 0)
3109 eflags |= REG_NOTBOL;
3111 /* Need to look at the entire string. */
3112 buf = xmalloc(size);
3113 buf[0] = '\0';
3114 buf = window_copy_stringify(gd, py, first, gd->sx, buf, &size);
3115 len = gd->sx - first;
3116 endline = gd->hsize + gd->sy - 1;
3117 pywrap = py;
3118 while (buf != NULL && (pywrap <= endline)) {
3119 gl = grid_get_line(gd, pywrap);
3120 if (~gl->flags & GRID_LINE_WRAPPED)
3121 break;
3122 pywrap++;
3123 buf = window_copy_stringify(gd, pywrap, 0, gd->sx, buf, &size);
3124 len += gd->sx;
3127 if (window_copy_last_regex(gd, py, first, last, len, ppx, psx, buf,
3128 reg, eflags))
3130 free(buf);
3131 return (1);
3134 free(buf);
3135 *ppx = 0;
3136 *psx = 0;
3137 return (0);
3140 static const char *
3141 window_copy_cellstring(const struct grid_line *gl, u_int px, size_t *size,
3142 int *allocated)
3144 static struct utf8_data ud;
3145 struct grid_cell_entry *gce;
3146 char *copy;
3148 if (px >= gl->cellsize) {
3149 *size = 1;
3150 *allocated = 0;
3151 return (" ");
3154 gce = &gl->celldata[px];
3155 if (gce->flags & GRID_FLAG_PADDING) {
3156 *size = 0;
3157 *allocated = 0;
3158 return (NULL);
3160 if (~gce->flags & GRID_FLAG_EXTENDED) {
3161 *size = 1;
3162 *allocated = 0;
3163 return (&gce->data.data);
3166 utf8_to_data(gl->extddata[gce->offset].data, &ud);
3167 if (ud.size == 0) {
3168 *size = 0;
3169 *allocated = 0;
3170 return (NULL);
3172 *size = ud.size;
3173 *allocated = 1;
3175 copy = xmalloc(ud.size);
3176 memcpy(copy, ud.data, ud.size);
3177 return (copy);
3180 /* Find last match in given range. */
3181 static int
3182 window_copy_last_regex(struct grid *gd, u_int py, u_int first, u_int last,
3183 u_int len, u_int *ppx, u_int *psx, const char *buf, const regex_t *preg,
3184 int eflags)
3186 u_int foundx, foundy, oldx, px = 0, savepx, savesx = 0;
3187 regmatch_t regmatch;
3189 foundx = first;
3190 foundy = py;
3191 oldx = first;
3192 while (regexec(preg, buf + px, 1, &regmatch, eflags) == 0) {
3193 if (regmatch.rm_so == regmatch.rm_eo)
3194 break;
3195 window_copy_cstrtocellpos(gd, len, &foundx, &foundy,
3196 buf + px + regmatch.rm_so);
3197 if (foundy > py || foundx >= last)
3198 break;
3199 len -= foundx - oldx;
3200 savepx = foundx;
3201 window_copy_cstrtocellpos(gd, len, &foundx, &foundy,
3202 buf + px + regmatch.rm_eo);
3203 if (foundy > py || foundx >= last) {
3204 *ppx = savepx;
3205 *psx = foundx;
3206 while (foundy > py) {
3207 *psx += gd->sx;
3208 foundy--;
3210 *psx -= *ppx;
3211 return (1);
3212 } else {
3213 savesx = foundx - savepx;
3214 len -= savesx;
3215 oldx = foundx;
3217 px += regmatch.rm_eo;
3220 if (savesx > 0) {
3221 *ppx = savepx;
3222 *psx = savesx;
3223 return (1);
3224 } else {
3225 *ppx = 0;
3226 *psx = 0;
3227 return (0);
3231 /* Stringify line and append to input buffer. Caller frees. */
3232 static char *
3233 window_copy_stringify(struct grid *gd, u_int py, u_int first, u_int last,
3234 char *buf, u_int *size)
3236 u_int ax, bx, newsize = *size;
3237 const struct grid_line *gl;
3238 const char *d;
3239 size_t bufsize = 1024, dlen;
3240 int allocated;
3242 while (bufsize < newsize)
3243 bufsize *= 2;
3244 buf = xrealloc(buf, bufsize);
3246 gl = grid_peek_line(gd, py);
3247 bx = *size - 1;
3248 for (ax = first; ax < last; ax++) {
3249 d = window_copy_cellstring(gl, ax, &dlen, &allocated);
3250 newsize += dlen;
3251 while (bufsize < newsize) {
3252 bufsize *= 2;
3253 buf = xrealloc(buf, bufsize);
3255 if (dlen == 1)
3256 buf[bx++] = *d;
3257 else {
3258 memcpy(buf + bx, d, dlen);
3259 bx += dlen;
3261 if (allocated)
3262 free((void *)d);
3264 buf[newsize - 1] = '\0';
3266 *size = newsize;
3267 return (buf);
3270 /* Map start of C string containing UTF-8 data to grid cell position. */
3271 static void
3272 window_copy_cstrtocellpos(struct grid *gd, u_int ncells, u_int *ppx, u_int *ppy,
3273 const char *str)
3275 u_int cell, ccell, px, pywrap, pos, len;
3276 int match;
3277 const struct grid_line *gl;
3278 const char *d;
3279 size_t dlen;
3280 struct {
3281 const char *d;
3282 size_t dlen;
3283 int allocated;
3284 } *cells;
3286 /* Populate the array of cell data. */
3287 cells = xreallocarray(NULL, ncells, sizeof cells[0]);
3288 cell = 0;
3289 px = *ppx;
3290 pywrap = *ppy;
3291 gl = grid_peek_line(gd, pywrap);
3292 while (cell < ncells) {
3293 cells[cell].d = window_copy_cellstring(gl, px,
3294 &cells[cell].dlen, &cells[cell].allocated);
3295 cell++;
3296 px++;
3297 if (px == gd->sx) {
3298 px = 0;
3299 pywrap++;
3300 gl = grid_peek_line(gd, pywrap);
3304 /* Locate starting cell. */
3305 cell = 0;
3306 len = strlen(str);
3307 while (cell < ncells) {
3308 ccell = cell;
3309 pos = 0;
3310 match = 1;
3311 while (ccell < ncells) {
3312 if (str[pos] == '\0') {
3313 match = 0;
3314 break;
3316 d = cells[ccell].d;
3317 dlen = cells[ccell].dlen;
3318 if (dlen == 1) {
3319 if (str[pos] != *d) {
3320 match = 0;
3321 break;
3323 pos++;
3324 } else {
3325 if (dlen > len - pos)
3326 dlen = len - pos;
3327 if (memcmp(str + pos, d, dlen) != 0) {
3328 match = 0;
3329 break;
3331 pos += dlen;
3333 ccell++;
3335 if (match)
3336 break;
3337 cell++;
3340 /* If not found this will be one past the end. */
3341 px = *ppx + cell;
3342 pywrap = *ppy;
3343 while (px >= gd->sx) {
3344 px -= gd->sx;
3345 pywrap++;
3348 *ppx = px;
3349 *ppy = pywrap;
3351 /* Free cell data. */
3352 for (cell = 0; cell < ncells; cell++) {
3353 if (cells[cell].allocated)
3354 free((void *)cells[cell].d);
3356 free(cells);
3359 static void
3360 window_copy_move_left(struct screen *s, u_int *fx, u_int *fy, int wrapflag)
3362 if (*fx == 0) { /* left */
3363 if (*fy == 0) { /* top */
3364 if (wrapflag) {
3365 *fx = screen_size_x(s) - 1;
3366 *fy = screen_hsize(s) + screen_size_y(s) - 1;
3368 return;
3370 *fx = screen_size_x(s) - 1;
3371 *fy = *fy - 1;
3372 } else
3373 *fx = *fx - 1;
3376 static void
3377 window_copy_move_right(struct screen *s, u_int *fx, u_int *fy, int wrapflag)
3379 if (*fx == screen_size_x(s) - 1) { /* right */
3380 if (*fy == screen_hsize(s) + screen_size_y(s) - 1) { /* bottom */
3381 if (wrapflag) {
3382 *fx = 0;
3383 *fy = 0;
3385 return;
3387 *fx = 0;
3388 *fy = *fy + 1;
3389 } else
3390 *fx = *fx + 1;
3393 static int
3394 window_copy_is_lowercase(const char *ptr)
3396 while (*ptr != '\0') {
3397 if (*ptr != tolower((u_char)*ptr))
3398 return (0);
3399 ++ptr;
3401 return (1);
3405 * Handle backward wrapped regex searches with overlapping matches. In this case
3406 * find the longest overlapping match from previous wrapped lines.
3408 static void
3409 window_copy_search_back_overlap(struct grid *gd, regex_t *preg, u_int *ppx,
3410 u_int *psx, u_int *ppy, u_int endline)
3412 u_int endx, endy, oldendx, oldendy, px, py, sx;
3413 int found = 1;
3415 oldendx = *ppx + *psx;
3416 oldendy = *ppy - 1;
3417 while (oldendx > gd->sx - 1) {
3418 oldendx -= gd->sx;
3419 oldendy++;
3421 endx = oldendx;
3422 endy = oldendy;
3423 px = *ppx;
3424 py = *ppy;
3425 while (found && px == 0 && py - 1 > endline &&
3426 grid_get_line(gd, py - 2)->flags & GRID_LINE_WRAPPED &&
3427 endx == oldendx && endy == oldendy) {
3428 py--;
3429 found = window_copy_search_rl_regex(gd, &px, &sx, py - 1, 0,
3430 gd->sx, preg);
3431 if (found) {
3432 endx = px + sx;
3433 endy = py - 1;
3434 while (endx > gd->sx - 1) {
3435 endx -= gd->sx;
3436 endy++;
3438 if (endx == oldendx && endy == oldendy) {
3439 *ppx = px;
3440 *ppy = py;
3447 * Search for text stored in sgd starting from position fx,fy up to endline. If
3448 * found, jump to it. If cis then ignore case. The direction is 0 for searching
3449 * up, down otherwise. If wrap then go to begin/end of grid and try again if
3450 * not found.
3452 static int
3453 window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd,
3454 struct grid *sgd, u_int fx, u_int fy, u_int endline, int cis, int wrap,
3455 int direction, int regex)
3457 u_int i, px, sx, ssize = 1;
3458 int found = 0, cflags = REG_EXTENDED;
3459 char *sbuf;
3460 regex_t reg;
3462 if (regex) {
3463 sbuf = xmalloc(ssize);
3464 sbuf[0] = '\0';
3465 sbuf = window_copy_stringify(sgd, 0, 0, sgd->sx, sbuf, &ssize);
3466 if (cis)
3467 cflags |= REG_ICASE;
3468 if (regcomp(&reg, sbuf, cflags) != 0) {
3469 free(sbuf);
3470 return (0);
3472 free(sbuf);
3475 if (direction) {
3476 for (i = fy; i <= endline; i++) {
3477 if (regex) {
3478 found = window_copy_search_lr_regex(gd,
3479 &px, &sx, i, fx, gd->sx, &reg);
3480 } else {
3481 found = window_copy_search_lr(gd, sgd,
3482 &px, i, fx, gd->sx, cis);
3484 if (found)
3485 break;
3486 fx = 0;
3488 } else {
3489 for (i = fy + 1; endline < i; i--) {
3490 if (regex) {
3491 found = window_copy_search_rl_regex(gd,
3492 &px, &sx, i - 1, 0, fx + 1, &reg);
3493 if (found) {
3494 window_copy_search_back_overlap(gd,
3495 &reg, &px, &sx, &i, endline);
3497 } else {
3498 found = window_copy_search_rl(gd, sgd,
3499 &px, i - 1, 0, fx + 1, cis);
3501 if (found) {
3502 i--;
3503 break;
3505 fx = gd->sx - 1;
3508 if (regex)
3509 regfree(&reg);
3511 if (found) {
3512 window_copy_scroll_to(wme, px, i, 1);
3513 return (1);
3515 if (wrap) {
3516 return (window_copy_search_jump(wme, gd, sgd,
3517 direction ? 0 : gd->sx - 1,
3518 direction ? 0 : gd->hsize + gd->sy - 1, fy, cis, 0,
3519 direction, regex));
3521 return (0);
3524 static void
3525 window_copy_move_after_search_mark(struct window_copy_mode_data *data,
3526 u_int *fx, u_int *fy, int wrapflag)
3528 struct screen *s = data->backing;
3529 u_int at, start;
3531 if (window_copy_search_mark_at(data, *fx, *fy, &start) == 0 &&
3532 data->searchmark[start] != 0) {
3533 while (window_copy_search_mark_at(data, *fx, *fy, &at) == 0) {
3534 if (data->searchmark[at] != data->searchmark[start])
3535 break;
3536 /* Stop if not wrapping and at the end of the grid. */
3537 if (!wrapflag &&
3538 *fx == screen_size_x(s) - 1 &&
3539 *fy == screen_hsize(s) + screen_size_y(s) - 1)
3540 break;
3542 window_copy_move_right(s, fx, fy, wrapflag);
3548 * Search in for text searchstr. If direction is 0 then search up, otherwise
3549 * down.
3551 static int
3552 window_copy_search(struct window_mode_entry *wme, int direction, int regex)
3554 struct window_pane *wp = wme->wp;
3555 struct window_copy_mode_data *data = wme->data;
3556 struct screen *s = data->backing, ss;
3557 struct screen_write_ctx ctx;
3558 struct grid *gd = s->grid;
3559 const char *str = data->searchstr;
3560 u_int at, endline, fx, fy, start;
3561 int cis, found, keys, visible_only;
3562 int wrapflag;
3564 if (regex && str[strcspn(str, "^$*+()?[].\\")] == '\0')
3565 regex = 0;
3567 data->searchdirection = direction;
3569 if (data->timeout)
3570 return (0);
3572 if (data->searchall || wp->searchstr == NULL ||
3573 wp->searchregex != regex) {
3574 visible_only = 0;
3575 data->searchall = 0;
3576 } else
3577 visible_only = (strcmp(wp->searchstr, str) == 0);
3578 free(wp->searchstr);
3579 wp->searchstr = xstrdup(str);
3580 wp->searchregex = regex;
3582 fx = data->cx;
3583 fy = screen_hsize(data->backing) - data->oy + data->cy;
3585 screen_init(&ss, screen_write_strlen("%s", str), 1, 0);
3586 screen_write_start(&ctx, &ss);
3587 screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", str);
3588 screen_write_stop(&ctx);
3590 wrapflag = options_get_number(wp->window->options, "wrap-search");
3591 cis = window_copy_is_lowercase(str);
3593 keys = options_get_number(wp->window->options, "mode-keys");
3595 if (direction) {
3597 * Behave according to mode-keys. If it is emacs, search forward
3598 * leaves the cursor after the match. If it is vi, the cursor
3599 * remains at the beginning of the match, regardless of
3600 * direction, which means that we need to start the next search
3601 * after the term the cursor is currently on when searching
3602 * forward.
3604 if (keys == MODEKEY_VI) {
3605 if (data->searchmark != NULL)
3606 window_copy_move_after_search_mark(data, &fx,
3607 &fy, wrapflag);
3608 else {
3610 * When there are no search marks, start the
3611 * search after the current cursor position.
3613 window_copy_move_right(s, &fx, &fy, wrapflag);
3616 endline = gd->hsize + gd->sy - 1;
3618 else {
3619 window_copy_move_left(s, &fx, &fy, wrapflag);
3620 endline = 0;
3623 found = window_copy_search_jump(wme, gd, ss.grid, fx, fy, endline, cis,
3624 wrapflag, direction, regex);
3625 if (found) {
3626 window_copy_search_marks(wme, &ss, regex, visible_only);
3627 fx = data->cx;
3628 fy = screen_hsize(data->backing) - data->oy + data->cy;
3631 * When searching forward, if the cursor is not at the beginning
3632 * of the mark, search again.
3634 if (direction &&
3635 window_copy_search_mark_at(data, fx, fy, &at) == 0 &&
3636 at > 0 &&
3637 data->searchmark[at] == data->searchmark[at - 1]) {
3638 window_copy_move_after_search_mark(data, &fx, &fy,
3639 wrapflag);
3640 window_copy_search_jump(wme, gd, ss.grid, fx,
3641 fy, endline, cis, wrapflag, direction,
3642 regex);
3643 fx = data->cx;
3644 fy = screen_hsize(data->backing) - data->oy + data->cy;
3647 if (direction) {
3649 * When in Emacs mode, position the cursor just after
3650 * the mark.
3652 if (keys == MODEKEY_EMACS) {
3653 window_copy_move_after_search_mark(data, &fx,
3654 &fy, wrapflag);
3655 data->cx = fx;
3656 data->cy = fy - screen_hsize(data->backing) +
3657 data-> oy;
3660 else {
3662 * When searching backward, position the cursor at the
3663 * beginning of the mark.
3665 if (window_copy_search_mark_at(data, fx, fy,
3666 &start) == 0) {
3667 while (window_copy_search_mark_at(data, fx, fy,
3668 &at) == 0 &&
3669 data->searchmark[at] ==
3670 data->searchmark[start]) {
3671 data->cx = fx;
3672 data->cy = fy -
3673 screen_hsize(data->backing) +
3674 data-> oy;
3675 if (at == 0)
3676 break;
3678 window_copy_move_left(s, &fx, &fy, 0);
3683 window_copy_redraw_screen(wme);
3685 screen_free(&ss);
3686 return (found);
3689 static void
3690 window_copy_visible_lines(struct window_copy_mode_data *data, u_int *start,
3691 u_int *end)
3693 struct grid *gd = data->backing->grid;
3694 const struct grid_line *gl;
3696 for (*start = gd->hsize - data->oy; *start > 0; (*start)--) {
3697 gl = grid_peek_line(gd, (*start) - 1);
3698 if (~gl->flags & GRID_LINE_WRAPPED)
3699 break;
3701 *end = gd->hsize - data->oy + gd->sy;
3704 static int
3705 window_copy_search_mark_at(struct window_copy_mode_data *data, u_int px,
3706 u_int py, u_int *at)
3708 struct screen *s = data->backing;
3709 struct grid *gd = s->grid;
3711 if (py < gd->hsize - data->oy)
3712 return (-1);
3713 if (py > gd->hsize - data->oy + gd->sy - 1)
3714 return (-1);
3715 *at = ((py - (gd->hsize - data->oy)) * gd->sx) + px;
3716 return (0);
3719 static int
3720 window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp,
3721 int regex, int visible_only)
3723 struct window_copy_mode_data *data = wme->data;
3724 struct screen *s = data->backing, ss;
3725 struct screen_write_ctx ctx;
3726 struct grid *gd = s->grid;
3727 int found, cis, stopped = 0;
3728 int cflags = REG_EXTENDED;
3729 u_int px, py, i, b, nfound = 0, width;
3730 u_int ssize = 1, start, end;
3731 char *sbuf;
3732 regex_t reg;
3733 uint64_t stop = 0, tstart, t;
3735 if (ssp == NULL) {
3736 width = screen_write_strlen("%s", data->searchstr);
3737 screen_init(&ss, width, 1, 0);
3738 screen_write_start(&ctx, &ss);
3739 screen_write_nputs(&ctx, -1, &grid_default_cell, "%s",
3740 data->searchstr);
3741 screen_write_stop(&ctx);
3742 ssp = &ss;
3743 } else
3744 width = screen_size_x(ssp);
3746 cis = window_copy_is_lowercase(data->searchstr);
3748 if (regex) {
3749 sbuf = xmalloc(ssize);
3750 sbuf[0] = '\0';
3751 sbuf = window_copy_stringify(ssp->grid, 0, 0, ssp->grid->sx,
3752 sbuf, &ssize);
3753 if (cis)
3754 cflags |= REG_ICASE;
3755 if (regcomp(&reg, sbuf, cflags) != 0) {
3756 free(sbuf);
3757 return (0);
3759 free(sbuf);
3761 tstart = get_timer();
3763 if (visible_only)
3764 window_copy_visible_lines(data, &start, &end);
3765 else {
3766 start = 0;
3767 end = gd->hsize + gd->sy;
3768 stop = get_timer() + WINDOW_COPY_SEARCH_ALL_TIMEOUT;
3771 again:
3772 free(data->searchmark);
3773 data->searchmark = xcalloc(gd->sx, gd->sy);
3774 data->searchgen = 1;
3776 for (py = start; py < end; py++) {
3777 px = 0;
3778 for (;;) {
3779 if (regex) {
3780 found = window_copy_search_lr_regex(gd,
3781 &px, &width, py, px, gd->sx, &reg);
3782 if (!found)
3783 break;
3784 } else {
3785 found = window_copy_search_lr(gd, ssp->grid,
3786 &px, py, px, gd->sx, cis);
3787 if (!found)
3788 break;
3790 nfound++;
3792 if (window_copy_search_mark_at(data, px, py, &b) == 0) {
3793 if (b + width > gd->sx * gd->sy)
3794 width = (gd->sx * gd->sy) - b;
3795 for (i = b; i < b + width; i++) {
3796 if (data->searchmark[i] != 0)
3797 continue;
3798 data->searchmark[i] = data->searchgen;
3800 if (data->searchgen == UCHAR_MAX)
3801 data->searchgen = 1;
3802 else
3803 data->searchgen++;
3805 px += width;
3808 t = get_timer();
3809 if (t - tstart > WINDOW_COPY_SEARCH_TIMEOUT) {
3810 data->timeout = 1;
3811 break;
3813 if (stop != 0 && t > stop) {
3814 stopped = 1;
3815 break;
3818 if (data->timeout) {
3819 window_copy_clear_marks(wme);
3820 goto out;
3823 if (stopped && stop != 0) {
3824 /* Try again but just the visible context. */
3825 window_copy_visible_lines(data, &start, &end);
3826 stop = 0;
3827 goto again;
3830 if (!visible_only) {
3831 if (stopped) {
3832 if (nfound > 1000)
3833 data->searchcount = 1000;
3834 else if (nfound > 100)
3835 data->searchcount = 100;
3836 else if (nfound > 10)
3837 data->searchcount = 10;
3838 else
3839 data->searchcount = -1;
3840 data->searchmore = 1;
3841 } else {
3842 data->searchcount = nfound;
3843 data->searchmore = 0;
3847 out:
3848 if (ssp == &ss)
3849 screen_free(&ss);
3850 if (regex)
3851 regfree(&reg);
3852 return (1);
3855 static void
3856 window_copy_clear_marks(struct window_mode_entry *wme)
3858 struct window_copy_mode_data *data = wme->data;
3860 free(data->searchmark);
3861 data->searchmark = NULL;
3864 static int
3865 window_copy_search_up(struct window_mode_entry *wme, int regex)
3867 return (window_copy_search(wme, 0, regex));
3870 static int
3871 window_copy_search_down(struct window_mode_entry *wme, int regex)
3873 return (window_copy_search(wme, 1, regex));
3876 static void
3877 window_copy_goto_line(struct window_mode_entry *wme, const char *linestr)
3879 struct window_copy_mode_data *data = wme->data;
3880 const char *errstr;
3881 int lineno;
3883 lineno = strtonum(linestr, -1, INT_MAX, &errstr);
3884 if (errstr != NULL)
3885 return;
3886 if (lineno < 0 || (u_int)lineno > screen_hsize(data->backing))
3887 lineno = screen_hsize(data->backing);
3889 data->oy = lineno;
3890 window_copy_update_selection(wme, 1, 0);
3891 window_copy_redraw_screen(wme);
3894 static void
3895 window_copy_match_start_end(struct window_copy_mode_data *data, u_int at,
3896 u_int *start, u_int *end)
3898 struct grid *gd = data->backing->grid;
3899 u_int last = (gd->sy * gd->sx) - 1;
3900 u_char mark = data->searchmark[at];
3902 *start = *end = at;
3903 while (*start != 0 && data->searchmark[*start] == mark)
3904 (*start)--;
3905 if (data->searchmark[*start] != mark)
3906 (*start)++;
3907 while (*end != last && data->searchmark[*end] == mark)
3908 (*end)++;
3909 if (data->searchmark[*end] != mark)
3910 (*end)--;
3913 static char *
3914 window_copy_match_at_cursor(struct window_copy_mode_data *data)
3916 struct grid *gd = data->backing->grid;
3917 struct grid_cell gc;
3918 u_int at, start, end, cy, px, py;
3919 u_int sx = screen_size_x(data->backing);
3920 char *buf = NULL;
3921 size_t len = 0;
3923 if (data->searchmark == NULL)
3924 return (NULL);
3926 cy = screen_hsize(data->backing) - data->oy + data->cy;
3927 if (window_copy_search_mark_at(data, data->cx, cy, &at) != 0)
3928 return (NULL);
3929 if (data->searchmark[at] == 0) {
3930 /* Allow one position after the match. */
3931 if (at == 0 || data->searchmark[--at] == 0)
3932 return (NULL);
3934 window_copy_match_start_end(data, at, &start, &end);
3937 * Cells will not be set in the marked array unless they are valid text
3938 * and wrapping will be taken care of, so we can just copy.
3940 for (at = start; at <= end; at++) {
3941 py = at / sx;
3942 px = at - (py * sx);
3944 grid_get_cell(gd, px, gd->hsize + py - data->oy, &gc);
3945 buf = xrealloc(buf, len + gc.data.size + 1);
3946 memcpy(buf + len, gc.data.data, gc.data.size);
3947 len += gc.data.size;
3949 if (len != 0)
3950 buf[len] = '\0';
3951 return (buf);
3954 static void
3955 window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy,
3956 struct grid_cell *gc, const struct grid_cell *mgc,
3957 const struct grid_cell *cgc, const struct grid_cell *mkgc)
3959 struct window_pane *wp = wme->wp;
3960 struct window_copy_mode_data *data = wme->data;
3961 u_int mark, start, end, cy, cursor, current;
3962 int inv = 0, found = 0;
3963 int keys;
3965 if (data->showmark && fy == data->my) {
3966 gc->attr = mkgc->attr;
3967 if (fx == data->mx)
3968 inv = 1;
3969 if (inv) {
3970 gc->fg = mkgc->bg;
3971 gc->bg = mkgc->fg;
3973 else {
3974 gc->fg = mkgc->fg;
3975 gc->bg = mkgc->bg;
3979 if (data->searchmark == NULL)
3980 return;
3982 if (window_copy_search_mark_at(data, fx, fy, &current) != 0)
3983 return;
3984 mark = data->searchmark[current];
3985 if (mark == 0)
3986 return;
3988 cy = screen_hsize(data->backing) - data->oy + data->cy;
3989 if (window_copy_search_mark_at(data, data->cx, cy, &cursor) == 0) {
3990 keys = options_get_number(wp->window->options, "mode-keys");
3991 if (cursor != 0 &&
3992 keys == MODEKEY_EMACS &&
3993 data->searchdirection) {
3994 if (data->searchmark[cursor - 1] == mark) {
3995 cursor--;
3996 found = 1;
3998 } else if (data->searchmark[cursor] == mark)
3999 found = 1;
4000 if (found) {
4001 window_copy_match_start_end(data, cursor, &start, &end);
4002 if (current >= start && current <= end) {
4003 gc->attr = cgc->attr;
4004 if (inv) {
4005 gc->fg = cgc->bg;
4006 gc->bg = cgc->fg;
4008 else {
4009 gc->fg = cgc->fg;
4010 gc->bg = cgc->bg;
4012 return;
4017 gc->attr = mgc->attr;
4018 if (inv) {
4019 gc->fg = mgc->bg;
4020 gc->bg = mgc->fg;
4022 else {
4023 gc->fg = mgc->fg;
4024 gc->bg = mgc->bg;
4028 static void
4029 window_copy_write_one(struct window_mode_entry *wme,
4030 struct screen_write_ctx *ctx, u_int py, u_int fy, u_int nx,
4031 const struct grid_cell *mgc, const struct grid_cell *cgc,
4032 const struct grid_cell *mkgc)
4034 struct window_copy_mode_data *data = wme->data;
4035 struct grid *gd = data->backing->grid;
4036 struct grid_cell gc;
4037 u_int fx;
4039 screen_write_cursormove(ctx, 0, py, 0);
4040 for (fx = 0; fx < nx; fx++) {
4041 grid_get_cell(gd, fx, fy, &gc);
4042 if (fx + gc.data.width <= nx) {
4043 window_copy_update_style(wme, fx, fy, &gc, mgc, cgc,
4044 mkgc);
4045 screen_write_cell(ctx, &gc);
4050 static void
4051 window_copy_write_line(struct window_mode_entry *wme,
4052 struct screen_write_ctx *ctx, u_int py)
4054 struct window_pane *wp = wme->wp;
4055 struct window_copy_mode_data *data = wme->data;
4056 struct screen *s = &data->screen;
4057 struct options *oo = wp->window->options;
4058 struct grid_cell gc, mgc, cgc, mkgc;
4059 char hdr[512];
4060 size_t size = 0;
4061 u_int hsize = screen_hsize(data->backing);
4063 style_apply(&gc, oo, "mode-style", NULL);
4064 gc.flags |= GRID_FLAG_NOPALETTE;
4065 style_apply(&mgc, oo, "copy-mode-match-style", NULL);
4066 mgc.flags |= GRID_FLAG_NOPALETTE;
4067 style_apply(&cgc, oo, "copy-mode-current-match-style", NULL);
4068 cgc.flags |= GRID_FLAG_NOPALETTE;
4069 style_apply(&mkgc, oo, "copy-mode-mark-style", NULL);
4070 mkgc.flags |= GRID_FLAG_NOPALETTE;
4072 if (py == 0 && s->rupper < s->rlower && !data->hide_position) {
4073 if (data->searchmark == NULL) {
4074 if (data->timeout) {
4075 size = xsnprintf(hdr, sizeof hdr,
4076 "(timed out) [%u/%u]", data->oy, hsize);
4077 } else {
4078 size = xsnprintf(hdr, sizeof hdr,
4079 "[%u/%u]", data->oy, hsize);
4081 } else {
4082 if (data->searchcount == -1) {
4083 size = xsnprintf(hdr, sizeof hdr,
4084 "[%u/%u]", data->oy, hsize);
4085 } else {
4086 size = xsnprintf(hdr, sizeof hdr,
4087 "(%d%s results) [%u/%u]", data->searchcount,
4088 data->searchmore ? "+" : "", data->oy,
4089 hsize);
4092 if (size > screen_size_x(s))
4093 size = screen_size_x(s);
4094 screen_write_cursormove(ctx, screen_size_x(s) - size, 0, 0);
4095 screen_write_puts(ctx, &gc, "%s", hdr);
4096 } else
4097 size = 0;
4099 if (size < screen_size_x(s)) {
4100 window_copy_write_one(wme, ctx, py, hsize - data->oy + py,
4101 screen_size_x(s) - size, &mgc, &cgc, &mkgc);
4104 if (py == data->cy && data->cx == screen_size_x(s)) {
4105 screen_write_cursormove(ctx, screen_size_x(s) - 1, py, 0);
4106 screen_write_putc(ctx, &grid_default_cell, '$');
4110 static void
4111 window_copy_write_lines(struct window_mode_entry *wme,
4112 struct screen_write_ctx *ctx, u_int py, u_int ny)
4114 u_int yy;
4116 for (yy = py; yy < py + ny; yy++)
4117 window_copy_write_line(wme, ctx, py);
4120 static void
4121 window_copy_redraw_selection(struct window_mode_entry *wme, u_int old_y)
4123 struct window_copy_mode_data *data = wme->data;
4124 struct grid *gd = data->backing->grid;
4125 u_int new_y, start, end;
4127 new_y = data->cy;
4128 if (old_y <= new_y) {
4129 start = old_y;
4130 end = new_y;
4131 } else {
4132 start = new_y;
4133 end = old_y;
4137 * In word selection mode the first word on the line below the cursor
4138 * might be selected, so add this line to the redraw area.
4140 if (data->selflag == SEL_WORD) {
4141 /* Last grid line in data coordinates. */
4142 if (end < gd->sy + data->oy - 1)
4143 end++;
4145 window_copy_redraw_lines(wme, start, end - start + 1);
4148 static void
4149 window_copy_redraw_lines(struct window_mode_entry *wme, u_int py, u_int ny)
4151 struct window_pane *wp = wme->wp;
4152 struct window_copy_mode_data *data = wme->data;
4153 struct screen_write_ctx ctx;
4154 u_int i;
4156 screen_write_start_pane(&ctx, wp, NULL);
4157 for (i = py; i < py + ny; i++)
4158 window_copy_write_line(wme, &ctx, i);
4159 screen_write_cursormove(&ctx, data->cx, data->cy, 0);
4160 screen_write_stop(&ctx);
4163 static void
4164 window_copy_redraw_screen(struct window_mode_entry *wme)
4166 struct window_copy_mode_data *data = wme->data;
4168 window_copy_redraw_lines(wme, 0, screen_size_y(&data->screen));
4171 static void
4172 window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin,
4173 int no_reset)
4175 struct window_copy_mode_data *data = wme->data;
4176 u_int xx, yy;
4178 xx = data->cx;
4179 yy = screen_hsize(data->backing) + data->cy - data->oy;
4180 switch (data->selflag) {
4181 case SEL_WORD:
4182 if (no_reset)
4183 break;
4184 begin = 0;
4185 if (data->dy > yy || (data->dy == yy && data->dx > xx)) {
4186 /* Right to left selection. */
4187 window_copy_cursor_previous_word_pos(wme,
4188 data->separators, &xx, &yy);
4189 begin = 1;
4191 /* Reset the end. */
4192 data->endselx = data->endselrx;
4193 data->endsely = data->endselry;
4194 } else {
4195 /* Left to right selection. */
4196 if (xx >= window_copy_find_length(wme, yy) ||
4197 !window_copy_in_set(wme, xx + 1, yy, WHITESPACE)) {
4198 window_copy_cursor_next_word_end_pos(wme,
4199 data->separators, &xx, &yy);
4202 /* Reset the start. */
4203 data->selx = data->selrx;
4204 data->sely = data->selry;
4206 break;
4207 case SEL_LINE:
4208 if (no_reset)
4209 break;
4210 begin = 0;
4211 if (data->dy > yy) {
4212 /* Right to left selection. */
4213 xx = 0;
4214 begin = 1;
4216 /* Reset the end. */
4217 data->endselx = data->endselrx;
4218 data->endsely = data->endselry;
4219 } else {
4220 /* Left to right selection. */
4221 if (yy < data->endselry)
4222 yy = data->endselry;
4223 xx = window_copy_find_length(wme, yy);
4225 /* Reset the start. */
4226 data->selx = data->selrx;
4227 data->sely = data->selry;
4229 break;
4230 case SEL_CHAR:
4231 break;
4233 if (begin) {
4234 data->selx = xx;
4235 data->sely = yy;
4236 } else {
4237 data->endselx = xx;
4238 data->endsely = yy;
4242 static void
4243 window_copy_synchronize_cursor(struct window_mode_entry *wme, int no_reset)
4245 struct window_copy_mode_data *data = wme->data;
4247 switch (data->cursordrag) {
4248 case CURSORDRAG_ENDSEL:
4249 window_copy_synchronize_cursor_end(wme, 0, no_reset);
4250 break;
4251 case CURSORDRAG_SEL:
4252 window_copy_synchronize_cursor_end(wme, 1, no_reset);
4253 break;
4254 case CURSORDRAG_NONE:
4255 break;
4259 static void
4260 window_copy_update_cursor(struct window_mode_entry *wme, u_int cx, u_int cy)
4262 struct window_pane *wp = wme->wp;
4263 struct window_copy_mode_data *data = wme->data;
4264 struct screen *s = &data->screen;
4265 struct screen_write_ctx ctx;
4266 u_int old_cx, old_cy;
4268 old_cx = data->cx; old_cy = data->cy;
4269 data->cx = cx; data->cy = cy;
4270 if (old_cx == screen_size_x(s))
4271 window_copy_redraw_lines(wme, old_cy, 1);
4272 if (data->cx == screen_size_x(s))
4273 window_copy_redraw_lines(wme, data->cy, 1);
4274 else {
4275 screen_write_start_pane(&ctx, wp, NULL);
4276 screen_write_cursormove(&ctx, data->cx, data->cy, 0);
4277 screen_write_stop(&ctx);
4281 static void
4282 window_copy_start_selection(struct window_mode_entry *wme)
4284 struct window_copy_mode_data *data = wme->data;
4286 data->selx = data->cx;
4287 data->sely = screen_hsize(data->backing) + data->cy - data->oy;
4289 data->endselx = data->selx;
4290 data->endsely = data->sely;
4292 data->cursordrag = CURSORDRAG_ENDSEL;
4294 window_copy_set_selection(wme, 1, 0);
4297 static int
4298 window_copy_adjust_selection(struct window_mode_entry *wme, u_int *selx,
4299 u_int *sely)
4301 struct window_copy_mode_data *data = wme->data;
4302 struct screen *s = &data->screen;
4303 u_int sx, sy, ty;
4304 int relpos;
4306 sx = *selx;
4307 sy = *sely;
4309 ty = screen_hsize(data->backing) - data->oy;
4310 if (sy < ty) {
4311 relpos = WINDOW_COPY_REL_POS_ABOVE;
4312 if (!data->rectflag)
4313 sx = 0;
4314 sy = 0;
4315 } else if (sy > ty + screen_size_y(s) - 1) {
4316 relpos = WINDOW_COPY_REL_POS_BELOW;
4317 if (!data->rectflag)
4318 sx = screen_size_x(s) - 1;
4319 sy = screen_size_y(s) - 1;
4320 } else {
4321 relpos = WINDOW_COPY_REL_POS_ON_SCREEN;
4322 sy -= ty;
4325 *selx = sx;
4326 *sely = sy;
4327 return (relpos);
4330 static int
4331 window_copy_update_selection(struct window_mode_entry *wme, int may_redraw,
4332 int no_reset)
4334 struct window_copy_mode_data *data = wme->data;
4335 struct screen *s = &data->screen;
4337 if (s->sel == NULL && data->lineflag == LINE_SEL_NONE)
4338 return (0);
4339 return (window_copy_set_selection(wme, may_redraw, no_reset));
4342 static int
4343 window_copy_set_selection(struct window_mode_entry *wme, int may_redraw,
4344 int no_reset)
4346 struct window_pane *wp = wme->wp;
4347 struct window_copy_mode_data *data = wme->data;
4348 struct screen *s = &data->screen;
4349 struct options *oo = wp->window->options;
4350 struct grid_cell gc;
4351 u_int sx, sy, cy, endsx, endsy;
4352 int startrelpos, endrelpos;
4354 window_copy_synchronize_cursor(wme, no_reset);
4356 /* Adjust the selection. */
4357 sx = data->selx;
4358 sy = data->sely;
4359 startrelpos = window_copy_adjust_selection(wme, &sx, &sy);
4361 /* Adjust the end of selection. */
4362 endsx = data->endselx;
4363 endsy = data->endsely;
4364 endrelpos = window_copy_adjust_selection(wme, &endsx, &endsy);
4366 /* Selection is outside of the current screen */
4367 if (startrelpos == endrelpos &&
4368 startrelpos != WINDOW_COPY_REL_POS_ON_SCREEN) {
4369 screen_hide_selection(s);
4370 return (0);
4373 /* Set colours and selection. */
4374 style_apply(&gc, oo, "mode-style", NULL);
4375 gc.flags |= GRID_FLAG_NOPALETTE;
4376 screen_set_selection(s, sx, sy, endsx, endsy, data->rectflag,
4377 data->modekeys, &gc);
4379 if (data->rectflag && may_redraw) {
4381 * Can't rely on the caller to redraw the right lines for
4382 * rectangle selection - find the highest line and the number
4383 * of lines, and redraw just past that in both directions
4385 cy = data->cy;
4386 if (data->cursordrag == CURSORDRAG_ENDSEL) {
4387 if (sy < cy)
4388 window_copy_redraw_lines(wme, sy, cy - sy + 1);
4389 else
4390 window_copy_redraw_lines(wme, cy, sy - cy + 1);
4391 } else {
4392 if (endsy < cy) {
4393 window_copy_redraw_lines(wme, endsy,
4394 cy - endsy + 1);
4395 } else {
4396 window_copy_redraw_lines(wme, cy,
4397 endsy - cy + 1);
4402 return (1);
4405 static void *
4406 window_copy_get_selection(struct window_mode_entry *wme, size_t *len)
4408 struct window_pane *wp = wme->wp;
4409 struct window_copy_mode_data *data = wme->data;
4410 struct screen *s = &data->screen;
4411 char *buf;
4412 size_t off;
4413 u_int i, xx, yy, sx, sy, ex, ey, ey_last;
4414 u_int firstsx, lastex, restex, restsx, selx;
4415 int keys;
4417 if (data->screen.sel == NULL && data->lineflag == LINE_SEL_NONE) {
4418 buf = window_copy_match_at_cursor(data);
4419 if (buf != NULL)
4420 *len = strlen(buf);
4421 else
4422 *len = 0;
4423 return (buf);
4426 buf = xmalloc(1);
4427 off = 0;
4429 *buf = '\0';
4432 * The selection extends from selx,sely to (adjusted) cx,cy on
4433 * the base screen.
4436 /* Find start and end. */
4437 xx = data->endselx;
4438 yy = data->endsely;
4439 if (yy < data->sely || (yy == data->sely && xx < data->selx)) {
4440 sx = xx; sy = yy;
4441 ex = data->selx; ey = data->sely;
4442 } else {
4443 sx = data->selx; sy = data->sely;
4444 ex = xx; ey = yy;
4447 /* Trim ex to end of line. */
4448 ey_last = window_copy_find_length(wme, ey);
4449 if (ex > ey_last)
4450 ex = ey_last;
4453 * Deal with rectangle-copy if necessary; four situations: start of
4454 * first line (firstsx), end of last line (lastex), start (restsx) and
4455 * end (restex) of all other lines.
4457 xx = screen_size_x(s);
4460 * Behave according to mode-keys. If it is emacs, copy like emacs,
4461 * keeping the top-left-most character, and dropping the
4462 * bottom-right-most, regardless of copy direction. If it is vi, also
4463 * keep bottom-right-most character.
4465 keys = options_get_number(wp->window->options, "mode-keys");
4466 if (data->rectflag) {
4468 * Need to ignore the column with the cursor in it, which for
4469 * rectangular copy means knowing which side the cursor is on.
4471 if (data->cursordrag == CURSORDRAG_ENDSEL)
4472 selx = data->selx;
4473 else
4474 selx = data->endselx;
4475 if (selx < data->cx) {
4476 /* Selection start is on the left. */
4477 if (keys == MODEKEY_EMACS) {
4478 lastex = data->cx;
4479 restex = data->cx;
4481 else {
4482 lastex = data->cx + 1;
4483 restex = data->cx + 1;
4485 firstsx = selx;
4486 restsx = selx;
4487 } else {
4488 /* Cursor is on the left. */
4489 lastex = selx + 1;
4490 restex = selx + 1;
4491 firstsx = data->cx;
4492 restsx = data->cx;
4494 } else {
4495 if (keys == MODEKEY_EMACS)
4496 lastex = ex;
4497 else
4498 lastex = ex + 1;
4499 restex = xx;
4500 firstsx = sx;
4501 restsx = 0;
4504 /* Copy the lines. */
4505 for (i = sy; i <= ey; i++) {
4506 window_copy_copy_line(wme, &buf, &off, i,
4507 (i == sy ? firstsx : restsx),
4508 (i == ey ? lastex : restex));
4511 /* Don't bother if no data. */
4512 if (off == 0) {
4513 free(buf);
4514 *len = 0;
4515 return (NULL);
4517 /* Remove final \n (unless at end in vi mode). */
4518 if (keys == MODEKEY_EMACS || lastex <= ey_last) {
4519 if (~grid_get_line(data->backing->grid, ey)->flags &
4520 GRID_LINE_WRAPPED || lastex != ey_last)
4521 off -= 1;
4523 *len = off;
4524 return (buf);
4527 static void
4528 window_copy_copy_buffer(struct window_mode_entry *wme, const char *prefix,
4529 void *buf, size_t len)
4531 struct window_pane *wp = wme->wp;
4532 struct screen_write_ctx ctx;
4534 if (options_get_number(global_options, "set-clipboard") != 0) {
4535 screen_write_start_pane(&ctx, wp, NULL);
4536 screen_write_setselection(&ctx, buf, len);
4537 screen_write_stop(&ctx);
4538 notify_pane("pane-set-clipboard", wp);
4541 paste_add(prefix, buf, len);
4544 static void *
4545 window_copy_pipe_run(struct window_mode_entry *wme, struct session *s,
4546 const char *cmd, size_t *len)
4548 void *buf;
4549 struct job *job;
4551 buf = window_copy_get_selection(wme, len);
4552 if (cmd == NULL || *cmd == '\0')
4553 cmd = options_get_string(global_options, "copy-command");
4554 if (cmd != NULL && *cmd != '\0') {
4555 job = job_run(cmd, 0, NULL, NULL, s, NULL, NULL, NULL, NULL,
4556 NULL, JOB_NOWAIT, -1, -1);
4557 bufferevent_write(job_get_event(job), buf, *len);
4559 return (buf);
4562 static void
4563 window_copy_pipe(struct window_mode_entry *wme, struct session *s,
4564 const char *cmd)
4566 size_t len;
4568 window_copy_pipe_run(wme, s, cmd, &len);
4571 static void
4572 window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s,
4573 const char *prefix, const char *cmd)
4575 void *buf;
4576 size_t len;
4578 buf = window_copy_pipe_run(wme, s, cmd, &len);
4579 if (buf != NULL)
4580 window_copy_copy_buffer(wme, prefix, buf, len);
4583 static void
4584 window_copy_copy_selection(struct window_mode_entry *wme, const char *prefix)
4586 char *buf;
4587 size_t len;
4589 buf = window_copy_get_selection(wme, &len);
4590 if (buf != NULL)
4591 window_copy_copy_buffer(wme, prefix, buf, len);
4594 static void
4595 window_copy_append_selection(struct window_mode_entry *wme)
4597 struct window_pane *wp = wme->wp;
4598 char *buf;
4599 struct paste_buffer *pb;
4600 const char *bufdata, *bufname = NULL;
4601 size_t len, bufsize;
4602 struct screen_write_ctx ctx;
4604 buf = window_copy_get_selection(wme, &len);
4605 if (buf == NULL)
4606 return;
4608 if (options_get_number(global_options, "set-clipboard") != 0) {
4609 screen_write_start_pane(&ctx, wp, NULL);
4610 screen_write_setselection(&ctx, buf, len);
4611 screen_write_stop(&ctx);
4612 notify_pane("pane-set-clipboard", wp);
4615 pb = paste_get_top(&bufname);
4616 if (pb != NULL) {
4617 bufdata = paste_buffer_data(pb, &bufsize);
4618 buf = xrealloc(buf, len + bufsize);
4619 memmove(buf + bufsize, buf, len);
4620 memcpy(buf, bufdata, bufsize);
4621 len += bufsize;
4623 if (paste_set(buf, len, bufname, NULL) != 0)
4624 free(buf);
4627 static void
4628 window_copy_copy_line(struct window_mode_entry *wme, char **buf, size_t *off,
4629 u_int sy, u_int sx, u_int ex)
4631 struct window_copy_mode_data *data = wme->data;
4632 struct grid *gd = data->backing->grid;
4633 struct grid_cell gc;
4634 struct grid_line *gl;
4635 struct utf8_data ud;
4636 u_int i, xx, wrapped = 0;
4637 const char *s;
4639 if (sx > ex)
4640 return;
4643 * Work out if the line was wrapped at the screen edge and all of it is
4644 * on screen.
4646 gl = grid_get_line(gd, sy);
4647 if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx)
4648 wrapped = 1;
4650 /* If the line was wrapped, don't strip spaces (use the full length). */
4651 if (wrapped)
4652 xx = gl->cellsize;
4653 else
4654 xx = window_copy_find_length(wme, sy);
4655 if (ex > xx)
4656 ex = xx;
4657 if (sx > xx)
4658 sx = xx;
4660 if (sx < ex) {
4661 for (i = sx; i < ex; i++) {
4662 grid_get_cell(gd, i, sy, &gc);
4663 if (gc.flags & GRID_FLAG_PADDING)
4664 continue;
4665 utf8_copy(&ud, &gc.data);
4666 if (ud.size == 1 && (gc.attr & GRID_ATTR_CHARSET)) {
4667 s = tty_acs_get(NULL, ud.data[0]);
4668 if (s != NULL && strlen(s) <= sizeof ud.data) {
4669 ud.size = strlen(s);
4670 memcpy(ud.data, s, ud.size);
4674 *buf = xrealloc(*buf, (*off) + ud.size);
4675 memcpy(*buf + *off, ud.data, ud.size);
4676 *off += ud.size;
4680 /* Only add a newline if the line wasn't wrapped. */
4681 if (!wrapped || ex != xx) {
4682 *buf = xrealloc(*buf, (*off) + 1);
4683 (*buf)[(*off)++] = '\n';
4687 static void
4688 window_copy_clear_selection(struct window_mode_entry *wme)
4690 struct window_copy_mode_data *data = wme->data;
4691 u_int px, py;
4693 screen_clear_selection(&data->screen);
4695 data->cursordrag = CURSORDRAG_NONE;
4696 data->lineflag = LINE_SEL_NONE;
4697 data->selflag = SEL_CHAR;
4699 py = screen_hsize(data->backing) + data->cy - data->oy;
4700 px = window_copy_find_length(wme, py);
4701 if (data->cx > px)
4702 window_copy_update_cursor(wme, px, data->cy);
4705 static int
4706 window_copy_in_set(struct window_mode_entry *wme, u_int px, u_int py,
4707 const char *set)
4709 struct window_copy_mode_data *data = wme->data;
4710 struct grid_cell gc;
4712 grid_get_cell(data->backing->grid, px, py, &gc);
4713 if (gc.flags & GRID_FLAG_PADDING)
4714 return (0);
4715 return (utf8_cstrhas(set, &gc.data));
4718 static u_int
4719 window_copy_find_length(struct window_mode_entry *wme, u_int py)
4721 struct window_copy_mode_data *data = wme->data;
4723 return (grid_line_length(data->backing->grid, py));
4726 static void
4727 window_copy_cursor_start_of_line(struct window_mode_entry *wme)
4729 struct window_copy_mode_data *data = wme->data;
4730 struct screen *back_s = data->backing;
4731 struct grid_reader gr;
4732 u_int px, py, oldy, hsize;
4734 px = data->cx;
4735 hsize = screen_hsize(back_s);
4736 py = hsize + data->cy - data->oy;
4737 oldy = data->cy;
4739 grid_reader_start(&gr, back_s->grid, px, py);
4740 grid_reader_cursor_start_of_line(&gr, 1);
4741 grid_reader_get_cursor(&gr, &px, &py);
4742 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
4745 static void
4746 window_copy_cursor_back_to_indentation(struct window_mode_entry *wme)
4748 struct window_copy_mode_data *data = wme->data;
4749 struct screen *back_s = data->backing;
4750 struct grid_reader gr;
4751 u_int px, py, oldy, hsize;
4753 px = data->cx;
4754 hsize = screen_hsize(back_s);
4755 py = hsize + data->cy - data->oy;
4756 oldy = data->cy;
4758 grid_reader_start(&gr, back_s->grid, px, py);
4759 grid_reader_cursor_back_to_indentation(&gr);
4760 grid_reader_get_cursor(&gr, &px, &py);
4761 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
4764 static void
4765 window_copy_cursor_end_of_line(struct window_mode_entry *wme)
4767 struct window_copy_mode_data *data = wme->data;
4768 struct screen *back_s = data->backing;
4769 struct grid_reader gr;
4770 u_int px, py, oldy, hsize;
4772 px = data->cx;
4773 hsize = screen_hsize(back_s);
4774 py = hsize + data->cy - data->oy;
4775 oldy = data->cy;
4777 grid_reader_start(&gr, back_s->grid, px, py);
4778 if (data->screen.sel != NULL && data->rectflag)
4779 grid_reader_cursor_end_of_line(&gr, 1, 1);
4780 else
4781 grid_reader_cursor_end_of_line(&gr, 1, 0);
4782 grid_reader_get_cursor(&gr, &px, &py);
4783 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
4784 data->oy, oldy, px, py, 0);
4787 static void
4788 window_copy_other_end(struct window_mode_entry *wme)
4790 struct window_copy_mode_data *data = wme->data;
4791 struct screen *s = &data->screen;
4792 u_int selx, sely, cy, yy, hsize;
4794 if (s->sel == NULL && data->lineflag == LINE_SEL_NONE)
4795 return;
4797 if (data->lineflag == LINE_SEL_LEFT_RIGHT)
4798 data->lineflag = LINE_SEL_RIGHT_LEFT;
4799 else if (data->lineflag == LINE_SEL_RIGHT_LEFT)
4800 data->lineflag = LINE_SEL_LEFT_RIGHT;
4802 switch (data->cursordrag) {
4803 case CURSORDRAG_NONE:
4804 case CURSORDRAG_SEL:
4805 data->cursordrag = CURSORDRAG_ENDSEL;
4806 break;
4807 case CURSORDRAG_ENDSEL:
4808 data->cursordrag = CURSORDRAG_SEL;
4809 break;
4812 selx = data->endselx;
4813 sely = data->endsely;
4814 if (data->cursordrag == CURSORDRAG_SEL) {
4815 selx = data->selx;
4816 sely = data->sely;
4819 cy = data->cy;
4820 yy = screen_hsize(data->backing) + data->cy - data->oy;
4822 data->cx = selx;
4824 hsize = screen_hsize(data->backing);
4825 if (sely < hsize - data->oy) { /* above */
4826 data->oy = hsize - sely;
4827 data->cy = 0;
4828 } else if (sely > hsize - data->oy + screen_size_y(s)) { /* below */
4829 data->oy = hsize - sely + screen_size_y(s) - 1;
4830 data->cy = screen_size_y(s) - 1;
4831 } else
4832 data->cy = cy + sely - yy;
4834 window_copy_update_selection(wme, 1, 1);
4835 window_copy_redraw_screen(wme);
4838 static void
4839 window_copy_cursor_left(struct window_mode_entry *wme)
4841 struct window_copy_mode_data *data = wme->data;
4842 struct screen *back_s = data->backing;
4843 struct grid_reader gr;
4844 u_int px, py, oldy, hsize;
4846 px = data->cx;
4847 hsize = screen_hsize(back_s);
4848 py = hsize + data->cy - data->oy;
4849 oldy = data->cy;
4851 grid_reader_start(&gr, back_s->grid, px, py);
4852 grid_reader_cursor_left(&gr, 1);
4853 grid_reader_get_cursor(&gr, &px, &py);
4854 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
4857 static void
4858 window_copy_cursor_right(struct window_mode_entry *wme, int all)
4860 struct window_copy_mode_data *data = wme->data;
4861 struct screen *back_s = data->backing;
4862 struct grid_reader gr;
4863 u_int px, py, oldy, hsize;
4865 px = data->cx;
4866 hsize = screen_hsize(back_s);
4867 py = hsize + data->cy - data->oy;
4868 oldy = data->cy;
4870 grid_reader_start(&gr, back_s->grid, px, py);
4871 grid_reader_cursor_right(&gr, 1, all);
4872 grid_reader_get_cursor(&gr, &px, &py);
4873 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
4874 data->oy, oldy, px, py, 0);
4877 static void
4878 window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only)
4880 struct window_copy_mode_data *data = wme->data;
4881 struct screen *s = &data->screen;
4882 u_int ox, oy, px, py;
4883 int norectsel;
4885 norectsel = data->screen.sel == NULL || !data->rectflag;
4886 oy = screen_hsize(data->backing) + data->cy - data->oy;
4887 ox = window_copy_find_length(wme, oy);
4888 if (norectsel && data->cx != ox) {
4889 data->lastcx = data->cx;
4890 data->lastsx = ox;
4893 if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely)
4894 window_copy_other_end(wme);
4896 if (scroll_only || data->cy == 0) {
4897 if (norectsel)
4898 data->cx = data->lastcx;
4899 window_copy_scroll_down(wme, 1);
4900 if (scroll_only) {
4901 if (data->cy == screen_size_y(s) - 1)
4902 window_copy_redraw_lines(wme, data->cy, 1);
4903 else
4904 window_copy_redraw_lines(wme, data->cy, 2);
4906 } else {
4907 if (norectsel) {
4908 window_copy_update_cursor(wme, data->lastcx,
4909 data->cy - 1);
4910 } else
4911 window_copy_update_cursor(wme, data->cx, data->cy - 1);
4912 if (window_copy_update_selection(wme, 1, 0)) {
4913 if (data->cy == screen_size_y(s) - 1)
4914 window_copy_redraw_lines(wme, data->cy, 1);
4915 else
4916 window_copy_redraw_lines(wme, data->cy, 2);
4920 if (norectsel) {
4921 py = screen_hsize(data->backing) + data->cy - data->oy;
4922 px = window_copy_find_length(wme, py);
4923 if ((data->cx >= data->lastsx && data->cx != px) ||
4924 data->cx > px)
4926 window_copy_update_cursor(wme, px, data->cy);
4927 if (window_copy_update_selection(wme, 1, 0))
4928 window_copy_redraw_lines(wme, data->cy, 1);
4932 if (data->lineflag == LINE_SEL_LEFT_RIGHT)
4934 py = screen_hsize(data->backing) + data->cy - data->oy;
4935 if (data->rectflag)
4936 px = screen_size_x(data->backing);
4937 else
4938 px = window_copy_find_length(wme, py);
4939 window_copy_update_cursor(wme, px, data->cy);
4940 if (window_copy_update_selection(wme, 1, 0))
4941 window_copy_redraw_lines(wme, data->cy, 1);
4943 else if (data->lineflag == LINE_SEL_RIGHT_LEFT)
4945 window_copy_update_cursor(wme, 0, data->cy);
4946 if (window_copy_update_selection(wme, 1, 0))
4947 window_copy_redraw_lines(wme, data->cy, 1);
4951 static void
4952 window_copy_cursor_down(struct window_mode_entry *wme, int scroll_only)
4954 struct window_copy_mode_data *data = wme->data;
4955 struct screen *s = &data->screen;
4956 u_int ox, oy, px, py;
4957 int norectsel;
4959 norectsel = data->screen.sel == NULL || !data->rectflag;
4960 oy = screen_hsize(data->backing) + data->cy - data->oy;
4961 ox = window_copy_find_length(wme, oy);
4962 if (norectsel && data->cx != ox) {
4963 data->lastcx = data->cx;
4964 data->lastsx = ox;
4967 if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely)
4968 window_copy_other_end(wme);
4970 if (scroll_only || data->cy == screen_size_y(s) - 1) {
4971 if (norectsel)
4972 data->cx = data->lastcx;
4973 window_copy_scroll_up(wme, 1);
4974 if (scroll_only && data->cy > 0)
4975 window_copy_redraw_lines(wme, data->cy - 1, 2);
4976 } else {
4977 if (norectsel) {
4978 window_copy_update_cursor(wme, data->lastcx,
4979 data->cy + 1);
4980 } else
4981 window_copy_update_cursor(wme, data->cx, data->cy + 1);
4982 if (window_copy_update_selection(wme, 1, 0))
4983 window_copy_redraw_lines(wme, data->cy - 1, 2);
4986 if (norectsel) {
4987 py = screen_hsize(data->backing) + data->cy - data->oy;
4988 px = window_copy_find_length(wme, py);
4989 if ((data->cx >= data->lastsx && data->cx != px) ||
4990 data->cx > px)
4992 window_copy_update_cursor(wme, px, data->cy);
4993 if (window_copy_update_selection(wme, 1, 0))
4994 window_copy_redraw_lines(wme, data->cy, 1);
4998 if (data->lineflag == LINE_SEL_LEFT_RIGHT)
5000 py = screen_hsize(data->backing) + data->cy - data->oy;
5001 if (data->rectflag)
5002 px = screen_size_x(data->backing);
5003 else
5004 px = window_copy_find_length(wme, py);
5005 window_copy_update_cursor(wme, px, data->cy);
5006 if (window_copy_update_selection(wme, 1, 0))
5007 window_copy_redraw_lines(wme, data->cy, 1);
5009 else if (data->lineflag == LINE_SEL_RIGHT_LEFT)
5011 window_copy_update_cursor(wme, 0, data->cy);
5012 if (window_copy_update_selection(wme, 1, 0))
5013 window_copy_redraw_lines(wme, data->cy, 1);
5017 static void
5018 window_copy_cursor_jump(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 + 1;
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 if (grid_reader_cursor_jump(&gr, data->jumpchar)) {
5032 grid_reader_get_cursor(&gr, &px, &py);
5033 window_copy_acquire_cursor_down(wme, hsize,
5034 screen_size_y(back_s), data->oy, oldy, px, py, 0);
5038 static void
5039 window_copy_cursor_jump_back(struct window_mode_entry *wme)
5041 struct window_copy_mode_data *data = wme->data;
5042 struct screen *back_s = data->backing;
5043 struct grid_reader gr;
5044 u_int px, py, oldy, hsize;
5046 px = data->cx;
5047 hsize = screen_hsize(back_s);
5048 py = hsize + data->cy - data->oy;
5049 oldy = data->cy;
5051 grid_reader_start(&gr, back_s->grid, px, py);
5052 grid_reader_cursor_left(&gr, 0);
5053 if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) {
5054 grid_reader_get_cursor(&gr, &px, &py);
5055 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px,
5056 py);
5060 static void
5061 window_copy_cursor_jump_to(struct window_mode_entry *wme)
5063 struct window_copy_mode_data *data = wme->data;
5064 struct screen *back_s = data->backing;
5065 struct grid_reader gr;
5066 u_int px, py, oldy, hsize;
5068 px = data->cx + 2;
5069 hsize = screen_hsize(back_s);
5070 py = hsize + data->cy - data->oy;
5071 oldy = data->cy;
5073 grid_reader_start(&gr, back_s->grid, px, py);
5074 if (grid_reader_cursor_jump(&gr, data->jumpchar)) {
5075 grid_reader_cursor_left(&gr, 1);
5076 grid_reader_get_cursor(&gr, &px, &py);
5077 window_copy_acquire_cursor_down(wme, hsize,
5078 screen_size_y(back_s), data->oy, oldy, px, py, 0);
5082 static void
5083 window_copy_cursor_jump_to_back(struct window_mode_entry *wme)
5085 struct window_copy_mode_data *data = wme->data;
5086 struct screen *back_s = data->backing;
5087 struct grid_reader gr;
5088 u_int px, py, oldy, hsize;
5090 px = data->cx;
5091 hsize = screen_hsize(back_s);
5092 py = hsize + data->cy - data->oy;
5093 oldy = data->cy;
5095 grid_reader_start(&gr, back_s->grid, px, py);
5096 grid_reader_cursor_left(&gr, 0);
5097 grid_reader_cursor_left(&gr, 0);
5098 if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) {
5099 grid_reader_cursor_right(&gr, 1, 0);
5100 grid_reader_get_cursor(&gr, &px, &py);
5101 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px,
5102 py);
5106 static void
5107 window_copy_cursor_next_word(struct window_mode_entry *wme,
5108 const char *separators)
5110 struct window_copy_mode_data *data = wme->data;
5111 struct screen *back_s = data->backing;
5112 struct grid_reader gr;
5113 u_int px, py, oldy, hsize;
5115 px = data->cx;
5116 hsize = screen_hsize(back_s);
5117 py = hsize + data->cy - data->oy;
5118 oldy = data->cy;
5120 grid_reader_start(&gr, back_s->grid, px, py);
5121 grid_reader_cursor_next_word(&gr, separators);
5122 grid_reader_get_cursor(&gr, &px, &py);
5123 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
5124 data->oy, oldy, px, py, 0);
5127 /* Compute the next place where a word ends. */
5128 static void
5129 window_copy_cursor_next_word_end_pos(struct window_mode_entry *wme,
5130 const char *separators, u_int *ppx, u_int *ppy)
5132 struct window_pane *wp = wme->wp;
5133 struct window_copy_mode_data *data = wme->data;
5134 struct options *oo = wp->window->options;
5135 struct screen *back_s = data->backing;
5136 struct grid_reader gr;
5137 u_int px, py, hsize;
5139 px = data->cx;
5140 hsize = screen_hsize(back_s);
5141 py = hsize + data->cy - data->oy;
5143 grid_reader_start(&gr, back_s->grid, px, py);
5144 if (options_get_number(oo, "mode-keys") == MODEKEY_VI) {
5145 if (!grid_reader_in_set(&gr, WHITESPACE))
5146 grid_reader_cursor_right(&gr, 0, 0);
5147 grid_reader_cursor_next_word_end(&gr, separators);
5148 grid_reader_cursor_left(&gr, 1);
5149 } else
5150 grid_reader_cursor_next_word_end(&gr, separators);
5151 grid_reader_get_cursor(&gr, &px, &py);
5152 *ppx = px;
5153 *ppy = py;
5156 /* Move to the next place where a word ends. */
5157 static void
5158 window_copy_cursor_next_word_end(struct window_mode_entry *wme,
5159 const char *separators, int no_reset)
5161 struct window_pane *wp = wme->wp;
5162 struct window_copy_mode_data *data = wme->data;
5163 struct options *oo = wp->window->options;
5164 struct screen *back_s = data->backing;
5165 struct grid_reader gr;
5166 u_int px, py, oldy, hsize;
5168 px = data->cx;
5169 hsize = screen_hsize(back_s);
5170 py = hsize + data->cy - data->oy;
5171 oldy = data->cy;
5173 grid_reader_start(&gr, back_s->grid, px, py);
5174 if (options_get_number(oo, "mode-keys") == MODEKEY_VI) {
5175 if (!grid_reader_in_set(&gr, WHITESPACE))
5176 grid_reader_cursor_right(&gr, 0, 0);
5177 grid_reader_cursor_next_word_end(&gr, separators);
5178 grid_reader_cursor_left(&gr, 1);
5179 } else
5180 grid_reader_cursor_next_word_end(&gr, separators);
5181 grid_reader_get_cursor(&gr, &px, &py);
5182 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
5183 data->oy, oldy, px, py, no_reset);
5186 /* Compute the previous place where a word begins. */
5187 static void
5188 window_copy_cursor_previous_word_pos(struct window_mode_entry *wme,
5189 const char *separators, u_int *ppx, u_int *ppy)
5191 struct window_copy_mode_data *data = wme->data;
5192 struct screen *back_s = data->backing;
5193 struct grid_reader gr;
5194 u_int px, py, hsize;
5196 px = data->cx;
5197 hsize = screen_hsize(back_s);
5198 py = hsize + data->cy - data->oy;
5200 grid_reader_start(&gr, back_s->grid, px, py);
5201 grid_reader_cursor_previous_word(&gr, separators, /* already= */ 0,
5202 /* stop_at_eol= */ 1);
5203 grid_reader_get_cursor(&gr, &px, &py);
5204 *ppx = px;
5205 *ppy = py;
5208 /* Move to the previous place where a word begins. */
5209 static void
5210 window_copy_cursor_previous_word(struct window_mode_entry *wme,
5211 const char *separators, int already)
5213 struct window_copy_mode_data *data = wme->data;
5214 struct window *w = wme->wp->window;
5215 struct screen *back_s = data->backing;
5216 struct grid_reader gr;
5217 u_int px, py, oldy, hsize;
5218 int stop_at_eol;
5220 if (options_get_number(w->options, "mode-keys") == MODEKEY_EMACS)
5221 stop_at_eol = 1;
5222 else
5223 stop_at_eol = 0;
5225 px = data->cx;
5226 hsize = screen_hsize(back_s);
5227 py = hsize + data->cy - data->oy;
5228 oldy = data->cy;
5230 grid_reader_start(&gr, back_s->grid, px, py);
5231 grid_reader_cursor_previous_word(&gr, separators, already, stop_at_eol);
5232 grid_reader_get_cursor(&gr, &px, &py);
5233 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
5236 static void
5237 window_copy_scroll_up(struct window_mode_entry *wme, u_int ny)
5239 struct window_pane *wp = wme->wp;
5240 struct window_copy_mode_data *data = wme->data;
5241 struct screen *s = &data->screen;
5242 struct screen_write_ctx ctx;
5244 if (data->oy < ny)
5245 ny = data->oy;
5246 if (ny == 0)
5247 return;
5248 data->oy -= ny;
5250 if (data->searchmark != NULL && !data->timeout)
5251 window_copy_search_marks(wme, NULL, data->searchregex, 1);
5252 window_copy_update_selection(wme, 0, 0);
5254 screen_write_start_pane(&ctx, wp, NULL);
5255 screen_write_cursormove(&ctx, 0, 0, 0);
5256 screen_write_deleteline(&ctx, ny, 8);
5257 window_copy_write_lines(wme, &ctx, screen_size_y(s) - ny, ny);
5258 window_copy_write_line(wme, &ctx, 0);
5259 if (screen_size_y(s) > 1)
5260 window_copy_write_line(wme, &ctx, 1);
5261 if (screen_size_y(s) > 3)
5262 window_copy_write_line(wme, &ctx, screen_size_y(s) - 2);
5263 if (s->sel != NULL && screen_size_y(s) > ny)
5264 window_copy_write_line(wme, &ctx, screen_size_y(s) - ny - 1);
5265 screen_write_cursormove(&ctx, data->cx, data->cy, 0);
5266 screen_write_stop(&ctx);
5269 static void
5270 window_copy_scroll_down(struct window_mode_entry *wme, u_int ny)
5272 struct window_pane *wp = wme->wp;
5273 struct window_copy_mode_data *data = wme->data;
5274 struct screen *s = &data->screen;
5275 struct screen_write_ctx ctx;
5277 if (ny > screen_hsize(data->backing))
5278 return;
5280 if (data->oy > screen_hsize(data->backing) - ny)
5281 ny = screen_hsize(data->backing) - data->oy;
5282 if (ny == 0)
5283 return;
5284 data->oy += ny;
5286 if (data->searchmark != NULL && !data->timeout)
5287 window_copy_search_marks(wme, NULL, data->searchregex, 1);
5288 window_copy_update_selection(wme, 0, 0);
5290 screen_write_start_pane(&ctx, wp, NULL);
5291 screen_write_cursormove(&ctx, 0, 0, 0);
5292 screen_write_insertline(&ctx, ny, 8);
5293 window_copy_write_lines(wme, &ctx, 0, ny);
5294 if (s->sel != NULL && screen_size_y(s) > ny)
5295 window_copy_write_line(wme, &ctx, ny);
5296 else if (ny == 1) /* nuke position */
5297 window_copy_write_line(wme, &ctx, 1);
5298 screen_write_cursormove(&ctx, data->cx, data->cy, 0);
5299 screen_write_stop(&ctx);
5302 static void
5303 window_copy_rectangle_set(struct window_mode_entry *wme, int rectflag)
5305 struct window_copy_mode_data *data = wme->data;
5306 u_int px, py;
5308 data->rectflag = rectflag;
5310 py = screen_hsize(data->backing) + data->cy - data->oy;
5311 px = window_copy_find_length(wme, py);
5312 if (data->cx > px)
5313 window_copy_update_cursor(wme, px, data->cy);
5315 window_copy_update_selection(wme, 1, 0);
5316 window_copy_redraw_screen(wme);
5319 static void
5320 window_copy_move_mouse(struct mouse_event *m)
5322 struct window_pane *wp;
5323 struct window_mode_entry *wme;
5324 u_int x, y;
5326 wp = cmd_mouse_pane(m, NULL, NULL);
5327 if (wp == NULL)
5328 return;
5329 wme = TAILQ_FIRST(&wp->modes);
5330 if (wme == NULL)
5331 return;
5332 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode)
5333 return;
5335 if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
5336 return;
5338 window_copy_update_cursor(wme, x, y);
5341 void
5342 window_copy_start_drag(struct client *c, struct mouse_event *m)
5344 struct window_pane *wp;
5345 struct window_mode_entry *wme;
5346 struct window_copy_mode_data *data;
5347 u_int x, y, yg;
5349 if (c == NULL)
5350 return;
5352 wp = cmd_mouse_pane(m, NULL, NULL);
5353 if (wp == NULL)
5354 return;
5355 wme = TAILQ_FIRST(&wp->modes);
5356 if (wme == NULL)
5357 return;
5358 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode)
5359 return;
5361 if (cmd_mouse_at(wp, m, &x, &y, 1) != 0)
5362 return;
5364 c->tty.mouse_drag_update = window_copy_drag_update;
5365 c->tty.mouse_drag_release = window_copy_drag_release;
5367 data = wme->data;
5368 yg = screen_hsize(data->backing) + y - data->oy;
5369 if (x < data->selrx || x > data->endselrx || yg != data->selry)
5370 data->selflag = SEL_CHAR;
5371 switch (data->selflag) {
5372 case SEL_WORD:
5373 if (data->separators != NULL) {
5374 window_copy_update_cursor(wme, x, y);
5375 window_copy_cursor_previous_word_pos(wme,
5376 data->separators, &x, &y);
5377 y -= screen_hsize(data->backing) - data->oy;
5379 window_copy_update_cursor(wme, x, y);
5380 break;
5381 case SEL_LINE:
5382 window_copy_update_cursor(wme, 0, y);
5383 break;
5384 case SEL_CHAR:
5385 window_copy_update_cursor(wme, x, y);
5386 window_copy_start_selection(wme);
5387 break;
5390 window_copy_redraw_screen(wme);
5391 window_copy_drag_update(c, m);
5394 static void
5395 window_copy_drag_update(struct client *c, struct mouse_event *m)
5397 struct window_pane *wp;
5398 struct window_mode_entry *wme;
5399 struct window_copy_mode_data *data;
5400 u_int x, y, old_cx, old_cy;
5401 struct timeval tv = {
5402 .tv_usec = WINDOW_COPY_DRAG_REPEAT_TIME
5405 if (c == NULL)
5406 return;
5408 wp = cmd_mouse_pane(m, NULL, NULL);
5409 if (wp == NULL)
5410 return;
5411 wme = TAILQ_FIRST(&wp->modes);
5412 if (wme == NULL)
5413 return;
5414 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode)
5415 return;
5417 data = wme->data;
5418 evtimer_del(&data->dragtimer);
5420 if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
5421 return;
5422 old_cx = data->cx;
5423 old_cy = data->cy;
5425 window_copy_update_cursor(wme, x, y);
5426 if (window_copy_update_selection(wme, 1, 0))
5427 window_copy_redraw_selection(wme, old_cy);
5428 if (old_cy != data->cy || old_cx == data->cx) {
5429 if (y == 0) {
5430 evtimer_add(&data->dragtimer, &tv);
5431 window_copy_cursor_up(wme, 1);
5432 } else if (y == screen_size_y(&data->screen) - 1) {
5433 evtimer_add(&data->dragtimer, &tv);
5434 window_copy_cursor_down(wme, 1);
5439 static void
5440 window_copy_drag_release(struct client *c, struct mouse_event *m)
5442 struct window_pane *wp;
5443 struct window_mode_entry *wme;
5444 struct window_copy_mode_data *data;
5446 if (c == NULL)
5447 return;
5449 wp = cmd_mouse_pane(m, NULL, NULL);
5450 if (wp == NULL)
5451 return;
5452 wme = TAILQ_FIRST(&wp->modes);
5453 if (wme == NULL)
5454 return;
5455 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode)
5456 return;
5458 data = wme->data;
5459 evtimer_del(&data->dragtimer);
5462 static void
5463 window_copy_jump_to_mark(struct window_mode_entry *wme)
5465 struct window_copy_mode_data *data = wme->data;
5466 u_int tmx, tmy;
5468 tmx = data->cx;
5469 tmy = screen_hsize(data->backing) + data->cy - data->oy;
5470 data->cx = data->mx;
5471 if (data->my < screen_hsize(data->backing)) {
5472 data->cy = 0;
5473 data->oy = screen_hsize(data->backing) - data->my;
5474 } else {
5475 data->cy = data->my - screen_hsize(data->backing);
5476 data->oy = 0;
5478 data->mx = tmx;
5479 data->my = tmy;
5480 data->showmark = 1;
5481 window_copy_update_selection(wme, 0, 0);
5482 window_copy_redraw_screen(wme);
5485 /* Scroll up if the cursor went off the visible screen. */
5486 static void
5487 window_copy_acquire_cursor_up(struct window_mode_entry *wme, u_int hsize,
5488 u_int oy, u_int oldy, u_int px, u_int py)
5490 u_int cy, yy, ny, nd;
5492 yy = hsize - oy;
5493 if (py < yy) {
5494 ny = yy - py;
5495 cy = 0;
5496 nd = 1;
5497 } else {
5498 ny = 0;
5499 cy = py - yy;
5500 nd = oldy - cy + 1;
5502 while (ny > 0) {
5503 window_copy_cursor_up(wme, 1);
5504 ny--;
5506 window_copy_update_cursor(wme, px, cy);
5507 if (window_copy_update_selection(wme, 1, 0))
5508 window_copy_redraw_lines(wme, cy, nd);
5511 /* Scroll down if the cursor went off the visible screen. */
5512 static void
5513 window_copy_acquire_cursor_down(struct window_mode_entry *wme, u_int hsize,
5514 u_int sy, u_int oy, u_int oldy, u_int px, u_int py, int no_reset)
5516 u_int cy, yy, ny, nd;
5518 cy = py - hsize + oy;
5519 yy = sy - 1;
5520 if (cy > yy) {
5521 ny = cy - yy;
5522 oldy = yy;
5523 nd = 1;
5524 } else {
5525 ny = 0;
5526 nd = cy - oldy + 1;
5528 while (ny > 0) {
5529 window_copy_cursor_down(wme, 1);
5530 ny--;
5532 if (cy > yy)
5533 window_copy_update_cursor(wme, px, yy);
5534 else
5535 window_copy_update_cursor(wme, px, cy);
5536 if (window_copy_update_selection(wme, 1, no_reset))
5537 window_copy_redraw_lines(wme, oldy, nd);