Fix save-buffer documentation - it writes to stdout. From Ilya Grigoriev.
[tmux-openbsd.git] / window-copy.c
blob36b9f89c997b4531ded396d2fb98c0231132b5e9
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_scroll1(struct window_mode_entry *,
44 struct window_pane *wp, int, u_int, int);
45 static void window_copy_pageup1(struct window_mode_entry *, int);
46 static int window_copy_pagedown1(struct window_mode_entry *, int, int);
47 static void window_copy_next_paragraph(struct window_mode_entry *);
48 static void window_copy_previous_paragraph(struct window_mode_entry *);
49 static void window_copy_redraw_selection(struct window_mode_entry *, u_int);
50 static void window_copy_redraw_lines(struct window_mode_entry *, u_int,
51 u_int);
52 static void window_copy_redraw_screen(struct window_mode_entry *);
53 static void window_copy_write_line(struct window_mode_entry *,
54 struct screen_write_ctx *, u_int);
55 static void window_copy_write_lines(struct window_mode_entry *,
56 struct screen_write_ctx *, u_int, u_int);
57 static char *window_copy_match_at_cursor(struct window_copy_mode_data *);
58 static void window_copy_scroll_to(struct window_mode_entry *, u_int, u_int,
59 int);
60 static int window_copy_search_compare(struct grid *, u_int, u_int,
61 struct grid *, u_int, int);
62 static int window_copy_search_lr(struct grid *, struct grid *, u_int *,
63 u_int, u_int, u_int, int);
64 static int window_copy_search_rl(struct grid *, struct grid *, u_int *,
65 u_int, u_int, u_int, int);
66 static int window_copy_last_regex(struct grid *, u_int, u_int, u_int,
67 u_int, u_int *, u_int *, const char *, const regex_t *,
68 int);
69 static int window_copy_search_mark_at(struct window_copy_mode_data *,
70 u_int, u_int, u_int *);
71 static char *window_copy_stringify(struct grid *, u_int, u_int, u_int,
72 char *, u_int *);
73 static void window_copy_cstrtocellpos(struct grid *, u_int, u_int *,
74 u_int *, const char *);
75 static int window_copy_search_marks(struct window_mode_entry *,
76 struct screen *, int, int);
77 static void window_copy_clear_marks(struct window_mode_entry *);
78 static int window_copy_is_lowercase(const char *);
79 static void window_copy_search_back_overlap(struct grid *, regex_t *,
80 u_int *, u_int *, u_int *, u_int);
81 static int window_copy_search_jump(struct window_mode_entry *,
82 struct grid *, struct grid *, u_int, u_int, u_int, int, int,
83 int, int);
84 static int window_copy_search(struct window_mode_entry *, int, int);
85 static int window_copy_search_up(struct window_mode_entry *, int);
86 static int window_copy_search_down(struct window_mode_entry *, int);
87 static void window_copy_goto_line(struct window_mode_entry *, const char *);
88 static void window_copy_update_cursor(struct window_mode_entry *, u_int,
89 u_int);
90 static void window_copy_start_selection(struct window_mode_entry *);
91 static int window_copy_adjust_selection(struct window_mode_entry *,
92 u_int *, u_int *);
93 static int window_copy_set_selection(struct window_mode_entry *, int, int);
94 static int window_copy_update_selection(struct window_mode_entry *, int,
95 int);
96 static void window_copy_synchronize_cursor(struct window_mode_entry *, int);
97 static void *window_copy_get_selection(struct window_mode_entry *, size_t *);
98 static void window_copy_copy_buffer(struct window_mode_entry *,
99 const char *, void *, size_t, int, int);
100 static void window_copy_pipe(struct window_mode_entry *,
101 struct session *, const char *);
102 static void window_copy_copy_pipe(struct window_mode_entry *,
103 struct session *, const char *, const char *,
104 int, int);
105 static void window_copy_copy_selection(struct window_mode_entry *,
106 const char *, int, int);
107 static void window_copy_append_selection(struct window_mode_entry *);
108 static void window_copy_clear_selection(struct window_mode_entry *);
109 static void window_copy_copy_line(struct window_mode_entry *, char **,
110 size_t *, u_int, u_int, u_int);
111 static int window_copy_in_set(struct window_mode_entry *, u_int, u_int,
112 const char *);
113 static u_int window_copy_find_length(struct window_mode_entry *, u_int);
114 static void window_copy_cursor_start_of_line(struct window_mode_entry *);
115 static void window_copy_cursor_back_to_indentation(
116 struct window_mode_entry *);
117 static void window_copy_cursor_end_of_line(struct window_mode_entry *);
118 static void window_copy_other_end(struct window_mode_entry *);
119 static void window_copy_cursor_left(struct window_mode_entry *);
120 static void window_copy_cursor_right(struct window_mode_entry *, int);
121 static void window_copy_cursor_up(struct window_mode_entry *, int);
122 static void window_copy_cursor_down(struct window_mode_entry *, int);
123 static void window_copy_cursor_jump(struct window_mode_entry *);
124 static void window_copy_cursor_jump_back(struct window_mode_entry *);
125 static void window_copy_cursor_jump_to(struct window_mode_entry *);
126 static void window_copy_cursor_jump_to_back(struct window_mode_entry *);
127 static void window_copy_cursor_next_word(struct window_mode_entry *,
128 const char *);
129 static void window_copy_cursor_next_word_end_pos(struct window_mode_entry *,
130 const char *, u_int *, u_int *);
131 static void window_copy_cursor_next_word_end(struct window_mode_entry *,
132 const char *, int);
133 static void window_copy_cursor_previous_word_pos(struct window_mode_entry *,
134 const char *, u_int *, u_int *);
135 static void window_copy_cursor_previous_word(struct window_mode_entry *,
136 const char *, int);
137 static void window_copy_cursor_prompt(struct window_mode_entry *, int,
138 int);
139 static void window_copy_scroll_up(struct window_mode_entry *, u_int);
140 static void window_copy_scroll_down(struct window_mode_entry *, u_int);
141 static void window_copy_rectangle_set(struct window_mode_entry *, int);
142 static void window_copy_move_mouse(struct mouse_event *);
143 static void window_copy_drag_update(struct client *, struct mouse_event *);
144 static void window_copy_drag_release(struct client *, struct mouse_event *);
145 static void window_copy_jump_to_mark(struct window_mode_entry *);
146 static void window_copy_acquire_cursor_up(struct window_mode_entry *,
147 u_int, u_int, u_int, u_int, u_int);
148 static void window_copy_acquire_cursor_down(struct window_mode_entry *,
149 u_int, u_int, u_int, u_int, u_int, u_int, int);
150 static u_int window_copy_clip_width(u_int, u_int, u_int, u_int);
151 static u_int window_copy_search_mark_match(struct window_copy_mode_data *,
152 u_int , u_int, u_int, int);
154 const struct window_mode window_copy_mode = {
155 .name = "copy-mode",
157 .init = window_copy_init,
158 .free = window_copy_free,
159 .resize = window_copy_resize,
160 .key_table = window_copy_key_table,
161 .command = window_copy_command,
162 .formats = window_copy_formats,
165 const struct window_mode window_view_mode = {
166 .name = "view-mode",
168 .init = window_copy_view_init,
169 .free = window_copy_free,
170 .resize = window_copy_resize,
171 .key_table = window_copy_key_table,
172 .command = window_copy_command,
173 .formats = window_copy_formats,
176 enum {
177 WINDOW_COPY_OFF,
178 WINDOW_COPY_SEARCHUP,
179 WINDOW_COPY_SEARCHDOWN,
180 WINDOW_COPY_JUMPFORWARD,
181 WINDOW_COPY_JUMPBACKWARD,
182 WINDOW_COPY_JUMPTOFORWARD,
183 WINDOW_COPY_JUMPTOBACKWARD,
186 enum {
187 WINDOW_COPY_REL_POS_ABOVE,
188 WINDOW_COPY_REL_POS_ON_SCREEN,
189 WINDOW_COPY_REL_POS_BELOW,
192 enum window_copy_cmd_action {
193 WINDOW_COPY_CMD_NOTHING,
194 WINDOW_COPY_CMD_REDRAW,
195 WINDOW_COPY_CMD_CANCEL,
198 enum window_copy_cmd_clear {
199 WINDOW_COPY_CMD_CLEAR_ALWAYS,
200 WINDOW_COPY_CMD_CLEAR_NEVER,
201 WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
204 struct window_copy_cmd_state {
205 struct window_mode_entry *wme;
206 struct args *args;
207 struct args *wargs;
208 struct mouse_event *m;
210 struct client *c;
211 struct session *s;
212 struct winlink *wl;
216 * Copy mode's visible screen (the "screen" field) is filled from one of two
217 * sources: the original contents of the pane (used when we actually enter via
218 * the "copy-mode" command, to copy the contents of the current pane), or else
219 * a series of lines containing the output from an output-writing tmux command
220 * (such as any of the "show-*" or "list-*" commands).
222 * In either case, the full content of the copy-mode grid is pointed at by the
223 * "backing" field, and is copied into "screen" as needed (that is, when
224 * scrolling occurs). When copy-mode is backed by a pane, backing points
225 * directly at that pane's screen structure (&wp->base); when backed by a list
226 * of output-lines from a command, it points at a newly-allocated screen
227 * structure (which is deallocated when the mode ends).
229 struct window_copy_mode_data {
230 struct screen screen;
232 struct screen *backing;
233 int backing_written; /* backing display started */
234 struct screen *writing;
235 struct input_ctx *ictx;
237 int viewmode; /* view mode entered */
239 u_int oy; /* number of lines scrolled up */
241 u_int selx; /* beginning of selection */
242 u_int sely;
244 u_int endselx; /* end of selection */
245 u_int endsely;
247 enum {
248 CURSORDRAG_NONE, /* selection is independent of cursor */
249 CURSORDRAG_ENDSEL, /* end is synchronized with cursor */
250 CURSORDRAG_SEL, /* start is synchronized with cursor */
251 } cursordrag;
253 int modekeys;
254 enum {
255 LINE_SEL_NONE,
256 LINE_SEL_LEFT_RIGHT,
257 LINE_SEL_RIGHT_LEFT,
258 } lineflag; /* line selection mode */
259 int rectflag; /* in rectangle copy mode? */
260 int scroll_exit; /* exit on scroll to end? */
261 int hide_position; /* hide position marker */
263 enum {
264 SEL_CHAR, /* select one char at a time */
265 SEL_WORD, /* select one word at a time */
266 SEL_LINE, /* select one line at a time */
267 } selflag;
269 const char *separators; /* word separators */
271 u_int dx; /* drag start position */
272 u_int dy;
274 u_int selrx; /* selection reset positions */
275 u_int selry;
276 u_int endselrx;
277 u_int endselry;
279 u_int cx;
280 u_int cy;
282 u_int lastcx; /* position in last line w/ content */
283 u_int lastsx; /* size of last line w/ content */
285 u_int mx; /* mark position */
286 u_int my;
287 int showmark;
289 int searchtype;
290 int searchdirection;
291 int searchregex;
292 char *searchstr;
293 u_char *searchmark;
294 int searchcount;
295 int searchmore;
296 int searchall;
297 int searchx;
298 int searchy;
299 int searcho;
300 u_char searchgen;
302 int timeout; /* search has timed out */
303 #define WINDOW_COPY_SEARCH_TIMEOUT 10000
304 #define WINDOW_COPY_SEARCH_ALL_TIMEOUT 200
305 #define WINDOW_COPY_SEARCH_MAX_LINE 2000
307 int jumptype;
308 struct utf8_data *jumpchar;
310 struct event dragtimer;
311 #define WINDOW_COPY_DRAG_REPEAT_TIME 50000
314 static void
315 window_copy_scroll_timer(__unused int fd, __unused short events, void *arg)
317 struct window_mode_entry *wme = arg;
318 struct window_pane *wp = wme->wp;
319 struct window_copy_mode_data *data = wme->data;
320 struct timeval tv = {
321 .tv_usec = WINDOW_COPY_DRAG_REPEAT_TIME
324 evtimer_del(&data->dragtimer);
326 if (TAILQ_FIRST(&wp->modes) != wme)
327 return;
329 if (data->cy == 0) {
330 evtimer_add(&data->dragtimer, &tv);
331 window_copy_cursor_up(wme, 1);
332 } else if (data->cy == screen_size_y(&data->screen) - 1) {
333 evtimer_add(&data->dragtimer, &tv);
334 window_copy_cursor_down(wme, 1);
338 static struct screen *
339 window_copy_clone_screen(struct screen *src, struct screen *hint, u_int *cx,
340 u_int *cy, int trim)
342 struct screen *dst;
343 const struct grid_line *gl;
344 u_int sy, wx, wy;
345 int reflow;
347 dst = xcalloc(1, sizeof *dst);
349 sy = screen_hsize(src) + screen_size_y(src);
350 if (trim) {
351 while (sy > screen_hsize(src)) {
352 gl = grid_peek_line(src->grid, sy - 1);
353 if (gl->cellused != 0)
354 break;
355 sy--;
358 log_debug("%s: target screen is %ux%u, source %ux%u", __func__,
359 screen_size_x(src), sy, screen_size_x(hint),
360 screen_hsize(src) + screen_size_y(src));
361 screen_init(dst, screen_size_x(src), sy, screen_hlimit(src));
364 * Ensure history is on for the backing grid so lines are not deleted
365 * during resizing.
367 dst->grid->flags |= GRID_HISTORY;
368 grid_duplicate_lines(dst->grid, 0, src->grid, 0, sy);
370 dst->grid->sy = sy - screen_hsize(src);
371 dst->grid->hsize = screen_hsize(src);
372 dst->grid->hscrolled = src->grid->hscrolled;
373 if (src->cy > dst->grid->sy - 1) {
374 dst->cx = 0;
375 dst->cy = dst->grid->sy - 1;
376 } else {
377 dst->cx = src->cx;
378 dst->cy = src->cy;
381 if (cx != NULL && cy != NULL) {
382 *cx = dst->cx;
383 *cy = screen_hsize(dst) + dst->cy;
384 reflow = (screen_size_x(hint) != screen_size_x(dst));
386 else
387 reflow = 0;
388 if (reflow)
389 grid_wrap_position(dst->grid, *cx, *cy, &wx, &wy);
390 screen_resize_cursor(dst, screen_size_x(hint), screen_size_y(hint), 1,
391 0, 0);
392 if (reflow)
393 grid_unwrap_position(dst->grid, cx, cy, wx, wy);
395 return (dst);
398 static struct window_copy_mode_data *
399 window_copy_common_init(struct window_mode_entry *wme)
401 struct window_pane *wp = wme->wp;
402 struct window_copy_mode_data *data;
403 struct screen *base = &wp->base;
405 wme->data = data = xcalloc(1, sizeof *data);
407 data->cursordrag = CURSORDRAG_NONE;
408 data->lineflag = LINE_SEL_NONE;
409 data->selflag = SEL_CHAR;
411 if (wp->searchstr != NULL) {
412 data->searchtype = WINDOW_COPY_SEARCHUP;
413 data->searchregex = wp->searchregex;
414 data->searchstr = xstrdup(wp->searchstr);
415 } else {
416 data->searchtype = WINDOW_COPY_OFF;
417 data->searchregex = 0;
418 data->searchstr = NULL;
420 data->searchx = data->searchy = data->searcho = -1;
421 data->searchall = 1;
423 data->jumptype = WINDOW_COPY_OFF;
424 data->jumpchar = NULL;
426 screen_init(&data->screen, screen_size_x(base), screen_size_y(base), 0);
427 screen_set_default_cursor(&data->screen, global_w_options);
428 data->modekeys = options_get_number(wp->window->options, "mode-keys");
430 evtimer_set(&data->dragtimer, window_copy_scroll_timer, wme);
432 return (data);
435 static struct screen *
436 window_copy_init(struct window_mode_entry *wme,
437 __unused struct cmd_find_state *fs, struct args *args)
439 struct window_pane *wp = wme->swp;
440 struct window_copy_mode_data *data;
441 struct screen *base = &wp->base;
442 struct screen_write_ctx ctx;
443 u_int i, cx, cy;
445 data = window_copy_common_init(wme);
446 data->backing = window_copy_clone_screen(base, &data->screen, &cx, &cy,
447 wme->swp != wme->wp);
449 data->cx = cx;
450 if (cy < screen_hsize(data->backing)) {
451 data->cy = 0;
452 data->oy = screen_hsize(data->backing) - cy;
453 } else {
454 data->cy = cy - screen_hsize(data->backing);
455 data->oy = 0;
458 data->scroll_exit = args_has(args, 'e');
459 data->hide_position = args_has(args, 'H');
461 if (base->hyperlinks != NULL)
462 data->screen.hyperlinks = hyperlinks_copy(base->hyperlinks);
463 data->screen.cx = data->cx;
464 data->screen.cy = data->cy;
465 data->mx = data->cx;
466 data->my = screen_hsize(data->backing) + data->cy - data->oy;
467 data->showmark = 0;
469 screen_write_start(&ctx, &data->screen);
470 for (i = 0; i < screen_size_y(&data->screen); i++)
471 window_copy_write_line(wme, &ctx, i);
472 screen_write_cursormove(&ctx, data->cx, data->cy, 0);
473 screen_write_stop(&ctx);
475 return (&data->screen);
478 static struct screen *
479 window_copy_view_init(struct window_mode_entry *wme,
480 __unused struct cmd_find_state *fs, __unused struct args *args)
482 struct window_pane *wp = wme->wp;
483 struct window_copy_mode_data *data;
484 struct screen *base = &wp->base;
485 u_int sx = screen_size_x(base);
487 data = window_copy_common_init(wme);
488 data->viewmode = 1;
490 data->backing = xmalloc(sizeof *data->backing);
491 screen_init(data->backing, sx, screen_size_y(base), UINT_MAX);
492 data->writing = xmalloc(sizeof *data->writing);
493 screen_init(data->writing, sx, screen_size_y(base), 0);
494 data->ictx = input_init(NULL, NULL, NULL);
495 data->mx = data->cx;
496 data->my = screen_hsize(data->backing) + data->cy - data->oy;
497 data->showmark = 0;
499 return (&data->screen);
502 static void
503 window_copy_free(struct window_mode_entry *wme)
505 struct window_copy_mode_data *data = wme->data;
507 evtimer_del(&data->dragtimer);
509 free(data->searchmark);
510 free(data->searchstr);
511 free(data->jumpchar);
513 if (data->writing != NULL) {
514 screen_free(data->writing);
515 free(data->writing);
517 if (data->ictx != NULL)
518 input_free(data->ictx);
519 screen_free(data->backing);
520 free(data->backing);
522 screen_free(&data->screen);
523 free(data);
526 void
527 window_copy_add(struct window_pane *wp, int parse, const char *fmt, ...)
529 va_list ap;
531 va_start(ap, fmt);
532 window_copy_vadd(wp, parse, fmt, ap);
533 va_end(ap);
536 static void
537 window_copy_init_ctx_cb(__unused struct screen_write_ctx *ctx,
538 struct tty_ctx *ttyctx)
540 memcpy(&ttyctx->defaults, &grid_default_cell, sizeof ttyctx->defaults);
541 ttyctx->palette = NULL;
542 ttyctx->redraw_cb = NULL;
543 ttyctx->set_client_cb = NULL;
544 ttyctx->arg = NULL;
547 void
548 window_copy_vadd(struct window_pane *wp, int parse, const char *fmt, va_list ap)
550 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
551 struct window_copy_mode_data *data = wme->data;
552 struct screen *backing = data->backing;
553 struct screen *writing = data->writing;
554 struct screen_write_ctx writing_ctx, backing_ctx, ctx;
555 struct grid_cell gc;
556 u_int old_hsize, old_cy;
557 u_int sx = screen_size_x(backing);
558 char *text;
560 if (parse) {
561 vasprintf(&text, fmt, ap);
562 screen_write_start(&writing_ctx, writing);
563 screen_write_reset(&writing_ctx);
564 input_parse_screen(data->ictx, writing, window_copy_init_ctx_cb,
565 data, text, strlen(text));
566 free(text);
569 old_hsize = screen_hsize(data->backing);
570 screen_write_start(&backing_ctx, backing);
571 if (data->backing_written) {
573 * On the second or later line, do a CRLF before writing
574 * (so it's on a new line).
576 screen_write_carriagereturn(&backing_ctx);
577 screen_write_linefeed(&backing_ctx, 0, 8);
578 } else
579 data->backing_written = 1;
580 old_cy = backing->cy;
581 if (parse)
582 screen_write_fast_copy(&backing_ctx, writing, 0, 0, sx, 1);
583 else {
584 memcpy(&gc, &grid_default_cell, sizeof gc);
585 screen_write_vnputs(&backing_ctx, 0, &gc, fmt, ap);
587 screen_write_stop(&backing_ctx);
589 data->oy += screen_hsize(data->backing) - old_hsize;
591 screen_write_start_pane(&ctx, wp, &data->screen);
594 * If the history has changed, draw the top line.
595 * (If there's any history at all, it has changed.)
597 if (screen_hsize(data->backing))
598 window_copy_redraw_lines(wme, 0, 1);
600 /* Write the new lines. */
601 window_copy_redraw_lines(wme, old_cy, backing->cy - old_cy + 1);
603 screen_write_stop(&ctx);
606 void
607 window_copy_scroll(struct window_pane *wp, int sl_mpos, u_int my,
608 int scroll_exit)
610 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
612 if (wme != NULL) {
613 window_set_active_pane(wp->window, wp, 0);
614 window_copy_scroll1(wme, wp, sl_mpos, my, scroll_exit);
618 static void
619 window_copy_scroll1(struct window_mode_entry *wme, struct window_pane *wp,
620 int sl_mpos, u_int my, int scroll_exit)
622 struct window_copy_mode_data *data = wme->data;
623 u_int ox, oy, px, py, n, offset, size;
624 u_int new_offset;
625 u_int slider_height = wp->sb_slider_h;
626 u_int sb_height = wp->sy, sb_top = wp->yoff;
627 u_int sy = screen_size_y(data->backing);
628 int new_slider_y, delta;
631 * sl_mpos is where in the slider the user is dragging, mouse is
632 * dragging this y point relative to top of slider.
634 if (my <= sb_top + sl_mpos) {
635 /* Slider banged into top. */
636 new_slider_y = sb_top - wp->yoff;
637 } else if (my - sl_mpos > sb_top + sb_height - slider_height) {
638 /* Slider banged into bottom. */
639 new_slider_y = sb_top - wp->yoff + (sb_height - slider_height);
640 } else {
641 /* Slider is somewhere in the middle. */
642 new_slider_y = my - wp->yoff - sl_mpos + 1;
645 if (TAILQ_FIRST(&wp->modes) == NULL ||
646 window_copy_get_current_offset(wp, &offset, &size) == 0)
647 return;
650 * See screen_redraw_draw_pane_scrollbar - this is the inverse of the
651 * formula used there.
653 new_offset = new_slider_y * ((float)(size + sb_height) / sb_height);
654 delta = (int)offset - new_offset;
657 * Move pane view around based on delta relative to the cursor,
658 * maintaining the selection.
660 oy = screen_hsize(data->backing) + data->cy - data->oy;
661 ox = window_copy_find_length(wme, oy);
663 if (data->cx != ox) {
664 data->lastcx = data->cx;
665 data->lastsx = ox;
667 data->cx = data->lastcx;
669 if (delta >= 0) {
670 n = (u_int)delta;
671 if (data->oy + n > screen_hsize(data->backing)) {
672 data->oy = screen_hsize(data->backing);
673 if (data->cy < n)
674 data->cy = 0;
675 else
676 data->cy -= n;
677 } else
678 data->oy += n;
679 } else {
680 n = (u_int)-delta;
681 if (data->oy < n) {
682 data->oy = 0;
683 if (data->cy + (n - data->oy) >= sy)
684 data->cy = sy - 1;
685 else
686 data->cy += n - data->oy;
687 } else
688 data->oy -= n;
691 /* Don't also drag tail when dragging a scrollbar, it looks weird. */
692 data->cursordrag = CURSORDRAG_NONE;
694 if (data->screen.sel == NULL || !data->rectflag) {
695 py = screen_hsize(data->backing) + data->cy - data->oy;
696 px = window_copy_find_length(wme, py);
697 if ((data->cx >= data->lastsx && data->cx != px) ||
698 data->cx > px)
699 window_copy_cursor_end_of_line(wme);
702 if (scroll_exit && data->oy == 0) {
703 window_pane_reset_mode(wp);
704 return;
707 if (data->searchmark != NULL && !data->timeout)
708 window_copy_search_marks(wme, NULL, data->searchregex, 1);
709 window_copy_update_selection(wme, 1, 0);
710 window_copy_redraw_screen(wme);
713 void
714 window_copy_pageup(struct window_pane *wp, int half_page)
716 window_copy_pageup1(TAILQ_FIRST(&wp->modes), half_page);
719 static void
720 window_copy_pageup1(struct window_mode_entry *wme, int half_page)
722 struct window_copy_mode_data *data = wme->data;
723 struct screen *s = &data->screen;
724 u_int n, ox, oy, px, py;
726 oy = screen_hsize(data->backing) + data->cy - data->oy;
727 ox = window_copy_find_length(wme, oy);
729 if (data->cx != ox) {
730 data->lastcx = data->cx;
731 data->lastsx = ox;
733 data->cx = data->lastcx;
735 n = 1;
736 if (screen_size_y(s) > 2) {
737 if (half_page)
738 n = screen_size_y(s) / 2;
739 else
740 n = screen_size_y(s) - 2;
743 if (data->oy + n > screen_hsize(data->backing)) {
744 data->oy = screen_hsize(data->backing);
745 if (data->cy < n)
746 data->cy = 0;
747 else
748 data->cy -= n;
749 } else
750 data->oy += n;
752 if (data->screen.sel == NULL || !data->rectflag) {
753 py = screen_hsize(data->backing) + data->cy - data->oy;
754 px = window_copy_find_length(wme, py);
755 if ((data->cx >= data->lastsx && data->cx != px) ||
756 data->cx > px)
757 window_copy_cursor_end_of_line(wme);
760 if (data->searchmark != NULL && !data->timeout)
761 window_copy_search_marks(wme, NULL, data->searchregex, 1);
762 window_copy_update_selection(wme, 1, 0);
763 window_copy_redraw_screen(wme);
766 void
767 window_copy_pagedown(struct window_pane *wp, int half_page, int scroll_exit)
769 if (window_copy_pagedown1(TAILQ_FIRST(&wp->modes), half_page,
770 scroll_exit)) {
771 window_pane_reset_mode(wp);
772 return;
776 static int
777 window_copy_pagedown1(struct window_mode_entry *wme, int half_page,
778 int scroll_exit)
780 struct window_copy_mode_data *data = wme->data;
781 struct screen *s = &data->screen;
782 u_int n, ox, oy, px, py;
784 oy = screen_hsize(data->backing) + data->cy - data->oy;
785 ox = window_copy_find_length(wme, oy);
787 if (data->cx != ox) {
788 data->lastcx = data->cx;
789 data->lastsx = ox;
791 data->cx = data->lastcx;
793 n = 1;
794 if (screen_size_y(s) > 2) {
795 if (half_page)
796 n = screen_size_y(s) / 2;
797 else
798 n = screen_size_y(s) - 2;
801 if (data->oy < n) {
802 data->oy = 0;
803 if (data->cy + (n - data->oy) >= screen_size_y(data->backing))
804 data->cy = screen_size_y(data->backing) - 1;
805 else
806 data->cy += n - data->oy;
807 } else
808 data->oy -= n;
810 if (data->screen.sel == NULL || !data->rectflag) {
811 py = screen_hsize(data->backing) + data->cy - data->oy;
812 px = window_copy_find_length(wme, py);
813 if ((data->cx >= data->lastsx && data->cx != px) ||
814 data->cx > px)
815 window_copy_cursor_end_of_line(wme);
818 if (scroll_exit && data->oy == 0)
819 return (1);
820 if (data->searchmark != NULL && !data->timeout)
821 window_copy_search_marks(wme, NULL, data->searchregex, 1);
822 window_copy_update_selection(wme, 1, 0);
823 window_copy_redraw_screen(wme);
824 return (0);
827 static void
828 window_copy_previous_paragraph(struct window_mode_entry *wme)
830 struct window_copy_mode_data *data = wme->data;
831 u_int oy;
833 oy = screen_hsize(data->backing) + data->cy - data->oy;
835 while (oy > 0 && window_copy_find_length(wme, oy) == 0)
836 oy--;
838 while (oy > 0 && window_copy_find_length(wme, oy) > 0)
839 oy--;
841 window_copy_scroll_to(wme, 0, oy, 0);
844 static void
845 window_copy_next_paragraph(struct window_mode_entry *wme)
847 struct window_copy_mode_data *data = wme->data;
848 struct screen *s = &data->screen;
849 u_int maxy, ox, oy;
851 oy = screen_hsize(data->backing) + data->cy - data->oy;
852 maxy = screen_hsize(data->backing) + screen_size_y(s) - 1;
854 while (oy < maxy && window_copy_find_length(wme, oy) == 0)
855 oy++;
857 while (oy < maxy && window_copy_find_length(wme, oy) > 0)
858 oy++;
860 ox = window_copy_find_length(wme, oy);
861 window_copy_scroll_to(wme, ox, oy, 0);
864 char *
865 window_copy_get_word(struct window_pane *wp, u_int x, u_int y)
867 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
868 struct window_copy_mode_data *data = wme->data;
869 struct grid *gd = data->screen.grid;
871 return (format_grid_word(gd, x, gd->hsize + y));
874 char *
875 window_copy_get_line(struct window_pane *wp, u_int y)
877 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
878 struct window_copy_mode_data *data = wme->data;
879 struct grid *gd = data->screen.grid;
881 return (format_grid_line(gd, gd->hsize + y));
884 static void *
885 window_copy_cursor_hyperlink_cb(struct format_tree *ft)
887 struct window_pane *wp = format_get_pane(ft);
888 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
889 struct window_copy_mode_data *data = wme->data;
890 struct grid *gd = data->screen.grid;
892 return (format_grid_hyperlink(gd, data->cx, gd->hsize + data->cy,
893 &data->screen));
896 static void *
897 window_copy_cursor_word_cb(struct format_tree *ft)
899 struct window_pane *wp = format_get_pane(ft);
900 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
901 struct window_copy_mode_data *data = wme->data;
903 return (window_copy_get_word(wp, data->cx, data->cy));
906 static void *
907 window_copy_cursor_line_cb(struct format_tree *ft)
909 struct window_pane *wp = format_get_pane(ft);
910 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
911 struct window_copy_mode_data *data = wme->data;
913 return (window_copy_get_line(wp, data->cy));
916 static void *
917 window_copy_search_match_cb(struct format_tree *ft)
919 struct window_pane *wp = format_get_pane(ft);
920 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
921 struct window_copy_mode_data *data = wme->data;
923 return (window_copy_match_at_cursor(data));
926 static void
927 window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft)
929 struct window_copy_mode_data *data = wme->data;
930 u_int hsize = screen_hsize(data->backing);
931 struct grid_line *gl;
933 gl = grid_get_line(data->backing->grid, hsize - data->oy);
934 format_add(ft, "top_line_time", "%llu", (unsigned long long)gl->time);
936 format_add(ft, "scroll_position", "%d", data->oy);
937 format_add(ft, "rectangle_toggle", "%d", data->rectflag);
939 format_add(ft, "copy_cursor_x", "%d", data->cx);
940 format_add(ft, "copy_cursor_y", "%d", data->cy);
942 if (data->screen.sel != NULL) {
943 format_add(ft, "selection_start_x", "%d", data->selx);
944 format_add(ft, "selection_start_y", "%d", data->sely);
945 format_add(ft, "selection_end_x", "%d", data->endselx);
946 format_add(ft, "selection_end_y", "%d", data->endsely);
948 if (data->cursordrag != CURSORDRAG_NONE)
949 format_add(ft, "selection_active", "1");
950 else
951 format_add(ft, "selection_active", "0");
952 if (data->endselx != data->selx || data->endsely != data->sely)
953 format_add(ft, "selection_present", "1");
954 else
955 format_add(ft, "selection_present", "0");
956 } else {
957 format_add(ft, "selection_active", "0");
958 format_add(ft, "selection_present", "0");
961 format_add(ft, "search_present", "%d", data->searchmark != NULL);
962 format_add(ft, "search_timed_out", "%d", data->timeout);
963 if (data->searchcount != -1) {
964 format_add(ft, "search_count", "%d", data->searchcount);
965 format_add(ft, "search_count_partial", "%d", data->searchmore);
967 format_add_cb(ft, "search_match", window_copy_search_match_cb);
969 format_add_cb(ft, "copy_cursor_word", window_copy_cursor_word_cb);
970 format_add_cb(ft, "copy_cursor_line", window_copy_cursor_line_cb);
971 format_add_cb(ft, "copy_cursor_hyperlink",
972 window_copy_cursor_hyperlink_cb);
975 static void
976 window_copy_size_changed(struct window_mode_entry *wme)
978 struct window_copy_mode_data *data = wme->data;
979 struct screen *s = &data->screen;
980 struct screen_write_ctx ctx;
981 int search = (data->searchmark != NULL);
983 window_copy_clear_selection(wme);
984 window_copy_clear_marks(wme);
986 screen_write_start(&ctx, s);
987 window_copy_write_lines(wme, &ctx, 0, screen_size_y(s));
988 screen_write_stop(&ctx);
990 if (search && !data->timeout)
991 window_copy_search_marks(wme, NULL, data->searchregex, 0);
992 data->searchx = data->cx;
993 data->searchy = data->cy;
994 data->searcho = data->oy;
997 static void
998 window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
1000 struct window_copy_mode_data *data = wme->data;
1001 struct screen *s = &data->screen;
1002 struct grid *gd = data->backing->grid;
1003 u_int cx, cy, wx, wy;
1004 int reflow;
1006 screen_resize(s, sx, sy, 0);
1007 cx = data->cx;
1008 cy = gd->hsize + data->cy - data->oy;
1009 reflow = (gd->sx != sx);
1010 if (reflow)
1011 grid_wrap_position(gd, cx, cy, &wx, &wy);
1012 screen_resize_cursor(data->backing, sx, sy, 1, 0, 0);
1013 if (reflow)
1014 grid_unwrap_position(gd, &cx, &cy, wx, wy);
1016 data->cx = cx;
1017 if (cy < gd->hsize) {
1018 data->cy = 0;
1019 data->oy = gd->hsize - cy;
1020 } else {
1021 data->cy = cy - gd->hsize;
1022 data->oy = 0;
1025 window_copy_size_changed(wme);
1026 window_copy_redraw_screen(wme);
1029 static const char *
1030 window_copy_key_table(struct window_mode_entry *wme)
1032 struct window_pane *wp = wme->wp;
1034 if (options_get_number(wp->window->options, "mode-keys") == MODEKEY_VI)
1035 return ("copy-mode-vi");
1036 return ("copy-mode");
1039 static int
1040 window_copy_expand_search_string(struct window_copy_cmd_state *cs)
1042 struct window_mode_entry *wme = cs->wme;
1043 struct window_copy_mode_data *data = wme->data;
1044 const char *ss = args_string(cs->wargs, 0);
1045 char *expanded;
1047 if (ss == NULL || *ss == '\0')
1048 return (0);
1050 if (args_has(cs->args, 'F')) {
1051 expanded = format_single(NULL, ss, NULL, NULL, NULL, wme->wp);
1052 if (*expanded == '\0') {
1053 free(expanded);
1054 return (0);
1056 free(data->searchstr);
1057 data->searchstr = expanded;
1058 } else {
1059 free(data->searchstr);
1060 data->searchstr = xstrdup(ss);
1062 return (1);
1065 static enum window_copy_cmd_action
1066 window_copy_cmd_append_selection(struct window_copy_cmd_state *cs)
1068 struct window_mode_entry *wme = cs->wme;
1069 struct session *s = cs->s;
1071 if (s != NULL)
1072 window_copy_append_selection(wme);
1073 window_copy_clear_selection(wme);
1074 return (WINDOW_COPY_CMD_REDRAW);
1077 static enum window_copy_cmd_action
1078 window_copy_cmd_append_selection_and_cancel(struct window_copy_cmd_state *cs)
1080 struct window_mode_entry *wme = cs->wme;
1081 struct session *s = cs->s;
1083 if (s != NULL)
1084 window_copy_append_selection(wme);
1085 window_copy_clear_selection(wme);
1086 return (WINDOW_COPY_CMD_CANCEL);
1089 static enum window_copy_cmd_action
1090 window_copy_cmd_back_to_indentation(struct window_copy_cmd_state *cs)
1092 struct window_mode_entry *wme = cs->wme;
1094 window_copy_cursor_back_to_indentation(wme);
1095 return (WINDOW_COPY_CMD_NOTHING);
1098 static enum window_copy_cmd_action
1099 window_copy_cmd_begin_selection(struct window_copy_cmd_state *cs)
1101 struct window_mode_entry *wme = cs->wme;
1102 struct client *c = cs->c;
1103 struct mouse_event *m = cs->m;
1104 struct window_copy_mode_data *data = wme->data;
1106 if (m != NULL) {
1107 window_copy_start_drag(c, m);
1108 return (WINDOW_COPY_CMD_NOTHING);
1111 data->lineflag = LINE_SEL_NONE;
1112 data->selflag = SEL_CHAR;
1113 window_copy_start_selection(wme);
1114 return (WINDOW_COPY_CMD_REDRAW);
1117 static enum window_copy_cmd_action
1118 window_copy_cmd_stop_selection(struct window_copy_cmd_state *cs)
1120 struct window_mode_entry *wme = cs->wme;
1121 struct window_copy_mode_data *data = wme->data;
1123 data->cursordrag = CURSORDRAG_NONE;
1124 data->lineflag = LINE_SEL_NONE;
1125 data->selflag = SEL_CHAR;
1126 return (WINDOW_COPY_CMD_NOTHING);
1129 static enum window_copy_cmd_action
1130 window_copy_cmd_bottom_line(struct window_copy_cmd_state *cs)
1132 struct window_mode_entry *wme = cs->wme;
1133 struct window_copy_mode_data *data = wme->data;
1135 data->cx = 0;
1136 data->cy = screen_size_y(&data->screen) - 1;
1138 window_copy_update_selection(wme, 1, 0);
1139 return (WINDOW_COPY_CMD_REDRAW);
1142 static enum window_copy_cmd_action
1143 window_copy_cmd_cancel(__unused struct window_copy_cmd_state *cs)
1145 return (WINDOW_COPY_CMD_CANCEL);
1148 static enum window_copy_cmd_action
1149 window_copy_cmd_clear_selection(struct window_copy_cmd_state *cs)
1151 struct window_mode_entry *wme = cs->wme;
1153 window_copy_clear_selection(wme);
1154 return (WINDOW_COPY_CMD_REDRAW);
1157 static enum window_copy_cmd_action
1158 window_copy_do_copy_end_of_line(struct window_copy_cmd_state *cs, int pipe,
1159 int cancel)
1161 struct window_mode_entry *wme = cs->wme;
1162 struct client *c = cs->c;
1163 struct session *s = cs->s;
1164 struct winlink *wl = cs->wl;
1165 struct window_pane *wp = wme->wp;
1166 u_int count = args_count(cs->wargs);
1167 u_int np = wme->prefix, ocx, ocy, ooy;
1168 struct window_copy_mode_data *data = wme->data;
1169 char *prefix = NULL, *command = NULL;
1170 const char *arg0 = args_string(cs->wargs, 0);
1171 const char *arg1 = args_string(cs->wargs, 1);
1172 int set_paste = !args_has(cs->wargs, 'P');
1173 int set_clip = !args_has(cs->wargs, 'C');
1175 if (pipe) {
1176 if (count == 2)
1177 prefix = format_single(NULL, arg1, c, s, wl, wp);
1178 if (s != NULL && count > 0 && *arg0 != '\0')
1179 command = format_single(NULL, arg0, c, s, wl, wp);
1180 } else {
1181 if (count == 1)
1182 prefix = format_single(NULL, arg0, c, s, wl, wp);
1185 ocx = data->cx;
1186 ocy = data->cy;
1187 ooy = data->oy;
1189 window_copy_start_selection(wme);
1190 for (; np > 1; np--)
1191 window_copy_cursor_down(wme, 0);
1192 window_copy_cursor_end_of_line(wme);
1194 if (s != NULL) {
1195 if (pipe)
1196 window_copy_copy_pipe(wme, s, prefix, command,
1197 set_paste, set_clip);
1198 else
1199 window_copy_copy_selection(wme, prefix,
1200 set_paste, set_clip);
1202 if (cancel) {
1203 free(prefix);
1204 free(command);
1205 return (WINDOW_COPY_CMD_CANCEL);
1208 window_copy_clear_selection(wme);
1210 data->cx = ocx;
1211 data->cy = ocy;
1212 data->oy = ooy;
1214 free(prefix);
1215 free(command);
1216 return (WINDOW_COPY_CMD_REDRAW);
1219 static enum window_copy_cmd_action
1220 window_copy_cmd_copy_end_of_line(struct window_copy_cmd_state *cs)
1222 return (window_copy_do_copy_end_of_line(cs, 0, 0));
1225 static enum window_copy_cmd_action
1226 window_copy_cmd_copy_end_of_line_and_cancel(struct window_copy_cmd_state *cs)
1228 return (window_copy_do_copy_end_of_line(cs, 0, 1));
1231 static enum window_copy_cmd_action
1232 window_copy_cmd_copy_pipe_end_of_line(struct window_copy_cmd_state *cs)
1234 return (window_copy_do_copy_end_of_line(cs, 1, 0));
1237 static enum window_copy_cmd_action
1238 window_copy_cmd_copy_pipe_end_of_line_and_cancel(
1239 struct window_copy_cmd_state *cs)
1241 return (window_copy_do_copy_end_of_line(cs, 1, 1));
1244 static enum window_copy_cmd_action
1245 window_copy_do_copy_line(struct window_copy_cmd_state *cs, int pipe, int cancel)
1247 struct window_mode_entry *wme = cs->wme;
1248 struct client *c = cs->c;
1249 struct session *s = cs->s;
1250 struct winlink *wl = cs->wl;
1251 struct window_pane *wp = wme->wp;
1252 struct window_copy_mode_data *data = wme->data;
1253 u_int count = args_count(cs->wargs);
1254 u_int np = wme->prefix, ocx, ocy, ooy;
1255 char *prefix = NULL, *command = NULL;
1256 const char *arg0 = args_string(cs->wargs, 0);
1257 const char *arg1 = args_string(cs->wargs, 1);
1258 int set_paste = !args_has(cs->wargs, 'P');
1259 int set_clip = !args_has(cs->wargs, 'C');
1261 if (pipe) {
1262 if (count == 2)
1263 prefix = format_single(NULL, arg1, c, s, wl, wp);
1264 if (s != NULL && count > 0 && *arg0 != '\0')
1265 command = format_single(NULL, arg0, c, s, wl, wp);
1266 } else {
1267 if (count == 1)
1268 prefix = format_single(NULL, arg0, c, s, wl, wp);
1271 ocx = data->cx;
1272 ocy = data->cy;
1273 ooy = data->oy;
1275 data->selflag = SEL_CHAR;
1276 window_copy_cursor_start_of_line(wme);
1277 window_copy_start_selection(wme);
1278 for (; np > 1; np--)
1279 window_copy_cursor_down(wme, 0);
1280 window_copy_cursor_end_of_line(wme);
1282 if (s != NULL) {
1283 if (pipe)
1284 window_copy_copy_pipe(wme, s, prefix, command,
1285 set_paste, set_clip);
1286 else
1287 window_copy_copy_selection(wme, prefix,
1288 set_paste, set_clip);
1290 if (cancel) {
1291 free(prefix);
1292 free(command);
1293 return (WINDOW_COPY_CMD_CANCEL);
1296 window_copy_clear_selection(wme);
1298 data->cx = ocx;
1299 data->cy = ocy;
1300 data->oy = ooy;
1302 free(prefix);
1303 free(command);
1304 return (WINDOW_COPY_CMD_REDRAW);
1307 static enum window_copy_cmd_action
1308 window_copy_cmd_copy_line(struct window_copy_cmd_state *cs)
1310 return (window_copy_do_copy_line(cs, 0, 0));
1313 static enum window_copy_cmd_action
1314 window_copy_cmd_copy_line_and_cancel(struct window_copy_cmd_state *cs)
1316 return (window_copy_do_copy_line(cs, 0, 1));
1319 static enum window_copy_cmd_action
1320 window_copy_cmd_copy_pipe_line(struct window_copy_cmd_state *cs)
1322 return (window_copy_do_copy_line(cs, 1, 0));
1325 static enum window_copy_cmd_action
1326 window_copy_cmd_copy_pipe_line_and_cancel(struct window_copy_cmd_state *cs)
1328 return (window_copy_do_copy_line(cs, 1, 1));
1331 static enum window_copy_cmd_action
1332 window_copy_cmd_copy_selection_no_clear(struct window_copy_cmd_state *cs)
1334 struct window_mode_entry *wme = cs->wme;
1335 struct client *c = cs->c;
1336 struct session *s = cs->s;
1337 struct winlink *wl = cs->wl;
1338 struct window_pane *wp = wme->wp;
1339 char *prefix = NULL;
1340 const char *arg0 = args_string(cs->wargs, 0);
1341 int set_paste = !args_has(cs->wargs, 'P');
1342 int set_clip = !args_has(cs->wargs, 'C');
1344 if (arg0 != NULL)
1345 prefix = format_single(NULL, arg0, c, s, wl, wp);
1347 if (s != NULL)
1348 window_copy_copy_selection(wme, prefix, set_paste, set_clip);
1350 free(prefix);
1351 return (WINDOW_COPY_CMD_NOTHING);
1354 static enum window_copy_cmd_action
1355 window_copy_cmd_copy_selection(struct window_copy_cmd_state *cs)
1357 struct window_mode_entry *wme = cs->wme;
1359 window_copy_cmd_copy_selection_no_clear(cs);
1360 window_copy_clear_selection(wme);
1361 return (WINDOW_COPY_CMD_REDRAW);
1364 static enum window_copy_cmd_action
1365 window_copy_cmd_copy_selection_and_cancel(struct window_copy_cmd_state *cs)
1367 struct window_mode_entry *wme = cs->wme;
1369 window_copy_cmd_copy_selection_no_clear(cs);
1370 window_copy_clear_selection(wme);
1371 return (WINDOW_COPY_CMD_CANCEL);
1374 static enum window_copy_cmd_action
1375 window_copy_cmd_cursor_down(struct window_copy_cmd_state *cs)
1377 struct window_mode_entry *wme = cs->wme;
1378 u_int np = wme->prefix;
1380 for (; np != 0; np--)
1381 window_copy_cursor_down(wme, 0);
1382 return (WINDOW_COPY_CMD_NOTHING);
1385 static enum window_copy_cmd_action
1386 window_copy_cmd_cursor_down_and_cancel(struct window_copy_cmd_state *cs)
1388 struct window_mode_entry *wme = cs->wme;
1389 struct window_copy_mode_data *data = wme->data;
1390 u_int np = wme->prefix, cy;
1392 cy = data->cy;
1393 for (; np != 0; np--)
1394 window_copy_cursor_down(wme, 0);
1395 if (cy == data->cy && data->oy == 0)
1396 return (WINDOW_COPY_CMD_CANCEL);
1397 return (WINDOW_COPY_CMD_NOTHING);
1400 static enum window_copy_cmd_action
1401 window_copy_cmd_cursor_left(struct window_copy_cmd_state *cs)
1403 struct window_mode_entry *wme = cs->wme;
1404 u_int np = wme->prefix;
1406 for (; np != 0; np--)
1407 window_copy_cursor_left(wme);
1408 return (WINDOW_COPY_CMD_NOTHING);
1411 static enum window_copy_cmd_action
1412 window_copy_cmd_cursor_right(struct window_copy_cmd_state *cs)
1414 struct window_mode_entry *wme = cs->wme;
1415 struct window_copy_mode_data *data = wme->data;
1416 u_int np = wme->prefix;
1418 for (; np != 0; np--) {
1419 window_copy_cursor_right(wme, data->screen.sel != NULL &&
1420 data->rectflag);
1422 return (WINDOW_COPY_CMD_NOTHING);
1425 /* Scroll line containing the cursor to the given position. */
1426 static enum window_copy_cmd_action
1427 window_copy_cmd_scroll_to(struct window_copy_cmd_state *cs, u_int to)
1429 struct window_mode_entry *wme = cs->wme;
1430 struct window_copy_mode_data *data = wme->data;
1431 u_int oy, delta;
1432 int scroll_up; /* >0 up, <0 down */
1434 scroll_up = data->cy - to;
1435 delta = abs(scroll_up);
1436 oy = screen_hsize(data->backing) - data->oy;
1439 * oy is the maximum scroll down amount, while data->oy is the maximum
1440 * scroll up amount.
1442 if (scroll_up > 0 && data->oy >= delta) {
1443 window_copy_scroll_up(wme, delta);
1444 data->cy -= delta;
1445 } else if (scroll_up < 0 && oy >= delta) {
1446 window_copy_scroll_down(wme, delta);
1447 data->cy += delta;
1450 window_copy_update_selection(wme, 0, 0);
1451 return (WINDOW_COPY_CMD_REDRAW);
1454 /* Scroll line containing the cursor to the bottom. */
1455 static enum window_copy_cmd_action
1456 window_copy_cmd_scroll_bottom(struct window_copy_cmd_state *cs)
1458 struct window_copy_mode_data *data = cs->wme->data;
1459 u_int bottom;
1461 bottom = screen_size_y(&data->screen) - 1;
1462 return (window_copy_cmd_scroll_to(cs, bottom));
1465 /* Scroll line containing the cursor to the middle. */
1466 static enum window_copy_cmd_action
1467 window_copy_cmd_scroll_middle(struct window_copy_cmd_state *cs)
1469 struct window_copy_mode_data *data = cs->wme->data;
1470 u_int mid_value;
1472 mid_value = (screen_size_y(&data->screen) - 1) / 2;
1473 return (window_copy_cmd_scroll_to(cs, mid_value));
1476 /* Scroll line containing the cursor to the top. */
1477 static enum window_copy_cmd_action
1478 window_copy_cmd_scroll_top(struct window_copy_cmd_state *cs)
1480 return (window_copy_cmd_scroll_to(cs, 0));
1483 static enum window_copy_cmd_action
1484 window_copy_cmd_cursor_up(struct window_copy_cmd_state *cs)
1486 struct window_mode_entry *wme = cs->wme;
1487 u_int np = wme->prefix;
1489 for (; np != 0; np--)
1490 window_copy_cursor_up(wme, 0);
1491 return (WINDOW_COPY_CMD_NOTHING);
1494 static enum window_copy_cmd_action
1495 window_copy_cmd_end_of_line(struct window_copy_cmd_state *cs)
1497 struct window_mode_entry *wme = cs->wme;
1499 window_copy_cursor_end_of_line(wme);
1500 return (WINDOW_COPY_CMD_NOTHING);
1503 static enum window_copy_cmd_action
1504 window_copy_cmd_halfpage_down(struct window_copy_cmd_state *cs)
1506 struct window_mode_entry *wme = cs->wme;
1507 struct window_copy_mode_data *data = wme->data;
1508 u_int np = wme->prefix;
1510 for (; np != 0; np--) {
1511 if (window_copy_pagedown1(wme, 1, data->scroll_exit))
1512 return (WINDOW_COPY_CMD_CANCEL);
1514 return (WINDOW_COPY_CMD_NOTHING);
1517 static enum window_copy_cmd_action
1518 window_copy_cmd_halfpage_down_and_cancel(struct window_copy_cmd_state *cs)
1521 struct window_mode_entry *wme = cs->wme;
1522 u_int np = wme->prefix;
1524 for (; np != 0; np--) {
1525 if (window_copy_pagedown1(wme, 1, 1))
1526 return (WINDOW_COPY_CMD_CANCEL);
1528 return (WINDOW_COPY_CMD_NOTHING);
1531 static enum window_copy_cmd_action
1532 window_copy_cmd_halfpage_up(struct window_copy_cmd_state *cs)
1534 struct window_mode_entry *wme = cs->wme;
1535 u_int np = wme->prefix;
1537 for (; np != 0; np--)
1538 window_copy_pageup1(wme, 1);
1539 return (WINDOW_COPY_CMD_NOTHING);
1542 static enum window_copy_cmd_action
1543 window_copy_cmd_toggle_position(struct window_copy_cmd_state *cs)
1545 struct window_mode_entry *wme = cs->wme;
1546 struct window_copy_mode_data *data = wme->data;
1548 data->hide_position = !data->hide_position;
1549 return (WINDOW_COPY_CMD_REDRAW);
1552 static enum window_copy_cmd_action
1553 window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs)
1555 struct window_mode_entry *wme = cs->wme;
1556 struct window_copy_mode_data *data = wme->data;
1557 struct screen *s = data->backing;
1558 u_int oy;
1560 oy = screen_hsize(s) + data->cy - data->oy;
1561 if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely)
1562 window_copy_other_end(wme);
1564 data->cy = screen_size_y(&data->screen) - 1;
1565 data->cx = window_copy_find_length(wme, screen_hsize(s) + data->cy);
1566 data->oy = 0;
1568 if (data->searchmark != NULL && !data->timeout)
1569 window_copy_search_marks(wme, NULL, data->searchregex, 1);
1570 window_copy_update_selection(wme, 1, 0);
1571 return (WINDOW_COPY_CMD_REDRAW);
1574 static enum window_copy_cmd_action
1575 window_copy_cmd_history_top(struct window_copy_cmd_state *cs)
1577 struct window_mode_entry *wme = cs->wme;
1578 struct window_copy_mode_data *data = wme->data;
1579 u_int oy;
1581 oy = screen_hsize(data->backing) + data->cy - data->oy;
1582 if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely)
1583 window_copy_other_end(wme);
1585 data->cy = 0;
1586 data->cx = 0;
1587 data->oy = screen_hsize(data->backing);
1589 if (data->searchmark != NULL && !data->timeout)
1590 window_copy_search_marks(wme, NULL, data->searchregex, 1);
1591 window_copy_update_selection(wme, 1, 0);
1592 return (WINDOW_COPY_CMD_REDRAW);
1595 static enum window_copy_cmd_action
1596 window_copy_cmd_jump_again(struct window_copy_cmd_state *cs)
1598 struct window_mode_entry *wme = cs->wme;
1599 struct window_copy_mode_data *data = wme->data;
1600 u_int np = wme->prefix;
1602 switch (data->jumptype) {
1603 case WINDOW_COPY_JUMPFORWARD:
1604 for (; np != 0; np--)
1605 window_copy_cursor_jump(wme);
1606 break;
1607 case WINDOW_COPY_JUMPBACKWARD:
1608 for (; np != 0; np--)
1609 window_copy_cursor_jump_back(wme);
1610 break;
1611 case WINDOW_COPY_JUMPTOFORWARD:
1612 for (; np != 0; np--)
1613 window_copy_cursor_jump_to(wme);
1614 break;
1615 case WINDOW_COPY_JUMPTOBACKWARD:
1616 for (; np != 0; np--)
1617 window_copy_cursor_jump_to_back(wme);
1618 break;
1620 return (WINDOW_COPY_CMD_NOTHING);
1623 static enum window_copy_cmd_action
1624 window_copy_cmd_jump_reverse(struct window_copy_cmd_state *cs)
1626 struct window_mode_entry *wme = cs->wme;
1627 struct window_copy_mode_data *data = wme->data;
1628 u_int np = wme->prefix;
1630 switch (data->jumptype) {
1631 case WINDOW_COPY_JUMPFORWARD:
1632 for (; np != 0; np--)
1633 window_copy_cursor_jump_back(wme);
1634 break;
1635 case WINDOW_COPY_JUMPBACKWARD:
1636 for (; np != 0; np--)
1637 window_copy_cursor_jump(wme);
1638 break;
1639 case WINDOW_COPY_JUMPTOFORWARD:
1640 for (; np != 0; np--)
1641 window_copy_cursor_jump_to_back(wme);
1642 break;
1643 case WINDOW_COPY_JUMPTOBACKWARD:
1644 for (; np != 0; np--)
1645 window_copy_cursor_jump_to(wme);
1646 break;
1648 return (WINDOW_COPY_CMD_NOTHING);
1651 static enum window_copy_cmd_action
1652 window_copy_cmd_middle_line(struct window_copy_cmd_state *cs)
1654 struct window_mode_entry *wme = cs->wme;
1655 struct window_copy_mode_data *data = wme->data;
1657 data->cx = 0;
1658 data->cy = (screen_size_y(&data->screen) - 1) / 2;
1660 window_copy_update_selection(wme, 1, 0);
1661 return (WINDOW_COPY_CMD_REDRAW);
1664 static enum window_copy_cmd_action
1665 window_copy_cmd_previous_matching_bracket(struct window_copy_cmd_state *cs)
1667 struct window_mode_entry *wme = cs->wme;
1668 u_int np = wme->prefix;
1669 struct window_copy_mode_data *data = wme->data;
1670 struct screen *s = data->backing;
1671 char open[] = "{[(", close[] = "}])";
1672 char tried, found, start, *cp;
1673 u_int px, py, xx, n;
1674 struct grid_cell gc;
1675 int failed;
1677 for (; np != 0; np--) {
1678 /* Get cursor position and line length. */
1679 px = data->cx;
1680 py = screen_hsize(s) + data->cy - data->oy;
1681 xx = window_copy_find_length(wme, py);
1682 if (xx == 0)
1683 break;
1686 * Get the current character. If not on a bracket, try the
1687 * previous. If still not, then behave like previous-word.
1689 tried = 0;
1690 retry:
1691 grid_get_cell(s->grid, px, py, &gc);
1692 if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING))
1693 cp = NULL;
1694 else {
1695 found = *gc.data.data;
1696 cp = strchr(close, found);
1698 if (cp == NULL) {
1699 if (data->modekeys == MODEKEY_EMACS) {
1700 if (!tried && px > 0) {
1701 px--;
1702 tried = 1;
1703 goto retry;
1705 window_copy_cursor_previous_word(wme, close, 1);
1707 continue;
1709 start = open[cp - close];
1711 /* Walk backward until the matching bracket is reached. */
1712 n = 1;
1713 failed = 0;
1714 do {
1715 if (px == 0) {
1716 if (py == 0) {
1717 failed = 1;
1718 break;
1720 do {
1721 py--;
1722 xx = window_copy_find_length(wme, py);
1723 } while (xx == 0 && py > 0);
1724 if (xx == 0 && py == 0) {
1725 failed = 1;
1726 break;
1728 px = xx - 1;
1729 } else
1730 px--;
1732 grid_get_cell(s->grid, px, py, &gc);
1733 if (gc.data.size == 1 &&
1734 (~gc.flags & GRID_FLAG_PADDING)) {
1735 if (*gc.data.data == found)
1736 n++;
1737 else if (*gc.data.data == start)
1738 n--;
1740 } while (n != 0);
1742 /* Move the cursor to the found location if any. */
1743 if (!failed)
1744 window_copy_scroll_to(wme, px, py, 0);
1747 return (WINDOW_COPY_CMD_NOTHING);
1750 static enum window_copy_cmd_action
1751 window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state *cs)
1753 struct window_mode_entry *wme = cs->wme;
1754 u_int np = wme->prefix;
1755 struct window_copy_mode_data *data = wme->data;
1756 struct screen *s = data->backing;
1757 char open[] = "{[(", close[] = "}])";
1758 char tried, found, end, *cp;
1759 u_int px, py, xx, yy, sx, sy, n;
1760 struct grid_cell gc;
1761 int failed;
1762 struct grid_line *gl;
1764 for (; np != 0; np--) {
1765 /* Get cursor position and line length. */
1766 px = data->cx;
1767 py = screen_hsize(s) + data->cy - data->oy;
1768 xx = window_copy_find_length(wme, py);
1769 yy = screen_hsize(s) + screen_size_y(s) - 1;
1770 if (xx == 0)
1771 break;
1774 * Get the current character. If not on a bracket, try the
1775 * next. If still not, then behave like next-word.
1777 tried = 0;
1778 retry:
1779 grid_get_cell(s->grid, px, py, &gc);
1780 if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING))
1781 cp = NULL;
1782 else {
1783 found = *gc.data.data;
1786 * In vi mode, attempt to move to previous bracket if a
1787 * closing bracket is found first. If this fails,
1788 * return to the original cursor position.
1790 cp = strchr(close, found);
1791 if (cp != NULL && data->modekeys == MODEKEY_VI) {
1792 sx = data->cx;
1793 sy = screen_hsize(s) + data->cy - data->oy;
1795 window_copy_scroll_to(wme, px, py, 0);
1796 window_copy_cmd_previous_matching_bracket(cs);
1798 px = data->cx;
1799 py = screen_hsize(s) + data->cy - data->oy;
1800 grid_get_cell(s->grid, px, py, &gc);
1801 if (gc.data.size == 1 &&
1802 (~gc.flags & GRID_FLAG_PADDING) &&
1803 strchr(close, *gc.data.data) != NULL)
1804 window_copy_scroll_to(wme, sx, sy, 0);
1805 break;
1808 cp = strchr(open, found);
1810 if (cp == NULL) {
1811 if (data->modekeys == MODEKEY_EMACS) {
1812 if (!tried && px <= xx) {
1813 px++;
1814 tried = 1;
1815 goto retry;
1817 window_copy_cursor_next_word_end(wme, open, 0);
1818 continue;
1820 /* For vi, continue searching for bracket until EOL. */
1821 if (px > xx) {
1822 if (py == yy)
1823 continue;
1824 gl = grid_get_line(s->grid, py);
1825 if (~gl->flags & GRID_LINE_WRAPPED)
1826 continue;
1827 if (gl->cellsize > s->grid->sx)
1828 continue;
1829 px = 0;
1830 py++;
1831 xx = window_copy_find_length(wme, py);
1832 } else
1833 px++;
1834 goto retry;
1836 end = close[cp - open];
1838 /* Walk forward until the matching bracket is reached. */
1839 n = 1;
1840 failed = 0;
1841 do {
1842 if (px > xx) {
1843 if (py == yy) {
1844 failed = 1;
1845 break;
1847 px = 0;
1848 py++;
1849 xx = window_copy_find_length(wme, py);
1850 } else
1851 px++;
1853 grid_get_cell(s->grid, px, py, &gc);
1854 if (gc.data.size == 1 &&
1855 (~gc.flags & GRID_FLAG_PADDING)) {
1856 if (*gc.data.data == found)
1857 n++;
1858 else if (*gc.data.data == end)
1859 n--;
1861 } while (n != 0);
1863 /* Move the cursor to the found location if any. */
1864 if (!failed)
1865 window_copy_scroll_to(wme, px, py, 0);
1868 return (WINDOW_COPY_CMD_NOTHING);
1871 static enum window_copy_cmd_action
1872 window_copy_cmd_next_paragraph(struct window_copy_cmd_state *cs)
1874 struct window_mode_entry *wme = cs->wme;
1875 u_int np = wme->prefix;
1877 for (; np != 0; np--)
1878 window_copy_next_paragraph(wme);
1879 return (WINDOW_COPY_CMD_NOTHING);
1882 static enum window_copy_cmd_action
1883 window_copy_cmd_next_space(struct window_copy_cmd_state *cs)
1885 struct window_mode_entry *wme = cs->wme;
1886 u_int np = wme->prefix;
1888 for (; np != 0; np--)
1889 window_copy_cursor_next_word(wme, "");
1890 return (WINDOW_COPY_CMD_NOTHING);
1893 static enum window_copy_cmd_action
1894 window_copy_cmd_next_space_end(struct window_copy_cmd_state *cs)
1896 struct window_mode_entry *wme = cs->wme;
1897 u_int np = wme->prefix;
1899 for (; np != 0; np--)
1900 window_copy_cursor_next_word_end(wme, "", 0);
1901 return (WINDOW_COPY_CMD_NOTHING);
1904 static enum window_copy_cmd_action
1905 window_copy_cmd_next_word(struct window_copy_cmd_state *cs)
1907 struct window_mode_entry *wme = cs->wme;
1908 u_int np = wme->prefix;
1909 const char *separators;
1911 separators = options_get_string(cs->s->options, "word-separators");
1913 for (; np != 0; np--)
1914 window_copy_cursor_next_word(wme, separators);
1915 return (WINDOW_COPY_CMD_NOTHING);
1918 static enum window_copy_cmd_action
1919 window_copy_cmd_next_word_end(struct window_copy_cmd_state *cs)
1921 struct window_mode_entry *wme = cs->wme;
1922 u_int np = wme->prefix;
1923 const char *separators;
1925 separators = options_get_string(cs->s->options, "word-separators");
1927 for (; np != 0; np--)
1928 window_copy_cursor_next_word_end(wme, separators, 0);
1929 return (WINDOW_COPY_CMD_NOTHING);
1932 static enum window_copy_cmd_action
1933 window_copy_cmd_other_end(struct window_copy_cmd_state *cs)
1935 struct window_mode_entry *wme = cs->wme;
1936 u_int np = wme->prefix;
1937 struct window_copy_mode_data *data = wme->data;
1939 data->selflag = SEL_CHAR;
1940 if ((np % 2) != 0)
1941 window_copy_other_end(wme);
1942 return (WINDOW_COPY_CMD_NOTHING);
1945 static enum window_copy_cmd_action
1946 window_copy_cmd_page_down(struct window_copy_cmd_state *cs)
1948 struct window_mode_entry *wme = cs->wme;
1949 struct window_copy_mode_data *data = wme->data;
1950 u_int np = wme->prefix;
1952 for (; np != 0; np--) {
1953 if (window_copy_pagedown1(wme, 0, data->scroll_exit))
1954 return (WINDOW_COPY_CMD_CANCEL);
1956 return (WINDOW_COPY_CMD_NOTHING);
1959 static enum window_copy_cmd_action
1960 window_copy_cmd_page_down_and_cancel(struct window_copy_cmd_state *cs)
1962 struct window_mode_entry *wme = cs->wme;
1963 u_int np = wme->prefix;
1965 for (; np != 0; np--) {
1966 if (window_copy_pagedown1(wme, 0, 1))
1967 return (WINDOW_COPY_CMD_CANCEL);
1969 return (WINDOW_COPY_CMD_NOTHING);
1972 static enum window_copy_cmd_action
1973 window_copy_cmd_page_up(struct window_copy_cmd_state *cs)
1975 struct window_mode_entry *wme = cs->wme;
1976 u_int np = wme->prefix;
1978 for (; np != 0; np--)
1979 window_copy_pageup1(wme, 0);
1980 return (WINDOW_COPY_CMD_NOTHING);
1983 static enum window_copy_cmd_action
1984 window_copy_cmd_previous_paragraph(struct window_copy_cmd_state *cs)
1986 struct window_mode_entry *wme = cs->wme;
1987 u_int np = wme->prefix;
1989 for (; np != 0; np--)
1990 window_copy_previous_paragraph(wme);
1991 return (WINDOW_COPY_CMD_NOTHING);
1994 static enum window_copy_cmd_action
1995 window_copy_cmd_previous_space(struct window_copy_cmd_state *cs)
1997 struct window_mode_entry *wme = cs->wme;
1998 u_int np = wme->prefix;
2000 for (; np != 0; np--)
2001 window_copy_cursor_previous_word(wme, "", 1);
2002 return (WINDOW_COPY_CMD_NOTHING);
2005 static enum window_copy_cmd_action
2006 window_copy_cmd_previous_word(struct window_copy_cmd_state *cs)
2008 struct window_mode_entry *wme = cs->wme;
2009 u_int np = wme->prefix;
2010 const char *separators;
2012 separators = options_get_string(cs->s->options, "word-separators");
2014 for (; np != 0; np--)
2015 window_copy_cursor_previous_word(wme, separators, 1);
2016 return (WINDOW_COPY_CMD_NOTHING);
2019 static enum window_copy_cmd_action
2020 window_copy_cmd_rectangle_on(struct window_copy_cmd_state *cs)
2022 struct window_mode_entry *wme = cs->wme;
2023 struct window_copy_mode_data *data = wme->data;
2025 data->lineflag = LINE_SEL_NONE;
2026 window_copy_rectangle_set(wme, 1);
2028 return (WINDOW_COPY_CMD_NOTHING);
2031 static enum window_copy_cmd_action
2032 window_copy_cmd_rectangle_off(struct window_copy_cmd_state *cs)
2034 struct window_mode_entry *wme = cs->wme;
2035 struct window_copy_mode_data *data = wme->data;
2037 data->lineflag = LINE_SEL_NONE;
2038 window_copy_rectangle_set(wme, 0);
2040 return (WINDOW_COPY_CMD_NOTHING);
2043 static enum window_copy_cmd_action
2044 window_copy_cmd_rectangle_toggle(struct window_copy_cmd_state *cs)
2046 struct window_mode_entry *wme = cs->wme;
2047 struct window_copy_mode_data *data = wme->data;
2049 data->lineflag = LINE_SEL_NONE;
2050 window_copy_rectangle_set(wme, !data->rectflag);
2052 return (WINDOW_COPY_CMD_NOTHING);
2055 static enum window_copy_cmd_action
2056 window_copy_cmd_scroll_down(struct window_copy_cmd_state *cs)
2058 struct window_mode_entry *wme = cs->wme;
2059 struct window_copy_mode_data *data = wme->data;
2060 u_int np = wme->prefix;
2062 for (; np != 0; np--)
2063 window_copy_cursor_down(wme, 1);
2064 if (data->scroll_exit && data->oy == 0)
2065 return (WINDOW_COPY_CMD_CANCEL);
2066 return (WINDOW_COPY_CMD_NOTHING);
2069 static enum window_copy_cmd_action
2070 window_copy_cmd_scroll_down_and_cancel(struct window_copy_cmd_state *cs)
2072 struct window_mode_entry *wme = cs->wme;
2073 struct window_copy_mode_data *data = wme->data;
2074 u_int np = wme->prefix;
2076 for (; np != 0; np--)
2077 window_copy_cursor_down(wme, 1);
2078 if (data->oy == 0)
2079 return (WINDOW_COPY_CMD_CANCEL);
2080 return (WINDOW_COPY_CMD_NOTHING);
2083 static enum window_copy_cmd_action
2084 window_copy_cmd_scroll_up(struct window_copy_cmd_state *cs)
2086 struct window_mode_entry *wme = cs->wme;
2087 u_int np = wme->prefix;
2089 for (; np != 0; np--)
2090 window_copy_cursor_up(wme, 1);
2091 return (WINDOW_COPY_CMD_NOTHING);
2094 static enum window_copy_cmd_action
2095 window_copy_cmd_search_again(struct window_copy_cmd_state *cs)
2097 struct window_mode_entry *wme = cs->wme;
2098 struct window_copy_mode_data *data = wme->data;
2099 u_int np = wme->prefix;
2101 if (data->searchtype == WINDOW_COPY_SEARCHUP) {
2102 for (; np != 0; np--)
2103 window_copy_search_up(wme, data->searchregex);
2104 } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
2105 for (; np != 0; np--)
2106 window_copy_search_down(wme, data->searchregex);
2108 return (WINDOW_COPY_CMD_NOTHING);
2111 static enum window_copy_cmd_action
2112 window_copy_cmd_search_reverse(struct window_copy_cmd_state *cs)
2114 struct window_mode_entry *wme = cs->wme;
2115 struct window_copy_mode_data *data = wme->data;
2116 u_int np = wme->prefix;
2118 if (data->searchtype == WINDOW_COPY_SEARCHUP) {
2119 for (; np != 0; np--)
2120 window_copy_search_down(wme, data->searchregex);
2121 } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
2122 for (; np != 0; np--)
2123 window_copy_search_up(wme, data->searchregex);
2125 return (WINDOW_COPY_CMD_NOTHING);
2128 static enum window_copy_cmd_action
2129 window_copy_cmd_select_line(struct window_copy_cmd_state *cs)
2131 struct window_mode_entry *wme = cs->wme;
2132 struct window_copy_mode_data *data = wme->data;
2133 u_int np = wme->prefix;
2135 data->lineflag = LINE_SEL_LEFT_RIGHT;
2136 data->rectflag = 0;
2137 data->selflag = SEL_LINE;
2138 data->dx = data->cx;
2139 data->dy = screen_hsize(data->backing) + data->cy - data->oy;
2141 window_copy_cursor_start_of_line(wme);
2142 data->selrx = data->cx;
2143 data->selry = screen_hsize(data->backing) + data->cy - data->oy;
2144 data->endselry = data->selry;
2145 window_copy_start_selection(wme);
2146 window_copy_cursor_end_of_line(wme);
2147 data->endselry = screen_hsize(data->backing) + data->cy - data->oy;
2148 data->endselrx = window_copy_find_length(wme, data->endselry);
2149 for (; np > 1; np--) {
2150 window_copy_cursor_down(wme, 0);
2151 window_copy_cursor_end_of_line(wme);
2154 return (WINDOW_COPY_CMD_REDRAW);
2157 static enum window_copy_cmd_action
2158 window_copy_cmd_select_word(struct window_copy_cmd_state *cs)
2160 struct window_mode_entry *wme = cs->wme;
2161 struct options *session_options = cs->s->options;
2162 struct window_copy_mode_data *data = wme->data;
2163 u_int px, py, nextx, nexty;
2165 data->lineflag = LINE_SEL_LEFT_RIGHT;
2166 data->rectflag = 0;
2167 data->selflag = SEL_WORD;
2168 data->dx = data->cx;
2169 data->dy = screen_hsize(data->backing) + data->cy - data->oy;
2171 data->separators = options_get_string(session_options,
2172 "word-separators");
2173 window_copy_cursor_previous_word(wme, data->separators, 0);
2174 px = data->cx;
2175 py = screen_hsize(data->backing) + data->cy - data->oy;
2176 data->selrx = px;
2177 data->selry = py;
2178 window_copy_start_selection(wme);
2180 /* Handle single character words. */
2181 nextx = px + 1;
2182 nexty = py;
2183 if (grid_get_line(data->backing->grid, nexty)->flags &
2184 GRID_LINE_WRAPPED && nextx > screen_size_x(data->backing) - 1) {
2185 nextx = 0;
2186 nexty++;
2188 if (px >= window_copy_find_length(wme, py) ||
2189 !window_copy_in_set(wme, nextx, nexty, WHITESPACE))
2190 window_copy_cursor_next_word_end(wme, data->separators, 1);
2191 else {
2192 window_copy_update_cursor(wme, px, data->cy);
2193 if (window_copy_update_selection(wme, 1, 1))
2194 window_copy_redraw_lines(wme, data->cy, 1);
2196 data->endselrx = data->cx;
2197 data->endselry = screen_hsize(data->backing) + data->cy - data->oy;
2198 if (data->dy > data->endselry) {
2199 data->dy = data->endselry;
2200 data->dx = data->endselrx;
2201 } else if (data->dx > data->endselrx)
2202 data->dx = data->endselrx;
2204 return (WINDOW_COPY_CMD_REDRAW);
2207 static enum window_copy_cmd_action
2208 window_copy_cmd_set_mark(struct window_copy_cmd_state *cs)
2210 struct window_copy_mode_data *data = cs->wme->data;
2212 data->mx = data->cx;
2213 data->my = screen_hsize(data->backing) + data->cy - data->oy;
2214 data->showmark = 1;
2215 return (WINDOW_COPY_CMD_REDRAW);
2218 static enum window_copy_cmd_action
2219 window_copy_cmd_start_of_line(struct window_copy_cmd_state *cs)
2221 struct window_mode_entry *wme = cs->wme;
2223 window_copy_cursor_start_of_line(wme);
2224 return (WINDOW_COPY_CMD_NOTHING);
2227 static enum window_copy_cmd_action
2228 window_copy_cmd_top_line(struct window_copy_cmd_state *cs)
2230 struct window_mode_entry *wme = cs->wme;
2231 struct window_copy_mode_data *data = wme->data;
2233 data->cx = 0;
2234 data->cy = 0;
2236 window_copy_update_selection(wme, 1, 0);
2237 return (WINDOW_COPY_CMD_REDRAW);
2240 static enum window_copy_cmd_action
2241 window_copy_cmd_copy_pipe_no_clear(struct window_copy_cmd_state *cs)
2243 struct window_mode_entry *wme = cs->wme;
2244 struct client *c = cs->c;
2245 struct session *s = cs->s;
2246 struct winlink *wl = cs->wl;
2247 struct window_pane *wp = wme->wp;
2248 char *command = NULL, *prefix = NULL;
2249 const char *arg0 = args_string(cs->wargs, 0);
2250 const char *arg1 = args_string(cs->wargs, 1);
2251 int set_paste = !args_has(cs->wargs, 'P');
2252 int set_clip = !args_has(cs->wargs, 'C');
2254 if (arg1 != NULL)
2255 prefix = format_single(NULL, arg1, c, s, wl, wp);
2257 if (s != NULL && arg0 != NULL && *arg0 != '\0')
2258 command = format_single(NULL, arg0, c, s, wl, wp);
2259 window_copy_copy_pipe(wme, s, prefix, command,
2260 set_paste, set_clip);
2261 free(command);
2263 free(prefix);
2264 return (WINDOW_COPY_CMD_NOTHING);
2267 static enum window_copy_cmd_action
2268 window_copy_cmd_copy_pipe(struct window_copy_cmd_state *cs)
2270 struct window_mode_entry *wme = cs->wme;
2272 window_copy_cmd_copy_pipe_no_clear(cs);
2273 window_copy_clear_selection(wme);
2274 return (WINDOW_COPY_CMD_REDRAW);
2277 static enum window_copy_cmd_action
2278 window_copy_cmd_copy_pipe_and_cancel(struct window_copy_cmd_state *cs)
2280 struct window_mode_entry *wme = cs->wme;
2282 window_copy_cmd_copy_pipe_no_clear(cs);
2283 window_copy_clear_selection(wme);
2284 return (WINDOW_COPY_CMD_CANCEL);
2287 static enum window_copy_cmd_action
2288 window_copy_cmd_pipe_no_clear(struct window_copy_cmd_state *cs)
2290 struct window_mode_entry *wme = cs->wme;
2291 struct client *c = cs->c;
2292 struct session *s = cs->s;
2293 struct winlink *wl = cs->wl;
2294 struct window_pane *wp = wme->wp;
2295 char *command = NULL;
2296 const char *arg0 = args_string(cs->wargs, 0);
2298 if (s != NULL && arg0 != NULL && *arg0 != '\0')
2299 command = format_single(NULL, arg0, c, s, wl, wp);
2300 window_copy_pipe(wme, s, command);
2301 free(command);
2303 return (WINDOW_COPY_CMD_NOTHING);
2306 static enum window_copy_cmd_action
2307 window_copy_cmd_pipe(struct window_copy_cmd_state *cs)
2309 struct window_mode_entry *wme = cs->wme;
2311 window_copy_cmd_pipe_no_clear(cs);
2312 window_copy_clear_selection(wme);
2313 return (WINDOW_COPY_CMD_REDRAW);
2316 static enum window_copy_cmd_action
2317 window_copy_cmd_pipe_and_cancel(struct window_copy_cmd_state *cs)
2319 struct window_mode_entry *wme = cs->wme;
2321 window_copy_cmd_pipe_no_clear(cs);
2322 window_copy_clear_selection(wme);
2323 return (WINDOW_COPY_CMD_CANCEL);
2326 static enum window_copy_cmd_action
2327 window_copy_cmd_goto_line(struct window_copy_cmd_state *cs)
2329 struct window_mode_entry *wme = cs->wme;
2330 const char *arg0 = args_string(cs->wargs, 0);
2332 if (*arg0 != '\0')
2333 window_copy_goto_line(wme, arg0);
2334 return (WINDOW_COPY_CMD_NOTHING);
2337 static enum window_copy_cmd_action
2338 window_copy_cmd_jump_backward(struct window_copy_cmd_state *cs)
2340 struct window_mode_entry *wme = cs->wme;
2341 struct window_copy_mode_data *data = wme->data;
2342 u_int np = wme->prefix;
2343 const char *arg0 = args_string(cs->wargs, 0);
2345 if (*arg0 != '\0') {
2346 data->jumptype = WINDOW_COPY_JUMPBACKWARD;
2347 free(data->jumpchar);
2348 data->jumpchar = utf8_fromcstr(arg0);
2349 for (; np != 0; np--)
2350 window_copy_cursor_jump_back(wme);
2352 return (WINDOW_COPY_CMD_NOTHING);
2355 static enum window_copy_cmd_action
2356 window_copy_cmd_jump_forward(struct window_copy_cmd_state *cs)
2358 struct window_mode_entry *wme = cs->wme;
2359 struct window_copy_mode_data *data = wme->data;
2360 u_int np = wme->prefix;
2361 const char *arg0 = args_string(cs->wargs, 0);
2363 if (*arg0 != '\0') {
2364 data->jumptype = WINDOW_COPY_JUMPFORWARD;
2365 free(data->jumpchar);
2366 data->jumpchar = utf8_fromcstr(arg0);
2367 for (; np != 0; np--)
2368 window_copy_cursor_jump(wme);
2370 return (WINDOW_COPY_CMD_NOTHING);
2373 static enum window_copy_cmd_action
2374 window_copy_cmd_jump_to_backward(struct window_copy_cmd_state *cs)
2376 struct window_mode_entry *wme = cs->wme;
2377 struct window_copy_mode_data *data = wme->data;
2378 u_int np = wme->prefix;
2379 const char *arg0 = args_string(cs->wargs, 0);
2381 if (*arg0 != '\0') {
2382 data->jumptype = WINDOW_COPY_JUMPTOBACKWARD;
2383 free(data->jumpchar);
2384 data->jumpchar = utf8_fromcstr(arg0);
2385 for (; np != 0; np--)
2386 window_copy_cursor_jump_to_back(wme);
2388 return (WINDOW_COPY_CMD_NOTHING);
2391 static enum window_copy_cmd_action
2392 window_copy_cmd_jump_to_forward(struct window_copy_cmd_state *cs)
2394 struct window_mode_entry *wme = cs->wme;
2395 struct window_copy_mode_data *data = wme->data;
2396 u_int np = wme->prefix;
2397 const char *arg0 = args_string(cs->wargs, 0);
2399 if (*arg0 != '\0') {
2400 data->jumptype = WINDOW_COPY_JUMPTOFORWARD;
2401 free(data->jumpchar);
2402 data->jumpchar = utf8_fromcstr(arg0);
2403 for (; np != 0; np--)
2404 window_copy_cursor_jump_to(wme);
2406 return (WINDOW_COPY_CMD_NOTHING);
2409 static enum window_copy_cmd_action
2410 window_copy_cmd_jump_to_mark(struct window_copy_cmd_state *cs)
2412 struct window_mode_entry *wme = cs->wme;
2414 window_copy_jump_to_mark(wme);
2415 return (WINDOW_COPY_CMD_NOTHING);
2418 static enum window_copy_cmd_action
2419 window_copy_cmd_next_prompt(struct window_copy_cmd_state *cs)
2421 struct window_mode_entry *wme = cs->wme;
2423 window_copy_cursor_prompt(wme, 1, args_has(cs->wargs, 'o'));
2424 return (WINDOW_COPY_CMD_NOTHING);
2427 static enum window_copy_cmd_action
2428 window_copy_cmd_previous_prompt(struct window_copy_cmd_state *cs)
2430 struct window_mode_entry *wme = cs->wme;
2432 window_copy_cursor_prompt(wme, 0, args_has(cs->wargs, 'o'));
2433 return (WINDOW_COPY_CMD_NOTHING);
2436 static enum window_copy_cmd_action
2437 window_copy_cmd_search_backward(struct window_copy_cmd_state *cs)
2439 struct window_mode_entry *wme = cs->wme;
2440 struct window_copy_mode_data *data = wme->data;
2441 u_int np = wme->prefix;
2443 if (!window_copy_expand_search_string(cs))
2444 return (WINDOW_COPY_CMD_NOTHING);
2446 if (data->searchstr != NULL) {
2447 data->searchtype = WINDOW_COPY_SEARCHUP;
2448 data->searchregex = 1;
2449 data->timeout = 0;
2450 for (; np != 0; np--)
2451 window_copy_search_up(wme, 1);
2453 return (WINDOW_COPY_CMD_NOTHING);
2456 static enum window_copy_cmd_action
2457 window_copy_cmd_search_backward_text(struct window_copy_cmd_state *cs)
2459 struct window_mode_entry *wme = cs->wme;
2460 struct window_copy_mode_data *data = wme->data;
2461 u_int np = wme->prefix;
2463 if (!window_copy_expand_search_string(cs))
2464 return (WINDOW_COPY_CMD_NOTHING);
2466 if (data->searchstr != NULL) {
2467 data->searchtype = WINDOW_COPY_SEARCHUP;
2468 data->searchregex = 0;
2469 data->timeout = 0;
2470 for (; np != 0; np--)
2471 window_copy_search_up(wme, 0);
2473 return (WINDOW_COPY_CMD_NOTHING);
2476 static enum window_copy_cmd_action
2477 window_copy_cmd_search_forward(struct window_copy_cmd_state *cs)
2479 struct window_mode_entry *wme = cs->wme;
2480 struct window_copy_mode_data *data = wme->data;
2481 u_int np = wme->prefix;
2483 if (!window_copy_expand_search_string(cs))
2484 return (WINDOW_COPY_CMD_NOTHING);
2486 if (data->searchstr != NULL) {
2487 data->searchtype = WINDOW_COPY_SEARCHDOWN;
2488 data->searchregex = 1;
2489 data->timeout = 0;
2490 for (; np != 0; np--)
2491 window_copy_search_down(wme, 1);
2493 return (WINDOW_COPY_CMD_NOTHING);
2496 static enum window_copy_cmd_action
2497 window_copy_cmd_search_forward_text(struct window_copy_cmd_state *cs)
2499 struct window_mode_entry *wme = cs->wme;
2500 struct window_copy_mode_data *data = wme->data;
2501 u_int np = wme->prefix;
2503 if (!window_copy_expand_search_string(cs))
2504 return (WINDOW_COPY_CMD_NOTHING);
2506 if (data->searchstr != NULL) {
2507 data->searchtype = WINDOW_COPY_SEARCHDOWN;
2508 data->searchregex = 0;
2509 data->timeout = 0;
2510 for (; np != 0; np--)
2511 window_copy_search_down(wme, 0);
2513 return (WINDOW_COPY_CMD_NOTHING);
2516 static enum window_copy_cmd_action
2517 window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs)
2519 struct window_mode_entry *wme = cs->wme;
2520 struct window_copy_mode_data *data = wme->data;
2521 const char *arg0 = args_string(cs->wargs, 0);
2522 const char *ss = data->searchstr;
2523 char prefix;
2524 enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING;
2526 data->timeout = 0;
2528 log_debug("%s: %s", __func__, arg0);
2530 prefix = *arg0++;
2531 if (data->searchx == -1 || data->searchy == -1) {
2532 data->searchx = data->cx;
2533 data->searchy = data->cy;
2534 data->searcho = data->oy;
2535 } else if (ss != NULL && strcmp(arg0, ss) != 0) {
2536 data->cx = data->searchx;
2537 data->cy = data->searchy;
2538 data->oy = data->searcho;
2539 action = WINDOW_COPY_CMD_REDRAW;
2541 if (*arg0 == '\0') {
2542 window_copy_clear_marks(wme);
2543 return (WINDOW_COPY_CMD_REDRAW);
2545 switch (prefix) {
2546 case '=':
2547 case '-':
2548 data->searchtype = WINDOW_COPY_SEARCHUP;
2549 data->searchregex = 0;
2550 free(data->searchstr);
2551 data->searchstr = xstrdup(arg0);
2552 if (!window_copy_search_up(wme, 0)) {
2553 window_copy_clear_marks(wme);
2554 return (WINDOW_COPY_CMD_REDRAW);
2556 break;
2557 case '+':
2558 data->searchtype = WINDOW_COPY_SEARCHDOWN;
2559 data->searchregex = 0;
2560 free(data->searchstr);
2561 data->searchstr = xstrdup(arg0);
2562 if (!window_copy_search_down(wme, 0)) {
2563 window_copy_clear_marks(wme);
2564 return (WINDOW_COPY_CMD_REDRAW);
2566 break;
2568 return (action);
2571 static enum window_copy_cmd_action
2572 window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs)
2574 struct window_mode_entry *wme = cs->wme;
2575 struct window_copy_mode_data *data = wme->data;
2576 const char *arg0 = args_string(cs->wargs, 0);
2577 const char *ss = data->searchstr;
2578 char prefix;
2579 enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING;
2581 data->timeout = 0;
2583 log_debug("%s: %s", __func__, arg0);
2585 prefix = *arg0++;
2586 if (data->searchx == -1 || data->searchy == -1) {
2587 data->searchx = data->cx;
2588 data->searchy = data->cy;
2589 data->searcho = data->oy;
2590 } else if (ss != NULL && strcmp(arg0, ss) != 0) {
2591 data->cx = data->searchx;
2592 data->cy = data->searchy;
2593 data->oy = data->searcho;
2594 action = WINDOW_COPY_CMD_REDRAW;
2596 if (*arg0 == '\0') {
2597 window_copy_clear_marks(wme);
2598 return (WINDOW_COPY_CMD_REDRAW);
2600 switch (prefix) {
2601 case '=':
2602 case '+':
2603 data->searchtype = WINDOW_COPY_SEARCHDOWN;
2604 data->searchregex = 0;
2605 free(data->searchstr);
2606 data->searchstr = xstrdup(arg0);
2607 if (!window_copy_search_down(wme, 0)) {
2608 window_copy_clear_marks(wme);
2609 return (WINDOW_COPY_CMD_REDRAW);
2611 break;
2612 case '-':
2613 data->searchtype = WINDOW_COPY_SEARCHUP;
2614 data->searchregex = 0;
2615 free(data->searchstr);
2616 data->searchstr = xstrdup(arg0);
2617 if (!window_copy_search_up(wme, 0)) {
2618 window_copy_clear_marks(wme);
2619 return (WINDOW_COPY_CMD_REDRAW);
2622 return (action);
2625 static enum window_copy_cmd_action
2626 window_copy_cmd_refresh_from_pane(struct window_copy_cmd_state *cs)
2628 struct window_mode_entry *wme = cs->wme;
2629 struct window_pane *wp = wme->swp;
2630 struct window_copy_mode_data *data = wme->data;
2632 if (data->viewmode)
2633 return (WINDOW_COPY_CMD_NOTHING);
2635 screen_free(data->backing);
2636 free(data->backing);
2637 data->backing = window_copy_clone_screen(&wp->base, &data->screen, NULL,
2638 NULL, wme->swp != wme->wp);
2640 window_copy_size_changed(wme);
2641 return (WINDOW_COPY_CMD_REDRAW);
2644 static const struct {
2645 const char *command;
2646 u_int minargs;
2647 u_int maxargs;
2648 struct args_parse args;
2649 enum window_copy_cmd_clear clear;
2650 enum window_copy_cmd_action (*f)(struct window_copy_cmd_state *);
2651 } window_copy_cmd_table[] = {
2652 { .command = "append-selection",
2653 .args = { "", 0, 0, NULL },
2654 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2655 .f = window_copy_cmd_append_selection
2657 { .command = "append-selection-and-cancel",
2658 .args = { "", 0, 0, NULL },
2659 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2660 .f = window_copy_cmd_append_selection_and_cancel
2662 { .command = "back-to-indentation",
2663 .args = { "", 0, 0, NULL },
2664 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2665 .f = window_copy_cmd_back_to_indentation
2667 { .command = "begin-selection",
2668 .args = { "", 0, 0, NULL },
2669 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2670 .f = window_copy_cmd_begin_selection
2672 { .command = "bottom-line",
2673 .args = { "", 0, 0, NULL },
2674 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2675 .f = window_copy_cmd_bottom_line
2677 { .command = "cancel",
2678 .args = { "", 0, 0, NULL },
2679 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2680 .f = window_copy_cmd_cancel
2682 { .command = "clear-selection",
2683 .args = { "", 0, 0, NULL },
2684 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2685 .f = window_copy_cmd_clear_selection
2687 { .command = "copy-end-of-line",
2688 .args = { "CP", 0, 1, NULL },
2689 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2690 .f = window_copy_cmd_copy_end_of_line
2692 { .command = "copy-end-of-line-and-cancel",
2693 .args = { "CP", 0, 1, NULL },
2694 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2695 .f = window_copy_cmd_copy_end_of_line_and_cancel
2697 { .command = "copy-pipe-end-of-line",
2698 .args = { "CP", 0, 2, NULL },
2699 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2700 .f = window_copy_cmd_copy_pipe_end_of_line
2702 { .command = "copy-pipe-end-of-line-and-cancel",
2703 .args = { "CP", 0, 2, NULL },
2704 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2705 .f = window_copy_cmd_copy_pipe_end_of_line_and_cancel
2707 { .command = "copy-line",
2708 .args = { "CP", 0, 1, NULL },
2709 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2710 .f = window_copy_cmd_copy_line
2712 { .command = "copy-line-and-cancel",
2713 .args = { "CP", 0, 1, NULL },
2714 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2715 .f = window_copy_cmd_copy_line_and_cancel
2717 { .command = "copy-pipe-line",
2718 .args = { "CP", 0, 2, NULL },
2719 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2720 .f = window_copy_cmd_copy_pipe_line
2722 { .command = "copy-pipe-line-and-cancel",
2723 .args = { "CP", 0, 2, NULL },
2724 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2725 .f = window_copy_cmd_copy_pipe_line_and_cancel
2727 { .command = "copy-pipe-no-clear",
2728 .args = { "CP", 0, 2, NULL },
2729 .clear = WINDOW_COPY_CMD_CLEAR_NEVER,
2730 .f = window_copy_cmd_copy_pipe_no_clear
2732 { .command = "copy-pipe",
2733 .args = { "CP", 0, 2, NULL },
2734 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2735 .f = window_copy_cmd_copy_pipe
2737 { .command = "copy-pipe-and-cancel",
2738 .args = { "CP", 0, 2, NULL },
2739 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2740 .f = window_copy_cmd_copy_pipe_and_cancel
2742 { .command = "copy-selection-no-clear",
2743 .args = { "CP", 0, 1, NULL },
2744 .clear = WINDOW_COPY_CMD_CLEAR_NEVER,
2745 .f = window_copy_cmd_copy_selection_no_clear
2747 { .command = "copy-selection",
2748 .args = { "CP", 0, 1, NULL },
2749 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2750 .f = window_copy_cmd_copy_selection
2752 { .command = "copy-selection-and-cancel",
2753 .args = { "CP", 0, 1, NULL },
2754 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2755 .f = window_copy_cmd_copy_selection_and_cancel
2757 { .command = "cursor-down",
2758 .args = { "", 0, 0, NULL },
2759 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2760 .f = window_copy_cmd_cursor_down
2762 { .command = "cursor-down-and-cancel",
2763 .args = { "", 0, 0, NULL },
2764 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2765 .f = window_copy_cmd_cursor_down_and_cancel
2767 { .command = "cursor-left",
2768 .args = { "", 0, 0, NULL },
2769 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2770 .f = window_copy_cmd_cursor_left
2772 { .command = "cursor-right",
2773 .args = { "", 0, 0, NULL },
2774 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2775 .f = window_copy_cmd_cursor_right
2777 { .command = "cursor-up",
2778 .args = { "", 0, 0, NULL },
2779 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2780 .f = window_copy_cmd_cursor_up
2782 { .command = "end-of-line",
2783 .args = { "", 0, 0, NULL },
2784 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2785 .f = window_copy_cmd_end_of_line
2787 { .command = "goto-line",
2788 .args = { "", 1, 1, NULL },
2789 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2790 .f = window_copy_cmd_goto_line
2792 { .command = "halfpage-down",
2793 .args = { "", 0, 0, NULL },
2794 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2795 .f = window_copy_cmd_halfpage_down
2797 { .command = "halfpage-down-and-cancel",
2798 .args = { "", 0, 0, NULL },
2799 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2800 .f = window_copy_cmd_halfpage_down_and_cancel
2802 { .command = "halfpage-up",
2803 .args = { "", 0, 0, NULL },
2804 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2805 .f = window_copy_cmd_halfpage_up
2807 { .command = "history-bottom",
2808 .args = { "", 0, 0, NULL },
2809 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2810 .f = window_copy_cmd_history_bottom
2812 { .command = "history-top",
2813 .args = { "", 0, 0, NULL },
2814 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2815 .f = window_copy_cmd_history_top
2817 { .command = "jump-again",
2818 .args = { "", 0, 0, NULL },
2819 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2820 .f = window_copy_cmd_jump_again
2822 { .command = "jump-backward",
2823 .args = { "", 1, 1, NULL },
2824 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2825 .f = window_copy_cmd_jump_backward
2827 { .command = "jump-forward",
2828 .args = { "", 1, 1, NULL },
2829 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2830 .f = window_copy_cmd_jump_forward
2832 { .command = "jump-reverse",
2833 .args = { "", 0, 0, NULL },
2834 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2835 .f = window_copy_cmd_jump_reverse
2837 { .command = "jump-to-backward",
2838 .args = { "", 1, 1, NULL },
2839 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2840 .f = window_copy_cmd_jump_to_backward
2842 { .command = "jump-to-forward",
2843 .args = { "", 1, 1, NULL },
2844 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2845 .f = window_copy_cmd_jump_to_forward
2847 { .command = "jump-to-mark",
2848 .args = { "", 0, 0, NULL },
2849 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2850 .f = window_copy_cmd_jump_to_mark
2852 { .command = "next-prompt",
2853 .args = { "o", 0, 0, NULL },
2854 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2855 .f = window_copy_cmd_next_prompt
2857 { .command = "previous-prompt",
2858 .args = { "o", 0, 0, NULL },
2859 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2860 .f = window_copy_cmd_previous_prompt
2862 { .command = "middle-line",
2863 .args = { "", 0, 0, NULL },
2864 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2865 .f = window_copy_cmd_middle_line
2867 { .command = "next-matching-bracket",
2868 .args = { "", 0, 0, NULL },
2869 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2870 .f = window_copy_cmd_next_matching_bracket
2872 { .command = "next-paragraph",
2873 .args = { "", 0, 0, NULL },
2874 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2875 .f = window_copy_cmd_next_paragraph
2877 { .command = "next-space",
2878 .args = { "", 0, 0, NULL },
2879 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2880 .f = window_copy_cmd_next_space
2882 { .command = "next-space-end",
2883 .args = { "", 0, 0, NULL },
2884 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2885 .f = window_copy_cmd_next_space_end
2887 { .command = "next-word",
2888 .args = { "", 0, 0, NULL },
2889 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2890 .f = window_copy_cmd_next_word
2892 { .command = "next-word-end",
2893 .args = { "", 0, 0, NULL },
2894 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2895 .f = window_copy_cmd_next_word_end
2897 { .command = "other-end",
2898 .args = { "", 0, 0, NULL },
2899 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2900 .f = window_copy_cmd_other_end
2902 { .command = "page-down",
2903 .args = { "", 0, 0, NULL },
2904 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2905 .f = window_copy_cmd_page_down
2907 { .command = "page-down-and-cancel",
2908 .args = { "", 0, 0, NULL },
2909 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2910 .f = window_copy_cmd_page_down_and_cancel
2912 { .command = "page-up",
2913 .args = { "", 0, 0, NULL },
2914 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2915 .f = window_copy_cmd_page_up
2917 { .command = "pipe-no-clear",
2918 .args = { "", 0, 1, NULL },
2919 .clear = WINDOW_COPY_CMD_CLEAR_NEVER,
2920 .f = window_copy_cmd_pipe_no_clear
2922 { .command = "pipe",
2923 .args = { "", 0, 1, NULL },
2924 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2925 .f = window_copy_cmd_pipe
2927 { .command = "pipe-and-cancel",
2928 .args = { "", 0, 1, NULL },
2929 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2930 .f = window_copy_cmd_pipe_and_cancel
2932 { .command = "previous-matching-bracket",
2933 .args = { "", 0, 0, NULL },
2934 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2935 .f = window_copy_cmd_previous_matching_bracket
2937 { .command = "previous-paragraph",
2938 .args = { "", 0, 0, NULL },
2939 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2940 .f = window_copy_cmd_previous_paragraph
2942 { .command = "previous-space",
2943 .args = { "", 0, 0, NULL },
2944 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2945 .f = window_copy_cmd_previous_space
2947 { .command = "previous-word",
2948 .args = { "", 0, 0, NULL },
2949 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2950 .f = window_copy_cmd_previous_word
2952 { .command = "rectangle-on",
2953 .args = { "", 0, 0, NULL },
2954 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2955 .f = window_copy_cmd_rectangle_on
2957 { .command = "rectangle-off",
2958 .args = { "", 0, 0, NULL },
2959 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2960 .f = window_copy_cmd_rectangle_off
2962 { .command = "rectangle-toggle",
2963 .args = { "", 0, 0, NULL },
2964 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2965 .f = window_copy_cmd_rectangle_toggle
2967 { .command = "refresh-from-pane",
2968 .args = { "", 0, 0, NULL },
2969 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2970 .f = window_copy_cmd_refresh_from_pane
2972 { .command = "scroll-bottom",
2973 .args = { "", 0, 0, NULL },
2974 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2975 .f = window_copy_cmd_scroll_bottom
2977 { .command = "scroll-down",
2978 .args = { "", 0, 0, NULL },
2979 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2980 .f = window_copy_cmd_scroll_down
2982 { .command = "scroll-down-and-cancel",
2983 .args = { "", 0, 0, NULL },
2984 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2985 .f = window_copy_cmd_scroll_down_and_cancel
2987 { .command = "scroll-middle",
2988 .args = { "", 0, 0, NULL },
2989 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2990 .f = window_copy_cmd_scroll_middle
2992 { .command = "scroll-top",
2993 .args = { "", 0, 0, NULL },
2994 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2995 .f = window_copy_cmd_scroll_top
2997 { .command = "scroll-up",
2998 .args = { "", 0, 0, NULL },
2999 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
3000 .f = window_copy_cmd_scroll_up
3002 { .command = "search-again",
3003 .args = { "", 0, 0, NULL },
3004 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
3005 .f = window_copy_cmd_search_again
3007 { .command = "search-backward",
3008 .args = { "", 0, 1, NULL },
3009 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
3010 .f = window_copy_cmd_search_backward
3012 { .command = "search-backward-text",
3013 .args = { "", 0, 1, NULL },
3014 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
3015 .f = window_copy_cmd_search_backward_text
3017 { .command = "search-backward-incremental",
3018 .args = { "", 1, 1, NULL },
3019 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
3020 .f = window_copy_cmd_search_backward_incremental
3022 { .command = "search-forward",
3023 .args = { "", 0, 1, NULL },
3024 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
3025 .f = window_copy_cmd_search_forward
3027 { .command = "search-forward-text",
3028 .args = { "", 0, 1, NULL },
3029 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
3030 .f = window_copy_cmd_search_forward_text
3032 { .command = "search-forward-incremental",
3033 .args = { "", 1, 1, NULL },
3034 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
3035 .f = window_copy_cmd_search_forward_incremental
3037 { .command = "search-reverse",
3038 .args = { "", 0, 0, NULL },
3039 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
3040 .f = window_copy_cmd_search_reverse
3042 { .command = "select-line",
3043 .args = { "", 0, 0, NULL },
3044 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
3045 .f = window_copy_cmd_select_line
3047 { .command = "select-word",
3048 .args = { "", 0, 0, NULL },
3049 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
3050 .f = window_copy_cmd_select_word
3052 { .command = "set-mark",
3053 .args = { "", 0, 0, NULL },
3054 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
3055 .f = window_copy_cmd_set_mark
3057 { .command = "start-of-line",
3058 .args = { "", 0, 0, NULL },
3059 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
3060 .f = window_copy_cmd_start_of_line
3062 { .command = "stop-selection",
3063 .args = { "", 0, 0, NULL },
3064 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
3065 .f = window_copy_cmd_stop_selection
3067 { .command = "toggle-position",
3068 .args = { "", 0, 0, NULL },
3069 .clear = WINDOW_COPY_CMD_CLEAR_NEVER,
3070 .f = window_copy_cmd_toggle_position
3072 { .command = "top-line",
3073 .args = { "", 0, 0, NULL },
3074 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
3075 .f = window_copy_cmd_top_line
3079 static void
3080 window_copy_command(struct window_mode_entry *wme, struct client *c,
3081 struct session *s, struct winlink *wl, struct args *args,
3082 struct mouse_event *m)
3084 struct window_copy_mode_data *data = wme->data;
3085 struct window_pane *wp = wme->wp;
3086 struct window_copy_cmd_state cs;
3087 enum window_copy_cmd_action action;
3088 enum window_copy_cmd_clear clear = WINDOW_COPY_CMD_CLEAR_NEVER;
3089 const char *command;
3090 u_int i, count = args_count(args);
3091 int keys;
3092 char *error = NULL;
3094 if (count == 0)
3095 return;
3096 command = args_string(args, 0);
3098 if (m != NULL && m->valid && !MOUSE_WHEEL(m->b))
3099 window_copy_move_mouse(m);
3101 cs.wme = wme;
3102 cs.args = args;
3103 cs.wargs = NULL;
3104 cs.m = m;
3106 cs.c = c;
3107 cs.s = s;
3108 cs.wl = wl;
3110 action = WINDOW_COPY_CMD_NOTHING;
3111 for (i = 0; i < nitems(window_copy_cmd_table); i++) {
3112 if (strcmp(window_copy_cmd_table[i].command, command) == 0) {
3113 cs.wargs = args_parse(&window_copy_cmd_table[i].args,
3114 args_values(args), count, &error);
3116 if (error != NULL) {
3117 free(error);
3118 error = NULL;
3120 if (cs.wargs == NULL)
3121 break;
3123 clear = window_copy_cmd_table[i].clear;
3124 action = window_copy_cmd_table[i].f(&cs);
3125 args_free(cs.wargs);
3126 cs.wargs = NULL;
3127 break;
3131 if (strncmp(command, "search-", 7) != 0 && data->searchmark != NULL) {
3132 keys = options_get_number(wp->window->options, "mode-keys");
3133 if (clear == WINDOW_COPY_CMD_CLEAR_EMACS_ONLY &&
3134 keys == MODEKEY_VI)
3135 clear = WINDOW_COPY_CMD_CLEAR_NEVER;
3136 if (clear != WINDOW_COPY_CMD_CLEAR_NEVER) {
3137 window_copy_clear_marks(wme);
3138 data->searchx = data->searchy = -1;
3140 if (action == WINDOW_COPY_CMD_NOTHING)
3141 action = WINDOW_COPY_CMD_REDRAW;
3143 wme->prefix = 1;
3145 if (action == WINDOW_COPY_CMD_CANCEL)
3146 window_pane_reset_mode(wp);
3147 else if (action == WINDOW_COPY_CMD_REDRAW)
3148 window_copy_redraw_screen(wme);
3151 static void
3152 window_copy_scroll_to(struct window_mode_entry *wme, u_int px, u_int py,
3153 int no_redraw)
3155 struct window_copy_mode_data *data = wme->data;
3156 struct grid *gd = data->backing->grid;
3157 u_int offset, gap;
3159 data->cx = px;
3161 if (py >= gd->hsize - data->oy && py < gd->hsize - data->oy + gd->sy)
3162 data->cy = py - (gd->hsize - data->oy);
3163 else {
3164 gap = gd->sy / 4;
3165 if (py < gd->sy) {
3166 offset = 0;
3167 data->cy = py;
3168 } else if (py > gd->hsize + gd->sy - gap) {
3169 offset = gd->hsize;
3170 data->cy = py - gd->hsize;
3171 } else {
3172 offset = py + gap - gd->sy;
3173 data->cy = py - offset;
3175 data->oy = gd->hsize - offset;
3178 if (!no_redraw && data->searchmark != NULL && !data->timeout)
3179 window_copy_search_marks(wme, NULL, data->searchregex, 1);
3180 window_copy_update_selection(wme, 1, 0);
3181 if (!no_redraw)
3182 window_copy_redraw_screen(wme);
3185 static int
3186 window_copy_search_compare(struct grid *gd, u_int px, u_int py,
3187 struct grid *sgd, u_int spx, int cis)
3189 struct grid_cell gc, sgc;
3190 const struct utf8_data *ud, *sud;
3192 grid_get_cell(gd, px, py, &gc);
3193 ud = &gc.data;
3194 grid_get_cell(sgd, spx, 0, &sgc);
3195 sud = &sgc.data;
3197 if (*sud->data == '\t' && sud->size == 1 && gc.flags & GRID_FLAG_TAB)
3198 return (1);
3200 if (ud->size != sud->size || ud->width != sud->width)
3201 return (0);
3203 if (cis && ud->size == 1)
3204 return (tolower(ud->data[0]) == sud->data[0]);
3206 return (memcmp(ud->data, sud->data, ud->size) == 0);
3209 static int
3210 window_copy_search_lr(struct grid *gd, struct grid *sgd, u_int *ppx, u_int py,
3211 u_int first, u_int last, int cis)
3213 u_int ax, bx, px, pywrap, endline, padding;
3214 int matched;
3215 struct grid_line *gl;
3216 struct grid_cell gc;
3218 endline = gd->hsize + gd->sy - 1;
3219 for (ax = first; ax < last; ax++) {
3220 padding = 0;
3221 for (bx = 0; bx < sgd->sx; bx++) {
3222 px = ax + bx + padding;
3223 pywrap = py;
3224 /* Wrap line. */
3225 while (px >= gd->sx && pywrap < endline) {
3226 gl = grid_get_line(gd, pywrap);
3227 if (~gl->flags & GRID_LINE_WRAPPED)
3228 break;
3229 px -= gd->sx;
3230 pywrap++;
3232 /* We have run off the end of the grid. */
3233 if (px - padding >= gd->sx)
3234 break;
3236 grid_get_cell(gd, px, pywrap, &gc);
3237 if (gc.flags & GRID_FLAG_TAB)
3238 padding += gc.data.width - 1;
3240 matched = window_copy_search_compare(gd, px, pywrap,
3241 sgd, bx, cis);
3242 if (!matched)
3243 break;
3245 if (bx == sgd->sx) {
3246 *ppx = ax;
3247 return (1);
3250 return (0);
3253 static int
3254 window_copy_search_rl(struct grid *gd,
3255 struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis)
3257 u_int ax, bx, px, pywrap, endline, padding;
3258 int matched;
3259 struct grid_line *gl;
3260 struct grid_cell gc;
3262 endline = gd->hsize + gd->sy - 1;
3263 for (ax = last; ax > first; ax--) {
3264 padding = 0;
3265 for (bx = 0; bx < sgd->sx; bx++) {
3266 px = ax - 1 + bx + padding;
3267 pywrap = py;
3268 /* Wrap line. */
3269 while (px >= gd->sx && pywrap < endline) {
3270 gl = grid_get_line(gd, pywrap);
3271 if (~gl->flags & GRID_LINE_WRAPPED)
3272 break;
3273 px -= gd->sx;
3274 pywrap++;
3276 /* We have run off the end of the grid. */
3277 if (px - padding >= gd->sx)
3278 break;
3280 grid_get_cell(gd, px, pywrap, &gc);
3281 if (gc.flags & GRID_FLAG_TAB)
3282 padding += gc.data.width - 1;
3284 matched = window_copy_search_compare(gd, px, pywrap,
3285 sgd, bx, cis);
3286 if (!matched)
3287 break;
3289 if (bx == sgd->sx) {
3290 *ppx = ax - 1;
3291 return (1);
3294 return (0);
3297 static int
3298 window_copy_search_lr_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py,
3299 u_int first, u_int last, regex_t *reg)
3301 int eflags = 0;
3302 u_int endline, foundx, foundy, len, pywrap, size = 1;
3303 char *buf;
3304 regmatch_t regmatch;
3305 struct grid_line *gl;
3308 * This can happen during search if the last match was the last
3309 * character on a line.
3311 if (first >= last)
3312 return (0);
3314 /* Set flags for regex search. */
3315 if (first != 0)
3316 eflags |= REG_NOTBOL;
3318 /* Need to look at the entire string. */
3319 buf = xmalloc(size);
3320 buf[0] = '\0';
3321 buf = window_copy_stringify(gd, py, first, gd->sx, buf, &size);
3322 len = gd->sx - first;
3323 endline = gd->hsize + gd->sy - 1;
3324 pywrap = py;
3325 while (buf != NULL &&
3326 pywrap <= endline &&
3327 len < WINDOW_COPY_SEARCH_MAX_LINE) {
3328 gl = grid_get_line(gd, pywrap);
3329 if (~gl->flags & GRID_LINE_WRAPPED)
3330 break;
3331 pywrap++;
3332 buf = window_copy_stringify(gd, pywrap, 0, gd->sx, buf, &size);
3333 len += gd->sx;
3336 if (regexec(reg, buf, 1, &regmatch, eflags) == 0 &&
3337 regmatch.rm_so != regmatch.rm_eo) {
3338 foundx = first;
3339 foundy = py;
3340 window_copy_cstrtocellpos(gd, len, &foundx, &foundy,
3341 buf + regmatch.rm_so);
3342 if (foundy == py && foundx < last) {
3343 *ppx = foundx;
3344 len -= foundx - first;
3345 window_copy_cstrtocellpos(gd, len, &foundx, &foundy,
3346 buf + regmatch.rm_eo);
3347 *psx = foundx;
3348 while (foundy > py) {
3349 *psx += gd->sx;
3350 foundy--;
3352 *psx -= *ppx;
3353 free(buf);
3354 return (1);
3358 free(buf);
3359 *ppx = 0;
3360 *psx = 0;
3361 return (0);
3364 static int
3365 window_copy_search_rl_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py,
3366 u_int first, u_int last, regex_t *reg)
3368 int eflags = 0;
3369 u_int endline, len, pywrap, size = 1;
3370 char *buf;
3371 struct grid_line *gl;
3373 /* Set flags for regex search. */
3374 if (first != 0)
3375 eflags |= REG_NOTBOL;
3377 /* Need to look at the entire string. */
3378 buf = xmalloc(size);
3379 buf[0] = '\0';
3380 buf = window_copy_stringify(gd, py, first, gd->sx, buf, &size);
3381 len = gd->sx - first;
3382 endline = gd->hsize + gd->sy - 1;
3383 pywrap = py;
3384 while (buf != NULL &&
3385 pywrap <= endline &&
3386 len < WINDOW_COPY_SEARCH_MAX_LINE) {
3387 gl = grid_get_line(gd, pywrap);
3388 if (~gl->flags & GRID_LINE_WRAPPED)
3389 break;
3390 pywrap++;
3391 buf = window_copy_stringify(gd, pywrap, 0, gd->sx, buf, &size);
3392 len += gd->sx;
3395 if (window_copy_last_regex(gd, py, first, last, len, ppx, psx, buf,
3396 reg, eflags))
3398 free(buf);
3399 return (1);
3402 free(buf);
3403 *ppx = 0;
3404 *psx = 0;
3405 return (0);
3408 static const char *
3409 window_copy_cellstring(const struct grid_line *gl, u_int px, size_t *size,
3410 int *allocated)
3412 static struct utf8_data ud;
3413 struct grid_cell_entry *gce;
3414 char *copy;
3416 if (px >= gl->cellsize) {
3417 *size = 1;
3418 *allocated = 0;
3419 return (" ");
3422 gce = &gl->celldata[px];
3423 if (gce->flags & GRID_FLAG_PADDING) {
3424 *size = 0;
3425 *allocated = 0;
3426 return (NULL);
3428 if (~gce->flags & GRID_FLAG_EXTENDED) {
3429 *size = 1;
3430 *allocated = 0;
3431 return (&gce->data.data);
3433 if (gce->flags & GRID_FLAG_TAB) {
3434 *size = 1;
3435 *allocated = 0;
3436 return ("\t");
3439 utf8_to_data(gl->extddata[gce->offset].data, &ud);
3440 if (ud.size == 0) {
3441 *size = 0;
3442 *allocated = 0;
3443 return (NULL);
3445 *size = ud.size;
3446 *allocated = 1;
3448 copy = xmalloc(ud.size);
3449 memcpy(copy, ud.data, ud.size);
3450 return (copy);
3453 /* Find last match in given range. */
3454 static int
3455 window_copy_last_regex(struct grid *gd, u_int py, u_int first, u_int last,
3456 u_int len, u_int *ppx, u_int *psx, const char *buf, const regex_t *preg,
3457 int eflags)
3459 u_int foundx, foundy, oldx, px = 0, savepx, savesx = 0;
3460 regmatch_t regmatch;
3462 foundx = first;
3463 foundy = py;
3464 oldx = first;
3465 while (regexec(preg, buf + px, 1, &regmatch, eflags) == 0) {
3466 if (regmatch.rm_so == regmatch.rm_eo)
3467 break;
3468 window_copy_cstrtocellpos(gd, len, &foundx, &foundy,
3469 buf + px + regmatch.rm_so);
3470 if (foundy > py || foundx >= last)
3471 break;
3472 len -= foundx - oldx;
3473 savepx = foundx;
3474 window_copy_cstrtocellpos(gd, len, &foundx, &foundy,
3475 buf + px + regmatch.rm_eo);
3476 if (foundy > py || foundx >= last) {
3477 *ppx = savepx;
3478 *psx = foundx;
3479 while (foundy > py) {
3480 *psx += gd->sx;
3481 foundy--;
3483 *psx -= *ppx;
3484 return (1);
3485 } else {
3486 savesx = foundx - savepx;
3487 len -= savesx;
3488 oldx = foundx;
3490 px += regmatch.rm_eo;
3493 if (savesx > 0) {
3494 *ppx = savepx;
3495 *psx = savesx;
3496 return (1);
3497 } else {
3498 *ppx = 0;
3499 *psx = 0;
3500 return (0);
3504 /* Stringify line and append to input buffer. Caller frees. */
3505 static char *
3506 window_copy_stringify(struct grid *gd, u_int py, u_int first, u_int last,
3507 char *buf, u_int *size)
3509 u_int ax, bx, newsize = *size;
3510 const struct grid_line *gl;
3511 const char *d;
3512 size_t bufsize = 1024, dlen;
3513 int allocated;
3515 while (bufsize < newsize)
3516 bufsize *= 2;
3517 buf = xrealloc(buf, bufsize);
3519 gl = grid_peek_line(gd, py);
3520 bx = *size - 1;
3521 for (ax = first; ax < last; ax++) {
3522 d = window_copy_cellstring(gl, ax, &dlen, &allocated);
3523 newsize += dlen;
3524 while (bufsize < newsize) {
3525 bufsize *= 2;
3526 buf = xrealloc(buf, bufsize);
3528 if (dlen == 1)
3529 buf[bx++] = *d;
3530 else {
3531 memcpy(buf + bx, d, dlen);
3532 bx += dlen;
3534 if (allocated)
3535 free((void *)d);
3537 buf[newsize - 1] = '\0';
3539 *size = newsize;
3540 return (buf);
3543 /* Map start of C string containing UTF-8 data to grid cell position. */
3544 static void
3545 window_copy_cstrtocellpos(struct grid *gd, u_int ncells, u_int *ppx, u_int *ppy,
3546 const char *str)
3548 u_int cell, ccell, px, pywrap, pos, len;
3549 int match;
3550 const struct grid_line *gl;
3551 const char *d;
3552 size_t dlen;
3553 struct {
3554 const char *d;
3555 size_t dlen;
3556 int allocated;
3557 } *cells;
3559 /* Populate the array of cell data. */
3560 cells = xreallocarray(NULL, ncells, sizeof cells[0]);
3561 cell = 0;
3562 px = *ppx;
3563 pywrap = *ppy;
3564 gl = grid_peek_line(gd, pywrap);
3565 while (cell < ncells) {
3566 cells[cell].d = window_copy_cellstring(gl, px,
3567 &cells[cell].dlen, &cells[cell].allocated);
3568 cell++;
3569 px++;
3570 if (px == gd->sx) {
3571 px = 0;
3572 pywrap++;
3573 gl = grid_peek_line(gd, pywrap);
3577 /* Locate starting cell. */
3578 cell = 0;
3579 len = strlen(str);
3580 while (cell < ncells) {
3581 ccell = cell;
3582 pos = 0;
3583 match = 1;
3584 while (ccell < ncells) {
3585 if (str[pos] == '\0') {
3586 match = 0;
3587 break;
3589 d = cells[ccell].d;
3590 dlen = cells[ccell].dlen;
3591 if (dlen == 1) {
3592 if (str[pos] != *d) {
3593 match = 0;
3594 break;
3596 pos++;
3597 } else {
3598 if (dlen > len - pos)
3599 dlen = len - pos;
3600 if (memcmp(str + pos, d, dlen) != 0) {
3601 match = 0;
3602 break;
3604 pos += dlen;
3606 ccell++;
3608 if (match)
3609 break;
3610 cell++;
3613 /* If not found this will be one past the end. */
3614 px = *ppx + cell;
3615 pywrap = *ppy;
3616 while (px >= gd->sx) {
3617 px -= gd->sx;
3618 pywrap++;
3621 *ppx = px;
3622 *ppy = pywrap;
3624 /* Free cell data. */
3625 for (cell = 0; cell < ncells; cell++) {
3626 if (cells[cell].allocated)
3627 free((void *)cells[cell].d);
3629 free(cells);
3632 static void
3633 window_copy_move_left(struct screen *s, u_int *fx, u_int *fy, int wrapflag)
3635 if (*fx == 0) { /* left */
3636 if (*fy == 0) { /* top */
3637 if (wrapflag) {
3638 *fx = screen_size_x(s) - 1;
3639 *fy = screen_hsize(s) + screen_size_y(s) - 1;
3641 return;
3643 *fx = screen_size_x(s) - 1;
3644 *fy = *fy - 1;
3645 } else
3646 *fx = *fx - 1;
3649 static void
3650 window_copy_move_right(struct screen *s, u_int *fx, u_int *fy, int wrapflag)
3652 if (*fx == screen_size_x(s) - 1) { /* right */
3653 if (*fy == screen_hsize(s) + screen_size_y(s) - 1) { /* bottom */
3654 if (wrapflag) {
3655 *fx = 0;
3656 *fy = 0;
3658 return;
3660 *fx = 0;
3661 *fy = *fy + 1;
3662 } else
3663 *fx = *fx + 1;
3666 static int
3667 window_copy_is_lowercase(const char *ptr)
3669 while (*ptr != '\0') {
3670 if (*ptr != tolower((u_char)*ptr))
3671 return (0);
3672 ++ptr;
3674 return (1);
3678 * Handle backward wrapped regex searches with overlapping matches. In this case
3679 * find the longest overlapping match from previous wrapped lines.
3681 static void
3682 window_copy_search_back_overlap(struct grid *gd, regex_t *preg, u_int *ppx,
3683 u_int *psx, u_int *ppy, u_int endline)
3685 u_int endx, endy, oldendx, oldendy, px, py, sx;
3686 int found = 1;
3688 oldendx = *ppx + *psx;
3689 oldendy = *ppy - 1;
3690 while (oldendx > gd->sx - 1) {
3691 oldendx -= gd->sx;
3692 oldendy++;
3694 endx = oldendx;
3695 endy = oldendy;
3696 px = *ppx;
3697 py = *ppy;
3698 while (found && px == 0 && py - 1 > endline &&
3699 grid_get_line(gd, py - 2)->flags & GRID_LINE_WRAPPED &&
3700 endx == oldendx && endy == oldendy) {
3701 py--;
3702 found = window_copy_search_rl_regex(gd, &px, &sx, py - 1, 0,
3703 gd->sx, preg);
3704 if (found) {
3705 endx = px + sx;
3706 endy = py - 1;
3707 while (endx > gd->sx - 1) {
3708 endx -= gd->sx;
3709 endy++;
3711 if (endx == oldendx && endy == oldendy) {
3712 *ppx = px;
3713 *ppy = py;
3720 * Search for text stored in sgd starting from position fx,fy up to endline. If
3721 * found, jump to it. If cis then ignore case. The direction is 0 for searching
3722 * up, down otherwise. If wrap then go to begin/end of grid and try again if
3723 * not found.
3725 static int
3726 window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd,
3727 struct grid *sgd, u_int fx, u_int fy, u_int endline, int cis, int wrap,
3728 int direction, int regex)
3730 u_int i, px, sx, ssize = 1;
3731 int found = 0, cflags = REG_EXTENDED;
3732 char *sbuf;
3733 regex_t reg;
3735 if (regex) {
3736 sbuf = xmalloc(ssize);
3737 sbuf[0] = '\0';
3738 sbuf = window_copy_stringify(sgd, 0, 0, sgd->sx, sbuf, &ssize);
3739 if (cis)
3740 cflags |= REG_ICASE;
3741 if (regcomp(&reg, sbuf, cflags) != 0) {
3742 free(sbuf);
3743 return (0);
3745 free(sbuf);
3748 if (direction) {
3749 for (i = fy; i <= endline; i++) {
3750 if (regex) {
3751 found = window_copy_search_lr_regex(gd,
3752 &px, &sx, i, fx, gd->sx, &reg);
3753 } else {
3754 found = window_copy_search_lr(gd, sgd,
3755 &px, i, fx, gd->sx, cis);
3757 if (found)
3758 break;
3759 fx = 0;
3761 } else {
3762 for (i = fy + 1; endline < i; i--) {
3763 if (regex) {
3764 found = window_copy_search_rl_regex(gd,
3765 &px, &sx, i - 1, 0, fx + 1, &reg);
3766 if (found) {
3767 window_copy_search_back_overlap(gd,
3768 &reg, &px, &sx, &i, endline);
3770 } else {
3771 found = window_copy_search_rl(gd, sgd,
3772 &px, i - 1, 0, fx + 1, cis);
3774 if (found) {
3775 i--;
3776 break;
3778 fx = gd->sx - 1;
3781 if (regex)
3782 regfree(&reg);
3784 if (found) {
3785 window_copy_scroll_to(wme, px, i, 1);
3786 return (1);
3788 if (wrap) {
3789 return (window_copy_search_jump(wme, gd, sgd,
3790 direction ? 0 : gd->sx - 1,
3791 direction ? 0 : gd->hsize + gd->sy - 1, fy, cis, 0,
3792 direction, regex));
3794 return (0);
3797 static void
3798 window_copy_move_after_search_mark(struct window_copy_mode_data *data,
3799 u_int *fx, u_int *fy, int wrapflag)
3801 struct screen *s = data->backing;
3802 u_int at, start;
3804 if (window_copy_search_mark_at(data, *fx, *fy, &start) == 0 &&
3805 data->searchmark[start] != 0) {
3806 while (window_copy_search_mark_at(data, *fx, *fy, &at) == 0) {
3807 if (data->searchmark[at] != data->searchmark[start])
3808 break;
3809 /* Stop if not wrapping and at the end of the grid. */
3810 if (!wrapflag &&
3811 *fx == screen_size_x(s) - 1 &&
3812 *fy == screen_hsize(s) + screen_size_y(s) - 1)
3813 break;
3815 window_copy_move_right(s, fx, fy, wrapflag);
3821 * Search in for text searchstr. If direction is 0 then search up, otherwise
3822 * down.
3824 static int
3825 window_copy_search(struct window_mode_entry *wme, int direction, int regex)
3827 struct window_pane *wp = wme->wp;
3828 struct window_copy_mode_data *data = wme->data;
3829 struct screen *s = data->backing, ss;
3830 struct screen_write_ctx ctx;
3831 struct grid *gd = s->grid;
3832 const char *str = data->searchstr;
3833 u_int at, endline, fx, fy, start, ssx;
3834 int cis, found, keys, visible_only;
3835 int wrapflag;
3837 if (regex && str[strcspn(str, "^$*+()?[].\\")] == '\0')
3838 regex = 0;
3840 data->searchdirection = direction;
3842 if (data->timeout)
3843 return (0);
3845 if (data->searchall || wp->searchstr == NULL ||
3846 wp->searchregex != regex) {
3847 visible_only = 0;
3848 data->searchall = 0;
3849 } else
3850 visible_only = (strcmp(wp->searchstr, str) == 0);
3851 if (visible_only == 0 && data->searchmark != NULL)
3852 window_copy_clear_marks(wme);
3853 free(wp->searchstr);
3854 wp->searchstr = xstrdup(str);
3855 wp->searchregex = regex;
3857 fx = data->cx;
3858 fy = screen_hsize(data->backing) - data->oy + data->cy;
3860 if ((ssx = screen_write_strlen("%s", str)) == 0)
3861 return (0);
3862 screen_init(&ss, ssx, 1, 0);
3863 screen_write_start(&ctx, &ss);
3864 screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", str);
3865 screen_write_stop(&ctx);
3867 wrapflag = options_get_number(wp->window->options, "wrap-search");
3868 cis = window_copy_is_lowercase(str);
3870 keys = options_get_number(wp->window->options, "mode-keys");
3872 if (direction) {
3874 * Behave according to mode-keys. If it is emacs, search forward
3875 * leaves the cursor after the match. If it is vi, the cursor
3876 * remains at the beginning of the match, regardless of
3877 * direction, which means that we need to start the next search
3878 * after the term the cursor is currently on when searching
3879 * forward.
3881 if (keys == MODEKEY_VI) {
3882 if (data->searchmark != NULL)
3883 window_copy_move_after_search_mark(data, &fx,
3884 &fy, wrapflag);
3885 else {
3887 * When there are no search marks, start the
3888 * search after the current cursor position.
3890 window_copy_move_right(s, &fx, &fy, wrapflag);
3893 endline = gd->hsize + gd->sy - 1;
3894 } else {
3895 window_copy_move_left(s, &fx, &fy, wrapflag);
3896 endline = 0;
3899 found = window_copy_search_jump(wme, gd, ss.grid, fx, fy, endline, cis,
3900 wrapflag, direction, regex);
3901 if (found) {
3902 window_copy_search_marks(wme, &ss, regex, visible_only);
3903 fx = data->cx;
3904 fy = screen_hsize(data->backing) - data->oy + data->cy;
3907 * When searching forward, if the cursor is not at the beginning
3908 * of the mark, search again.
3910 if (direction &&
3911 window_copy_search_mark_at(data, fx, fy, &at) == 0 &&
3912 at > 0 &&
3913 data->searchmark != NULL &&
3914 data->searchmark[at] == data->searchmark[at - 1]) {
3915 window_copy_move_after_search_mark(data, &fx, &fy,
3916 wrapflag);
3917 window_copy_search_jump(wme, gd, ss.grid, fx,
3918 fy, endline, cis, wrapflag, direction,
3919 regex);
3920 fx = data->cx;
3921 fy = screen_hsize(data->backing) - data->oy + data->cy;
3924 if (direction) {
3926 * When in Emacs mode, position the cursor just after
3927 * the mark.
3929 if (keys == MODEKEY_EMACS) {
3930 window_copy_move_after_search_mark(data, &fx,
3931 &fy, wrapflag);
3932 data->cx = fx;
3933 data->cy = fy - screen_hsize(data->backing) +
3934 data-> oy;
3936 } else {
3938 * When searching backward, position the cursor at the
3939 * beginning of the mark.
3941 if (window_copy_search_mark_at(data, fx, fy,
3942 &start) == 0) {
3943 while (window_copy_search_mark_at(data, fx, fy,
3944 &at) == 0 &&
3945 data->searchmark != NULL &&
3946 data->searchmark[at] ==
3947 data->searchmark[start]) {
3948 data->cx = fx;
3949 data->cy = fy -
3950 screen_hsize(data->backing) +
3951 data-> oy;
3952 if (at == 0)
3953 break;
3955 window_copy_move_left(s, &fx, &fy, 0);
3960 window_copy_redraw_screen(wme);
3962 screen_free(&ss);
3963 return (found);
3966 static void
3967 window_copy_visible_lines(struct window_copy_mode_data *data, u_int *start,
3968 u_int *end)
3970 struct grid *gd = data->backing->grid;
3971 const struct grid_line *gl;
3973 for (*start = gd->hsize - data->oy; *start > 0; (*start)--) {
3974 gl = grid_peek_line(gd, (*start) - 1);
3975 if (~gl->flags & GRID_LINE_WRAPPED)
3976 break;
3978 *end = gd->hsize - data->oy + gd->sy;
3981 static int
3982 window_copy_search_mark_at(struct window_copy_mode_data *data, u_int px,
3983 u_int py, u_int *at)
3985 struct screen *s = data->backing;
3986 struct grid *gd = s->grid;
3988 if (py < gd->hsize - data->oy)
3989 return (-1);
3990 if (py > gd->hsize - data->oy + gd->sy - 1)
3991 return (-1);
3992 *at = ((py - (gd->hsize - data->oy)) * gd->sx) + px;
3993 return (0);
3996 static u_int
3997 window_copy_clip_width(u_int width, u_int b, u_int sx, u_int sy)
3999 return ((b + width > sx * sy) ? (sx * sy) - b : width);
4002 static u_int
4003 window_copy_search_mark_match(struct window_copy_mode_data *data, u_int px,
4004 u_int py, u_int width, int regex)
4006 struct grid *gd = data->backing->grid;
4007 struct grid_cell gc;
4008 u_int i, b, w = width, sx = gd->sx, sy = gd->sy;
4010 if (window_copy_search_mark_at(data, px, py, &b) == 0) {
4011 width = window_copy_clip_width(width, b, sx, sy);
4012 w = width;
4013 for (i = b; i < b + w; i++) {
4014 if (!regex) {
4015 grid_get_cell(gd, px + (i - b), py, &gc);
4016 if (gc.flags & GRID_FLAG_TAB)
4017 w += gc.data.width - 1;
4018 w = window_copy_clip_width(w, b, sx, sy);
4020 if (data->searchmark[i] != 0)
4021 continue;
4022 data->searchmark[i] = data->searchgen;
4024 if (data->searchgen == UCHAR_MAX)
4025 data->searchgen = 1;
4026 else
4027 data->searchgen++;
4030 return (w);
4033 static int
4034 window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp,
4035 int regex, int visible_only)
4037 struct window_copy_mode_data *data = wme->data;
4038 struct screen *s = data->backing, ss;
4039 struct screen_write_ctx ctx;
4040 struct grid *gd = s->grid;
4041 struct grid_cell gc;
4042 int found, cis, stopped = 0;
4043 int cflags = REG_EXTENDED;
4044 u_int px, py, nfound = 0, width;
4045 u_int ssize = 1, start, end, sx = gd->sx;
4046 u_int sy = gd->sy;
4047 char *sbuf;
4048 regex_t reg;
4049 uint64_t stop = 0, tstart, t;
4051 if (ssp == NULL) {
4052 width = screen_write_strlen("%s", data->searchstr);
4053 screen_init(&ss, width, 1, 0);
4054 screen_write_start(&ctx, &ss);
4055 screen_write_nputs(&ctx, -1, &grid_default_cell, "%s",
4056 data->searchstr);
4057 screen_write_stop(&ctx);
4058 ssp = &ss;
4059 } else
4060 width = screen_size_x(ssp);
4062 cis = window_copy_is_lowercase(data->searchstr);
4064 if (regex) {
4065 sbuf = xmalloc(ssize);
4066 sbuf[0] = '\0';
4067 sbuf = window_copy_stringify(ssp->grid, 0, 0, ssp->grid->sx,
4068 sbuf, &ssize);
4069 if (cis)
4070 cflags |= REG_ICASE;
4071 if (regcomp(&reg, sbuf, cflags) != 0) {
4072 free(sbuf);
4073 return (0);
4075 free(sbuf);
4077 tstart = get_timer();
4079 if (visible_only)
4080 window_copy_visible_lines(data, &start, &end);
4081 else {
4082 start = 0;
4083 end = gd->hsize + sy;
4084 stop = get_timer() + WINDOW_COPY_SEARCH_ALL_TIMEOUT;
4087 again:
4088 free(data->searchmark);
4089 data->searchmark = xcalloc(sx, sy);
4090 data->searchgen = 1;
4092 for (py = start; py < end; py++) {
4093 px = 0;
4094 for (;;) {
4095 if (regex) {
4096 found = window_copy_search_lr_regex(gd,
4097 &px, &width, py, px, sx, &reg);
4098 grid_get_cell(gd, px + width - 1, py, &gc);
4099 if (gc.data.width > 2)
4100 width += gc.data.width - 1;
4101 if (!found)
4102 break;
4103 } else {
4104 found = window_copy_search_lr(gd, ssp->grid,
4105 &px, py, px, sx, cis);
4106 if (!found)
4107 break;
4109 nfound++;
4110 px += window_copy_search_mark_match(data, px, py, width,
4111 regex);
4114 t = get_timer();
4115 if (t - tstart > WINDOW_COPY_SEARCH_TIMEOUT) {
4116 data->timeout = 1;
4117 break;
4119 if (stop != 0 && t > stop) {
4120 stopped = 1;
4121 break;
4124 if (data->timeout) {
4125 window_copy_clear_marks(wme);
4126 goto out;
4129 if (stopped && stop != 0) {
4130 /* Try again but just the visible context. */
4131 window_copy_visible_lines(data, &start, &end);
4132 stop = 0;
4133 goto again;
4136 if (!visible_only) {
4137 if (stopped) {
4138 if (nfound > 1000)
4139 data->searchcount = 1000;
4140 else if (nfound > 100)
4141 data->searchcount = 100;
4142 else if (nfound > 10)
4143 data->searchcount = 10;
4144 else
4145 data->searchcount = -1;
4146 data->searchmore = 1;
4147 } else {
4148 data->searchcount = nfound;
4149 data->searchmore = 0;
4153 out:
4154 if (ssp == &ss)
4155 screen_free(&ss);
4156 if (regex)
4157 regfree(&reg);
4158 return (1);
4161 static void
4162 window_copy_clear_marks(struct window_mode_entry *wme)
4164 struct window_copy_mode_data *data = wme->data;
4166 free(data->searchmark);
4167 data->searchmark = NULL;
4170 static int
4171 window_copy_search_up(struct window_mode_entry *wme, int regex)
4173 return (window_copy_search(wme, 0, regex));
4176 static int
4177 window_copy_search_down(struct window_mode_entry *wme, int regex)
4179 return (window_copy_search(wme, 1, regex));
4182 static void
4183 window_copy_goto_line(struct window_mode_entry *wme, const char *linestr)
4185 struct window_copy_mode_data *data = wme->data;
4186 const char *errstr;
4187 int lineno;
4189 lineno = strtonum(linestr, -1, INT_MAX, &errstr);
4190 if (errstr != NULL)
4191 return;
4192 if (lineno < 0 || (u_int)lineno > screen_hsize(data->backing))
4193 lineno = screen_hsize(data->backing);
4195 data->oy = lineno;
4196 window_copy_update_selection(wme, 1, 0);
4197 window_copy_redraw_screen(wme);
4200 static void
4201 window_copy_match_start_end(struct window_copy_mode_data *data, u_int at,
4202 u_int *start, u_int *end)
4204 struct grid *gd = data->backing->grid;
4205 u_int last = (gd->sy * gd->sx) - 1;
4206 u_char mark = data->searchmark[at];
4208 *start = *end = at;
4209 while (*start != 0 && data->searchmark[*start] == mark)
4210 (*start)--;
4211 if (data->searchmark[*start] != mark)
4212 (*start)++;
4213 while (*end != last && data->searchmark[*end] == mark)
4214 (*end)++;
4215 if (data->searchmark[*end] != mark)
4216 (*end)--;
4219 static char *
4220 window_copy_match_at_cursor(struct window_copy_mode_data *data)
4222 struct grid *gd = data->backing->grid;
4223 struct grid_cell gc;
4224 u_int at, start, end, cy, px, py;
4225 u_int sx = screen_size_x(data->backing);
4226 char *buf = NULL;
4227 size_t len = 0;
4229 if (data->searchmark == NULL)
4230 return (NULL);
4232 cy = screen_hsize(data->backing) - data->oy + data->cy;
4233 if (window_copy_search_mark_at(data, data->cx, cy, &at) != 0)
4234 return (NULL);
4235 if (data->searchmark[at] == 0) {
4236 /* Allow one position after the match. */
4237 if (at == 0 || data->searchmark[--at] == 0)
4238 return (NULL);
4240 window_copy_match_start_end(data, at, &start, &end);
4243 * Cells will not be set in the marked array unless they are valid text
4244 * and wrapping will be taken care of, so we can just copy.
4246 for (at = start; at <= end; at++) {
4247 py = at / sx;
4248 px = at - (py * sx);
4250 grid_get_cell(gd, px, gd->hsize + py - data->oy, &gc);
4251 if (gc.flags & GRID_FLAG_TAB) {
4252 buf = xrealloc(buf, len + 2);
4253 buf[len] = '\t';
4254 len++;
4255 } else {
4256 buf = xrealloc(buf, len + gc.data.size + 1);
4257 memcpy(buf + len, gc.data.data, gc.data.size);
4258 len += gc.data.size;
4261 if (len != 0)
4262 buf[len] = '\0';
4263 return (buf);
4266 static void
4267 window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy,
4268 struct grid_cell *gc, const struct grid_cell *mgc,
4269 const struct grid_cell *cgc, const struct grid_cell *mkgc)
4271 struct window_pane *wp = wme->wp;
4272 struct window_copy_mode_data *data = wme->data;
4273 u_int mark, start, end, cy, cursor, current;
4274 int inv = 0, found = 0;
4275 int keys;
4277 if (data->showmark && fy == data->my) {
4278 gc->attr = mkgc->attr;
4279 if (fx == data->mx)
4280 inv = 1;
4281 if (inv) {
4282 gc->fg = mkgc->bg;
4283 gc->bg = mkgc->fg;
4285 else {
4286 gc->fg = mkgc->fg;
4287 gc->bg = mkgc->bg;
4291 if (data->searchmark == NULL)
4292 return;
4294 if (window_copy_search_mark_at(data, fx, fy, &current) != 0)
4295 return;
4296 mark = data->searchmark[current];
4297 if (mark == 0)
4298 return;
4300 cy = screen_hsize(data->backing) - data->oy + data->cy;
4301 if (window_copy_search_mark_at(data, data->cx, cy, &cursor) == 0) {
4302 keys = options_get_number(wp->window->options, "mode-keys");
4303 if (cursor != 0 &&
4304 keys == MODEKEY_EMACS &&
4305 data->searchdirection) {
4306 if (data->searchmark[cursor - 1] == mark) {
4307 cursor--;
4308 found = 1;
4310 } else if (data->searchmark[cursor] == mark)
4311 found = 1;
4312 if (found) {
4313 window_copy_match_start_end(data, cursor, &start, &end);
4314 if (current >= start && current <= end) {
4315 gc->attr = cgc->attr;
4316 if (inv) {
4317 gc->fg = cgc->bg;
4318 gc->bg = cgc->fg;
4320 else {
4321 gc->fg = cgc->fg;
4322 gc->bg = cgc->bg;
4324 return;
4329 gc->attr = mgc->attr;
4330 if (inv) {
4331 gc->fg = mgc->bg;
4332 gc->bg = mgc->fg;
4334 else {
4335 gc->fg = mgc->fg;
4336 gc->bg = mgc->bg;
4340 static void
4341 window_copy_write_one(struct window_mode_entry *wme,
4342 struct screen_write_ctx *ctx, u_int py, u_int fy, u_int nx,
4343 const struct grid_cell *mgc, const struct grid_cell *cgc,
4344 const struct grid_cell *mkgc)
4346 struct window_copy_mode_data *data = wme->data;
4347 struct grid *gd = data->backing->grid;
4348 struct grid_cell gc;
4349 u_int fx;
4351 screen_write_cursormove(ctx, 0, py, 0);
4352 for (fx = 0; fx < nx; fx++) {
4353 grid_get_cell(gd, fx, fy, &gc);
4354 if (fx + gc.data.width <= nx) {
4355 window_copy_update_style(wme, fx, fy, &gc, mgc, cgc,
4356 mkgc);
4357 screen_write_cell(ctx, &gc);
4363 window_copy_get_current_offset(struct window_pane *wp, u_int *offset,
4364 u_int *size)
4366 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
4367 struct window_copy_mode_data *data = wme->data;
4368 u_int hsize;
4370 if (data == NULL)
4371 return (0);
4372 hsize = screen_hsize(data->backing);
4374 *offset = hsize - data->oy;
4375 *size = hsize;
4376 return (1);
4379 static void
4380 window_copy_write_line(struct window_mode_entry *wme,
4381 struct screen_write_ctx *ctx, u_int py)
4383 struct window_pane *wp = wme->wp;
4384 struct window_copy_mode_data *data = wme->data;
4385 struct screen *s = &data->screen;
4386 struct options *oo = wp->window->options;
4387 struct grid_cell gc, mgc, cgc, mkgc;
4388 u_int sx = screen_size_x(s);
4389 u_int hsize = screen_hsize(data->backing);
4390 const char *value;
4391 char *expanded;
4392 struct format_tree *ft;
4394 style_apply(&gc, oo, "mode-style", NULL);
4395 gc.flags |= GRID_FLAG_NOPALETTE;
4396 style_apply(&mgc, oo, "copy-mode-match-style", NULL);
4397 mgc.flags |= GRID_FLAG_NOPALETTE;
4398 style_apply(&cgc, oo, "copy-mode-current-match-style", NULL);
4399 cgc.flags |= GRID_FLAG_NOPALETTE;
4400 style_apply(&mkgc, oo, "copy-mode-mark-style", NULL);
4401 mkgc.flags |= GRID_FLAG_NOPALETTE;
4403 window_copy_write_one(wme, ctx, py, hsize - data->oy + py,
4404 screen_size_x(s), &mgc, &cgc, &mkgc);
4406 if (py == 0 && s->rupper < s->rlower && !data->hide_position) {
4407 value = options_get_string(oo, "copy-mode-position-format");
4408 if (*value != '\0') {
4409 ft = format_create_defaults(NULL, NULL, NULL, NULL, wp);
4410 expanded = format_expand(ft, value);
4411 if (*expanded != '\0') {
4412 screen_write_cursormove(ctx, 0, 0, 0);
4413 format_draw(ctx, &gc, sx, expanded, NULL, 0);
4415 free(expanded);
4416 format_free(ft);
4420 if (py == data->cy && data->cx == screen_size_x(s)) {
4421 screen_write_cursormove(ctx, screen_size_x(s) - 1, py, 0);
4422 screen_write_putc(ctx, &grid_default_cell, '$');
4426 static void
4427 window_copy_write_lines(struct window_mode_entry *wme,
4428 struct screen_write_ctx *ctx, u_int py, u_int ny)
4430 u_int yy;
4432 for (yy = py; yy < py + ny; yy++)
4433 window_copy_write_line(wme, ctx, py);
4436 static void
4437 window_copy_redraw_selection(struct window_mode_entry *wme, u_int old_y)
4439 struct window_copy_mode_data *data = wme->data;
4440 struct grid *gd = data->backing->grid;
4441 u_int new_y, start, end;
4443 new_y = data->cy;
4444 if (old_y <= new_y) {
4445 start = old_y;
4446 end = new_y;
4447 } else {
4448 start = new_y;
4449 end = old_y;
4453 * In word selection mode the first word on the line below the cursor
4454 * might be selected, so add this line to the redraw area.
4456 if (data->selflag == SEL_WORD) {
4457 /* Last grid line in data coordinates. */
4458 if (end < gd->sy + data->oy - 1)
4459 end++;
4461 window_copy_redraw_lines(wme, start, end - start + 1);
4464 static void
4465 window_copy_redraw_lines(struct window_mode_entry *wme, u_int py, u_int ny)
4467 struct window_pane *wp = wme->wp;
4468 struct window_copy_mode_data *data = wme->data;
4469 struct screen_write_ctx ctx;
4470 u_int i;
4472 screen_write_start_pane(&ctx, wp, NULL);
4473 for (i = py; i < py + ny; i++)
4474 window_copy_write_line(wme, &ctx, i);
4475 screen_write_cursormove(&ctx, data->cx, data->cy, 0);
4476 screen_write_stop(&ctx);
4478 wp->flags |= PANE_REDRAWSCROLLBAR;
4481 static void
4482 window_copy_redraw_screen(struct window_mode_entry *wme)
4484 struct window_copy_mode_data *data = wme->data;
4486 window_copy_redraw_lines(wme, 0, screen_size_y(&data->screen));
4489 static void
4490 window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin,
4491 int no_reset)
4493 struct window_copy_mode_data *data = wme->data;
4494 u_int xx, yy;
4496 xx = data->cx;
4497 yy = screen_hsize(data->backing) + data->cy - data->oy;
4498 switch (data->selflag) {
4499 case SEL_WORD:
4500 if (no_reset)
4501 break;
4502 begin = 0;
4503 if (data->dy > yy || (data->dy == yy && data->dx > xx)) {
4504 /* Right to left selection. */
4505 window_copy_cursor_previous_word_pos(wme,
4506 data->separators, &xx, &yy);
4507 begin = 1;
4509 /* Reset the end. */
4510 data->endselx = data->endselrx;
4511 data->endsely = data->endselry;
4512 } else {
4513 /* Left to right selection. */
4514 if (xx >= window_copy_find_length(wme, yy) ||
4515 !window_copy_in_set(wme, xx + 1, yy, WHITESPACE)) {
4516 window_copy_cursor_next_word_end_pos(wme,
4517 data->separators, &xx, &yy);
4520 /* Reset the start. */
4521 data->selx = data->selrx;
4522 data->sely = data->selry;
4524 break;
4525 case SEL_LINE:
4526 if (no_reset)
4527 break;
4528 begin = 0;
4529 if (data->dy > yy) {
4530 /* Right to left selection. */
4531 xx = 0;
4532 begin = 1;
4534 /* Reset the end. */
4535 data->endselx = data->endselrx;
4536 data->endsely = data->endselry;
4537 } else {
4538 /* Left to right selection. */
4539 if (yy < data->endselry)
4540 yy = data->endselry;
4541 xx = window_copy_find_length(wme, yy);
4543 /* Reset the start. */
4544 data->selx = data->selrx;
4545 data->sely = data->selry;
4547 break;
4548 case SEL_CHAR:
4549 break;
4551 if (begin) {
4552 data->selx = xx;
4553 data->sely = yy;
4554 } else {
4555 data->endselx = xx;
4556 data->endsely = yy;
4560 static void
4561 window_copy_synchronize_cursor(struct window_mode_entry *wme, int no_reset)
4563 struct window_copy_mode_data *data = wme->data;
4565 switch (data->cursordrag) {
4566 case CURSORDRAG_ENDSEL:
4567 window_copy_synchronize_cursor_end(wme, 0, no_reset);
4568 break;
4569 case CURSORDRAG_SEL:
4570 window_copy_synchronize_cursor_end(wme, 1, no_reset);
4571 break;
4572 case CURSORDRAG_NONE:
4573 break;
4577 static void
4578 window_copy_update_cursor(struct window_mode_entry *wme, u_int cx, u_int cy)
4580 struct window_pane *wp = wme->wp;
4581 struct window_copy_mode_data *data = wme->data;
4582 struct screen *s = &data->screen;
4583 struct screen_write_ctx ctx;
4584 u_int old_cx, old_cy;
4586 old_cx = data->cx; old_cy = data->cy;
4587 data->cx = cx; data->cy = cy;
4588 if (old_cx == screen_size_x(s))
4589 window_copy_redraw_lines(wme, old_cy, 1);
4590 if (data->cx == screen_size_x(s))
4591 window_copy_redraw_lines(wme, data->cy, 1);
4592 else {
4593 screen_write_start_pane(&ctx, wp, NULL);
4594 screen_write_cursormove(&ctx, data->cx, data->cy, 0);
4595 screen_write_stop(&ctx);
4599 static void
4600 window_copy_start_selection(struct window_mode_entry *wme)
4602 struct window_copy_mode_data *data = wme->data;
4604 data->selx = data->cx;
4605 data->sely = screen_hsize(data->backing) + data->cy - data->oy;
4607 data->endselx = data->selx;
4608 data->endsely = data->sely;
4610 data->cursordrag = CURSORDRAG_ENDSEL;
4612 window_copy_set_selection(wme, 1, 0);
4615 static int
4616 window_copy_adjust_selection(struct window_mode_entry *wme, u_int *selx,
4617 u_int *sely)
4619 struct window_copy_mode_data *data = wme->data;
4620 struct screen *s = &data->screen;
4621 u_int sx, sy, ty;
4622 int relpos;
4624 sx = *selx;
4625 sy = *sely;
4627 ty = screen_hsize(data->backing) - data->oy;
4628 if (sy < ty) {
4629 relpos = WINDOW_COPY_REL_POS_ABOVE;
4630 if (!data->rectflag)
4631 sx = 0;
4632 sy = 0;
4633 } else if (sy > ty + screen_size_y(s) - 1) {
4634 relpos = WINDOW_COPY_REL_POS_BELOW;
4635 if (!data->rectflag)
4636 sx = screen_size_x(s) - 1;
4637 sy = screen_size_y(s) - 1;
4638 } else {
4639 relpos = WINDOW_COPY_REL_POS_ON_SCREEN;
4640 sy -= ty;
4643 *selx = sx;
4644 *sely = sy;
4645 return (relpos);
4648 static int
4649 window_copy_update_selection(struct window_mode_entry *wme, int may_redraw,
4650 int no_reset)
4652 struct window_copy_mode_data *data = wme->data;
4653 struct screen *s = &data->screen;
4655 if (s->sel == NULL && data->lineflag == LINE_SEL_NONE)
4656 return (0);
4657 return (window_copy_set_selection(wme, may_redraw, no_reset));
4660 static int
4661 window_copy_set_selection(struct window_mode_entry *wme, int may_redraw,
4662 int no_reset)
4664 struct window_pane *wp = wme->wp;
4665 struct window_copy_mode_data *data = wme->data;
4666 struct screen *s = &data->screen;
4667 struct options *oo = wp->window->options;
4668 struct grid_cell gc;
4669 u_int sx, sy, cy, endsx, endsy;
4670 int startrelpos, endrelpos;
4672 window_copy_synchronize_cursor(wme, no_reset);
4674 /* Adjust the selection. */
4675 sx = data->selx;
4676 sy = data->sely;
4677 startrelpos = window_copy_adjust_selection(wme, &sx, &sy);
4679 /* Adjust the end of selection. */
4680 endsx = data->endselx;
4681 endsy = data->endsely;
4682 endrelpos = window_copy_adjust_selection(wme, &endsx, &endsy);
4684 /* Selection is outside of the current screen */
4685 if (startrelpos == endrelpos &&
4686 startrelpos != WINDOW_COPY_REL_POS_ON_SCREEN) {
4687 screen_hide_selection(s);
4688 return (0);
4691 /* Set colours and selection. */
4692 style_apply(&gc, oo, "mode-style", NULL);
4693 gc.flags |= GRID_FLAG_NOPALETTE;
4694 screen_set_selection(s, sx, sy, endsx, endsy, data->rectflag,
4695 data->modekeys, &gc);
4697 if (data->rectflag && may_redraw) {
4699 * Can't rely on the caller to redraw the right lines for
4700 * rectangle selection - find the highest line and the number
4701 * of lines, and redraw just past that in both directions
4703 cy = data->cy;
4704 if (data->cursordrag == CURSORDRAG_ENDSEL) {
4705 if (sy < cy)
4706 window_copy_redraw_lines(wme, sy, cy - sy + 1);
4707 else
4708 window_copy_redraw_lines(wme, cy, sy - cy + 1);
4709 } else {
4710 if (endsy < cy) {
4711 window_copy_redraw_lines(wme, endsy,
4712 cy - endsy + 1);
4713 } else {
4714 window_copy_redraw_lines(wme, cy,
4715 endsy - cy + 1);
4720 return (1);
4723 static void *
4724 window_copy_get_selection(struct window_mode_entry *wme, size_t *len)
4726 struct window_pane *wp = wme->wp;
4727 struct window_copy_mode_data *data = wme->data;
4728 struct screen *s = &data->screen;
4729 char *buf;
4730 size_t off;
4731 u_int i, xx, yy, sx, sy, ex, ey, ey_last;
4732 u_int firstsx, lastex, restex, restsx, selx;
4733 int keys;
4735 if (data->screen.sel == NULL && data->lineflag == LINE_SEL_NONE) {
4736 buf = window_copy_match_at_cursor(data);
4737 if (buf != NULL)
4738 *len = strlen(buf);
4739 else
4740 *len = 0;
4741 return (buf);
4744 buf = xmalloc(1);
4745 off = 0;
4747 *buf = '\0';
4750 * The selection extends from selx,sely to (adjusted) cx,cy on
4751 * the base screen.
4754 /* Find start and end. */
4755 xx = data->endselx;
4756 yy = data->endsely;
4757 if (yy < data->sely || (yy == data->sely && xx < data->selx)) {
4758 sx = xx; sy = yy;
4759 ex = data->selx; ey = data->sely;
4760 } else {
4761 sx = data->selx; sy = data->sely;
4762 ex = xx; ey = yy;
4765 /* Trim ex to end of line. */
4766 ey_last = window_copy_find_length(wme, ey);
4767 if (ex > ey_last)
4768 ex = ey_last;
4771 * Deal with rectangle-copy if necessary; four situations: start of
4772 * first line (firstsx), end of last line (lastex), start (restsx) and
4773 * end (restex) of all other lines.
4775 xx = screen_size_x(s);
4778 * Behave according to mode-keys. If it is emacs, copy like emacs,
4779 * keeping the top-left-most character, and dropping the
4780 * bottom-right-most, regardless of copy direction. If it is vi, also
4781 * keep bottom-right-most character.
4783 keys = options_get_number(wp->window->options, "mode-keys");
4784 if (data->rectflag) {
4786 * Need to ignore the column with the cursor in it, which for
4787 * rectangular copy means knowing which side the cursor is on.
4789 if (data->cursordrag == CURSORDRAG_ENDSEL)
4790 selx = data->selx;
4791 else
4792 selx = data->endselx;
4793 if (selx < data->cx) {
4794 /* Selection start is on the left. */
4795 if (keys == MODEKEY_EMACS) {
4796 lastex = data->cx;
4797 restex = data->cx;
4799 else {
4800 lastex = data->cx + 1;
4801 restex = data->cx + 1;
4803 firstsx = selx;
4804 restsx = selx;
4805 } else {
4806 /* Cursor is on the left. */
4807 lastex = selx + 1;
4808 restex = selx + 1;
4809 firstsx = data->cx;
4810 restsx = data->cx;
4812 } else {
4813 if (keys == MODEKEY_EMACS)
4814 lastex = ex;
4815 else
4816 lastex = ex + 1;
4817 restex = xx;
4818 firstsx = sx;
4819 restsx = 0;
4822 /* Copy the lines. */
4823 for (i = sy; i <= ey; i++) {
4824 window_copy_copy_line(wme, &buf, &off, i,
4825 (i == sy ? firstsx : restsx),
4826 (i == ey ? lastex : restex));
4829 /* Don't bother if no data. */
4830 if (off == 0) {
4831 free(buf);
4832 *len = 0;
4833 return (NULL);
4835 /* Remove final \n (unless at end in vi mode). */
4836 if (keys == MODEKEY_EMACS || lastex <= ey_last) {
4837 if (~grid_get_line(data->backing->grid, ey)->flags &
4838 GRID_LINE_WRAPPED || lastex != ey_last)
4839 off -= 1;
4841 *len = off;
4842 return (buf);
4845 static void
4846 window_copy_copy_buffer(struct window_mode_entry *wme, const char *prefix,
4847 void *buf, size_t len, int set_paste, int set_clip)
4849 struct window_pane *wp = wme->wp;
4850 struct screen_write_ctx ctx;
4852 if (set_clip &&
4853 options_get_number(global_options, "set-clipboard") != 0) {
4854 screen_write_start_pane(&ctx, wp, NULL);
4855 screen_write_setselection(&ctx, "", buf, len);
4856 screen_write_stop(&ctx);
4857 notify_pane("pane-set-clipboard", wp);
4860 if (set_paste)
4861 paste_add(prefix, buf, len);
4864 static void *
4865 window_copy_pipe_run(struct window_mode_entry *wme, struct session *s,
4866 const char *cmd, size_t *len)
4868 void *buf;
4869 struct job *job;
4871 buf = window_copy_get_selection(wme, len);
4872 if (cmd == NULL || *cmd == '\0')
4873 cmd = options_get_string(global_options, "copy-command");
4874 if (cmd != NULL && *cmd != '\0') {
4875 job = job_run(cmd, 0, NULL, NULL, s, NULL, NULL, NULL, NULL,
4876 NULL, JOB_NOWAIT, -1, -1);
4877 bufferevent_write(job_get_event(job), buf, *len);
4879 return (buf);
4882 static void
4883 window_copy_pipe(struct window_mode_entry *wme, struct session *s,
4884 const char *cmd)
4886 size_t len;
4888 window_copy_pipe_run(wme, s, cmd, &len);
4891 static void
4892 window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s,
4893 const char *prefix, const char *cmd, int set_paste, int set_clip)
4895 void *buf;
4896 size_t len;
4898 buf = window_copy_pipe_run(wme, s, cmd, &len);
4899 if (buf != NULL)
4900 window_copy_copy_buffer(wme, prefix, buf, len, set_paste,
4901 set_clip);
4904 static void
4905 window_copy_copy_selection(struct window_mode_entry *wme, const char *prefix,
4906 int set_paste, int set_clip)
4908 char *buf;
4909 size_t len;
4911 buf = window_copy_get_selection(wme, &len);
4912 if (buf != NULL)
4913 window_copy_copy_buffer(wme, prefix, buf, len, set_paste,
4914 set_clip);
4917 static void
4918 window_copy_append_selection(struct window_mode_entry *wme)
4920 struct window_pane *wp = wme->wp;
4921 char *buf;
4922 struct paste_buffer *pb;
4923 const char *bufdata, *bufname = NULL;
4924 size_t len, bufsize;
4925 struct screen_write_ctx ctx;
4927 buf = window_copy_get_selection(wme, &len);
4928 if (buf == NULL)
4929 return;
4931 if (options_get_number(global_options, "set-clipboard") != 0) {
4932 screen_write_start_pane(&ctx, wp, NULL);
4933 screen_write_setselection(&ctx, "", buf, len);
4934 screen_write_stop(&ctx);
4935 notify_pane("pane-set-clipboard", wp);
4938 pb = paste_get_top(&bufname);
4939 if (pb != NULL) {
4940 bufdata = paste_buffer_data(pb, &bufsize);
4941 buf = xrealloc(buf, len + bufsize);
4942 memmove(buf + bufsize, buf, len);
4943 memcpy(buf, bufdata, bufsize);
4944 len += bufsize;
4946 if (paste_set(buf, len, bufname, NULL) != 0)
4947 free(buf);
4950 static void
4951 window_copy_copy_line(struct window_mode_entry *wme, char **buf, size_t *off,
4952 u_int sy, u_int sx, u_int ex)
4954 struct window_copy_mode_data *data = wme->data;
4955 struct grid *gd = data->backing->grid;
4956 struct grid_cell gc;
4957 struct grid_line *gl;
4958 struct utf8_data ud;
4959 u_int i, xx, wrapped = 0;
4960 const char *s;
4962 if (sx > ex)
4963 return;
4966 * Work out if the line was wrapped at the screen edge and all of it is
4967 * on screen.
4969 gl = grid_get_line(gd, sy);
4970 if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx)
4971 wrapped = 1;
4973 /* If the line was wrapped, don't strip spaces (use the full length). */
4974 if (wrapped)
4975 xx = gl->cellsize;
4976 else
4977 xx = window_copy_find_length(wme, sy);
4978 if (ex > xx)
4979 ex = xx;
4980 if (sx > xx)
4981 sx = xx;
4983 if (sx < ex) {
4984 for (i = sx; i < ex; i++) {
4985 grid_get_cell(gd, i, sy, &gc);
4986 if (gc.flags & GRID_FLAG_PADDING)
4987 continue;
4988 if (gc.flags & GRID_FLAG_TAB)
4989 utf8_set(&ud, '\t');
4990 else
4991 utf8_copy(&ud, &gc.data);
4992 if (ud.size == 1 && (gc.attr & GRID_ATTR_CHARSET)) {
4993 s = tty_acs_get(NULL, ud.data[0]);
4994 if (s != NULL && strlen(s) <= sizeof ud.data) {
4995 ud.size = strlen(s);
4996 memcpy(ud.data, s, ud.size);
5000 *buf = xrealloc(*buf, (*off) + ud.size);
5001 memcpy(*buf + *off, ud.data, ud.size);
5002 *off += ud.size;
5006 /* Only add a newline if the line wasn't wrapped. */
5007 if (!wrapped || ex != xx) {
5008 *buf = xrealloc(*buf, (*off) + 1);
5009 (*buf)[(*off)++] = '\n';
5013 static void
5014 window_copy_clear_selection(struct window_mode_entry *wme)
5016 struct window_copy_mode_data *data = wme->data;
5017 u_int px, py;
5019 screen_clear_selection(&data->screen);
5021 data->cursordrag = CURSORDRAG_NONE;
5022 data->lineflag = LINE_SEL_NONE;
5023 data->selflag = SEL_CHAR;
5025 py = screen_hsize(data->backing) + data->cy - data->oy;
5026 px = window_copy_find_length(wme, py);
5027 if (data->cx > px)
5028 window_copy_update_cursor(wme, px, data->cy);
5031 static int
5032 window_copy_in_set(struct window_mode_entry *wme, u_int px, u_int py,
5033 const char *set)
5035 struct window_copy_mode_data *data = wme->data;
5037 return (grid_in_set(data->backing->grid, px, py, set));
5040 static u_int
5041 window_copy_find_length(struct window_mode_entry *wme, u_int py)
5043 struct window_copy_mode_data *data = wme->data;
5045 return (grid_line_length(data->backing->grid, py));
5048 static void
5049 window_copy_cursor_start_of_line(struct window_mode_entry *wme)
5051 struct window_copy_mode_data *data = wme->data;
5052 struct screen *back_s = data->backing;
5053 struct grid_reader gr;
5054 u_int px, py, oldy, hsize;
5056 px = data->cx;
5057 hsize = screen_hsize(back_s);
5058 py = hsize + data->cy - data->oy;
5059 oldy = data->cy;
5061 grid_reader_start(&gr, back_s->grid, px, py);
5062 grid_reader_cursor_start_of_line(&gr, 1);
5063 grid_reader_get_cursor(&gr, &px, &py);
5064 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
5067 static void
5068 window_copy_cursor_back_to_indentation(struct window_mode_entry *wme)
5070 struct window_copy_mode_data *data = wme->data;
5071 struct screen *back_s = data->backing;
5072 struct grid_reader gr;
5073 u_int px, py, oldy, hsize;
5075 px = data->cx;
5076 hsize = screen_hsize(back_s);
5077 py = hsize + data->cy - data->oy;
5078 oldy = data->cy;
5080 grid_reader_start(&gr, back_s->grid, px, py);
5081 grid_reader_cursor_back_to_indentation(&gr);
5082 grid_reader_get_cursor(&gr, &px, &py);
5083 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
5086 static void
5087 window_copy_cursor_end_of_line(struct window_mode_entry *wme)
5089 struct window_copy_mode_data *data = wme->data;
5090 struct screen *back_s = data->backing;
5091 struct grid_reader gr;
5092 u_int px, py, oldy, hsize;
5094 px = data->cx;
5095 hsize = screen_hsize(back_s);
5096 py = hsize + data->cy - data->oy;
5097 oldy = data->cy;
5099 grid_reader_start(&gr, back_s->grid, px, py);
5100 if (data->screen.sel != NULL && data->rectflag)
5101 grid_reader_cursor_end_of_line(&gr, 1, 1);
5102 else
5103 grid_reader_cursor_end_of_line(&gr, 1, 0);
5104 grid_reader_get_cursor(&gr, &px, &py);
5105 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
5106 data->oy, oldy, px, py, 0);
5109 static void
5110 window_copy_other_end(struct window_mode_entry *wme)
5112 struct window_copy_mode_data *data = wme->data;
5113 struct screen *s = &data->screen;
5114 u_int selx, sely, cy, yy, hsize;
5116 if (s->sel == NULL && data->lineflag == LINE_SEL_NONE)
5117 return;
5119 if (data->lineflag == LINE_SEL_LEFT_RIGHT)
5120 data->lineflag = LINE_SEL_RIGHT_LEFT;
5121 else if (data->lineflag == LINE_SEL_RIGHT_LEFT)
5122 data->lineflag = LINE_SEL_LEFT_RIGHT;
5124 switch (data->cursordrag) {
5125 case CURSORDRAG_NONE:
5126 case CURSORDRAG_SEL:
5127 data->cursordrag = CURSORDRAG_ENDSEL;
5128 break;
5129 case CURSORDRAG_ENDSEL:
5130 data->cursordrag = CURSORDRAG_SEL;
5131 break;
5134 selx = data->endselx;
5135 sely = data->endsely;
5136 if (data->cursordrag == CURSORDRAG_SEL) {
5137 selx = data->selx;
5138 sely = data->sely;
5141 cy = data->cy;
5142 yy = screen_hsize(data->backing) + data->cy - data->oy;
5144 data->cx = selx;
5146 hsize = screen_hsize(data->backing);
5147 if (sely < hsize - data->oy) { /* above */
5148 data->oy = hsize - sely;
5149 data->cy = 0;
5150 } else if (sely > hsize - data->oy + screen_size_y(s)) { /* below */
5151 data->oy = hsize - sely + screen_size_y(s) - 1;
5152 data->cy = screen_size_y(s) - 1;
5153 } else
5154 data->cy = cy + sely - yy;
5156 window_copy_update_selection(wme, 1, 1);
5157 window_copy_redraw_screen(wme);
5160 static void
5161 window_copy_cursor_left(struct window_mode_entry *wme)
5163 struct window_copy_mode_data *data = wme->data;
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 grid_reader_cursor_left(&gr, 1);
5175 grid_reader_get_cursor(&gr, &px, &py);
5176 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
5179 static void
5180 window_copy_cursor_right(struct window_mode_entry *wme, int all)
5182 struct window_copy_mode_data *data = wme->data;
5183 struct screen *back_s = data->backing;
5184 struct grid_reader gr;
5185 u_int px, py, oldy, hsize;
5187 px = data->cx;
5188 hsize = screen_hsize(back_s);
5189 py = hsize + data->cy - data->oy;
5190 oldy = data->cy;
5192 grid_reader_start(&gr, back_s->grid, px, py);
5193 grid_reader_cursor_right(&gr, 1, all);
5194 grid_reader_get_cursor(&gr, &px, &py);
5195 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
5196 data->oy, oldy, px, py, 0);
5199 static void
5200 window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only)
5202 struct window_copy_mode_data *data = wme->data;
5203 struct screen *s = &data->screen;
5204 u_int ox, oy, px, py;
5205 int norectsel;
5207 norectsel = data->screen.sel == NULL || !data->rectflag;
5208 oy = screen_hsize(data->backing) + data->cy - data->oy;
5209 ox = window_copy_find_length(wme, oy);
5210 if (norectsel && data->cx != ox) {
5211 data->lastcx = data->cx;
5212 data->lastsx = ox;
5215 if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely)
5216 window_copy_other_end(wme);
5218 if (scroll_only || data->cy == 0) {
5219 if (norectsel)
5220 data->cx = data->lastcx;
5221 window_copy_scroll_down(wme, 1);
5222 if (scroll_only) {
5223 if (data->cy == screen_size_y(s) - 1)
5224 window_copy_redraw_lines(wme, data->cy, 1);
5225 else
5226 window_copy_redraw_lines(wme, data->cy, 2);
5228 } else {
5229 if (norectsel) {
5230 window_copy_update_cursor(wme, data->lastcx,
5231 data->cy - 1);
5232 } else
5233 window_copy_update_cursor(wme, data->cx, data->cy - 1);
5234 if (window_copy_update_selection(wme, 1, 0)) {
5235 if (data->cy == screen_size_y(s) - 1)
5236 window_copy_redraw_lines(wme, data->cy, 1);
5237 else
5238 window_copy_redraw_lines(wme, data->cy, 2);
5242 if (norectsel) {
5243 py = screen_hsize(data->backing) + data->cy - data->oy;
5244 px = window_copy_find_length(wme, py);
5245 if ((data->cx >= data->lastsx && data->cx != px) ||
5246 data->cx > px)
5248 window_copy_update_cursor(wme, px, data->cy);
5249 if (window_copy_update_selection(wme, 1, 0))
5250 window_copy_redraw_lines(wme, data->cy, 1);
5254 if (data->lineflag == LINE_SEL_LEFT_RIGHT)
5256 py = screen_hsize(data->backing) + data->cy - data->oy;
5257 if (data->rectflag)
5258 px = screen_size_x(data->backing);
5259 else
5260 px = window_copy_find_length(wme, py);
5261 window_copy_update_cursor(wme, px, data->cy);
5262 if (window_copy_update_selection(wme, 1, 0))
5263 window_copy_redraw_lines(wme, data->cy, 1);
5265 else if (data->lineflag == LINE_SEL_RIGHT_LEFT)
5267 window_copy_update_cursor(wme, 0, data->cy);
5268 if (window_copy_update_selection(wme, 1, 0))
5269 window_copy_redraw_lines(wme, data->cy, 1);
5273 static void
5274 window_copy_cursor_down(struct window_mode_entry *wme, int scroll_only)
5276 struct window_copy_mode_data *data = wme->data;
5277 struct screen *s = &data->screen;
5278 u_int ox, oy, px, py;
5279 int norectsel;
5281 norectsel = data->screen.sel == NULL || !data->rectflag;
5282 oy = screen_hsize(data->backing) + data->cy - data->oy;
5283 ox = window_copy_find_length(wme, oy);
5284 if (norectsel && data->cx != ox) {
5285 data->lastcx = data->cx;
5286 data->lastsx = ox;
5289 if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely)
5290 window_copy_other_end(wme);
5292 if (scroll_only || data->cy == screen_size_y(s) - 1) {
5293 if (norectsel)
5294 data->cx = data->lastcx;
5295 window_copy_scroll_up(wme, 1);
5296 if (scroll_only && data->cy > 0)
5297 window_copy_redraw_lines(wme, data->cy - 1, 2);
5298 } else {
5299 if (norectsel) {
5300 window_copy_update_cursor(wme, data->lastcx,
5301 data->cy + 1);
5302 } else
5303 window_copy_update_cursor(wme, data->cx, data->cy + 1);
5304 if (window_copy_update_selection(wme, 1, 0))
5305 window_copy_redraw_lines(wme, data->cy - 1, 2);
5308 if (norectsel) {
5309 py = screen_hsize(data->backing) + data->cy - data->oy;
5310 px = window_copy_find_length(wme, py);
5311 if ((data->cx >= data->lastsx && data->cx != px) ||
5312 data->cx > px)
5314 window_copy_update_cursor(wme, px, data->cy);
5315 if (window_copy_update_selection(wme, 1, 0))
5316 window_copy_redraw_lines(wme, data->cy, 1);
5320 if (data->lineflag == LINE_SEL_LEFT_RIGHT)
5322 py = screen_hsize(data->backing) + data->cy - data->oy;
5323 if (data->rectflag)
5324 px = screen_size_x(data->backing);
5325 else
5326 px = window_copy_find_length(wme, py);
5327 window_copy_update_cursor(wme, px, data->cy);
5328 if (window_copy_update_selection(wme, 1, 0))
5329 window_copy_redraw_lines(wme, data->cy, 1);
5331 else if (data->lineflag == LINE_SEL_RIGHT_LEFT)
5333 window_copy_update_cursor(wme, 0, data->cy);
5334 if (window_copy_update_selection(wme, 1, 0))
5335 window_copy_redraw_lines(wme, data->cy, 1);
5339 static void
5340 window_copy_cursor_jump(struct window_mode_entry *wme)
5342 struct window_copy_mode_data *data = wme->data;
5343 struct screen *back_s = data->backing;
5344 struct grid_reader gr;
5345 u_int px, py, oldy, hsize;
5347 px = data->cx + 1;
5348 hsize = screen_hsize(back_s);
5349 py = hsize + data->cy - data->oy;
5350 oldy = data->cy;
5352 grid_reader_start(&gr, back_s->grid, px, py);
5353 if (grid_reader_cursor_jump(&gr, data->jumpchar)) {
5354 grid_reader_get_cursor(&gr, &px, &py);
5355 window_copy_acquire_cursor_down(wme, hsize,
5356 screen_size_y(back_s), data->oy, oldy, px, py, 0);
5360 static void
5361 window_copy_cursor_jump_back(struct window_mode_entry *wme)
5363 struct window_copy_mode_data *data = wme->data;
5364 struct screen *back_s = data->backing;
5365 struct grid_reader gr;
5366 u_int px, py, oldy, hsize;
5368 px = data->cx;
5369 hsize = screen_hsize(back_s);
5370 py = hsize + data->cy - data->oy;
5371 oldy = data->cy;
5373 grid_reader_start(&gr, back_s->grid, px, py);
5374 grid_reader_cursor_left(&gr, 0);
5375 if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) {
5376 grid_reader_get_cursor(&gr, &px, &py);
5377 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px,
5378 py);
5382 static void
5383 window_copy_cursor_jump_to(struct window_mode_entry *wme)
5385 struct window_copy_mode_data *data = wme->data;
5386 struct screen *back_s = data->backing;
5387 struct grid_reader gr;
5388 u_int px, py, oldy, hsize;
5390 px = data->cx + 2;
5391 hsize = screen_hsize(back_s);
5392 py = hsize + data->cy - data->oy;
5393 oldy = data->cy;
5395 grid_reader_start(&gr, back_s->grid, px, py);
5396 if (grid_reader_cursor_jump(&gr, data->jumpchar)) {
5397 grid_reader_cursor_left(&gr, 1);
5398 grid_reader_get_cursor(&gr, &px, &py);
5399 window_copy_acquire_cursor_down(wme, hsize,
5400 screen_size_y(back_s), data->oy, oldy, px, py, 0);
5404 static void
5405 window_copy_cursor_jump_to_back(struct window_mode_entry *wme)
5407 struct window_copy_mode_data *data = wme->data;
5408 struct screen *back_s = data->backing;
5409 struct grid_reader gr;
5410 u_int px, py, oldy, hsize;
5412 px = data->cx;
5413 hsize = screen_hsize(back_s);
5414 py = hsize + data->cy - data->oy;
5415 oldy = data->cy;
5417 grid_reader_start(&gr, back_s->grid, px, py);
5418 grid_reader_cursor_left(&gr, 0);
5419 grid_reader_cursor_left(&gr, 0);
5420 if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) {
5421 grid_reader_cursor_right(&gr, 1, 0);
5422 grid_reader_get_cursor(&gr, &px, &py);
5423 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px,
5424 py);
5428 static void
5429 window_copy_cursor_next_word(struct window_mode_entry *wme,
5430 const char *separators)
5432 struct window_copy_mode_data *data = wme->data;
5433 struct screen *back_s = data->backing;
5434 struct grid_reader gr;
5435 u_int px, py, oldy, hsize;
5437 px = data->cx;
5438 hsize = screen_hsize(back_s);
5439 py = hsize + data->cy - data->oy;
5440 oldy = data->cy;
5442 grid_reader_start(&gr, back_s->grid, px, py);
5443 grid_reader_cursor_next_word(&gr, separators);
5444 grid_reader_get_cursor(&gr, &px, &py);
5445 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
5446 data->oy, oldy, px, py, 0);
5449 /* Compute the next place where a word ends. */
5450 static void
5451 window_copy_cursor_next_word_end_pos(struct window_mode_entry *wme,
5452 const char *separators, u_int *ppx, u_int *ppy)
5454 struct window_pane *wp = wme->wp;
5455 struct window_copy_mode_data *data = wme->data;
5456 struct options *oo = wp->window->options;
5457 struct screen *back_s = data->backing;
5458 struct grid_reader gr;
5459 u_int px, py, hsize;
5461 px = data->cx;
5462 hsize = screen_hsize(back_s);
5463 py = hsize + data->cy - data->oy;
5465 grid_reader_start(&gr, back_s->grid, px, py);
5466 if (options_get_number(oo, "mode-keys") == MODEKEY_VI) {
5467 if (!grid_reader_in_set(&gr, WHITESPACE))
5468 grid_reader_cursor_right(&gr, 0, 0);
5469 grid_reader_cursor_next_word_end(&gr, separators);
5470 grid_reader_cursor_left(&gr, 1);
5471 } else
5472 grid_reader_cursor_next_word_end(&gr, separators);
5473 grid_reader_get_cursor(&gr, &px, &py);
5474 *ppx = px;
5475 *ppy = py;
5478 /* Move to the next place where a word ends. */
5479 static void
5480 window_copy_cursor_next_word_end(struct window_mode_entry *wme,
5481 const char *separators, int no_reset)
5483 struct window_pane *wp = wme->wp;
5484 struct window_copy_mode_data *data = wme->data;
5485 struct options *oo = wp->window->options;
5486 struct screen *back_s = data->backing;
5487 struct grid_reader gr;
5488 u_int px, py, oldy, hsize;
5490 px = data->cx;
5491 hsize = screen_hsize(back_s);
5492 py = hsize + data->cy - data->oy;
5493 oldy = data->cy;
5495 grid_reader_start(&gr, back_s->grid, px, py);
5496 if (options_get_number(oo, "mode-keys") == MODEKEY_VI) {
5497 if (!grid_reader_in_set(&gr, WHITESPACE))
5498 grid_reader_cursor_right(&gr, 0, 0);
5499 grid_reader_cursor_next_word_end(&gr, separators);
5500 grid_reader_cursor_left(&gr, 1);
5501 } else
5502 grid_reader_cursor_next_word_end(&gr, separators);
5503 grid_reader_get_cursor(&gr, &px, &py);
5504 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
5505 data->oy, oldy, px, py, no_reset);
5508 /* Compute the previous place where a word begins. */
5509 static void
5510 window_copy_cursor_previous_word_pos(struct window_mode_entry *wme,
5511 const char *separators, u_int *ppx, u_int *ppy)
5513 struct window_copy_mode_data *data = wme->data;
5514 struct screen *back_s = data->backing;
5515 struct grid_reader gr;
5516 u_int px, py, hsize;
5518 px = data->cx;
5519 hsize = screen_hsize(back_s);
5520 py = hsize + data->cy - data->oy;
5522 grid_reader_start(&gr, back_s->grid, px, py);
5523 grid_reader_cursor_previous_word(&gr, separators, 0, 1);
5524 grid_reader_get_cursor(&gr, &px, &py);
5525 *ppx = px;
5526 *ppy = py;
5529 /* Move to the previous place where a word begins. */
5530 static void
5531 window_copy_cursor_previous_word(struct window_mode_entry *wme,
5532 const char *separators, int already)
5534 struct window_copy_mode_data *data = wme->data;
5535 struct window *w = wme->wp->window;
5536 struct screen *back_s = data->backing;
5537 struct grid_reader gr;
5538 u_int px, py, oldy, hsize;
5539 int stop_at_eol;
5541 if (options_get_number(w->options, "mode-keys") == MODEKEY_EMACS)
5542 stop_at_eol = 1;
5543 else
5544 stop_at_eol = 0;
5546 px = data->cx;
5547 hsize = screen_hsize(back_s);
5548 py = hsize + data->cy - data->oy;
5549 oldy = data->cy;
5551 grid_reader_start(&gr, back_s->grid, px, py);
5552 grid_reader_cursor_previous_word(&gr, separators, already, stop_at_eol);
5553 grid_reader_get_cursor(&gr, &px, &py);
5554 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
5557 static void
5558 window_copy_cursor_prompt(struct window_mode_entry *wme, int direction,
5559 int start_output)
5561 struct window_copy_mode_data *data = wme->data;
5562 struct screen *s = data->backing;
5563 struct grid *gd = s->grid;
5564 u_int end_line;
5565 u_int line = gd->hsize - data->oy + data->cy;
5566 int add, line_flag;
5568 if (start_output)
5569 line_flag = GRID_LINE_START_OUTPUT;
5570 else
5571 line_flag = GRID_LINE_START_PROMPT;
5573 if (direction == 0) { /* up */
5574 add = -1;
5575 end_line = 0;
5576 } else { /* down */
5577 add = 1;
5578 end_line = gd->hsize + gd->sy - 1;
5581 if (line == end_line)
5582 return;
5583 for (;;) {
5584 if (line == end_line)
5585 return;
5586 line += add;
5588 if (grid_get_line(gd, line)->flags & line_flag)
5589 break;
5592 data->cx = 0;
5593 if (line > gd->hsize) {
5594 data->cy = line - gd->hsize;
5595 data->oy = 0;
5596 } else {
5597 data->cy = 0;
5598 data->oy = gd->hsize - line;
5601 window_copy_update_selection(wme, 1, 0);
5602 window_copy_redraw_screen(wme);
5605 static void
5606 window_copy_scroll_up(struct window_mode_entry *wme, u_int ny)
5608 struct window_pane *wp = wme->wp;
5609 struct window_copy_mode_data *data = wme->data;
5610 struct screen *s = &data->screen;
5611 struct screen_write_ctx ctx;
5613 if (data->oy < ny)
5614 ny = data->oy;
5615 if (ny == 0)
5616 return;
5617 data->oy -= ny;
5619 if (data->searchmark != NULL && !data->timeout)
5620 window_copy_search_marks(wme, NULL, data->searchregex, 1);
5621 window_copy_update_selection(wme, 0, 0);
5623 screen_write_start_pane(&ctx, wp, NULL);
5624 screen_write_cursormove(&ctx, 0, 0, 0);
5625 screen_write_deleteline(&ctx, ny, 8);
5626 window_copy_write_lines(wme, &ctx, screen_size_y(s) - ny, ny);
5627 window_copy_write_line(wme, &ctx, 0);
5628 if (screen_size_y(s) > 1)
5629 window_copy_write_line(wme, &ctx, 1);
5630 if (screen_size_y(s) > 3)
5631 window_copy_write_line(wme, &ctx, screen_size_y(s) - 2);
5632 if (s->sel != NULL && screen_size_y(s) > ny)
5633 window_copy_write_line(wme, &ctx, screen_size_y(s) - ny - 1);
5634 screen_write_cursormove(&ctx, data->cx, data->cy, 0);
5635 screen_write_stop(&ctx);
5636 wp->flags |= PANE_REDRAWSCROLLBAR;
5639 static void
5640 window_copy_scroll_down(struct window_mode_entry *wme, u_int ny)
5642 struct window_pane *wp = wme->wp;
5643 struct window_copy_mode_data *data = wme->data;
5644 struct screen *s = &data->screen;
5645 struct screen_write_ctx ctx;
5647 if (ny > screen_hsize(data->backing))
5648 return;
5650 if (data->oy > screen_hsize(data->backing) - ny)
5651 ny = screen_hsize(data->backing) - data->oy;
5652 if (ny == 0)
5653 return;
5654 data->oy += ny;
5656 if (data->searchmark != NULL && !data->timeout)
5657 window_copy_search_marks(wme, NULL, data->searchregex, 1);
5658 window_copy_update_selection(wme, 0, 0);
5660 screen_write_start_pane(&ctx, wp, NULL);
5661 screen_write_cursormove(&ctx, 0, 0, 0);
5662 screen_write_insertline(&ctx, ny, 8);
5663 window_copy_write_lines(wme, &ctx, 0, ny);
5664 if (s->sel != NULL && screen_size_y(s) > ny)
5665 window_copy_write_line(wme, &ctx, ny);
5666 else if (ny == 1) /* nuke position */
5667 window_copy_write_line(wme, &ctx, 1);
5668 screen_write_cursormove(&ctx, data->cx, data->cy, 0);
5669 screen_write_stop(&ctx);
5670 wp->flags |= PANE_REDRAWSCROLLBAR;
5673 static void
5674 window_copy_rectangle_set(struct window_mode_entry *wme, int rectflag)
5676 struct window_copy_mode_data *data = wme->data;
5677 u_int px, py;
5679 data->rectflag = rectflag;
5681 py = screen_hsize(data->backing) + data->cy - data->oy;
5682 px = window_copy_find_length(wme, py);
5683 if (data->cx > px)
5684 window_copy_update_cursor(wme, px, data->cy);
5686 window_copy_update_selection(wme, 1, 0);
5687 window_copy_redraw_screen(wme);
5690 static void
5691 window_copy_move_mouse(struct mouse_event *m)
5693 struct window_pane *wp;
5694 struct window_mode_entry *wme;
5695 u_int x, y;
5697 wp = cmd_mouse_pane(m, NULL, NULL);
5698 if (wp == NULL)
5699 return;
5700 wme = TAILQ_FIRST(&wp->modes);
5701 if (wme == NULL)
5702 return;
5703 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode)
5704 return;
5706 if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
5707 return;
5709 window_copy_update_cursor(wme, x, y);
5712 void
5713 window_copy_start_drag(struct client *c, struct mouse_event *m)
5715 struct window_pane *wp;
5716 struct window_mode_entry *wme;
5717 struct window_copy_mode_data *data;
5718 u_int x, y, yg;
5720 if (c == NULL)
5721 return;
5723 wp = cmd_mouse_pane(m, NULL, NULL);
5724 if (wp == NULL)
5725 return;
5726 wme = TAILQ_FIRST(&wp->modes);
5727 if (wme == NULL)
5728 return;
5729 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode)
5730 return;
5732 if (cmd_mouse_at(wp, m, &x, &y, 1) != 0)
5733 return;
5735 c->tty.mouse_drag_update = window_copy_drag_update;
5736 c->tty.mouse_drag_release = window_copy_drag_release;
5738 data = wme->data;
5739 yg = screen_hsize(data->backing) + y - data->oy;
5740 if (x < data->selrx || x > data->endselrx || yg != data->selry)
5741 data->selflag = SEL_CHAR;
5742 switch (data->selflag) {
5743 case SEL_WORD:
5744 if (data->separators != NULL) {
5745 window_copy_update_cursor(wme, x, y);
5746 window_copy_cursor_previous_word_pos(wme,
5747 data->separators, &x, &y);
5748 y -= screen_hsize(data->backing) - data->oy;
5750 window_copy_update_cursor(wme, x, y);
5751 break;
5752 case SEL_LINE:
5753 window_copy_update_cursor(wme, 0, y);
5754 break;
5755 case SEL_CHAR:
5756 window_copy_update_cursor(wme, x, y);
5757 window_copy_start_selection(wme);
5758 break;
5761 window_copy_redraw_screen(wme);
5762 window_copy_drag_update(c, m);
5765 static void
5766 window_copy_drag_update(struct client *c, struct mouse_event *m)
5768 struct window_pane *wp;
5769 struct window_mode_entry *wme;
5770 struct window_copy_mode_data *data;
5771 u_int x, y, old_cx, old_cy;
5772 struct timeval tv = {
5773 .tv_usec = WINDOW_COPY_DRAG_REPEAT_TIME
5776 if (c == NULL)
5777 return;
5779 wp = cmd_mouse_pane(m, NULL, NULL);
5780 if (wp == NULL)
5781 return;
5782 wme = TAILQ_FIRST(&wp->modes);
5783 if (wme == NULL)
5784 return;
5785 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode)
5786 return;
5788 data = wme->data;
5789 evtimer_del(&data->dragtimer);
5791 if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
5792 return;
5793 old_cx = data->cx;
5794 old_cy = data->cy;
5796 window_copy_update_cursor(wme, x, y);
5797 if (window_copy_update_selection(wme, 1, 0))
5798 window_copy_redraw_selection(wme, old_cy);
5799 if (old_cy != data->cy || old_cx == data->cx) {
5800 if (y == 0) {
5801 evtimer_add(&data->dragtimer, &tv);
5802 window_copy_cursor_up(wme, 1);
5803 } else if (y == screen_size_y(&data->screen) - 1) {
5804 evtimer_add(&data->dragtimer, &tv);
5805 window_copy_cursor_down(wme, 1);
5810 static void
5811 window_copy_drag_release(struct client *c, struct mouse_event *m)
5813 struct window_pane *wp;
5814 struct window_mode_entry *wme;
5815 struct window_copy_mode_data *data;
5817 if (c == NULL)
5818 return;
5820 wp = cmd_mouse_pane(m, NULL, NULL);
5821 if (wp == NULL)
5822 return;
5823 wme = TAILQ_FIRST(&wp->modes);
5824 if (wme == NULL)
5825 return;
5826 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode)
5827 return;
5829 data = wme->data;
5830 evtimer_del(&data->dragtimer);
5833 static void
5834 window_copy_jump_to_mark(struct window_mode_entry *wme)
5836 struct window_copy_mode_data *data = wme->data;
5837 u_int tmx, tmy;
5839 tmx = data->cx;
5840 tmy = screen_hsize(data->backing) + data->cy - data->oy;
5841 data->cx = data->mx;
5842 if (data->my < screen_hsize(data->backing)) {
5843 data->cy = 0;
5844 data->oy = screen_hsize(data->backing) - data->my;
5845 } else {
5846 data->cy = data->my - screen_hsize(data->backing);
5847 data->oy = 0;
5849 data->mx = tmx;
5850 data->my = tmy;
5851 data->showmark = 1;
5852 window_copy_update_selection(wme, 0, 0);
5853 window_copy_redraw_screen(wme);
5856 /* Scroll up if the cursor went off the visible screen. */
5857 static void
5858 window_copy_acquire_cursor_up(struct window_mode_entry *wme, u_int hsize,
5859 u_int oy, u_int oldy, u_int px, u_int py)
5861 u_int cy, yy, ny, nd;
5863 yy = hsize - oy;
5864 if (py < yy) {
5865 ny = yy - py;
5866 cy = 0;
5867 nd = 1;
5868 } else {
5869 ny = 0;
5870 cy = py - yy;
5871 nd = oldy - cy + 1;
5873 while (ny > 0) {
5874 window_copy_cursor_up(wme, 1);
5875 ny--;
5877 window_copy_update_cursor(wme, px, cy);
5878 if (window_copy_update_selection(wme, 1, 0))
5879 window_copy_redraw_lines(wme, cy, nd);
5882 /* Scroll down if the cursor went off the visible screen. */
5883 static void
5884 window_copy_acquire_cursor_down(struct window_mode_entry *wme, u_int hsize,
5885 u_int sy, u_int oy, u_int oldy, u_int px, u_int py, int no_reset)
5887 u_int cy, yy, ny, nd;
5889 cy = py - hsize + oy;
5890 yy = sy - 1;
5891 if (cy > yy) {
5892 ny = cy - yy;
5893 oldy = yy;
5894 nd = 1;
5895 } else {
5896 ny = 0;
5897 nd = cy - oldy + 1;
5899 while (ny > 0) {
5900 window_copy_cursor_down(wme, 1);
5901 ny--;
5903 if (cy > yy)
5904 window_copy_update_cursor(wme, px, yy);
5905 else
5906 window_copy_update_cursor(wme, px, cy);
5907 if (window_copy_update_selection(wme, 1, no_reset))
5908 window_copy_redraw_lines(wme, oldy, nd);