Merge branch 'obsd-master'
[tmux.git] / window-copy.c
blob7c8436f1fae614c4bbef39dcc1d77d7df14831e1
1 /* $OpenBSD$ */
3 /*
4 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include <sys/types.h>
21 #include <ctype.h>
22 #include <regex.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <time.h>
27 #include "tmux.h"
29 struct window_copy_mode_data;
31 static const char *window_copy_key_table(struct window_mode_entry *);
32 static void window_copy_command(struct window_mode_entry *, struct client *,
33 struct session *, struct winlink *, struct args *,
34 struct mouse_event *);
35 static struct screen *window_copy_init(struct window_mode_entry *,
36 struct cmd_find_state *, struct args *);
37 static struct screen *window_copy_view_init(struct window_mode_entry *,
38 struct cmd_find_state *, struct args *);
39 static void window_copy_free(struct window_mode_entry *);
40 static void window_copy_resize(struct window_mode_entry *, u_int, u_int);
41 static void window_copy_formats(struct window_mode_entry *,
42 struct format_tree *);
43 static void window_copy_pageup1(struct window_mode_entry *, int);
44 static int window_copy_pagedown1(struct window_mode_entry *, int, int);
45 static void window_copy_next_paragraph(struct window_mode_entry *);
46 static void window_copy_previous_paragraph(struct window_mode_entry *);
47 static void window_copy_redraw_selection(struct window_mode_entry *, u_int);
48 static void window_copy_redraw_lines(struct window_mode_entry *, u_int,
49 u_int);
50 static void window_copy_redraw_screen(struct window_mode_entry *);
51 static void window_copy_write_line(struct window_mode_entry *,
52 struct screen_write_ctx *, u_int);
53 static void window_copy_write_lines(struct window_mode_entry *,
54 struct screen_write_ctx *, u_int, u_int);
55 static char *window_copy_match_at_cursor(struct window_copy_mode_data *);
56 static void window_copy_scroll_to(struct window_mode_entry *, u_int, u_int,
57 int);
58 static int window_copy_search_compare(struct grid *, u_int, u_int,
59 struct grid *, u_int, int);
60 static int window_copy_search_lr(struct grid *, struct grid *, u_int *,
61 u_int, u_int, u_int, int);
62 static int window_copy_search_rl(struct grid *, struct grid *, u_int *,
63 u_int, u_int, u_int, int);
64 static int window_copy_last_regex(struct grid *, u_int, u_int, u_int,
65 u_int, u_int *, u_int *, const char *, const regex_t *,
66 int);
67 static int window_copy_search_mark_at(struct window_copy_mode_data *,
68 u_int, u_int, u_int *);
69 static char *window_copy_stringify(struct grid *, u_int, u_int, u_int,
70 char *, u_int *);
71 static void window_copy_cstrtocellpos(struct grid *, u_int, u_int *,
72 u_int *, const char *);
73 static int window_copy_search_marks(struct window_mode_entry *,
74 struct screen *, int, int);
75 static void window_copy_clear_marks(struct window_mode_entry *);
76 static int window_copy_is_lowercase(const char *);
77 static void window_copy_search_back_overlap(struct grid *, regex_t *,
78 u_int *, u_int *, u_int *, u_int);
79 static int window_copy_search_jump(struct window_mode_entry *,
80 struct grid *, struct grid *, u_int, u_int, u_int, int, int,
81 int, int);
82 static int window_copy_search(struct window_mode_entry *, int, int);
83 static int window_copy_search_up(struct window_mode_entry *, int);
84 static int window_copy_search_down(struct window_mode_entry *, int);
85 static void window_copy_goto_line(struct window_mode_entry *, const char *);
86 static void window_copy_update_cursor(struct window_mode_entry *, u_int,
87 u_int);
88 static void window_copy_start_selection(struct window_mode_entry *);
89 static int window_copy_adjust_selection(struct window_mode_entry *,
90 u_int *, u_int *);
91 static int window_copy_set_selection(struct window_mode_entry *, int, int);
92 static int window_copy_update_selection(struct window_mode_entry *, int,
93 int);
94 static void window_copy_synchronize_cursor(struct window_mode_entry *, int);
95 static void *window_copy_get_selection(struct window_mode_entry *, size_t *);
96 static void window_copy_copy_buffer(struct window_mode_entry *,
97 const char *, void *, size_t, int, int);
98 static void window_copy_pipe(struct window_mode_entry *,
99 struct session *, const char *);
100 static void window_copy_copy_pipe(struct window_mode_entry *,
101 struct session *, const char *, const char *,
102 int, int);
103 static void window_copy_copy_selection(struct window_mode_entry *,
104 const char *, int, int);
105 static void window_copy_append_selection(struct window_mode_entry *);
106 static void window_copy_clear_selection(struct window_mode_entry *);
107 static void window_copy_copy_line(struct window_mode_entry *, char **,
108 size_t *, u_int, u_int, u_int);
109 static int window_copy_in_set(struct window_mode_entry *, u_int, u_int,
110 const char *);
111 static u_int window_copy_find_length(struct window_mode_entry *, u_int);
112 static void window_copy_cursor_start_of_line(struct window_mode_entry *);
113 static void window_copy_cursor_back_to_indentation(
114 struct window_mode_entry *);
115 static void window_copy_cursor_end_of_line(struct window_mode_entry *);
116 static void window_copy_other_end(struct window_mode_entry *);
117 static void window_copy_cursor_left(struct window_mode_entry *);
118 static void window_copy_cursor_right(struct window_mode_entry *, int);
119 static void window_copy_cursor_up(struct window_mode_entry *, int);
120 static void window_copy_cursor_down(struct window_mode_entry *, int);
121 static void window_copy_cursor_jump(struct window_mode_entry *);
122 static void window_copy_cursor_jump_back(struct window_mode_entry *);
123 static void window_copy_cursor_jump_to(struct window_mode_entry *);
124 static void window_copy_cursor_jump_to_back(struct window_mode_entry *);
125 static void window_copy_cursor_next_word(struct window_mode_entry *,
126 const char *);
127 static void window_copy_cursor_next_word_end_pos(struct window_mode_entry *,
128 const char *, u_int *, u_int *);
129 static void window_copy_cursor_next_word_end(struct window_mode_entry *,
130 const char *, int);
131 static void window_copy_cursor_previous_word_pos(struct window_mode_entry *,
132 const char *, u_int *, u_int *);
133 static void window_copy_cursor_previous_word(struct window_mode_entry *,
134 const char *, int);
135 static void window_copy_cursor_prompt(struct window_mode_entry *, int,
136 int);
137 static void window_copy_scroll_up(struct window_mode_entry *, u_int);
138 static void window_copy_scroll_down(struct window_mode_entry *, u_int);
139 static void window_copy_rectangle_set(struct window_mode_entry *, int);
140 static void window_copy_move_mouse(struct mouse_event *);
141 static void window_copy_drag_update(struct client *, struct mouse_event *);
142 static void window_copy_drag_release(struct client *, struct mouse_event *);
143 static void window_copy_jump_to_mark(struct window_mode_entry *);
144 static void window_copy_acquire_cursor_up(struct window_mode_entry *,
145 u_int, u_int, u_int, u_int, u_int);
146 static void window_copy_acquire_cursor_down(struct window_mode_entry *,
147 u_int, u_int, u_int, u_int, u_int, u_int, int);
149 const struct window_mode window_copy_mode = {
150 .name = "copy-mode",
152 .init = window_copy_init,
153 .free = window_copy_free,
154 .resize = window_copy_resize,
155 .key_table = window_copy_key_table,
156 .command = window_copy_command,
157 .formats = window_copy_formats,
160 const struct window_mode window_view_mode = {
161 .name = "view-mode",
163 .init = window_copy_view_init,
164 .free = window_copy_free,
165 .resize = window_copy_resize,
166 .key_table = window_copy_key_table,
167 .command = window_copy_command,
168 .formats = window_copy_formats,
171 enum {
172 WINDOW_COPY_OFF,
173 WINDOW_COPY_SEARCHUP,
174 WINDOW_COPY_SEARCHDOWN,
175 WINDOW_COPY_JUMPFORWARD,
176 WINDOW_COPY_JUMPBACKWARD,
177 WINDOW_COPY_JUMPTOFORWARD,
178 WINDOW_COPY_JUMPTOBACKWARD,
181 enum {
182 WINDOW_COPY_REL_POS_ABOVE,
183 WINDOW_COPY_REL_POS_ON_SCREEN,
184 WINDOW_COPY_REL_POS_BELOW,
187 enum window_copy_cmd_action {
188 WINDOW_COPY_CMD_NOTHING,
189 WINDOW_COPY_CMD_REDRAW,
190 WINDOW_COPY_CMD_CANCEL,
193 enum window_copy_cmd_clear {
194 WINDOW_COPY_CMD_CLEAR_ALWAYS,
195 WINDOW_COPY_CMD_CLEAR_NEVER,
196 WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
199 struct window_copy_cmd_state {
200 struct window_mode_entry *wme;
201 struct args *args;
202 struct args *wargs;
203 struct mouse_event *m;
205 struct client *c;
206 struct session *s;
207 struct winlink *wl;
211 * Copy mode's visible screen (the "screen" field) is filled from one of two
212 * sources: the original contents of the pane (used when we actually enter via
213 * the "copy-mode" command, to copy the contents of the current pane), or else
214 * a series of lines containing the output from an output-writing tmux command
215 * (such as any of the "show-*" or "list-*" commands).
217 * In either case, the full content of the copy-mode grid is pointed at by the
218 * "backing" field, and is copied into "screen" as needed (that is, when
219 * scrolling occurs). When copy-mode is backed by a pane, backing points
220 * directly at that pane's screen structure (&wp->base); when backed by a list
221 * of output-lines from a command, it points at a newly-allocated screen
222 * structure (which is deallocated when the mode ends).
224 struct window_copy_mode_data {
225 struct screen screen;
227 struct screen *backing;
228 int backing_written; /* backing display started */
229 struct screen *writing;
230 struct input_ctx *ictx;
232 int viewmode; /* view mode entered */
234 u_int oy; /* number of lines scrolled up */
236 u_int selx; /* beginning of selection */
237 u_int sely;
239 u_int endselx; /* end of selection */
240 u_int endsely;
242 enum {
243 CURSORDRAG_NONE, /* selection is independent of cursor */
244 CURSORDRAG_ENDSEL, /* end is synchronized with cursor */
245 CURSORDRAG_SEL, /* start is synchronized with cursor */
246 } cursordrag;
248 int modekeys;
249 enum {
250 LINE_SEL_NONE,
251 LINE_SEL_LEFT_RIGHT,
252 LINE_SEL_RIGHT_LEFT,
253 } lineflag; /* line selection mode */
254 int rectflag; /* in rectangle copy mode? */
255 int scroll_exit; /* exit on scroll to end? */
256 int hide_position; /* hide position marker */
258 enum {
259 SEL_CHAR, /* select one char at a time */
260 SEL_WORD, /* select one word at a time */
261 SEL_LINE, /* select one line at a time */
262 } selflag;
264 const char *separators; /* word separators */
266 u_int dx; /* drag start position */
267 u_int dy;
269 u_int selrx; /* selection reset positions */
270 u_int selry;
271 u_int endselrx;
272 u_int endselry;
274 u_int cx;
275 u_int cy;
277 u_int lastcx; /* position in last line w/ content */
278 u_int lastsx; /* size of last line w/ content */
280 u_int mx; /* mark position */
281 u_int my;
282 int showmark;
284 int searchtype;
285 int searchdirection;
286 int searchregex;
287 char *searchstr;
288 u_char *searchmark;
289 int searchcount;
290 int searchmore;
291 int searchall;
292 int searchx;
293 int searchy;
294 int searcho;
295 u_char searchgen;
297 int timeout; /* search has timed out */
298 #define WINDOW_COPY_SEARCH_TIMEOUT 10000
299 #define WINDOW_COPY_SEARCH_ALL_TIMEOUT 200
300 #define WINDOW_COPY_SEARCH_MAX_LINE 2000
302 int jumptype;
303 struct utf8_data *jumpchar;
305 struct event dragtimer;
306 #define WINDOW_COPY_DRAG_REPEAT_TIME 50000
309 static void
310 window_copy_scroll_timer(__unused int fd, __unused short events, void *arg)
312 struct window_mode_entry *wme = arg;
313 struct window_pane *wp = wme->wp;
314 struct window_copy_mode_data *data = wme->data;
315 struct timeval tv = {
316 .tv_usec = WINDOW_COPY_DRAG_REPEAT_TIME
319 evtimer_del(&data->dragtimer);
321 if (TAILQ_FIRST(&wp->modes) != wme)
322 return;
324 if (data->cy == 0) {
325 evtimer_add(&data->dragtimer, &tv);
326 window_copy_cursor_up(wme, 1);
327 } else if (data->cy == screen_size_y(&data->screen) - 1) {
328 evtimer_add(&data->dragtimer, &tv);
329 window_copy_cursor_down(wme, 1);
333 static struct screen *
334 window_copy_clone_screen(struct screen *src, struct screen *hint, u_int *cx,
335 u_int *cy, int trim)
337 struct screen *dst;
338 const struct grid_line *gl;
339 u_int sy, wx, wy;
340 int reflow;
342 dst = xcalloc(1, sizeof *dst);
344 sy = screen_hsize(src) + screen_size_y(src);
345 if (trim) {
346 while (sy > screen_hsize(src)) {
347 gl = grid_peek_line(src->grid, sy - 1);
348 if (gl->cellused != 0)
349 break;
350 sy--;
353 log_debug("%s: target screen is %ux%u, source %ux%u", __func__,
354 screen_size_x(src), sy, screen_size_x(hint),
355 screen_hsize(src) + screen_size_y(src));
356 screen_init(dst, screen_size_x(src), sy, screen_hlimit(src));
359 * Ensure history is on for the backing grid so lines are not deleted
360 * during resizing.
362 dst->grid->flags |= GRID_HISTORY;
363 grid_duplicate_lines(dst->grid, 0, src->grid, 0, sy);
365 dst->grid->sy = sy - screen_hsize(src);
366 dst->grid->hsize = screen_hsize(src);
367 dst->grid->hscrolled = src->grid->hscrolled;
368 if (src->cy > dst->grid->sy - 1) {
369 dst->cx = 0;
370 dst->cy = dst->grid->sy - 1;
371 } else {
372 dst->cx = src->cx;
373 dst->cy = src->cy;
376 if (cx != NULL && cy != NULL) {
377 *cx = dst->cx;
378 *cy = screen_hsize(dst) + dst->cy;
379 reflow = (screen_size_x(hint) != screen_size_x(dst));
381 else
382 reflow = 0;
383 if (reflow)
384 grid_wrap_position(dst->grid, *cx, *cy, &wx, &wy);
385 screen_resize_cursor(dst, screen_size_x(hint), screen_size_y(hint), 1,
386 0, 0);
387 if (reflow)
388 grid_unwrap_position(dst->grid, cx, cy, wx, wy);
390 return (dst);
393 static struct window_copy_mode_data *
394 window_copy_common_init(struct window_mode_entry *wme)
396 struct window_pane *wp = wme->wp;
397 struct window_copy_mode_data *data;
398 struct screen *base = &wp->base;
400 wme->data = data = xcalloc(1, sizeof *data);
402 data->cursordrag = CURSORDRAG_NONE;
403 data->lineflag = LINE_SEL_NONE;
404 data->selflag = SEL_CHAR;
406 if (wp->searchstr != NULL) {
407 data->searchtype = WINDOW_COPY_SEARCHUP;
408 data->searchregex = wp->searchregex;
409 data->searchstr = xstrdup(wp->searchstr);
410 } else {
411 data->searchtype = WINDOW_COPY_OFF;
412 data->searchregex = 0;
413 data->searchstr = NULL;
415 data->searchx = data->searchy = data->searcho = -1;
416 data->searchall = 1;
418 data->jumptype = WINDOW_COPY_OFF;
419 data->jumpchar = NULL;
421 screen_init(&data->screen, screen_size_x(base), screen_size_y(base), 0);
422 screen_set_default_cursor(&data->screen, global_w_options);
423 data->modekeys = options_get_number(wp->window->options, "mode-keys");
425 evtimer_set(&data->dragtimer, window_copy_scroll_timer, wme);
427 return (data);
430 static struct screen *
431 window_copy_init(struct window_mode_entry *wme,
432 __unused struct cmd_find_state *fs, struct args *args)
434 struct window_pane *wp = wme->swp;
435 struct window_copy_mode_data *data;
436 struct screen *base = &wp->base;
437 struct screen_write_ctx ctx;
438 u_int i, cx, cy;
440 data = window_copy_common_init(wme);
441 data->backing = window_copy_clone_screen(base, &data->screen, &cx, &cy,
442 wme->swp != wme->wp);
444 data->cx = cx;
445 if (cy < screen_hsize(data->backing)) {
446 data->cy = 0;
447 data->oy = screen_hsize(data->backing) - cy;
448 } else {
449 data->cy = cy - screen_hsize(data->backing);
450 data->oy = 0;
453 data->scroll_exit = args_has(args, 'e');
454 data->hide_position = args_has(args, 'H');
456 if (base->hyperlinks != NULL)
457 data->screen.hyperlinks = hyperlinks_copy(base->hyperlinks);
458 data->screen.cx = data->cx;
459 data->screen.cy = data->cy;
460 data->mx = data->cx;
461 data->my = screen_hsize(data->backing) + data->cy - data->oy;
462 data->showmark = 0;
464 screen_write_start(&ctx, &data->screen);
465 for (i = 0; i < screen_size_y(&data->screen); i++)
466 window_copy_write_line(wme, &ctx, i);
467 screen_write_cursormove(&ctx, data->cx, data->cy, 0);
468 screen_write_stop(&ctx);
470 return (&data->screen);
473 static struct screen *
474 window_copy_view_init(struct window_mode_entry *wme,
475 __unused struct cmd_find_state *fs, __unused struct args *args)
477 struct window_pane *wp = wme->wp;
478 struct window_copy_mode_data *data;
479 struct screen *base = &wp->base;
480 u_int sx = screen_size_x(base);
482 data = window_copy_common_init(wme);
483 data->viewmode = 1;
485 data->backing = xmalloc(sizeof *data->backing);
486 screen_init(data->backing, sx, screen_size_y(base), UINT_MAX);
487 data->writing = xmalloc(sizeof *data->writing);
488 screen_init(data->writing, sx, screen_size_y(base), 0);
489 data->ictx = input_init(NULL, NULL, NULL);
490 data->mx = data->cx;
491 data->my = screen_hsize(data->backing) + data->cy - data->oy;
492 data->showmark = 0;
494 return (&data->screen);
497 static void
498 window_copy_free(struct window_mode_entry *wme)
500 struct window_copy_mode_data *data = wme->data;
502 evtimer_del(&data->dragtimer);
504 free(data->searchmark);
505 free(data->searchstr);
506 free(data->jumpchar);
508 if (data->writing != NULL) {
509 screen_free(data->writing);
510 free(data->writing);
512 if (data->ictx != NULL)
513 input_free(data->ictx);
514 screen_free(data->backing);
515 free(data->backing);
517 screen_free(&data->screen);
518 free(data);
521 void
522 window_copy_add(struct window_pane *wp, int parse, const char *fmt, ...)
524 va_list ap;
526 va_start(ap, fmt);
527 window_copy_vadd(wp, parse, fmt, ap);
528 va_end(ap);
531 static void
532 window_copy_init_ctx_cb(__unused struct screen_write_ctx *ctx,
533 struct tty_ctx *ttyctx)
535 memcpy(&ttyctx->defaults, &grid_default_cell, sizeof ttyctx->defaults);
536 ttyctx->palette = NULL;
537 ttyctx->redraw_cb = NULL;
538 ttyctx->set_client_cb = NULL;
539 ttyctx->arg = NULL;
542 void
543 window_copy_vadd(struct window_pane *wp, int parse, const char *fmt, va_list ap)
545 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
546 struct window_copy_mode_data *data = wme->data;
547 struct screen *backing = data->backing;
548 struct screen *writing = data->writing;
549 struct screen_write_ctx writing_ctx, backing_ctx, ctx;
550 struct grid_cell gc;
551 u_int old_hsize, old_cy;
552 u_int sx = screen_size_x(backing);
553 char *text;
555 if (parse) {
556 vasprintf(&text, fmt, ap);
557 screen_write_start(&writing_ctx, writing);
558 screen_write_reset(&writing_ctx);
559 input_parse_screen(data->ictx, writing, window_copy_init_ctx_cb,
560 data, text, strlen(text));
561 free(text);
564 old_hsize = screen_hsize(data->backing);
565 screen_write_start(&backing_ctx, backing);
566 if (data->backing_written) {
568 * On the second or later line, do a CRLF before writing
569 * (so it's on a new line).
571 screen_write_carriagereturn(&backing_ctx);
572 screen_write_linefeed(&backing_ctx, 0, 8);
573 } else
574 data->backing_written = 1;
575 old_cy = backing->cy;
576 if (parse)
577 screen_write_fast_copy(&backing_ctx, writing, 0, 0, sx, 1);
578 else {
579 memcpy(&gc, &grid_default_cell, sizeof gc);
580 screen_write_vnputs(&backing_ctx, 0, &gc, fmt, ap);
582 screen_write_stop(&backing_ctx);
584 data->oy += screen_hsize(data->backing) - old_hsize;
586 screen_write_start_pane(&ctx, wp, &data->screen);
589 * If the history has changed, draw the top line.
590 * (If there's any history at all, it has changed.)
592 if (screen_hsize(data->backing))
593 window_copy_redraw_lines(wme, 0, 1);
595 /* Write the new lines. */
596 window_copy_redraw_lines(wme, old_cy, backing->cy - old_cy + 1);
598 screen_write_stop(&ctx);
601 void
602 window_copy_pageup(struct window_pane *wp, int half_page)
604 window_copy_pageup1(TAILQ_FIRST(&wp->modes), half_page);
607 static void
608 window_copy_pageup1(struct window_mode_entry *wme, int half_page)
610 struct window_copy_mode_data *data = wme->data;
611 struct screen *s = &data->screen;
612 u_int n, ox, oy, px, py;
614 oy = screen_hsize(data->backing) + data->cy - data->oy;
615 ox = window_copy_find_length(wme, oy);
617 if (data->cx != ox) {
618 data->lastcx = data->cx;
619 data->lastsx = ox;
621 data->cx = data->lastcx;
623 n = 1;
624 if (screen_size_y(s) > 2) {
625 if (half_page)
626 n = screen_size_y(s) / 2;
627 else
628 n = screen_size_y(s) - 2;
631 if (data->oy + n > screen_hsize(data->backing)) {
632 data->oy = screen_hsize(data->backing);
633 if (data->cy < n)
634 data->cy = 0;
635 else
636 data->cy -= n;
637 } else
638 data->oy += n;
640 if (data->screen.sel == NULL || !data->rectflag) {
641 py = screen_hsize(data->backing) + data->cy - data->oy;
642 px = window_copy_find_length(wme, py);
643 if ((data->cx >= data->lastsx && data->cx != px) ||
644 data->cx > px)
645 window_copy_cursor_end_of_line(wme);
648 if (data->searchmark != NULL && !data->timeout)
649 window_copy_search_marks(wme, NULL, data->searchregex, 1);
650 window_copy_update_selection(wme, 1, 0);
651 window_copy_redraw_screen(wme);
654 void
655 window_copy_pagedown(struct window_pane *wp, int half_page, int scroll_exit)
657 if (window_copy_pagedown1(TAILQ_FIRST(&wp->modes), half_page,
658 scroll_exit)) {
659 window_pane_reset_mode(wp);
660 return;
664 static int
665 window_copy_pagedown1(struct window_mode_entry *wme, int half_page,
666 int scroll_exit)
668 struct window_copy_mode_data *data = wme->data;
669 struct screen *s = &data->screen;
670 u_int n, ox, oy, px, py;
672 oy = screen_hsize(data->backing) + data->cy - data->oy;
673 ox = window_copy_find_length(wme, oy);
675 if (data->cx != ox) {
676 data->lastcx = data->cx;
677 data->lastsx = ox;
679 data->cx = data->lastcx;
681 n = 1;
682 if (screen_size_y(s) > 2) {
683 if (half_page)
684 n = screen_size_y(s) / 2;
685 else
686 n = screen_size_y(s) - 2;
689 if (data->oy < n) {
690 data->oy = 0;
691 if (data->cy + (n - data->oy) >= screen_size_y(data->backing))
692 data->cy = screen_size_y(data->backing) - 1;
693 else
694 data->cy += n - data->oy;
695 } else
696 data->oy -= n;
698 if (data->screen.sel == NULL || !data->rectflag) {
699 py = screen_hsize(data->backing) + data->cy - data->oy;
700 px = window_copy_find_length(wme, py);
701 if ((data->cx >= data->lastsx && data->cx != px) ||
702 data->cx > px)
703 window_copy_cursor_end_of_line(wme);
706 if (scroll_exit && data->oy == 0)
707 return (1);
708 if (data->searchmark != NULL && !data->timeout)
709 window_copy_search_marks(wme, NULL, data->searchregex, 1);
710 window_copy_update_selection(wme, 1, 0);
711 window_copy_redraw_screen(wme);
712 return (0);
715 static void
716 window_copy_previous_paragraph(struct window_mode_entry *wme)
718 struct window_copy_mode_data *data = wme->data;
719 u_int oy;
721 oy = screen_hsize(data->backing) + data->cy - data->oy;
723 while (oy > 0 && window_copy_find_length(wme, oy) == 0)
724 oy--;
726 while (oy > 0 && window_copy_find_length(wme, oy) > 0)
727 oy--;
729 window_copy_scroll_to(wme, 0, oy, 0);
732 static void
733 window_copy_next_paragraph(struct window_mode_entry *wme)
735 struct window_copy_mode_data *data = wme->data;
736 struct screen *s = &data->screen;
737 u_int maxy, ox, oy;
739 oy = screen_hsize(data->backing) + data->cy - data->oy;
740 maxy = screen_hsize(data->backing) + screen_size_y(s) - 1;
742 while (oy < maxy && window_copy_find_length(wme, oy) == 0)
743 oy++;
745 while (oy < maxy && window_copy_find_length(wme, oy) > 0)
746 oy++;
748 ox = window_copy_find_length(wme, oy);
749 window_copy_scroll_to(wme, ox, oy, 0);
752 char *
753 window_copy_get_word(struct window_pane *wp, u_int x, u_int y)
755 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
756 struct window_copy_mode_data *data = wme->data;
757 struct grid *gd = data->screen.grid;
759 return (format_grid_word(gd, x, gd->hsize + y));
762 char *
763 window_copy_get_line(struct window_pane *wp, u_int y)
765 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
766 struct window_copy_mode_data *data = wme->data;
767 struct grid *gd = data->screen.grid;
769 return (format_grid_line(gd, gd->hsize + y));
772 static void *
773 window_copy_cursor_hyperlink_cb(struct format_tree *ft)
775 struct window_pane *wp = format_get_pane(ft);
776 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
777 struct window_copy_mode_data *data = wme->data;
778 struct grid *gd = data->screen.grid;
780 return (format_grid_hyperlink(gd, data->cx, gd->hsize + data->cy,
781 &data->screen));
784 static void *
785 window_copy_cursor_word_cb(struct format_tree *ft)
787 struct window_pane *wp = format_get_pane(ft);
788 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
789 struct window_copy_mode_data *data = wme->data;
791 return (window_copy_get_word(wp, data->cx, data->cy));
794 static void *
795 window_copy_cursor_line_cb(struct format_tree *ft)
797 struct window_pane *wp = format_get_pane(ft);
798 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
799 struct window_copy_mode_data *data = wme->data;
801 return (window_copy_get_line(wp, data->cy));
804 static void *
805 window_copy_search_match_cb(struct format_tree *ft)
807 struct window_pane *wp = format_get_pane(ft);
808 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
809 struct window_copy_mode_data *data = wme->data;
811 return (window_copy_match_at_cursor(data));
814 static void
815 window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft)
817 struct window_copy_mode_data *data = wme->data;
818 u_int hsize = screen_hsize(data->backing);
819 struct grid_line *gl;
821 gl = grid_get_line(data->backing->grid, hsize - data->oy);
822 format_add(ft, "top_line_time", "%llu", (unsigned long long)gl->time);
824 format_add(ft, "scroll_position", "%d", data->oy);
825 format_add(ft, "rectangle_toggle", "%d", data->rectflag);
827 format_add(ft, "copy_cursor_x", "%d", data->cx);
828 format_add(ft, "copy_cursor_y", "%d", data->cy);
830 if (data->screen.sel != NULL) {
831 format_add(ft, "selection_start_x", "%d", data->selx);
832 format_add(ft, "selection_start_y", "%d", data->sely);
833 format_add(ft, "selection_end_x", "%d", data->endselx);
834 format_add(ft, "selection_end_y", "%d", data->endsely);
836 if (data->cursordrag != CURSORDRAG_NONE)
837 format_add(ft, "selection_active", "1");
838 else
839 format_add(ft, "selection_active", "0");
840 if (data->endselx != data->selx || data->endsely != data->sely)
841 format_add(ft, "selection_present", "1");
842 else
843 format_add(ft, "selection_present", "0");
844 } else {
845 format_add(ft, "selection_active", "0");
846 format_add(ft, "selection_present", "0");
849 format_add(ft, "search_present", "%d", data->searchmark != NULL);
850 format_add(ft, "search_timed_out", "%d", data->timeout);
851 if (data->searchcount != -1) {
852 format_add(ft, "search_count", "%d", data->searchcount);
853 format_add(ft, "search_count_partial", "%d", data->searchmore);
855 format_add_cb(ft, "search_match", window_copy_search_match_cb);
857 format_add_cb(ft, "copy_cursor_word", window_copy_cursor_word_cb);
858 format_add_cb(ft, "copy_cursor_line", window_copy_cursor_line_cb);
859 format_add_cb(ft, "copy_cursor_hyperlink",
860 window_copy_cursor_hyperlink_cb);
863 static void
864 window_copy_size_changed(struct window_mode_entry *wme)
866 struct window_copy_mode_data *data = wme->data;
867 struct screen *s = &data->screen;
868 struct screen_write_ctx ctx;
869 int search = (data->searchmark != NULL);
871 window_copy_clear_selection(wme);
872 window_copy_clear_marks(wme);
874 screen_write_start(&ctx, s);
875 window_copy_write_lines(wme, &ctx, 0, screen_size_y(s));
876 screen_write_stop(&ctx);
878 if (search && !data->timeout)
879 window_copy_search_marks(wme, NULL, data->searchregex, 0);
880 data->searchx = data->cx;
881 data->searchy = data->cy;
882 data->searcho = data->oy;
885 static void
886 window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
888 struct window_copy_mode_data *data = wme->data;
889 struct screen *s = &data->screen;
890 struct grid *gd = data->backing->grid;
891 u_int cx, cy, wx, wy;
892 int reflow;
894 screen_resize(s, sx, sy, 0);
895 cx = data->cx;
896 cy = gd->hsize + data->cy - data->oy;
897 reflow = (gd->sx != sx);
898 if (reflow)
899 grid_wrap_position(gd, cx, cy, &wx, &wy);
900 screen_resize_cursor(data->backing, sx, sy, 1, 0, 0);
901 if (reflow)
902 grid_unwrap_position(gd, &cx, &cy, wx, wy);
904 data->cx = cx;
905 if (cy < gd->hsize) {
906 data->cy = 0;
907 data->oy = gd->hsize - cy;
908 } else {
909 data->cy = cy - gd->hsize;
910 data->oy = 0;
913 window_copy_size_changed(wme);
914 window_copy_redraw_screen(wme);
917 static const char *
918 window_copy_key_table(struct window_mode_entry *wme)
920 struct window_pane *wp = wme->wp;
922 if (options_get_number(wp->window->options, "mode-keys") == MODEKEY_VI)
923 return ("copy-mode-vi");
924 return ("copy-mode");
927 static int
928 window_copy_expand_search_string(struct window_copy_cmd_state *cs)
930 struct window_mode_entry *wme = cs->wme;
931 struct window_copy_mode_data *data = wme->data;
932 const char *ss = args_string(cs->wargs, 0);
933 char *expanded;
935 if (ss == NULL || *ss == '\0')
936 return (0);
938 if (args_has(cs->args, 'F')) {
939 expanded = format_single(NULL, ss, NULL, NULL, NULL, wme->wp);
940 if (*expanded == '\0') {
941 free(expanded);
942 return (0);
944 free(data->searchstr);
945 data->searchstr = expanded;
946 } else {
947 free(data->searchstr);
948 data->searchstr = xstrdup(ss);
950 return (1);
953 static enum window_copy_cmd_action
954 window_copy_cmd_append_selection(struct window_copy_cmd_state *cs)
956 struct window_mode_entry *wme = cs->wme;
957 struct session *s = cs->s;
959 if (s != NULL)
960 window_copy_append_selection(wme);
961 window_copy_clear_selection(wme);
962 return (WINDOW_COPY_CMD_REDRAW);
965 static enum window_copy_cmd_action
966 window_copy_cmd_append_selection_and_cancel(struct window_copy_cmd_state *cs)
968 struct window_mode_entry *wme = cs->wme;
969 struct session *s = cs->s;
971 if (s != NULL)
972 window_copy_append_selection(wme);
973 window_copy_clear_selection(wme);
974 return (WINDOW_COPY_CMD_CANCEL);
977 static enum window_copy_cmd_action
978 window_copy_cmd_back_to_indentation(struct window_copy_cmd_state *cs)
980 struct window_mode_entry *wme = cs->wme;
982 window_copy_cursor_back_to_indentation(wme);
983 return (WINDOW_COPY_CMD_NOTHING);
986 static enum window_copy_cmd_action
987 window_copy_cmd_begin_selection(struct window_copy_cmd_state *cs)
989 struct window_mode_entry *wme = cs->wme;
990 struct client *c = cs->c;
991 struct mouse_event *m = cs->m;
992 struct window_copy_mode_data *data = wme->data;
994 if (m != NULL) {
995 window_copy_start_drag(c, m);
996 return (WINDOW_COPY_CMD_NOTHING);
999 data->lineflag = LINE_SEL_NONE;
1000 data->selflag = SEL_CHAR;
1001 window_copy_start_selection(wme);
1002 return (WINDOW_COPY_CMD_REDRAW);
1005 static enum window_copy_cmd_action
1006 window_copy_cmd_stop_selection(struct window_copy_cmd_state *cs)
1008 struct window_mode_entry *wme = cs->wme;
1009 struct window_copy_mode_data *data = wme->data;
1011 data->cursordrag = CURSORDRAG_NONE;
1012 data->lineflag = LINE_SEL_NONE;
1013 data->selflag = SEL_CHAR;
1014 return (WINDOW_COPY_CMD_NOTHING);
1017 static enum window_copy_cmd_action
1018 window_copy_cmd_bottom_line(struct window_copy_cmd_state *cs)
1020 struct window_mode_entry *wme = cs->wme;
1021 struct window_copy_mode_data *data = wme->data;
1023 data->cx = 0;
1024 data->cy = screen_size_y(&data->screen) - 1;
1026 window_copy_update_selection(wme, 1, 0);
1027 return (WINDOW_COPY_CMD_REDRAW);
1030 static enum window_copy_cmd_action
1031 window_copy_cmd_cancel(__unused struct window_copy_cmd_state *cs)
1033 return (WINDOW_COPY_CMD_CANCEL);
1036 static enum window_copy_cmd_action
1037 window_copy_cmd_clear_selection(struct window_copy_cmd_state *cs)
1039 struct window_mode_entry *wme = cs->wme;
1041 window_copy_clear_selection(wme);
1042 return (WINDOW_COPY_CMD_REDRAW);
1045 static enum window_copy_cmd_action
1046 window_copy_do_copy_end_of_line(struct window_copy_cmd_state *cs, int pipe,
1047 int cancel)
1049 struct window_mode_entry *wme = cs->wme;
1050 struct client *c = cs->c;
1051 struct session *s = cs->s;
1052 struct winlink *wl = cs->wl;
1053 struct window_pane *wp = wme->wp;
1054 u_int count = args_count(cs->wargs);
1055 u_int np = wme->prefix, ocx, ocy, ooy;
1056 struct window_copy_mode_data *data = wme->data;
1057 char *prefix = NULL, *command = NULL;
1058 const char *arg0 = args_string(cs->wargs, 0);
1059 const char *arg1 = args_string(cs->wargs, 1);
1060 int set_paste = !args_has(cs->wargs, 'P');
1061 int set_clip = !args_has(cs->wargs, 'C');
1063 if (pipe) {
1064 if (count == 2)
1065 prefix = format_single(NULL, arg1, c, s, wl, wp);
1066 if (s != NULL && count > 0 && *arg0 != '\0')
1067 command = format_single(NULL, arg0, c, s, wl, wp);
1068 } else {
1069 if (count == 1)
1070 prefix = format_single(NULL, arg0, c, s, wl, wp);
1073 ocx = data->cx;
1074 ocy = data->cy;
1075 ooy = data->oy;
1077 window_copy_start_selection(wme);
1078 for (; np > 1; np--)
1079 window_copy_cursor_down(wme, 0);
1080 window_copy_cursor_end_of_line(wme);
1082 if (s != NULL) {
1083 if (pipe)
1084 window_copy_copy_pipe(wme, s, prefix, command,
1085 set_paste, set_clip);
1086 else
1087 window_copy_copy_selection(wme, prefix,
1088 set_paste, set_clip);
1090 if (cancel) {
1091 free(prefix);
1092 free(command);
1093 return (WINDOW_COPY_CMD_CANCEL);
1096 window_copy_clear_selection(wme);
1098 data->cx = ocx;
1099 data->cy = ocy;
1100 data->oy = ooy;
1102 free(prefix);
1103 free(command);
1104 return (WINDOW_COPY_CMD_REDRAW);
1107 static enum window_copy_cmd_action
1108 window_copy_cmd_copy_end_of_line(struct window_copy_cmd_state *cs)
1110 return (window_copy_do_copy_end_of_line(cs, 0, 0));
1113 static enum window_copy_cmd_action
1114 window_copy_cmd_copy_end_of_line_and_cancel(struct window_copy_cmd_state *cs)
1116 return (window_copy_do_copy_end_of_line(cs, 0, 1));
1119 static enum window_copy_cmd_action
1120 window_copy_cmd_copy_pipe_end_of_line(struct window_copy_cmd_state *cs)
1122 return (window_copy_do_copy_end_of_line(cs, 1, 0));
1125 static enum window_copy_cmd_action
1126 window_copy_cmd_copy_pipe_end_of_line_and_cancel(
1127 struct window_copy_cmd_state *cs)
1129 return (window_copy_do_copy_end_of_line(cs, 1, 1));
1132 static enum window_copy_cmd_action
1133 window_copy_do_copy_line(struct window_copy_cmd_state *cs, int pipe, int cancel)
1135 struct window_mode_entry *wme = cs->wme;
1136 struct client *c = cs->c;
1137 struct session *s = cs->s;
1138 struct winlink *wl = cs->wl;
1139 struct window_pane *wp = wme->wp;
1140 struct window_copy_mode_data *data = wme->data;
1141 u_int count = args_count(cs->wargs);
1142 u_int np = wme->prefix, ocx, ocy, ooy;
1143 char *prefix = NULL, *command = NULL;
1144 const char *arg0 = args_string(cs->wargs, 0);
1145 const char *arg1 = args_string(cs->wargs, 1);
1146 int set_paste = !args_has(cs->wargs, 'P');
1147 int set_clip = !args_has(cs->wargs, 'C');
1149 if (pipe) {
1150 if (count == 2)
1151 prefix = format_single(NULL, arg1, c, s, wl, wp);
1152 if (s != NULL && count > 0 && *arg0 != '\0')
1153 command = format_single(NULL, arg0, c, s, wl, wp);
1154 } else {
1155 if (count == 1)
1156 prefix = format_single(NULL, arg0, c, s, wl, wp);
1159 ocx = data->cx;
1160 ocy = data->cy;
1161 ooy = data->oy;
1163 data->selflag = SEL_CHAR;
1164 window_copy_cursor_start_of_line(wme);
1165 window_copy_start_selection(wme);
1166 for (; np > 1; np--)
1167 window_copy_cursor_down(wme, 0);
1168 window_copy_cursor_end_of_line(wme);
1170 if (s != NULL) {
1171 if (pipe)
1172 window_copy_copy_pipe(wme, s, prefix, command,
1173 set_paste, set_clip);
1174 else
1175 window_copy_copy_selection(wme, prefix,
1176 set_paste, set_clip);
1178 if (cancel) {
1179 free(prefix);
1180 free(command);
1181 return (WINDOW_COPY_CMD_CANCEL);
1184 window_copy_clear_selection(wme);
1186 data->cx = ocx;
1187 data->cy = ocy;
1188 data->oy = ooy;
1190 free(prefix);
1191 free(command);
1192 return (WINDOW_COPY_CMD_REDRAW);
1195 static enum window_copy_cmd_action
1196 window_copy_cmd_copy_line(struct window_copy_cmd_state *cs)
1198 return (window_copy_do_copy_line(cs, 0, 0));
1201 static enum window_copy_cmd_action
1202 window_copy_cmd_copy_line_and_cancel(struct window_copy_cmd_state *cs)
1204 return (window_copy_do_copy_line(cs, 0, 1));
1207 static enum window_copy_cmd_action
1208 window_copy_cmd_copy_pipe_line(struct window_copy_cmd_state *cs)
1210 return (window_copy_do_copy_line(cs, 1, 0));
1213 static enum window_copy_cmd_action
1214 window_copy_cmd_copy_pipe_line_and_cancel(struct window_copy_cmd_state *cs)
1216 return (window_copy_do_copy_line(cs, 1, 1));
1219 static enum window_copy_cmd_action
1220 window_copy_cmd_copy_selection_no_clear(struct window_copy_cmd_state *cs)
1222 struct window_mode_entry *wme = cs->wme;
1223 struct client *c = cs->c;
1224 struct session *s = cs->s;
1225 struct winlink *wl = cs->wl;
1226 struct window_pane *wp = wme->wp;
1227 char *prefix = NULL;
1228 const char *arg0 = args_string(cs->wargs, 0);
1229 int set_paste = !args_has(cs->wargs, 'P');
1230 int set_clip = !args_has(cs->wargs, 'C');
1232 if (arg0 != NULL)
1233 prefix = format_single(NULL, arg0, c, s, wl, wp);
1235 if (s != NULL)
1236 window_copy_copy_selection(wme, prefix, set_paste, set_clip);
1238 free(prefix);
1239 return (WINDOW_COPY_CMD_NOTHING);
1242 static enum window_copy_cmd_action
1243 window_copy_cmd_copy_selection(struct window_copy_cmd_state *cs)
1245 struct window_mode_entry *wme = cs->wme;
1247 window_copy_cmd_copy_selection_no_clear(cs);
1248 window_copy_clear_selection(wme);
1249 return (WINDOW_COPY_CMD_REDRAW);
1252 static enum window_copy_cmd_action
1253 window_copy_cmd_copy_selection_and_cancel(struct window_copy_cmd_state *cs)
1255 struct window_mode_entry *wme = cs->wme;
1257 window_copy_cmd_copy_selection_no_clear(cs);
1258 window_copy_clear_selection(wme);
1259 return (WINDOW_COPY_CMD_CANCEL);
1262 static enum window_copy_cmd_action
1263 window_copy_cmd_cursor_down(struct window_copy_cmd_state *cs)
1265 struct window_mode_entry *wme = cs->wme;
1266 u_int np = wme->prefix;
1268 for (; np != 0; np--)
1269 window_copy_cursor_down(wme, 0);
1270 return (WINDOW_COPY_CMD_NOTHING);
1273 static enum window_copy_cmd_action
1274 window_copy_cmd_cursor_down_and_cancel(struct window_copy_cmd_state *cs)
1276 struct window_mode_entry *wme = cs->wme;
1277 struct window_copy_mode_data *data = wme->data;
1278 u_int np = wme->prefix, cy;
1280 cy = data->cy;
1281 for (; np != 0; np--)
1282 window_copy_cursor_down(wme, 0);
1283 if (cy == data->cy && data->oy == 0)
1284 return (WINDOW_COPY_CMD_CANCEL);
1285 return (WINDOW_COPY_CMD_NOTHING);
1288 static enum window_copy_cmd_action
1289 window_copy_cmd_cursor_left(struct window_copy_cmd_state *cs)
1291 struct window_mode_entry *wme = cs->wme;
1292 u_int np = wme->prefix;
1294 for (; np != 0; np--)
1295 window_copy_cursor_left(wme);
1296 return (WINDOW_COPY_CMD_NOTHING);
1299 static enum window_copy_cmd_action
1300 window_copy_cmd_cursor_right(struct window_copy_cmd_state *cs)
1302 struct window_mode_entry *wme = cs->wme;
1303 struct window_copy_mode_data *data = wme->data;
1304 u_int np = wme->prefix;
1306 for (; np != 0; np--) {
1307 window_copy_cursor_right(wme, data->screen.sel != NULL &&
1308 data->rectflag);
1310 return (WINDOW_COPY_CMD_NOTHING);
1313 /* Scroll line containing the cursor to the given position. */
1314 static enum window_copy_cmd_action
1315 window_copy_cmd_scroll_to(struct window_copy_cmd_state *cs, u_int to)
1317 struct window_mode_entry *wme = cs->wme;
1318 struct window_copy_mode_data *data = wme->data;
1319 u_int oy, delta;
1320 int scroll_up; /* >0 up, <0 down */
1322 scroll_up = data->cy - to;
1323 delta = abs(scroll_up);
1324 oy = screen_hsize(data->backing) - data->oy;
1327 * oy is the maximum scroll down amount, while data->oy is the maximum
1328 * scroll up amount.
1330 if (scroll_up > 0 && data->oy >= delta) {
1331 window_copy_scroll_up(wme, delta);
1332 data->cy -= delta;
1333 } else if (scroll_up < 0 && oy >= delta) {
1334 window_copy_scroll_down(wme, delta);
1335 data->cy += delta;
1338 window_copy_update_selection(wme, 0, 0);
1339 return (WINDOW_COPY_CMD_REDRAW);
1342 /* Scroll line containing the cursor to the bottom. */
1343 static enum window_copy_cmd_action
1344 window_copy_cmd_scroll_bottom(struct window_copy_cmd_state *cs)
1346 struct window_copy_mode_data *data = cs->wme->data;
1347 u_int bottom;
1349 bottom = screen_size_y(&data->screen) - 1;
1350 return (window_copy_cmd_scroll_to(cs, bottom));
1353 /* Scroll line containing the cursor to the middle. */
1354 static enum window_copy_cmd_action
1355 window_copy_cmd_scroll_middle(struct window_copy_cmd_state *cs)
1357 struct window_copy_mode_data *data = cs->wme->data;
1358 u_int mid_value;
1360 mid_value = (screen_size_y(&data->screen) - 1) / 2;
1361 return (window_copy_cmd_scroll_to(cs, mid_value));
1364 /* Scroll line containing the cursor to the top. */
1365 static enum window_copy_cmd_action
1366 window_copy_cmd_scroll_top(struct window_copy_cmd_state *cs)
1368 return (window_copy_cmd_scroll_to(cs, 0));
1371 static enum window_copy_cmd_action
1372 window_copy_cmd_cursor_up(struct window_copy_cmd_state *cs)
1374 struct window_mode_entry *wme = cs->wme;
1375 u_int np = wme->prefix;
1377 for (; np != 0; np--)
1378 window_copy_cursor_up(wme, 0);
1379 return (WINDOW_COPY_CMD_NOTHING);
1382 static enum window_copy_cmd_action
1383 window_copy_cmd_end_of_line(struct window_copy_cmd_state *cs)
1385 struct window_mode_entry *wme = cs->wme;
1387 window_copy_cursor_end_of_line(wme);
1388 return (WINDOW_COPY_CMD_NOTHING);
1391 static enum window_copy_cmd_action
1392 window_copy_cmd_halfpage_down(struct window_copy_cmd_state *cs)
1394 struct window_mode_entry *wme = cs->wme;
1395 struct window_copy_mode_data *data = wme->data;
1396 u_int np = wme->prefix;
1398 for (; np != 0; np--) {
1399 if (window_copy_pagedown1(wme, 1, data->scroll_exit))
1400 return (WINDOW_COPY_CMD_CANCEL);
1402 return (WINDOW_COPY_CMD_NOTHING);
1405 static enum window_copy_cmd_action
1406 window_copy_cmd_halfpage_down_and_cancel(struct window_copy_cmd_state *cs)
1409 struct window_mode_entry *wme = cs->wme;
1410 u_int np = wme->prefix;
1412 for (; np != 0; np--) {
1413 if (window_copy_pagedown1(wme, 1, 1))
1414 return (WINDOW_COPY_CMD_CANCEL);
1416 return (WINDOW_COPY_CMD_NOTHING);
1419 static enum window_copy_cmd_action
1420 window_copy_cmd_halfpage_up(struct window_copy_cmd_state *cs)
1422 struct window_mode_entry *wme = cs->wme;
1423 u_int np = wme->prefix;
1425 for (; np != 0; np--)
1426 window_copy_pageup1(wme, 1);
1427 return (WINDOW_COPY_CMD_NOTHING);
1430 static enum window_copy_cmd_action
1431 window_copy_cmd_toggle_position(struct window_copy_cmd_state *cs)
1433 struct window_mode_entry *wme = cs->wme;
1434 struct window_copy_mode_data *data = wme->data;
1436 data->hide_position = !data->hide_position;
1437 return (WINDOW_COPY_CMD_REDRAW);
1440 static enum window_copy_cmd_action
1441 window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs)
1443 struct window_mode_entry *wme = cs->wme;
1444 struct window_copy_mode_data *data = wme->data;
1445 struct screen *s = data->backing;
1446 u_int oy;
1448 oy = screen_hsize(s) + data->cy - data->oy;
1449 if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely)
1450 window_copy_other_end(wme);
1452 data->cy = screen_size_y(&data->screen) - 1;
1453 data->cx = window_copy_find_length(wme, screen_hsize(s) + data->cy);
1454 data->oy = 0;
1456 if (data->searchmark != NULL && !data->timeout)
1457 window_copy_search_marks(wme, NULL, data->searchregex, 1);
1458 window_copy_update_selection(wme, 1, 0);
1459 return (WINDOW_COPY_CMD_REDRAW);
1462 static enum window_copy_cmd_action
1463 window_copy_cmd_history_top(struct window_copy_cmd_state *cs)
1465 struct window_mode_entry *wme = cs->wme;
1466 struct window_copy_mode_data *data = wme->data;
1467 u_int oy;
1469 oy = screen_hsize(data->backing) + data->cy - data->oy;
1470 if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely)
1471 window_copy_other_end(wme);
1473 data->cy = 0;
1474 data->cx = 0;
1475 data->oy = screen_hsize(data->backing);
1477 if (data->searchmark != NULL && !data->timeout)
1478 window_copy_search_marks(wme, NULL, data->searchregex, 1);
1479 window_copy_update_selection(wme, 1, 0);
1480 return (WINDOW_COPY_CMD_REDRAW);
1483 static enum window_copy_cmd_action
1484 window_copy_cmd_jump_again(struct window_copy_cmd_state *cs)
1486 struct window_mode_entry *wme = cs->wme;
1487 struct window_copy_mode_data *data = wme->data;
1488 u_int np = wme->prefix;
1490 switch (data->jumptype) {
1491 case WINDOW_COPY_JUMPFORWARD:
1492 for (; np != 0; np--)
1493 window_copy_cursor_jump(wme);
1494 break;
1495 case WINDOW_COPY_JUMPBACKWARD:
1496 for (; np != 0; np--)
1497 window_copy_cursor_jump_back(wme);
1498 break;
1499 case WINDOW_COPY_JUMPTOFORWARD:
1500 for (; np != 0; np--)
1501 window_copy_cursor_jump_to(wme);
1502 break;
1503 case WINDOW_COPY_JUMPTOBACKWARD:
1504 for (; np != 0; np--)
1505 window_copy_cursor_jump_to_back(wme);
1506 break;
1508 return (WINDOW_COPY_CMD_NOTHING);
1511 static enum window_copy_cmd_action
1512 window_copy_cmd_jump_reverse(struct window_copy_cmd_state *cs)
1514 struct window_mode_entry *wme = cs->wme;
1515 struct window_copy_mode_data *data = wme->data;
1516 u_int np = wme->prefix;
1518 switch (data->jumptype) {
1519 case WINDOW_COPY_JUMPFORWARD:
1520 for (; np != 0; np--)
1521 window_copy_cursor_jump_back(wme);
1522 break;
1523 case WINDOW_COPY_JUMPBACKWARD:
1524 for (; np != 0; np--)
1525 window_copy_cursor_jump(wme);
1526 break;
1527 case WINDOW_COPY_JUMPTOFORWARD:
1528 for (; np != 0; np--)
1529 window_copy_cursor_jump_to_back(wme);
1530 break;
1531 case WINDOW_COPY_JUMPTOBACKWARD:
1532 for (; np != 0; np--)
1533 window_copy_cursor_jump_to(wme);
1534 break;
1536 return (WINDOW_COPY_CMD_NOTHING);
1539 static enum window_copy_cmd_action
1540 window_copy_cmd_middle_line(struct window_copy_cmd_state *cs)
1542 struct window_mode_entry *wme = cs->wme;
1543 struct window_copy_mode_data *data = wme->data;
1545 data->cx = 0;
1546 data->cy = (screen_size_y(&data->screen) - 1) / 2;
1548 window_copy_update_selection(wme, 1, 0);
1549 return (WINDOW_COPY_CMD_REDRAW);
1552 static enum window_copy_cmd_action
1553 window_copy_cmd_previous_matching_bracket(struct window_copy_cmd_state *cs)
1555 struct window_mode_entry *wme = cs->wme;
1556 u_int np = wme->prefix;
1557 struct window_copy_mode_data *data = wme->data;
1558 struct screen *s = data->backing;
1559 char open[] = "{[(", close[] = "}])";
1560 char tried, found, start, *cp;
1561 u_int px, py, xx, n;
1562 struct grid_cell gc;
1563 int failed;
1565 for (; np != 0; np--) {
1566 /* Get cursor position and line length. */
1567 px = data->cx;
1568 py = screen_hsize(s) + data->cy - data->oy;
1569 xx = window_copy_find_length(wme, py);
1570 if (xx == 0)
1571 break;
1574 * Get the current character. If not on a bracket, try the
1575 * previous. If still not, then behave like previous-word.
1577 tried = 0;
1578 retry:
1579 grid_get_cell(s->grid, px, py, &gc);
1580 if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING))
1581 cp = NULL;
1582 else {
1583 found = *gc.data.data;
1584 cp = strchr(close, found);
1586 if (cp == NULL) {
1587 if (data->modekeys == MODEKEY_EMACS) {
1588 if (!tried && px > 0) {
1589 px--;
1590 tried = 1;
1591 goto retry;
1593 window_copy_cursor_previous_word(wme, close, 1);
1595 continue;
1597 start = open[cp - close];
1599 /* Walk backward until the matching bracket is reached. */
1600 n = 1;
1601 failed = 0;
1602 do {
1603 if (px == 0) {
1604 if (py == 0) {
1605 failed = 1;
1606 break;
1608 do {
1609 py--;
1610 xx = window_copy_find_length(wme, py);
1611 } while (xx == 0 && py > 0);
1612 if (xx == 0 && py == 0) {
1613 failed = 1;
1614 break;
1616 px = xx - 1;
1617 } else
1618 px--;
1620 grid_get_cell(s->grid, px, py, &gc);
1621 if (gc.data.size == 1 &&
1622 (~gc.flags & GRID_FLAG_PADDING)) {
1623 if (*gc.data.data == found)
1624 n++;
1625 else if (*gc.data.data == start)
1626 n--;
1628 } while (n != 0);
1630 /* Move the cursor to the found location if any. */
1631 if (!failed)
1632 window_copy_scroll_to(wme, px, py, 0);
1635 return (WINDOW_COPY_CMD_NOTHING);
1638 static enum window_copy_cmd_action
1639 window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state *cs)
1641 struct window_mode_entry *wme = cs->wme;
1642 u_int np = wme->prefix;
1643 struct window_copy_mode_data *data = wme->data;
1644 struct screen *s = data->backing;
1645 char open[] = "{[(", close[] = "}])";
1646 char tried, found, end, *cp;
1647 u_int px, py, xx, yy, sx, sy, n;
1648 struct grid_cell gc;
1649 int failed;
1650 struct grid_line *gl;
1652 for (; np != 0; np--) {
1653 /* Get cursor position and line length. */
1654 px = data->cx;
1655 py = screen_hsize(s) + data->cy - data->oy;
1656 xx = window_copy_find_length(wme, py);
1657 yy = screen_hsize(s) + screen_size_y(s) - 1;
1658 if (xx == 0)
1659 break;
1662 * Get the current character. If not on a bracket, try the
1663 * next. If still not, then behave like next-word.
1665 tried = 0;
1666 retry:
1667 grid_get_cell(s->grid, px, py, &gc);
1668 if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING))
1669 cp = NULL;
1670 else {
1671 found = *gc.data.data;
1674 * In vi mode, attempt to move to previous bracket if a
1675 * closing bracket is found first. If this fails,
1676 * return to the original cursor position.
1678 cp = strchr(close, found);
1679 if (cp != NULL && data->modekeys == MODEKEY_VI) {
1680 sx = data->cx;
1681 sy = screen_hsize(s) + data->cy - data->oy;
1683 window_copy_scroll_to(wme, px, py, 0);
1684 window_copy_cmd_previous_matching_bracket(cs);
1686 px = data->cx;
1687 py = screen_hsize(s) + data->cy - data->oy;
1688 grid_get_cell(s->grid, px, py, &gc);
1689 if (gc.data.size == 1 &&
1690 (~gc.flags & GRID_FLAG_PADDING) &&
1691 strchr(close, *gc.data.data) != NULL)
1692 window_copy_scroll_to(wme, sx, sy, 0);
1693 break;
1696 cp = strchr(open, found);
1698 if (cp == NULL) {
1699 if (data->modekeys == MODEKEY_EMACS) {
1700 if (!tried && px <= xx) {
1701 px++;
1702 tried = 1;
1703 goto retry;
1705 window_copy_cursor_next_word_end(wme, open, 0);
1706 continue;
1708 /* For vi, continue searching for bracket until EOL. */
1709 if (px > xx) {
1710 if (py == yy)
1711 continue;
1712 gl = grid_get_line(s->grid, py);
1713 if (~gl->flags & GRID_LINE_WRAPPED)
1714 continue;
1715 if (gl->cellsize > s->grid->sx)
1716 continue;
1717 px = 0;
1718 py++;
1719 xx = window_copy_find_length(wme, py);
1720 } else
1721 px++;
1722 goto retry;
1724 end = close[cp - open];
1726 /* Walk forward until the matching bracket is reached. */
1727 n = 1;
1728 failed = 0;
1729 do {
1730 if (px > xx) {
1731 if (py == yy) {
1732 failed = 1;
1733 break;
1735 px = 0;
1736 py++;
1737 xx = window_copy_find_length(wme, py);
1738 } else
1739 px++;
1741 grid_get_cell(s->grid, px, py, &gc);
1742 if (gc.data.size == 1 &&
1743 (~gc.flags & GRID_FLAG_PADDING)) {
1744 if (*gc.data.data == found)
1745 n++;
1746 else if (*gc.data.data == end)
1747 n--;
1749 } while (n != 0);
1751 /* Move the cursor to the found location if any. */
1752 if (!failed)
1753 window_copy_scroll_to(wme, px, py, 0);
1756 return (WINDOW_COPY_CMD_NOTHING);
1759 static enum window_copy_cmd_action
1760 window_copy_cmd_next_paragraph(struct window_copy_cmd_state *cs)
1762 struct window_mode_entry *wme = cs->wme;
1763 u_int np = wme->prefix;
1765 for (; np != 0; np--)
1766 window_copy_next_paragraph(wme);
1767 return (WINDOW_COPY_CMD_NOTHING);
1770 static enum window_copy_cmd_action
1771 window_copy_cmd_next_space(struct window_copy_cmd_state *cs)
1773 struct window_mode_entry *wme = cs->wme;
1774 u_int np = wme->prefix;
1776 for (; np != 0; np--)
1777 window_copy_cursor_next_word(wme, "");
1778 return (WINDOW_COPY_CMD_NOTHING);
1781 static enum window_copy_cmd_action
1782 window_copy_cmd_next_space_end(struct window_copy_cmd_state *cs)
1784 struct window_mode_entry *wme = cs->wme;
1785 u_int np = wme->prefix;
1787 for (; np != 0; np--)
1788 window_copy_cursor_next_word_end(wme, "", 0);
1789 return (WINDOW_COPY_CMD_NOTHING);
1792 static enum window_copy_cmd_action
1793 window_copy_cmd_next_word(struct window_copy_cmd_state *cs)
1795 struct window_mode_entry *wme = cs->wme;
1796 u_int np = wme->prefix;
1797 const char *separators;
1799 separators = options_get_string(cs->s->options, "word-separators");
1801 for (; np != 0; np--)
1802 window_copy_cursor_next_word(wme, separators);
1803 return (WINDOW_COPY_CMD_NOTHING);
1806 static enum window_copy_cmd_action
1807 window_copy_cmd_next_word_end(struct window_copy_cmd_state *cs)
1809 struct window_mode_entry *wme = cs->wme;
1810 u_int np = wme->prefix;
1811 const char *separators;
1813 separators = options_get_string(cs->s->options, "word-separators");
1815 for (; np != 0; np--)
1816 window_copy_cursor_next_word_end(wme, separators, 0);
1817 return (WINDOW_COPY_CMD_NOTHING);
1820 static enum window_copy_cmd_action
1821 window_copy_cmd_other_end(struct window_copy_cmd_state *cs)
1823 struct window_mode_entry *wme = cs->wme;
1824 u_int np = wme->prefix;
1825 struct window_copy_mode_data *data = wme->data;
1827 data->selflag = SEL_CHAR;
1828 if ((np % 2) != 0)
1829 window_copy_other_end(wme);
1830 return (WINDOW_COPY_CMD_NOTHING);
1833 static enum window_copy_cmd_action
1834 window_copy_cmd_page_down(struct window_copy_cmd_state *cs)
1836 struct window_mode_entry *wme = cs->wme;
1837 struct window_copy_mode_data *data = wme->data;
1838 u_int np = wme->prefix;
1840 for (; np != 0; np--) {
1841 if (window_copy_pagedown1(wme, 0, data->scroll_exit))
1842 return (WINDOW_COPY_CMD_CANCEL);
1844 return (WINDOW_COPY_CMD_NOTHING);
1847 static enum window_copy_cmd_action
1848 window_copy_cmd_page_down_and_cancel(struct window_copy_cmd_state *cs)
1850 struct window_mode_entry *wme = cs->wme;
1851 u_int np = wme->prefix;
1853 for (; np != 0; np--) {
1854 if (window_copy_pagedown1(wme, 0, 1))
1855 return (WINDOW_COPY_CMD_CANCEL);
1857 return (WINDOW_COPY_CMD_NOTHING);
1860 static enum window_copy_cmd_action
1861 window_copy_cmd_page_up(struct window_copy_cmd_state *cs)
1863 struct window_mode_entry *wme = cs->wme;
1864 u_int np = wme->prefix;
1866 for (; np != 0; np--)
1867 window_copy_pageup1(wme, 0);
1868 return (WINDOW_COPY_CMD_NOTHING);
1871 static enum window_copy_cmd_action
1872 window_copy_cmd_previous_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_previous_paragraph(wme);
1879 return (WINDOW_COPY_CMD_NOTHING);
1882 static enum window_copy_cmd_action
1883 window_copy_cmd_previous_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_previous_word(wme, "", 1);
1890 return (WINDOW_COPY_CMD_NOTHING);
1893 static enum window_copy_cmd_action
1894 window_copy_cmd_previous_word(struct window_copy_cmd_state *cs)
1896 struct window_mode_entry *wme = cs->wme;
1897 u_int np = wme->prefix;
1898 const char *separators;
1900 separators = options_get_string(cs->s->options, "word-separators");
1902 for (; np != 0; np--)
1903 window_copy_cursor_previous_word(wme, separators, 1);
1904 return (WINDOW_COPY_CMD_NOTHING);
1907 static enum window_copy_cmd_action
1908 window_copy_cmd_rectangle_on(struct window_copy_cmd_state *cs)
1910 struct window_mode_entry *wme = cs->wme;
1911 struct window_copy_mode_data *data = wme->data;
1913 data->lineflag = LINE_SEL_NONE;
1914 window_copy_rectangle_set(wme, 1);
1916 return (WINDOW_COPY_CMD_NOTHING);
1919 static enum window_copy_cmd_action
1920 window_copy_cmd_rectangle_off(struct window_copy_cmd_state *cs)
1922 struct window_mode_entry *wme = cs->wme;
1923 struct window_copy_mode_data *data = wme->data;
1925 data->lineflag = LINE_SEL_NONE;
1926 window_copy_rectangle_set(wme, 0);
1928 return (WINDOW_COPY_CMD_NOTHING);
1931 static enum window_copy_cmd_action
1932 window_copy_cmd_rectangle_toggle(struct window_copy_cmd_state *cs)
1934 struct window_mode_entry *wme = cs->wme;
1935 struct window_copy_mode_data *data = wme->data;
1937 data->lineflag = LINE_SEL_NONE;
1938 window_copy_rectangle_set(wme, !data->rectflag);
1940 return (WINDOW_COPY_CMD_NOTHING);
1943 static enum window_copy_cmd_action
1944 window_copy_cmd_scroll_down(struct window_copy_cmd_state *cs)
1946 struct window_mode_entry *wme = cs->wme;
1947 struct window_copy_mode_data *data = wme->data;
1948 u_int np = wme->prefix;
1950 for (; np != 0; np--)
1951 window_copy_cursor_down(wme, 1);
1952 if (data->scroll_exit && data->oy == 0)
1953 return (WINDOW_COPY_CMD_CANCEL);
1954 return (WINDOW_COPY_CMD_NOTHING);
1957 static enum window_copy_cmd_action
1958 window_copy_cmd_scroll_down_and_cancel(struct window_copy_cmd_state *cs)
1960 struct window_mode_entry *wme = cs->wme;
1961 struct window_copy_mode_data *data = wme->data;
1962 u_int np = wme->prefix;
1964 for (; np != 0; np--)
1965 window_copy_cursor_down(wme, 1);
1966 if (data->oy == 0)
1967 return (WINDOW_COPY_CMD_CANCEL);
1968 return (WINDOW_COPY_CMD_NOTHING);
1971 static enum window_copy_cmd_action
1972 window_copy_cmd_scroll_up(struct window_copy_cmd_state *cs)
1974 struct window_mode_entry *wme = cs->wme;
1975 u_int np = wme->prefix;
1977 for (; np != 0; np--)
1978 window_copy_cursor_up(wme, 1);
1979 return (WINDOW_COPY_CMD_NOTHING);
1982 static enum window_copy_cmd_action
1983 window_copy_cmd_search_again(struct window_copy_cmd_state *cs)
1985 struct window_mode_entry *wme = cs->wme;
1986 struct window_copy_mode_data *data = wme->data;
1987 u_int np = wme->prefix;
1989 if (data->searchtype == WINDOW_COPY_SEARCHUP) {
1990 for (; np != 0; np--)
1991 window_copy_search_up(wme, data->searchregex);
1992 } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
1993 for (; np != 0; np--)
1994 window_copy_search_down(wme, data->searchregex);
1996 return (WINDOW_COPY_CMD_NOTHING);
1999 static enum window_copy_cmd_action
2000 window_copy_cmd_search_reverse(struct window_copy_cmd_state *cs)
2002 struct window_mode_entry *wme = cs->wme;
2003 struct window_copy_mode_data *data = wme->data;
2004 u_int np = wme->prefix;
2006 if (data->searchtype == WINDOW_COPY_SEARCHUP) {
2007 for (; np != 0; np--)
2008 window_copy_search_down(wme, data->searchregex);
2009 } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
2010 for (; np != 0; np--)
2011 window_copy_search_up(wme, data->searchregex);
2013 return (WINDOW_COPY_CMD_NOTHING);
2016 static enum window_copy_cmd_action
2017 window_copy_cmd_select_line(struct window_copy_cmd_state *cs)
2019 struct window_mode_entry *wme = cs->wme;
2020 struct window_copy_mode_data *data = wme->data;
2021 u_int np = wme->prefix;
2023 data->lineflag = LINE_SEL_LEFT_RIGHT;
2024 data->rectflag = 0;
2025 data->selflag = SEL_LINE;
2026 data->dx = data->cx;
2027 data->dy = screen_hsize(data->backing) + data->cy - data->oy;
2029 window_copy_cursor_start_of_line(wme);
2030 data->selrx = data->cx;
2031 data->selry = screen_hsize(data->backing) + data->cy - data->oy;
2032 data->endselry = data->selry;
2033 window_copy_start_selection(wme);
2034 window_copy_cursor_end_of_line(wme);
2035 data->endselry = screen_hsize(data->backing) + data->cy - data->oy;
2036 data->endselrx = window_copy_find_length(wme, data->endselry);
2037 for (; np > 1; np--) {
2038 window_copy_cursor_down(wme, 0);
2039 window_copy_cursor_end_of_line(wme);
2042 return (WINDOW_COPY_CMD_REDRAW);
2045 static enum window_copy_cmd_action
2046 window_copy_cmd_select_word(struct window_copy_cmd_state *cs)
2048 struct window_mode_entry *wme = cs->wme;
2049 struct options *session_options = cs->s->options;
2050 struct window_copy_mode_data *data = wme->data;
2051 u_int px, py, nextx, nexty;
2053 data->lineflag = LINE_SEL_LEFT_RIGHT;
2054 data->rectflag = 0;
2055 data->selflag = SEL_WORD;
2056 data->dx = data->cx;
2057 data->dy = screen_hsize(data->backing) + data->cy - data->oy;
2059 data->separators = options_get_string(session_options,
2060 "word-separators");
2061 window_copy_cursor_previous_word(wme, data->separators, 0);
2062 px = data->cx;
2063 py = screen_hsize(data->backing) + data->cy - data->oy;
2064 data->selrx = px;
2065 data->selry = py;
2066 window_copy_start_selection(wme);
2068 /* Handle single character words. */
2069 nextx = px + 1;
2070 nexty = py;
2071 if (grid_get_line(data->backing->grid, nexty)->flags &
2072 GRID_LINE_WRAPPED && nextx > screen_size_x(data->backing) - 1) {
2073 nextx = 0;
2074 nexty++;
2076 if (px >= window_copy_find_length(wme, py) ||
2077 !window_copy_in_set(wme, nextx, nexty, WHITESPACE))
2078 window_copy_cursor_next_word_end(wme, data->separators, 1);
2079 else {
2080 window_copy_update_cursor(wme, px, data->cy);
2081 if (window_copy_update_selection(wme, 1, 1))
2082 window_copy_redraw_lines(wme, data->cy, 1);
2084 data->endselrx = data->cx;
2085 data->endselry = screen_hsize(data->backing) + data->cy - data->oy;
2086 if (data->dy > data->endselry) {
2087 data->dy = data->endselry;
2088 data->dx = data->endselrx;
2089 } else if (data->dx > data->endselrx)
2090 data->dx = data->endselrx;
2092 return (WINDOW_COPY_CMD_REDRAW);
2095 static enum window_copy_cmd_action
2096 window_copy_cmd_set_mark(struct window_copy_cmd_state *cs)
2098 struct window_copy_mode_data *data = cs->wme->data;
2100 data->mx = data->cx;
2101 data->my = screen_hsize(data->backing) + data->cy - data->oy;
2102 data->showmark = 1;
2103 return (WINDOW_COPY_CMD_REDRAW);
2106 static enum window_copy_cmd_action
2107 window_copy_cmd_start_of_line(struct window_copy_cmd_state *cs)
2109 struct window_mode_entry *wme = cs->wme;
2111 window_copy_cursor_start_of_line(wme);
2112 return (WINDOW_COPY_CMD_NOTHING);
2115 static enum window_copy_cmd_action
2116 window_copy_cmd_top_line(struct window_copy_cmd_state *cs)
2118 struct window_mode_entry *wme = cs->wme;
2119 struct window_copy_mode_data *data = wme->data;
2121 data->cx = 0;
2122 data->cy = 0;
2124 window_copy_update_selection(wme, 1, 0);
2125 return (WINDOW_COPY_CMD_REDRAW);
2128 static enum window_copy_cmd_action
2129 window_copy_cmd_copy_pipe_no_clear(struct window_copy_cmd_state *cs)
2131 struct window_mode_entry *wme = cs->wme;
2132 struct client *c = cs->c;
2133 struct session *s = cs->s;
2134 struct winlink *wl = cs->wl;
2135 struct window_pane *wp = wme->wp;
2136 char *command = NULL, *prefix = NULL;
2137 const char *arg0 = args_string(cs->wargs, 0);
2138 const char *arg1 = args_string(cs->wargs, 1);
2139 int set_paste = !args_has(cs->wargs, 'P');
2140 int set_clip = !args_has(cs->wargs, 'C');
2142 if (arg1 != NULL)
2143 prefix = format_single(NULL, arg1, c, s, wl, wp);
2145 if (s != NULL && arg0 != NULL && *arg0 != '\0')
2146 command = format_single(NULL, arg0, c, s, wl, wp);
2147 window_copy_copy_pipe(wme, s, prefix, command,
2148 set_paste, set_clip);
2149 free(command);
2151 free(prefix);
2152 return (WINDOW_COPY_CMD_NOTHING);
2155 static enum window_copy_cmd_action
2156 window_copy_cmd_copy_pipe(struct window_copy_cmd_state *cs)
2158 struct window_mode_entry *wme = cs->wme;
2160 window_copy_cmd_copy_pipe_no_clear(cs);
2161 window_copy_clear_selection(wme);
2162 return (WINDOW_COPY_CMD_REDRAW);
2165 static enum window_copy_cmd_action
2166 window_copy_cmd_copy_pipe_and_cancel(struct window_copy_cmd_state *cs)
2168 struct window_mode_entry *wme = cs->wme;
2170 window_copy_cmd_copy_pipe_no_clear(cs);
2171 window_copy_clear_selection(wme);
2172 return (WINDOW_COPY_CMD_CANCEL);
2175 static enum window_copy_cmd_action
2176 window_copy_cmd_pipe_no_clear(struct window_copy_cmd_state *cs)
2178 struct window_mode_entry *wme = cs->wme;
2179 struct client *c = cs->c;
2180 struct session *s = cs->s;
2181 struct winlink *wl = cs->wl;
2182 struct window_pane *wp = wme->wp;
2183 char *command = NULL;
2184 const char *arg0 = args_string(cs->wargs, 0);
2186 if (s != NULL && arg0 != NULL && *arg0 != '\0')
2187 command = format_single(NULL, arg0, c, s, wl, wp);
2188 window_copy_pipe(wme, s, command);
2189 free(command);
2191 return (WINDOW_COPY_CMD_NOTHING);
2194 static enum window_copy_cmd_action
2195 window_copy_cmd_pipe(struct window_copy_cmd_state *cs)
2197 struct window_mode_entry *wme = cs->wme;
2199 window_copy_cmd_pipe_no_clear(cs);
2200 window_copy_clear_selection(wme);
2201 return (WINDOW_COPY_CMD_REDRAW);
2204 static enum window_copy_cmd_action
2205 window_copy_cmd_pipe_and_cancel(struct window_copy_cmd_state *cs)
2207 struct window_mode_entry *wme = cs->wme;
2209 window_copy_cmd_pipe_no_clear(cs);
2210 window_copy_clear_selection(wme);
2211 return (WINDOW_COPY_CMD_CANCEL);
2214 static enum window_copy_cmd_action
2215 window_copy_cmd_goto_line(struct window_copy_cmd_state *cs)
2217 struct window_mode_entry *wme = cs->wme;
2218 const char *arg0 = args_string(cs->wargs, 0);
2220 if (*arg0 != '\0')
2221 window_copy_goto_line(wme, arg0);
2222 return (WINDOW_COPY_CMD_NOTHING);
2225 static enum window_copy_cmd_action
2226 window_copy_cmd_jump_backward(struct window_copy_cmd_state *cs)
2228 struct window_mode_entry *wme = cs->wme;
2229 struct window_copy_mode_data *data = wme->data;
2230 u_int np = wme->prefix;
2231 const char *arg0 = args_string(cs->wargs, 0);
2233 if (*arg0 != '\0') {
2234 data->jumptype = WINDOW_COPY_JUMPBACKWARD;
2235 free(data->jumpchar);
2236 data->jumpchar = utf8_fromcstr(arg0);
2237 for (; np != 0; np--)
2238 window_copy_cursor_jump_back(wme);
2240 return (WINDOW_COPY_CMD_NOTHING);
2243 static enum window_copy_cmd_action
2244 window_copy_cmd_jump_forward(struct window_copy_cmd_state *cs)
2246 struct window_mode_entry *wme = cs->wme;
2247 struct window_copy_mode_data *data = wme->data;
2248 u_int np = wme->prefix;
2249 const char *arg0 = args_string(cs->wargs, 0);
2251 if (*arg0 != '\0') {
2252 data->jumptype = WINDOW_COPY_JUMPFORWARD;
2253 free(data->jumpchar);
2254 data->jumpchar = utf8_fromcstr(arg0);
2255 for (; np != 0; np--)
2256 window_copy_cursor_jump(wme);
2258 return (WINDOW_COPY_CMD_NOTHING);
2261 static enum window_copy_cmd_action
2262 window_copy_cmd_jump_to_backward(struct window_copy_cmd_state *cs)
2264 struct window_mode_entry *wme = cs->wme;
2265 struct window_copy_mode_data *data = wme->data;
2266 u_int np = wme->prefix;
2267 const char *arg0 = args_string(cs->wargs, 0);
2269 if (*arg0 != '\0') {
2270 data->jumptype = WINDOW_COPY_JUMPTOBACKWARD;
2271 free(data->jumpchar);
2272 data->jumpchar = utf8_fromcstr(arg0);
2273 for (; np != 0; np--)
2274 window_copy_cursor_jump_to_back(wme);
2276 return (WINDOW_COPY_CMD_NOTHING);
2279 static enum window_copy_cmd_action
2280 window_copy_cmd_jump_to_forward(struct window_copy_cmd_state *cs)
2282 struct window_mode_entry *wme = cs->wme;
2283 struct window_copy_mode_data *data = wme->data;
2284 u_int np = wme->prefix;
2285 const char *arg0 = args_string(cs->wargs, 0);
2287 if (*arg0 != '\0') {
2288 data->jumptype = WINDOW_COPY_JUMPTOFORWARD;
2289 free(data->jumpchar);
2290 data->jumpchar = utf8_fromcstr(arg0);
2291 for (; np != 0; np--)
2292 window_copy_cursor_jump_to(wme);
2294 return (WINDOW_COPY_CMD_NOTHING);
2297 static enum window_copy_cmd_action
2298 window_copy_cmd_jump_to_mark(struct window_copy_cmd_state *cs)
2300 struct window_mode_entry *wme = cs->wme;
2302 window_copy_jump_to_mark(wme);
2303 return (WINDOW_COPY_CMD_NOTHING);
2306 static enum window_copy_cmd_action
2307 window_copy_cmd_next_prompt(struct window_copy_cmd_state *cs)
2309 struct window_mode_entry *wme = cs->wme;
2311 window_copy_cursor_prompt(wme, 1, args_has(cs->wargs, 'o'));
2312 return (WINDOW_COPY_CMD_NOTHING);
2315 static enum window_copy_cmd_action
2316 window_copy_cmd_previous_prompt(struct window_copy_cmd_state *cs)
2318 struct window_mode_entry *wme = cs->wme;
2320 window_copy_cursor_prompt(wme, 0, args_has(cs->wargs, 'o'));
2321 return (WINDOW_COPY_CMD_NOTHING);
2324 static enum window_copy_cmd_action
2325 window_copy_cmd_search_backward(struct window_copy_cmd_state *cs)
2327 struct window_mode_entry *wme = cs->wme;
2328 struct window_copy_mode_data *data = wme->data;
2329 u_int np = wme->prefix;
2331 if (!window_copy_expand_search_string(cs))
2332 return (WINDOW_COPY_CMD_NOTHING);
2334 if (data->searchstr != NULL) {
2335 data->searchtype = WINDOW_COPY_SEARCHUP;
2336 data->searchregex = 1;
2337 data->timeout = 0;
2338 for (; np != 0; np--)
2339 window_copy_search_up(wme, 1);
2341 return (WINDOW_COPY_CMD_NOTHING);
2344 static enum window_copy_cmd_action
2345 window_copy_cmd_search_backward_text(struct window_copy_cmd_state *cs)
2347 struct window_mode_entry *wme = cs->wme;
2348 struct window_copy_mode_data *data = wme->data;
2349 u_int np = wme->prefix;
2351 if (!window_copy_expand_search_string(cs))
2352 return (WINDOW_COPY_CMD_NOTHING);
2354 if (data->searchstr != NULL) {
2355 data->searchtype = WINDOW_COPY_SEARCHUP;
2356 data->searchregex = 0;
2357 data->timeout = 0;
2358 for (; np != 0; np--)
2359 window_copy_search_up(wme, 0);
2361 return (WINDOW_COPY_CMD_NOTHING);
2364 static enum window_copy_cmd_action
2365 window_copy_cmd_search_forward(struct window_copy_cmd_state *cs)
2367 struct window_mode_entry *wme = cs->wme;
2368 struct window_copy_mode_data *data = wme->data;
2369 u_int np = wme->prefix;
2371 if (!window_copy_expand_search_string(cs))
2372 return (WINDOW_COPY_CMD_NOTHING);
2374 if (data->searchstr != NULL) {
2375 data->searchtype = WINDOW_COPY_SEARCHDOWN;
2376 data->searchregex = 1;
2377 data->timeout = 0;
2378 for (; np != 0; np--)
2379 window_copy_search_down(wme, 1);
2381 return (WINDOW_COPY_CMD_NOTHING);
2384 static enum window_copy_cmd_action
2385 window_copy_cmd_search_forward_text(struct window_copy_cmd_state *cs)
2387 struct window_mode_entry *wme = cs->wme;
2388 struct window_copy_mode_data *data = wme->data;
2389 u_int np = wme->prefix;
2391 if (!window_copy_expand_search_string(cs))
2392 return (WINDOW_COPY_CMD_NOTHING);
2394 if (data->searchstr != NULL) {
2395 data->searchtype = WINDOW_COPY_SEARCHDOWN;
2396 data->searchregex = 0;
2397 data->timeout = 0;
2398 for (; np != 0; np--)
2399 window_copy_search_down(wme, 0);
2401 return (WINDOW_COPY_CMD_NOTHING);
2404 static enum window_copy_cmd_action
2405 window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs)
2407 struct window_mode_entry *wme = cs->wme;
2408 struct window_copy_mode_data *data = wme->data;
2409 const char *arg0 = args_string(cs->wargs, 0);
2410 const char *ss = data->searchstr;
2411 char prefix;
2412 enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING;
2414 data->timeout = 0;
2416 log_debug("%s: %s", __func__, arg0);
2418 prefix = *arg0++;
2419 if (data->searchx == -1 || data->searchy == -1) {
2420 data->searchx = data->cx;
2421 data->searchy = data->cy;
2422 data->searcho = data->oy;
2423 } else if (ss != NULL && strcmp(arg0, ss) != 0) {
2424 data->cx = data->searchx;
2425 data->cy = data->searchy;
2426 data->oy = data->searcho;
2427 action = WINDOW_COPY_CMD_REDRAW;
2429 if (*arg0 == '\0') {
2430 window_copy_clear_marks(wme);
2431 return (WINDOW_COPY_CMD_REDRAW);
2433 switch (prefix) {
2434 case '=':
2435 case '-':
2436 data->searchtype = WINDOW_COPY_SEARCHUP;
2437 data->searchregex = 0;
2438 free(data->searchstr);
2439 data->searchstr = xstrdup(arg0);
2440 if (!window_copy_search_up(wme, 0)) {
2441 window_copy_clear_marks(wme);
2442 return (WINDOW_COPY_CMD_REDRAW);
2444 break;
2445 case '+':
2446 data->searchtype = WINDOW_COPY_SEARCHDOWN;
2447 data->searchregex = 0;
2448 free(data->searchstr);
2449 data->searchstr = xstrdup(arg0);
2450 if (!window_copy_search_down(wme, 0)) {
2451 window_copy_clear_marks(wme);
2452 return (WINDOW_COPY_CMD_REDRAW);
2454 break;
2456 return (action);
2459 static enum window_copy_cmd_action
2460 window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs)
2462 struct window_mode_entry *wme = cs->wme;
2463 struct window_copy_mode_data *data = wme->data;
2464 const char *arg0 = args_string(cs->wargs, 0);
2465 const char *ss = data->searchstr;
2466 char prefix;
2467 enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING;
2469 data->timeout = 0;
2471 log_debug("%s: %s", __func__, arg0);
2473 prefix = *arg0++;
2474 if (data->searchx == -1 || data->searchy == -1) {
2475 data->searchx = data->cx;
2476 data->searchy = data->cy;
2477 data->searcho = data->oy;
2478 } else if (ss != NULL && strcmp(arg0, ss) != 0) {
2479 data->cx = data->searchx;
2480 data->cy = data->searchy;
2481 data->oy = data->searcho;
2482 action = WINDOW_COPY_CMD_REDRAW;
2484 if (*arg0 == '\0') {
2485 window_copy_clear_marks(wme);
2486 return (WINDOW_COPY_CMD_REDRAW);
2488 switch (prefix) {
2489 case '=':
2490 case '+':
2491 data->searchtype = WINDOW_COPY_SEARCHDOWN;
2492 data->searchregex = 0;
2493 free(data->searchstr);
2494 data->searchstr = xstrdup(arg0);
2495 if (!window_copy_search_down(wme, 0)) {
2496 window_copy_clear_marks(wme);
2497 return (WINDOW_COPY_CMD_REDRAW);
2499 break;
2500 case '-':
2501 data->searchtype = WINDOW_COPY_SEARCHUP;
2502 data->searchregex = 0;
2503 free(data->searchstr);
2504 data->searchstr = xstrdup(arg0);
2505 if (!window_copy_search_up(wme, 0)) {
2506 window_copy_clear_marks(wme);
2507 return (WINDOW_COPY_CMD_REDRAW);
2510 return (action);
2513 static enum window_copy_cmd_action
2514 window_copy_cmd_refresh_from_pane(struct window_copy_cmd_state *cs)
2516 struct window_mode_entry *wme = cs->wme;
2517 struct window_pane *wp = wme->swp;
2518 struct window_copy_mode_data *data = wme->data;
2520 if (data->viewmode)
2521 return (WINDOW_COPY_CMD_NOTHING);
2523 screen_free(data->backing);
2524 free(data->backing);
2525 data->backing = window_copy_clone_screen(&wp->base, &data->screen, NULL,
2526 NULL, wme->swp != wme->wp);
2528 window_copy_size_changed(wme);
2529 return (WINDOW_COPY_CMD_REDRAW);
2532 static const struct {
2533 const char *command;
2534 u_int minargs;
2535 u_int maxargs;
2536 struct args_parse args;
2537 enum window_copy_cmd_clear clear;
2538 enum window_copy_cmd_action (*f)(struct window_copy_cmd_state *);
2539 } window_copy_cmd_table[] = {
2540 { .command = "append-selection",
2541 .args = { "", 0, 0, NULL },
2542 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2543 .f = window_copy_cmd_append_selection
2545 { .command = "append-selection-and-cancel",
2546 .args = { "", 0, 0, NULL },
2547 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2548 .f = window_copy_cmd_append_selection_and_cancel
2550 { .command = "back-to-indentation",
2551 .args = { "", 0, 0, NULL },
2552 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2553 .f = window_copy_cmd_back_to_indentation
2555 { .command = "begin-selection",
2556 .args = { "", 0, 0, NULL },
2557 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2558 .f = window_copy_cmd_begin_selection
2560 { .command = "bottom-line",
2561 .args = { "", 0, 0, NULL },
2562 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2563 .f = window_copy_cmd_bottom_line
2565 { .command = "cancel",
2566 .args = { "", 0, 0, NULL },
2567 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2568 .f = window_copy_cmd_cancel
2570 { .command = "clear-selection",
2571 .args = { "", 0, 0, NULL },
2572 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2573 .f = window_copy_cmd_clear_selection
2575 { .command = "copy-end-of-line",
2576 .args = { "CP", 0, 1, NULL },
2577 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2578 .f = window_copy_cmd_copy_end_of_line
2580 { .command = "copy-end-of-line-and-cancel",
2581 .args = { "CP", 0, 1, NULL },
2582 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2583 .f = window_copy_cmd_copy_end_of_line_and_cancel
2585 { .command = "copy-pipe-end-of-line",
2586 .args = { "CP", 0, 2, NULL },
2587 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2588 .f = window_copy_cmd_copy_pipe_end_of_line
2590 { .command = "copy-pipe-end-of-line-and-cancel",
2591 .args = { "CP", 0, 2, NULL },
2592 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2593 .f = window_copy_cmd_copy_pipe_end_of_line_and_cancel
2595 { .command = "copy-line",
2596 .args = { "CP", 0, 1, NULL },
2597 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2598 .f = window_copy_cmd_copy_line
2600 { .command = "copy-line-and-cancel",
2601 .args = { "CP", 0, 1, NULL },
2602 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2603 .f = window_copy_cmd_copy_line_and_cancel
2605 { .command = "copy-pipe-line",
2606 .args = { "CP", 0, 2, NULL },
2607 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2608 .f = window_copy_cmd_copy_pipe_line
2610 { .command = "copy-pipe-line-and-cancel",
2611 .args = { "CP", 0, 2, NULL },
2612 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2613 .f = window_copy_cmd_copy_pipe_line_and_cancel
2615 { .command = "copy-pipe-no-clear",
2616 .args = { "CP", 0, 2, NULL },
2617 .clear = WINDOW_COPY_CMD_CLEAR_NEVER,
2618 .f = window_copy_cmd_copy_pipe_no_clear
2620 { .command = "copy-pipe",
2621 .args = { "CP", 0, 2, NULL },
2622 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2623 .f = window_copy_cmd_copy_pipe
2625 { .command = "copy-pipe-and-cancel",
2626 .args = { "CP", 0, 2, NULL },
2627 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2628 .f = window_copy_cmd_copy_pipe_and_cancel
2630 { .command = "copy-selection-no-clear",
2631 .args = { "CP", 0, 1, NULL },
2632 .clear = WINDOW_COPY_CMD_CLEAR_NEVER,
2633 .f = window_copy_cmd_copy_selection_no_clear
2635 { .command = "copy-selection",
2636 .args = { "CP", 0, 1, NULL },
2637 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2638 .f = window_copy_cmd_copy_selection
2640 { .command = "copy-selection-and-cancel",
2641 .args = { "CP", 0, 1, NULL },
2642 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2643 .f = window_copy_cmd_copy_selection_and_cancel
2645 { .command = "cursor-down",
2646 .args = { "", 0, 0, NULL },
2647 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2648 .f = window_copy_cmd_cursor_down
2650 { .command = "cursor-down-and-cancel",
2651 .args = { "", 0, 0, NULL },
2652 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2653 .f = window_copy_cmd_cursor_down_and_cancel
2655 { .command = "cursor-left",
2656 .args = { "", 0, 0, NULL },
2657 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2658 .f = window_copy_cmd_cursor_left
2660 { .command = "cursor-right",
2661 .args = { "", 0, 0, NULL },
2662 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2663 .f = window_copy_cmd_cursor_right
2665 { .command = "cursor-up",
2666 .args = { "", 0, 0, NULL },
2667 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2668 .f = window_copy_cmd_cursor_up
2670 { .command = "end-of-line",
2671 .args = { "", 0, 0, NULL },
2672 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2673 .f = window_copy_cmd_end_of_line
2675 { .command = "goto-line",
2676 .args = { "", 1, 1, NULL },
2677 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2678 .f = window_copy_cmd_goto_line
2680 { .command = "halfpage-down",
2681 .args = { "", 0, 0, NULL },
2682 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2683 .f = window_copy_cmd_halfpage_down
2685 { .command = "halfpage-down-and-cancel",
2686 .args = { "", 0, 0, NULL },
2687 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2688 .f = window_copy_cmd_halfpage_down_and_cancel
2690 { .command = "halfpage-up",
2691 .args = { "", 0, 0, NULL },
2692 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2693 .f = window_copy_cmd_halfpage_up
2695 { .command = "history-bottom",
2696 .args = { "", 0, 0, NULL },
2697 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2698 .f = window_copy_cmd_history_bottom
2700 { .command = "history-top",
2701 .args = { "", 0, 0, NULL },
2702 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2703 .f = window_copy_cmd_history_top
2705 { .command = "jump-again",
2706 .args = { "", 0, 0, NULL },
2707 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2708 .f = window_copy_cmd_jump_again
2710 { .command = "jump-backward",
2711 .args = { "", 1, 1, NULL },
2712 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2713 .f = window_copy_cmd_jump_backward
2715 { .command = "jump-forward",
2716 .args = { "", 1, 1, NULL },
2717 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2718 .f = window_copy_cmd_jump_forward
2720 { .command = "jump-reverse",
2721 .args = { "", 0, 0, NULL },
2722 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2723 .f = window_copy_cmd_jump_reverse
2725 { .command = "jump-to-backward",
2726 .args = { "", 1, 1, NULL },
2727 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2728 .f = window_copy_cmd_jump_to_backward
2730 { .command = "jump-to-forward",
2731 .args = { "", 1, 1, NULL },
2732 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2733 .f = window_copy_cmd_jump_to_forward
2735 { .command = "jump-to-mark",
2736 .args = { "", 0, 0, NULL },
2737 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2738 .f = window_copy_cmd_jump_to_mark
2740 { .command = "next-prompt",
2741 .args = { "o", 0, 0, NULL },
2742 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2743 .f = window_copy_cmd_next_prompt
2745 { .command = "previous-prompt",
2746 .args = { "o", 0, 0, NULL },
2747 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2748 .f = window_copy_cmd_previous_prompt
2750 { .command = "middle-line",
2751 .args = { "", 0, 0, NULL },
2752 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2753 .f = window_copy_cmd_middle_line
2755 { .command = "next-matching-bracket",
2756 .args = { "", 0, 0, NULL },
2757 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2758 .f = window_copy_cmd_next_matching_bracket
2760 { .command = "next-paragraph",
2761 .args = { "", 0, 0, NULL },
2762 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2763 .f = window_copy_cmd_next_paragraph
2765 { .command = "next-space",
2766 .args = { "", 0, 0, NULL },
2767 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2768 .f = window_copy_cmd_next_space
2770 { .command = "next-space-end",
2771 .args = { "", 0, 0, NULL },
2772 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2773 .f = window_copy_cmd_next_space_end
2775 { .command = "next-word",
2776 .args = { "", 0, 0, NULL },
2777 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2778 .f = window_copy_cmd_next_word
2780 { .command = "next-word-end",
2781 .args = { "", 0, 0, NULL },
2782 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2783 .f = window_copy_cmd_next_word_end
2785 { .command = "other-end",
2786 .args = { "", 0, 0, NULL },
2787 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2788 .f = window_copy_cmd_other_end
2790 { .command = "page-down",
2791 .args = { "", 0, 0, NULL },
2792 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2793 .f = window_copy_cmd_page_down
2795 { .command = "page-down-and-cancel",
2796 .args = { "", 0, 0, NULL },
2797 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2798 .f = window_copy_cmd_page_down_and_cancel
2800 { .command = "page-up",
2801 .args = { "", 0, 0, NULL },
2802 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2803 .f = window_copy_cmd_page_up
2805 { .command = "pipe-no-clear",
2806 .args = { "", 0, 1, NULL },
2807 .clear = WINDOW_COPY_CMD_CLEAR_NEVER,
2808 .f = window_copy_cmd_pipe_no_clear
2810 { .command = "pipe",
2811 .args = { "", 0, 1, NULL },
2812 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2813 .f = window_copy_cmd_pipe
2815 { .command = "pipe-and-cancel",
2816 .args = { "", 0, 1, NULL },
2817 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2818 .f = window_copy_cmd_pipe_and_cancel
2820 { .command = "previous-matching-bracket",
2821 .args = { "", 0, 0, NULL },
2822 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2823 .f = window_copy_cmd_previous_matching_bracket
2825 { .command = "previous-paragraph",
2826 .args = { "", 0, 0, NULL },
2827 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2828 .f = window_copy_cmd_previous_paragraph
2830 { .command = "previous-space",
2831 .args = { "", 0, 0, NULL },
2832 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2833 .f = window_copy_cmd_previous_space
2835 { .command = "previous-word",
2836 .args = { "", 0, 0, NULL },
2837 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2838 .f = window_copy_cmd_previous_word
2840 { .command = "rectangle-on",
2841 .args = { "", 0, 0, NULL },
2842 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2843 .f = window_copy_cmd_rectangle_on
2845 { .command = "rectangle-off",
2846 .args = { "", 0, 0, NULL },
2847 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2848 .f = window_copy_cmd_rectangle_off
2850 { .command = "rectangle-toggle",
2851 .args = { "", 0, 0, NULL },
2852 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2853 .f = window_copy_cmd_rectangle_toggle
2855 { .command = "refresh-from-pane",
2856 .args = { "", 0, 0, NULL },
2857 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2858 .f = window_copy_cmd_refresh_from_pane
2860 { .command = "scroll-bottom",
2861 .args = { "", 0, 0, NULL },
2862 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2863 .f = window_copy_cmd_scroll_bottom
2865 { .command = "scroll-down",
2866 .args = { "", 0, 0, NULL },
2867 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2868 .f = window_copy_cmd_scroll_down
2870 { .command = "scroll-down-and-cancel",
2871 .args = { "", 0, 0, NULL },
2872 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2873 .f = window_copy_cmd_scroll_down_and_cancel
2875 { .command = "scroll-middle",
2876 .args = { "", 0, 0, NULL },
2877 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2878 .f = window_copy_cmd_scroll_middle
2880 { .command = "scroll-top",
2881 .args = { "", 0, 0, NULL },
2882 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2883 .f = window_copy_cmd_scroll_top
2885 { .command = "scroll-up",
2886 .args = { "", 0, 0, NULL },
2887 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2888 .f = window_copy_cmd_scroll_up
2890 { .command = "search-again",
2891 .args = { "", 0, 0, NULL },
2892 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2893 .f = window_copy_cmd_search_again
2895 { .command = "search-backward",
2896 .args = { "", 0, 1, NULL },
2897 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2898 .f = window_copy_cmd_search_backward
2900 { .command = "search-backward-text",
2901 .args = { "", 0, 1, NULL },
2902 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2903 .f = window_copy_cmd_search_backward_text
2905 { .command = "search-backward-incremental",
2906 .args = { "", 1, 1, NULL },
2907 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2908 .f = window_copy_cmd_search_backward_incremental
2910 { .command = "search-forward",
2911 .args = { "", 0, 1, NULL },
2912 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2913 .f = window_copy_cmd_search_forward
2915 { .command = "search-forward-text",
2916 .args = { "", 0, 1, NULL },
2917 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2918 .f = window_copy_cmd_search_forward_text
2920 { .command = "search-forward-incremental",
2921 .args = { "", 1, 1, NULL },
2922 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2923 .f = window_copy_cmd_search_forward_incremental
2925 { .command = "search-reverse",
2926 .args = { "", 0, 0, NULL },
2927 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2928 .f = window_copy_cmd_search_reverse
2930 { .command = "select-line",
2931 .args = { "", 0, 0, NULL },
2932 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2933 .f = window_copy_cmd_select_line
2935 { .command = "select-word",
2936 .args = { "", 0, 0, NULL },
2937 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2938 .f = window_copy_cmd_select_word
2940 { .command = "set-mark",
2941 .args = { "", 0, 0, NULL },
2942 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2943 .f = window_copy_cmd_set_mark
2945 { .command = "start-of-line",
2946 .args = { "", 0, 0, NULL },
2947 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2948 .f = window_copy_cmd_start_of_line
2950 { .command = "stop-selection",
2951 .args = { "", 0, 0, NULL },
2952 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2953 .f = window_copy_cmd_stop_selection
2955 { .command = "toggle-position",
2956 .args = { "", 0, 0, NULL },
2957 .clear = WINDOW_COPY_CMD_CLEAR_NEVER,
2958 .f = window_copy_cmd_toggle_position
2960 { .command = "top-line",
2961 .args = { "", 0, 0, NULL },
2962 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2963 .f = window_copy_cmd_top_line
2967 static void
2968 window_copy_command(struct window_mode_entry *wme, struct client *c,
2969 struct session *s, struct winlink *wl, struct args *args,
2970 struct mouse_event *m)
2972 struct window_copy_mode_data *data = wme->data;
2973 struct window_pane *wp = wme->wp;
2974 struct window_copy_cmd_state cs;
2975 enum window_copy_cmd_action action;
2976 enum window_copy_cmd_clear clear = WINDOW_COPY_CMD_CLEAR_NEVER;
2977 const char *command;
2978 u_int i, count = args_count(args);
2979 int keys;
2980 char *error = NULL;
2982 if (count == 0)
2983 return;
2984 command = args_string(args, 0);
2986 if (m != NULL && m->valid && !MOUSE_WHEEL(m->b))
2987 window_copy_move_mouse(m);
2989 cs.wme = wme;
2990 cs.args = args;
2991 cs.wargs = NULL;
2992 cs.m = m;
2994 cs.c = c;
2995 cs.s = s;
2996 cs.wl = wl;
2998 action = WINDOW_COPY_CMD_NOTHING;
2999 for (i = 0; i < nitems(window_copy_cmd_table); i++) {
3000 if (strcmp(window_copy_cmd_table[i].command, command) == 0) {
3001 cs.wargs = args_parse(&window_copy_cmd_table[i].args,
3002 args_values(args), count, &error);
3004 if (error != NULL) {
3005 free(error);
3006 error = NULL;
3008 if (cs.wargs == NULL)
3009 break;
3011 clear = window_copy_cmd_table[i].clear;
3012 action = window_copy_cmd_table[i].f(&cs);
3013 args_free(cs.wargs);
3014 cs.wargs = NULL;
3015 break;
3019 if (strncmp(command, "search-", 7) != 0 && data->searchmark != NULL) {
3020 keys = options_get_number(wp->window->options, "mode-keys");
3021 if (clear == WINDOW_COPY_CMD_CLEAR_EMACS_ONLY &&
3022 keys == MODEKEY_VI)
3023 clear = WINDOW_COPY_CMD_CLEAR_NEVER;
3024 if (clear != WINDOW_COPY_CMD_CLEAR_NEVER) {
3025 window_copy_clear_marks(wme);
3026 data->searchx = data->searchy = -1;
3028 if (action == WINDOW_COPY_CMD_NOTHING)
3029 action = WINDOW_COPY_CMD_REDRAW;
3031 wme->prefix = 1;
3033 if (action == WINDOW_COPY_CMD_CANCEL)
3034 window_pane_reset_mode(wp);
3035 else if (action == WINDOW_COPY_CMD_REDRAW)
3036 window_copy_redraw_screen(wme);
3039 static void
3040 window_copy_scroll_to(struct window_mode_entry *wme, u_int px, u_int py,
3041 int no_redraw)
3043 struct window_copy_mode_data *data = wme->data;
3044 struct grid *gd = data->backing->grid;
3045 u_int offset, gap;
3047 data->cx = px;
3049 if (py >= gd->hsize - data->oy && py < gd->hsize - data->oy + gd->sy)
3050 data->cy = py - (gd->hsize - data->oy);
3051 else {
3052 gap = gd->sy / 4;
3053 if (py < gd->sy) {
3054 offset = 0;
3055 data->cy = py;
3056 } else if (py > gd->hsize + gd->sy - gap) {
3057 offset = gd->hsize;
3058 data->cy = py - gd->hsize;
3059 } else {
3060 offset = py + gap - gd->sy;
3061 data->cy = py - offset;
3063 data->oy = gd->hsize - offset;
3066 if (!no_redraw && data->searchmark != NULL && !data->timeout)
3067 window_copy_search_marks(wme, NULL, data->searchregex, 1);
3068 window_copy_update_selection(wme, 1, 0);
3069 if (!no_redraw)
3070 window_copy_redraw_screen(wme);
3073 static int
3074 window_copy_search_compare(struct grid *gd, u_int px, u_int py,
3075 struct grid *sgd, u_int spx, int cis)
3077 struct grid_cell gc, sgc;
3078 const struct utf8_data *ud, *sud;
3080 grid_get_cell(gd, px, py, &gc);
3081 ud = &gc.data;
3082 grid_get_cell(sgd, spx, 0, &sgc);
3083 sud = &sgc.data;
3085 if (ud->size != sud->size || ud->width != sud->width)
3086 return (0);
3088 if (cis && ud->size == 1)
3089 return (tolower(ud->data[0]) == sud->data[0]);
3091 return (memcmp(ud->data, sud->data, ud->size) == 0);
3094 static int
3095 window_copy_search_lr(struct grid *gd, struct grid *sgd, u_int *ppx, u_int py,
3096 u_int first, u_int last, int cis)
3098 u_int ax, bx, px, pywrap, endline;
3099 int matched;
3100 struct grid_line *gl;
3102 endline = gd->hsize + gd->sy - 1;
3103 for (ax = first; ax < last; ax++) {
3104 for (bx = 0; bx < sgd->sx; bx++) {
3105 px = ax + bx;
3106 pywrap = py;
3107 /* Wrap line. */
3108 while (px >= gd->sx && pywrap < endline) {
3109 gl = grid_get_line(gd, pywrap);
3110 if (~gl->flags & GRID_LINE_WRAPPED)
3111 break;
3112 px -= gd->sx;
3113 pywrap++;
3115 /* We have run off the end of the grid. */
3116 if (px >= gd->sx)
3117 break;
3118 matched = window_copy_search_compare(gd, px, pywrap,
3119 sgd, bx, cis);
3120 if (!matched)
3121 break;
3123 if (bx == sgd->sx) {
3124 *ppx = ax;
3125 return (1);
3128 return (0);
3131 static int
3132 window_copy_search_rl(struct grid *gd,
3133 struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis)
3135 u_int ax, bx, px, pywrap, endline;
3136 int matched;
3137 struct grid_line *gl;
3139 endline = gd->hsize + gd->sy - 1;
3140 for (ax = last; ax > first; ax--) {
3141 for (bx = 0; bx < sgd->sx; bx++) {
3142 px = ax - 1 + bx;
3143 pywrap = py;
3144 /* Wrap line. */
3145 while (px >= gd->sx && pywrap < endline) {
3146 gl = grid_get_line(gd, pywrap);
3147 if (~gl->flags & GRID_LINE_WRAPPED)
3148 break;
3149 px -= gd->sx;
3150 pywrap++;
3152 /* We have run off the end of the grid. */
3153 if (px >= gd->sx)
3154 break;
3155 matched = window_copy_search_compare(gd, px, pywrap,
3156 sgd, bx, cis);
3157 if (!matched)
3158 break;
3160 if (bx == sgd->sx) {
3161 *ppx = ax - 1;
3162 return (1);
3165 return (0);
3168 static int
3169 window_copy_search_lr_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py,
3170 u_int first, u_int last, regex_t *reg)
3172 int eflags = 0;
3173 u_int endline, foundx, foundy, len, pywrap, size = 1;
3174 char *buf;
3175 regmatch_t regmatch;
3176 struct grid_line *gl;
3179 * This can happen during search if the last match was the last
3180 * character on a line.
3182 if (first >= last)
3183 return (0);
3185 /* Set flags for regex search. */
3186 if (first != 0)
3187 eflags |= REG_NOTBOL;
3189 /* Need to look at the entire string. */
3190 buf = xmalloc(size);
3191 buf[0] = '\0';
3192 buf = window_copy_stringify(gd, py, first, gd->sx, buf, &size);
3193 len = gd->sx - first;
3194 endline = gd->hsize + gd->sy - 1;
3195 pywrap = py;
3196 while (buf != NULL &&
3197 pywrap <= endline &&
3198 len < WINDOW_COPY_SEARCH_MAX_LINE) {
3199 gl = grid_get_line(gd, pywrap);
3200 if (~gl->flags & GRID_LINE_WRAPPED)
3201 break;
3202 pywrap++;
3203 buf = window_copy_stringify(gd, pywrap, 0, gd->sx, buf, &size);
3204 len += gd->sx;
3207 if (regexec(reg, buf, 1, &regmatch, eflags) == 0 &&
3208 regmatch.rm_so != regmatch.rm_eo) {
3209 foundx = first;
3210 foundy = py;
3211 window_copy_cstrtocellpos(gd, len, &foundx, &foundy,
3212 buf + regmatch.rm_so);
3213 if (foundy == py && foundx < last) {
3214 *ppx = foundx;
3215 len -= foundx - first;
3216 window_copy_cstrtocellpos(gd, len, &foundx, &foundy,
3217 buf + regmatch.rm_eo);
3218 *psx = foundx;
3219 while (foundy > py) {
3220 *psx += gd->sx;
3221 foundy--;
3223 *psx -= *ppx;
3224 free(buf);
3225 return (1);
3229 free(buf);
3230 *ppx = 0;
3231 *psx = 0;
3232 return (0);
3235 static int
3236 window_copy_search_rl_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py,
3237 u_int first, u_int last, regex_t *reg)
3239 int eflags = 0;
3240 u_int endline, len, pywrap, size = 1;
3241 char *buf;
3242 struct grid_line *gl;
3244 /* Set flags for regex search. */
3245 if (first != 0)
3246 eflags |= REG_NOTBOL;
3248 /* Need to look at the entire string. */
3249 buf = xmalloc(size);
3250 buf[0] = '\0';
3251 buf = window_copy_stringify(gd, py, first, gd->sx, buf, &size);
3252 len = gd->sx - first;
3253 endline = gd->hsize + gd->sy - 1;
3254 pywrap = py;
3255 while (buf != NULL &&
3256 pywrap <= endline &&
3257 len < WINDOW_COPY_SEARCH_MAX_LINE) {
3258 gl = grid_get_line(gd, pywrap);
3259 if (~gl->flags & GRID_LINE_WRAPPED)
3260 break;
3261 pywrap++;
3262 buf = window_copy_stringify(gd, pywrap, 0, gd->sx, buf, &size);
3263 len += gd->sx;
3266 if (window_copy_last_regex(gd, py, first, last, len, ppx, psx, buf,
3267 reg, eflags))
3269 free(buf);
3270 return (1);
3273 free(buf);
3274 *ppx = 0;
3275 *psx = 0;
3276 return (0);
3279 static const char *
3280 window_copy_cellstring(const struct grid_line *gl, u_int px, size_t *size,
3281 int *allocated)
3283 static struct utf8_data ud;
3284 struct grid_cell_entry *gce;
3285 char *copy;
3287 if (px >= gl->cellsize) {
3288 *size = 1;
3289 *allocated = 0;
3290 return (" ");
3293 gce = &gl->celldata[px];
3294 if (gce->flags & GRID_FLAG_PADDING) {
3295 *size = 0;
3296 *allocated = 0;
3297 return (NULL);
3299 if (~gce->flags & GRID_FLAG_EXTENDED) {
3300 *size = 1;
3301 *allocated = 0;
3302 return (&gce->data.data);
3305 utf8_to_data(gl->extddata[gce->offset].data, &ud);
3306 if (ud.size == 0) {
3307 *size = 0;
3308 *allocated = 0;
3309 return (NULL);
3311 *size = ud.size;
3312 *allocated = 1;
3314 copy = xmalloc(ud.size);
3315 memcpy(copy, ud.data, ud.size);
3316 return (copy);
3319 /* Find last match in given range. */
3320 static int
3321 window_copy_last_regex(struct grid *gd, u_int py, u_int first, u_int last,
3322 u_int len, u_int *ppx, u_int *psx, const char *buf, const regex_t *preg,
3323 int eflags)
3325 u_int foundx, foundy, oldx, px = 0, savepx, savesx = 0;
3326 regmatch_t regmatch;
3328 foundx = first;
3329 foundy = py;
3330 oldx = first;
3331 while (regexec(preg, buf + px, 1, &regmatch, eflags) == 0) {
3332 if (regmatch.rm_so == regmatch.rm_eo)
3333 break;
3334 window_copy_cstrtocellpos(gd, len, &foundx, &foundy,
3335 buf + px + regmatch.rm_so);
3336 if (foundy > py || foundx >= last)
3337 break;
3338 len -= foundx - oldx;
3339 savepx = foundx;
3340 window_copy_cstrtocellpos(gd, len, &foundx, &foundy,
3341 buf + px + regmatch.rm_eo);
3342 if (foundy > py || foundx >= last) {
3343 *ppx = savepx;
3344 *psx = foundx;
3345 while (foundy > py) {
3346 *psx += gd->sx;
3347 foundy--;
3349 *psx -= *ppx;
3350 return (1);
3351 } else {
3352 savesx = foundx - savepx;
3353 len -= savesx;
3354 oldx = foundx;
3356 px += regmatch.rm_eo;
3359 if (savesx > 0) {
3360 *ppx = savepx;
3361 *psx = savesx;
3362 return (1);
3363 } else {
3364 *ppx = 0;
3365 *psx = 0;
3366 return (0);
3370 /* Stringify line and append to input buffer. Caller frees. */
3371 static char *
3372 window_copy_stringify(struct grid *gd, u_int py, u_int first, u_int last,
3373 char *buf, u_int *size)
3375 u_int ax, bx, newsize = *size;
3376 const struct grid_line *gl;
3377 const char *d;
3378 size_t bufsize = 1024, dlen;
3379 int allocated;
3381 while (bufsize < newsize)
3382 bufsize *= 2;
3383 buf = xrealloc(buf, bufsize);
3385 gl = grid_peek_line(gd, py);
3386 bx = *size - 1;
3387 for (ax = first; ax < last; ax++) {
3388 d = window_copy_cellstring(gl, ax, &dlen, &allocated);
3389 newsize += dlen;
3390 while (bufsize < newsize) {
3391 bufsize *= 2;
3392 buf = xrealloc(buf, bufsize);
3394 if (dlen == 1)
3395 buf[bx++] = *d;
3396 else {
3397 memcpy(buf + bx, d, dlen);
3398 bx += dlen;
3400 if (allocated)
3401 free((void *)d);
3403 buf[newsize - 1] = '\0';
3405 *size = newsize;
3406 return (buf);
3409 /* Map start of C string containing UTF-8 data to grid cell position. */
3410 static void
3411 window_copy_cstrtocellpos(struct grid *gd, u_int ncells, u_int *ppx, u_int *ppy,
3412 const char *str)
3414 u_int cell, ccell, px, pywrap, pos, len;
3415 int match;
3416 const struct grid_line *gl;
3417 const char *d;
3418 size_t dlen;
3419 struct {
3420 const char *d;
3421 size_t dlen;
3422 int allocated;
3423 } *cells;
3425 /* Populate the array of cell data. */
3426 cells = xreallocarray(NULL, ncells, sizeof cells[0]);
3427 cell = 0;
3428 px = *ppx;
3429 pywrap = *ppy;
3430 gl = grid_peek_line(gd, pywrap);
3431 while (cell < ncells) {
3432 cells[cell].d = window_copy_cellstring(gl, px,
3433 &cells[cell].dlen, &cells[cell].allocated);
3434 cell++;
3435 px++;
3436 if (px == gd->sx) {
3437 px = 0;
3438 pywrap++;
3439 gl = grid_peek_line(gd, pywrap);
3443 /* Locate starting cell. */
3444 cell = 0;
3445 len = strlen(str);
3446 while (cell < ncells) {
3447 ccell = cell;
3448 pos = 0;
3449 match = 1;
3450 while (ccell < ncells) {
3451 if (str[pos] == '\0') {
3452 match = 0;
3453 break;
3455 d = cells[ccell].d;
3456 dlen = cells[ccell].dlen;
3457 if (dlen == 1) {
3458 if (str[pos] != *d) {
3459 match = 0;
3460 break;
3462 pos++;
3463 } else {
3464 if (dlen > len - pos)
3465 dlen = len - pos;
3466 if (memcmp(str + pos, d, dlen) != 0) {
3467 match = 0;
3468 break;
3470 pos += dlen;
3472 ccell++;
3474 if (match)
3475 break;
3476 cell++;
3479 /* If not found this will be one past the end. */
3480 px = *ppx + cell;
3481 pywrap = *ppy;
3482 while (px >= gd->sx) {
3483 px -= gd->sx;
3484 pywrap++;
3487 *ppx = px;
3488 *ppy = pywrap;
3490 /* Free cell data. */
3491 for (cell = 0; cell < ncells; cell++) {
3492 if (cells[cell].allocated)
3493 free((void *)cells[cell].d);
3495 free(cells);
3498 static void
3499 window_copy_move_left(struct screen *s, u_int *fx, u_int *fy, int wrapflag)
3501 if (*fx == 0) { /* left */
3502 if (*fy == 0) { /* top */
3503 if (wrapflag) {
3504 *fx = screen_size_x(s) - 1;
3505 *fy = screen_hsize(s) + screen_size_y(s) - 1;
3507 return;
3509 *fx = screen_size_x(s) - 1;
3510 *fy = *fy - 1;
3511 } else
3512 *fx = *fx - 1;
3515 static void
3516 window_copy_move_right(struct screen *s, u_int *fx, u_int *fy, int wrapflag)
3518 if (*fx == screen_size_x(s) - 1) { /* right */
3519 if (*fy == screen_hsize(s) + screen_size_y(s) - 1) { /* bottom */
3520 if (wrapflag) {
3521 *fx = 0;
3522 *fy = 0;
3524 return;
3526 *fx = 0;
3527 *fy = *fy + 1;
3528 } else
3529 *fx = *fx + 1;
3532 static int
3533 window_copy_is_lowercase(const char *ptr)
3535 while (*ptr != '\0') {
3536 if (*ptr != tolower((u_char)*ptr))
3537 return (0);
3538 ++ptr;
3540 return (1);
3544 * Handle backward wrapped regex searches with overlapping matches. In this case
3545 * find the longest overlapping match from previous wrapped lines.
3547 static void
3548 window_copy_search_back_overlap(struct grid *gd, regex_t *preg, u_int *ppx,
3549 u_int *psx, u_int *ppy, u_int endline)
3551 u_int endx, endy, oldendx, oldendy, px, py, sx;
3552 int found = 1;
3554 oldendx = *ppx + *psx;
3555 oldendy = *ppy - 1;
3556 while (oldendx > gd->sx - 1) {
3557 oldendx -= gd->sx;
3558 oldendy++;
3560 endx = oldendx;
3561 endy = oldendy;
3562 px = *ppx;
3563 py = *ppy;
3564 while (found && px == 0 && py - 1 > endline &&
3565 grid_get_line(gd, py - 2)->flags & GRID_LINE_WRAPPED &&
3566 endx == oldendx && endy == oldendy) {
3567 py--;
3568 found = window_copy_search_rl_regex(gd, &px, &sx, py - 1, 0,
3569 gd->sx, preg);
3570 if (found) {
3571 endx = px + sx;
3572 endy = py - 1;
3573 while (endx > gd->sx - 1) {
3574 endx -= gd->sx;
3575 endy++;
3577 if (endx == oldendx && endy == oldendy) {
3578 *ppx = px;
3579 *ppy = py;
3586 * Search for text stored in sgd starting from position fx,fy up to endline. If
3587 * found, jump to it. If cis then ignore case. The direction is 0 for searching
3588 * up, down otherwise. If wrap then go to begin/end of grid and try again if
3589 * not found.
3591 static int
3592 window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd,
3593 struct grid *sgd, u_int fx, u_int fy, u_int endline, int cis, int wrap,
3594 int direction, int regex)
3596 u_int i, px, sx, ssize = 1;
3597 int found = 0, cflags = REG_EXTENDED;
3598 char *sbuf;
3599 regex_t reg;
3601 if (regex) {
3602 sbuf = xmalloc(ssize);
3603 sbuf[0] = '\0';
3604 sbuf = window_copy_stringify(sgd, 0, 0, sgd->sx, sbuf, &ssize);
3605 if (cis)
3606 cflags |= REG_ICASE;
3607 if (regcomp(&reg, sbuf, cflags) != 0) {
3608 free(sbuf);
3609 return (0);
3611 free(sbuf);
3614 if (direction) {
3615 for (i = fy; i <= endline; i++) {
3616 if (regex) {
3617 found = window_copy_search_lr_regex(gd,
3618 &px, &sx, i, fx, gd->sx, &reg);
3619 } else {
3620 found = window_copy_search_lr(gd, sgd,
3621 &px, i, fx, gd->sx, cis);
3623 if (found)
3624 break;
3625 fx = 0;
3627 } else {
3628 for (i = fy + 1; endline < i; i--) {
3629 if (regex) {
3630 found = window_copy_search_rl_regex(gd,
3631 &px, &sx, i - 1, 0, fx + 1, &reg);
3632 if (found) {
3633 window_copy_search_back_overlap(gd,
3634 &reg, &px, &sx, &i, endline);
3636 } else {
3637 found = window_copy_search_rl(gd, sgd,
3638 &px, i - 1, 0, fx + 1, cis);
3640 if (found) {
3641 i--;
3642 break;
3644 fx = gd->sx - 1;
3647 if (regex)
3648 regfree(&reg);
3650 if (found) {
3651 window_copy_scroll_to(wme, px, i, 1);
3652 return (1);
3654 if (wrap) {
3655 return (window_copy_search_jump(wme, gd, sgd,
3656 direction ? 0 : gd->sx - 1,
3657 direction ? 0 : gd->hsize + gd->sy - 1, fy, cis, 0,
3658 direction, regex));
3660 return (0);
3663 static void
3664 window_copy_move_after_search_mark(struct window_copy_mode_data *data,
3665 u_int *fx, u_int *fy, int wrapflag)
3667 struct screen *s = data->backing;
3668 u_int at, start;
3670 if (window_copy_search_mark_at(data, *fx, *fy, &start) == 0 &&
3671 data->searchmark[start] != 0) {
3672 while (window_copy_search_mark_at(data, *fx, *fy, &at) == 0) {
3673 if (data->searchmark[at] != data->searchmark[start])
3674 break;
3675 /* Stop if not wrapping and at the end of the grid. */
3676 if (!wrapflag &&
3677 *fx == screen_size_x(s) - 1 &&
3678 *fy == screen_hsize(s) + screen_size_y(s) - 1)
3679 break;
3681 window_copy_move_right(s, fx, fy, wrapflag);
3687 * Search in for text searchstr. If direction is 0 then search up, otherwise
3688 * down.
3690 static int
3691 window_copy_search(struct window_mode_entry *wme, int direction, int regex)
3693 struct window_pane *wp = wme->wp;
3694 struct window_copy_mode_data *data = wme->data;
3695 struct screen *s = data->backing, ss;
3696 struct screen_write_ctx ctx;
3697 struct grid *gd = s->grid;
3698 const char *str = data->searchstr;
3699 u_int at, endline, fx, fy, start;
3700 int cis, found, keys, visible_only;
3701 int wrapflag;
3703 if (regex && str[strcspn(str, "^$*+()?[].\\")] == '\0')
3704 regex = 0;
3706 data->searchdirection = direction;
3708 if (data->timeout)
3709 return (0);
3711 if (data->searchall || wp->searchstr == NULL ||
3712 wp->searchregex != regex) {
3713 visible_only = 0;
3714 data->searchall = 0;
3715 } else
3716 visible_only = (strcmp(wp->searchstr, str) == 0);
3717 if (visible_only == 0 && data->searchmark != NULL)
3718 window_copy_clear_marks(wme);
3719 free(wp->searchstr);
3720 wp->searchstr = xstrdup(str);
3721 wp->searchregex = regex;
3723 fx = data->cx;
3724 fy = screen_hsize(data->backing) - data->oy + data->cy;
3726 screen_init(&ss, screen_write_strlen("%s", str), 1, 0);
3727 screen_write_start(&ctx, &ss);
3728 screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", str);
3729 screen_write_stop(&ctx);
3731 wrapflag = options_get_number(wp->window->options, "wrap-search");
3732 cis = window_copy_is_lowercase(str);
3734 keys = options_get_number(wp->window->options, "mode-keys");
3736 if (direction) {
3738 * Behave according to mode-keys. If it is emacs, search forward
3739 * leaves the cursor after the match. If it is vi, the cursor
3740 * remains at the beginning of the match, regardless of
3741 * direction, which means that we need to start the next search
3742 * after the term the cursor is currently on when searching
3743 * forward.
3745 if (keys == MODEKEY_VI) {
3746 if (data->searchmark != NULL)
3747 window_copy_move_after_search_mark(data, &fx,
3748 &fy, wrapflag);
3749 else {
3751 * When there are no search marks, start the
3752 * search after the current cursor position.
3754 window_copy_move_right(s, &fx, &fy, wrapflag);
3757 endline = gd->hsize + gd->sy - 1;
3758 } else {
3759 window_copy_move_left(s, &fx, &fy, wrapflag);
3760 endline = 0;
3763 found = window_copy_search_jump(wme, gd, ss.grid, fx, fy, endline, cis,
3764 wrapflag, direction, regex);
3765 if (found) {
3766 window_copy_search_marks(wme, &ss, regex, visible_only);
3767 fx = data->cx;
3768 fy = screen_hsize(data->backing) - data->oy + data->cy;
3771 * When searching forward, if the cursor is not at the beginning
3772 * of the mark, search again.
3774 if (direction &&
3775 window_copy_search_mark_at(data, fx, fy, &at) == 0 &&
3776 at > 0 &&
3777 data->searchmark != NULL &&
3778 data->searchmark[at] == data->searchmark[at - 1]) {
3779 window_copy_move_after_search_mark(data, &fx, &fy,
3780 wrapflag);
3781 window_copy_search_jump(wme, gd, ss.grid, fx,
3782 fy, endline, cis, wrapflag, direction,
3783 regex);
3784 fx = data->cx;
3785 fy = screen_hsize(data->backing) - data->oy + data->cy;
3788 if (direction) {
3790 * When in Emacs mode, position the cursor just after
3791 * the mark.
3793 if (keys == MODEKEY_EMACS) {
3794 window_copy_move_after_search_mark(data, &fx,
3795 &fy, wrapflag);
3796 data->cx = fx;
3797 data->cy = fy - screen_hsize(data->backing) +
3798 data-> oy;
3800 } else {
3802 * When searching backward, position the cursor at the
3803 * beginning of the mark.
3805 if (window_copy_search_mark_at(data, fx, fy,
3806 &start) == 0) {
3807 while (window_copy_search_mark_at(data, fx, fy,
3808 &at) == 0 &&
3809 data->searchmark != NULL &&
3810 data->searchmark[at] ==
3811 data->searchmark[start]) {
3812 data->cx = fx;
3813 data->cy = fy -
3814 screen_hsize(data->backing) +
3815 data-> oy;
3816 if (at == 0)
3817 break;
3819 window_copy_move_left(s, &fx, &fy, 0);
3824 window_copy_redraw_screen(wme);
3826 screen_free(&ss);
3827 return (found);
3830 static void
3831 window_copy_visible_lines(struct window_copy_mode_data *data, u_int *start,
3832 u_int *end)
3834 struct grid *gd = data->backing->grid;
3835 const struct grid_line *gl;
3837 for (*start = gd->hsize - data->oy; *start > 0; (*start)--) {
3838 gl = grid_peek_line(gd, (*start) - 1);
3839 if (~gl->flags & GRID_LINE_WRAPPED)
3840 break;
3842 *end = gd->hsize - data->oy + gd->sy;
3845 static int
3846 window_copy_search_mark_at(struct window_copy_mode_data *data, u_int px,
3847 u_int py, u_int *at)
3849 struct screen *s = data->backing;
3850 struct grid *gd = s->grid;
3852 if (py < gd->hsize - data->oy)
3853 return (-1);
3854 if (py > gd->hsize - data->oy + gd->sy - 1)
3855 return (-1);
3856 *at = ((py - (gd->hsize - data->oy)) * gd->sx) + px;
3857 return (0);
3860 static int
3861 window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp,
3862 int regex, int visible_only)
3864 struct window_copy_mode_data *data = wme->data;
3865 struct screen *s = data->backing, ss;
3866 struct screen_write_ctx ctx;
3867 struct grid *gd = s->grid;
3868 int found, cis, stopped = 0;
3869 int cflags = REG_EXTENDED;
3870 u_int px, py, i, b, nfound = 0, width;
3871 u_int ssize = 1, start, end;
3872 char *sbuf;
3873 regex_t reg;
3874 uint64_t stop = 0, tstart, t;
3876 if (ssp == NULL) {
3877 width = screen_write_strlen("%s", data->searchstr);
3878 screen_init(&ss, width, 1, 0);
3879 screen_write_start(&ctx, &ss);
3880 screen_write_nputs(&ctx, -1, &grid_default_cell, "%s",
3881 data->searchstr);
3882 screen_write_stop(&ctx);
3883 ssp = &ss;
3884 } else
3885 width = screen_size_x(ssp);
3887 cis = window_copy_is_lowercase(data->searchstr);
3889 if (regex) {
3890 sbuf = xmalloc(ssize);
3891 sbuf[0] = '\0';
3892 sbuf = window_copy_stringify(ssp->grid, 0, 0, ssp->grid->sx,
3893 sbuf, &ssize);
3894 if (cis)
3895 cflags |= REG_ICASE;
3896 if (regcomp(&reg, sbuf, cflags) != 0) {
3897 free(sbuf);
3898 return (0);
3900 free(sbuf);
3902 tstart = get_timer();
3904 if (visible_only)
3905 window_copy_visible_lines(data, &start, &end);
3906 else {
3907 start = 0;
3908 end = gd->hsize + gd->sy;
3909 stop = get_timer() + WINDOW_COPY_SEARCH_ALL_TIMEOUT;
3912 again:
3913 free(data->searchmark);
3914 data->searchmark = xcalloc(gd->sx, gd->sy);
3915 data->searchgen = 1;
3917 for (py = start; py < end; py++) {
3918 px = 0;
3919 for (;;) {
3920 if (regex) {
3921 found = window_copy_search_lr_regex(gd,
3922 &px, &width, py, px, gd->sx, &reg);
3923 if (!found)
3924 break;
3925 } else {
3926 found = window_copy_search_lr(gd, ssp->grid,
3927 &px, py, px, gd->sx, cis);
3928 if (!found)
3929 break;
3931 nfound++;
3933 if (window_copy_search_mark_at(data, px, py, &b) == 0) {
3934 if (b + width > gd->sx * gd->sy)
3935 width = (gd->sx * gd->sy) - b;
3936 for (i = b; i < b + width; i++) {
3937 if (data->searchmark[i] != 0)
3938 continue;
3939 data->searchmark[i] = data->searchgen;
3941 if (data->searchgen == UCHAR_MAX)
3942 data->searchgen = 1;
3943 else
3944 data->searchgen++;
3946 px += width;
3949 t = get_timer();
3950 if (t - tstart > WINDOW_COPY_SEARCH_TIMEOUT) {
3951 data->timeout = 1;
3952 break;
3954 if (stop != 0 && t > stop) {
3955 stopped = 1;
3956 break;
3959 if (data->timeout) {
3960 window_copy_clear_marks(wme);
3961 goto out;
3964 if (stopped && stop != 0) {
3965 /* Try again but just the visible context. */
3966 window_copy_visible_lines(data, &start, &end);
3967 stop = 0;
3968 goto again;
3971 if (!visible_only) {
3972 if (stopped) {
3973 if (nfound > 1000)
3974 data->searchcount = 1000;
3975 else if (nfound > 100)
3976 data->searchcount = 100;
3977 else if (nfound > 10)
3978 data->searchcount = 10;
3979 else
3980 data->searchcount = -1;
3981 data->searchmore = 1;
3982 } else {
3983 data->searchcount = nfound;
3984 data->searchmore = 0;
3988 out:
3989 if (ssp == &ss)
3990 screen_free(&ss);
3991 if (regex)
3992 regfree(&reg);
3993 return (1);
3996 static void
3997 window_copy_clear_marks(struct window_mode_entry *wme)
3999 struct window_copy_mode_data *data = wme->data;
4001 free(data->searchmark);
4002 data->searchmark = NULL;
4005 static int
4006 window_copy_search_up(struct window_mode_entry *wme, int regex)
4008 return (window_copy_search(wme, 0, regex));
4011 static int
4012 window_copy_search_down(struct window_mode_entry *wme, int regex)
4014 return (window_copy_search(wme, 1, regex));
4017 static void
4018 window_copy_goto_line(struct window_mode_entry *wme, const char *linestr)
4020 struct window_copy_mode_data *data = wme->data;
4021 const char *errstr;
4022 int lineno;
4024 lineno = strtonum(linestr, -1, INT_MAX, &errstr);
4025 if (errstr != NULL)
4026 return;
4027 if (lineno < 0 || (u_int)lineno > screen_hsize(data->backing))
4028 lineno = screen_hsize(data->backing);
4030 data->oy = lineno;
4031 window_copy_update_selection(wme, 1, 0);
4032 window_copy_redraw_screen(wme);
4035 static void
4036 window_copy_match_start_end(struct window_copy_mode_data *data, u_int at,
4037 u_int *start, u_int *end)
4039 struct grid *gd = data->backing->grid;
4040 u_int last = (gd->sy * gd->sx) - 1;
4041 u_char mark = data->searchmark[at];
4043 *start = *end = at;
4044 while (*start != 0 && data->searchmark[*start] == mark)
4045 (*start)--;
4046 if (data->searchmark[*start] != mark)
4047 (*start)++;
4048 while (*end != last && data->searchmark[*end] == mark)
4049 (*end)++;
4050 if (data->searchmark[*end] != mark)
4051 (*end)--;
4054 static char *
4055 window_copy_match_at_cursor(struct window_copy_mode_data *data)
4057 struct grid *gd = data->backing->grid;
4058 struct grid_cell gc;
4059 u_int at, start, end, cy, px, py;
4060 u_int sx = screen_size_x(data->backing);
4061 char *buf = NULL;
4062 size_t len = 0;
4064 if (data->searchmark == NULL)
4065 return (NULL);
4067 cy = screen_hsize(data->backing) - data->oy + data->cy;
4068 if (window_copy_search_mark_at(data, data->cx, cy, &at) != 0)
4069 return (NULL);
4070 if (data->searchmark[at] == 0) {
4071 /* Allow one position after the match. */
4072 if (at == 0 || data->searchmark[--at] == 0)
4073 return (NULL);
4075 window_copy_match_start_end(data, at, &start, &end);
4078 * Cells will not be set in the marked array unless they are valid text
4079 * and wrapping will be taken care of, so we can just copy.
4081 for (at = start; at <= end; at++) {
4082 py = at / sx;
4083 px = at - (py * sx);
4085 grid_get_cell(gd, px, gd->hsize + py - data->oy, &gc);
4086 buf = xrealloc(buf, len + gc.data.size + 1);
4087 memcpy(buf + len, gc.data.data, gc.data.size);
4088 len += gc.data.size;
4090 if (len != 0)
4091 buf[len] = '\0';
4092 return (buf);
4095 static void
4096 window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy,
4097 struct grid_cell *gc, const struct grid_cell *mgc,
4098 const struct grid_cell *cgc, const struct grid_cell *mkgc)
4100 struct window_pane *wp = wme->wp;
4101 struct window_copy_mode_data *data = wme->data;
4102 u_int mark, start, end, cy, cursor, current;
4103 int inv = 0, found = 0;
4104 int keys;
4106 if (data->showmark && fy == data->my) {
4107 gc->attr = mkgc->attr;
4108 if (fx == data->mx)
4109 inv = 1;
4110 if (inv) {
4111 gc->fg = mkgc->bg;
4112 gc->bg = mkgc->fg;
4114 else {
4115 gc->fg = mkgc->fg;
4116 gc->bg = mkgc->bg;
4120 if (data->searchmark == NULL)
4121 return;
4123 if (window_copy_search_mark_at(data, fx, fy, &current) != 0)
4124 return;
4125 mark = data->searchmark[current];
4126 if (mark == 0)
4127 return;
4129 cy = screen_hsize(data->backing) - data->oy + data->cy;
4130 if (window_copy_search_mark_at(data, data->cx, cy, &cursor) == 0) {
4131 keys = options_get_number(wp->window->options, "mode-keys");
4132 if (cursor != 0 &&
4133 keys == MODEKEY_EMACS &&
4134 data->searchdirection) {
4135 if (data->searchmark[cursor - 1] == mark) {
4136 cursor--;
4137 found = 1;
4139 } else if (data->searchmark[cursor] == mark)
4140 found = 1;
4141 if (found) {
4142 window_copy_match_start_end(data, cursor, &start, &end);
4143 if (current >= start && current <= end) {
4144 gc->attr = cgc->attr;
4145 if (inv) {
4146 gc->fg = cgc->bg;
4147 gc->bg = cgc->fg;
4149 else {
4150 gc->fg = cgc->fg;
4151 gc->bg = cgc->bg;
4153 return;
4158 gc->attr = mgc->attr;
4159 if (inv) {
4160 gc->fg = mgc->bg;
4161 gc->bg = mgc->fg;
4163 else {
4164 gc->fg = mgc->fg;
4165 gc->bg = mgc->bg;
4169 static void
4170 window_copy_write_one(struct window_mode_entry *wme,
4171 struct screen_write_ctx *ctx, u_int py, u_int fy, u_int nx,
4172 const struct grid_cell *mgc, const struct grid_cell *cgc,
4173 const struct grid_cell *mkgc)
4175 struct window_copy_mode_data *data = wme->data;
4176 struct grid *gd = data->backing->grid;
4177 struct grid_cell gc;
4178 u_int fx;
4180 screen_write_cursormove(ctx, 0, py, 0);
4181 for (fx = 0; fx < nx; fx++) {
4182 grid_get_cell(gd, fx, fy, &gc);
4183 if (fx + gc.data.width <= nx) {
4184 window_copy_update_style(wme, fx, fy, &gc, mgc, cgc,
4185 mkgc);
4186 screen_write_cell(ctx, &gc);
4191 static void
4192 window_copy_write_line(struct window_mode_entry *wme,
4193 struct screen_write_ctx *ctx, u_int py)
4195 struct window_pane *wp = wme->wp;
4196 struct window_copy_mode_data *data = wme->data;
4197 struct screen *s = &data->screen;
4198 struct options *oo = wp->window->options;
4199 struct grid_cell gc, mgc, cgc, mkgc;
4200 u_int sx = screen_size_x(s);
4201 u_int hsize = screen_hsize(data->backing);
4202 const char *value;
4203 char *expanded;
4204 struct format_tree *ft;
4206 style_apply(&gc, oo, "mode-style", NULL);
4207 gc.flags |= GRID_FLAG_NOPALETTE;
4208 style_apply(&mgc, oo, "copy-mode-match-style", NULL);
4209 mgc.flags |= GRID_FLAG_NOPALETTE;
4210 style_apply(&cgc, oo, "copy-mode-current-match-style", NULL);
4211 cgc.flags |= GRID_FLAG_NOPALETTE;
4212 style_apply(&mkgc, oo, "copy-mode-mark-style", NULL);
4213 mkgc.flags |= GRID_FLAG_NOPALETTE;
4215 window_copy_write_one(wme, ctx, py, hsize - data->oy + py,
4216 screen_size_x(s), &mgc, &cgc, &mkgc);
4218 if (py == 0 && s->rupper < s->rlower && !data->hide_position) {
4219 value = options_get_string(oo, "copy-mode-position-format");
4220 if (*value != '\0') {
4221 ft = format_create_defaults(NULL, NULL, NULL, NULL, wp);
4222 expanded = format_expand(ft, value);
4223 if (*expanded != '\0') {
4224 screen_write_cursormove(ctx, 0, 0, 0);
4225 format_draw(ctx, &gc, sx, expanded, NULL, 0);
4227 free(expanded);
4228 format_free(ft);
4232 if (py == data->cy && data->cx == screen_size_x(s)) {
4233 screen_write_cursormove(ctx, screen_size_x(s) - 1, py, 0);
4234 screen_write_putc(ctx, &grid_default_cell, '$');
4238 static void
4239 window_copy_write_lines(struct window_mode_entry *wme,
4240 struct screen_write_ctx *ctx, u_int py, u_int ny)
4242 u_int yy;
4244 for (yy = py; yy < py + ny; yy++)
4245 window_copy_write_line(wme, ctx, py);
4248 static void
4249 window_copy_redraw_selection(struct window_mode_entry *wme, u_int old_y)
4251 struct window_copy_mode_data *data = wme->data;
4252 struct grid *gd = data->backing->grid;
4253 u_int new_y, start, end;
4255 new_y = data->cy;
4256 if (old_y <= new_y) {
4257 start = old_y;
4258 end = new_y;
4259 } else {
4260 start = new_y;
4261 end = old_y;
4265 * In word selection mode the first word on the line below the cursor
4266 * might be selected, so add this line to the redraw area.
4268 if (data->selflag == SEL_WORD) {
4269 /* Last grid line in data coordinates. */
4270 if (end < gd->sy + data->oy - 1)
4271 end++;
4273 window_copy_redraw_lines(wme, start, end - start + 1);
4276 static void
4277 window_copy_redraw_lines(struct window_mode_entry *wme, u_int py, u_int ny)
4279 struct window_pane *wp = wme->wp;
4280 struct window_copy_mode_data *data = wme->data;
4281 struct screen_write_ctx ctx;
4282 u_int i;
4284 screen_write_start_pane(&ctx, wp, NULL);
4285 for (i = py; i < py + ny; i++)
4286 window_copy_write_line(wme, &ctx, i);
4287 screen_write_cursormove(&ctx, data->cx, data->cy, 0);
4288 screen_write_stop(&ctx);
4291 static void
4292 window_copy_redraw_screen(struct window_mode_entry *wme)
4294 struct window_copy_mode_data *data = wme->data;
4296 window_copy_redraw_lines(wme, 0, screen_size_y(&data->screen));
4299 static void
4300 window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin,
4301 int no_reset)
4303 struct window_copy_mode_data *data = wme->data;
4304 u_int xx, yy;
4306 xx = data->cx;
4307 yy = screen_hsize(data->backing) + data->cy - data->oy;
4308 switch (data->selflag) {
4309 case SEL_WORD:
4310 if (no_reset)
4311 break;
4312 begin = 0;
4313 if (data->dy > yy || (data->dy == yy && data->dx > xx)) {
4314 /* Right to left selection. */
4315 window_copy_cursor_previous_word_pos(wme,
4316 data->separators, &xx, &yy);
4317 begin = 1;
4319 /* Reset the end. */
4320 data->endselx = data->endselrx;
4321 data->endsely = data->endselry;
4322 } else {
4323 /* Left to right selection. */
4324 if (xx >= window_copy_find_length(wme, yy) ||
4325 !window_copy_in_set(wme, xx + 1, yy, WHITESPACE)) {
4326 window_copy_cursor_next_word_end_pos(wme,
4327 data->separators, &xx, &yy);
4330 /* Reset the start. */
4331 data->selx = data->selrx;
4332 data->sely = data->selry;
4334 break;
4335 case SEL_LINE:
4336 if (no_reset)
4337 break;
4338 begin = 0;
4339 if (data->dy > yy) {
4340 /* Right to left selection. */
4341 xx = 0;
4342 begin = 1;
4344 /* Reset the end. */
4345 data->endselx = data->endselrx;
4346 data->endsely = data->endselry;
4347 } else {
4348 /* Left to right selection. */
4349 if (yy < data->endselry)
4350 yy = data->endselry;
4351 xx = window_copy_find_length(wme, yy);
4353 /* Reset the start. */
4354 data->selx = data->selrx;
4355 data->sely = data->selry;
4357 break;
4358 case SEL_CHAR:
4359 break;
4361 if (begin) {
4362 data->selx = xx;
4363 data->sely = yy;
4364 } else {
4365 data->endselx = xx;
4366 data->endsely = yy;
4370 static void
4371 window_copy_synchronize_cursor(struct window_mode_entry *wme, int no_reset)
4373 struct window_copy_mode_data *data = wme->data;
4375 switch (data->cursordrag) {
4376 case CURSORDRAG_ENDSEL:
4377 window_copy_synchronize_cursor_end(wme, 0, no_reset);
4378 break;
4379 case CURSORDRAG_SEL:
4380 window_copy_synchronize_cursor_end(wme, 1, no_reset);
4381 break;
4382 case CURSORDRAG_NONE:
4383 break;
4387 static void
4388 window_copy_update_cursor(struct window_mode_entry *wme, u_int cx, u_int cy)
4390 struct window_pane *wp = wme->wp;
4391 struct window_copy_mode_data *data = wme->data;
4392 struct screen *s = &data->screen;
4393 struct screen_write_ctx ctx;
4394 u_int old_cx, old_cy;
4396 old_cx = data->cx; old_cy = data->cy;
4397 data->cx = cx; data->cy = cy;
4398 if (old_cx == screen_size_x(s))
4399 window_copy_redraw_lines(wme, old_cy, 1);
4400 if (data->cx == screen_size_x(s))
4401 window_copy_redraw_lines(wme, data->cy, 1);
4402 else {
4403 screen_write_start_pane(&ctx, wp, NULL);
4404 screen_write_cursormove(&ctx, data->cx, data->cy, 0);
4405 screen_write_stop(&ctx);
4409 static void
4410 window_copy_start_selection(struct window_mode_entry *wme)
4412 struct window_copy_mode_data *data = wme->data;
4414 data->selx = data->cx;
4415 data->sely = screen_hsize(data->backing) + data->cy - data->oy;
4417 data->endselx = data->selx;
4418 data->endsely = data->sely;
4420 data->cursordrag = CURSORDRAG_ENDSEL;
4422 window_copy_set_selection(wme, 1, 0);
4425 static int
4426 window_copy_adjust_selection(struct window_mode_entry *wme, u_int *selx,
4427 u_int *sely)
4429 struct window_copy_mode_data *data = wme->data;
4430 struct screen *s = &data->screen;
4431 u_int sx, sy, ty;
4432 int relpos;
4434 sx = *selx;
4435 sy = *sely;
4437 ty = screen_hsize(data->backing) - data->oy;
4438 if (sy < ty) {
4439 relpos = WINDOW_COPY_REL_POS_ABOVE;
4440 if (!data->rectflag)
4441 sx = 0;
4442 sy = 0;
4443 } else if (sy > ty + screen_size_y(s) - 1) {
4444 relpos = WINDOW_COPY_REL_POS_BELOW;
4445 if (!data->rectflag)
4446 sx = screen_size_x(s) - 1;
4447 sy = screen_size_y(s) - 1;
4448 } else {
4449 relpos = WINDOW_COPY_REL_POS_ON_SCREEN;
4450 sy -= ty;
4453 *selx = sx;
4454 *sely = sy;
4455 return (relpos);
4458 static int
4459 window_copy_update_selection(struct window_mode_entry *wme, int may_redraw,
4460 int no_reset)
4462 struct window_copy_mode_data *data = wme->data;
4463 struct screen *s = &data->screen;
4465 if (s->sel == NULL && data->lineflag == LINE_SEL_NONE)
4466 return (0);
4467 return (window_copy_set_selection(wme, may_redraw, no_reset));
4470 static int
4471 window_copy_set_selection(struct window_mode_entry *wme, int may_redraw,
4472 int no_reset)
4474 struct window_pane *wp = wme->wp;
4475 struct window_copy_mode_data *data = wme->data;
4476 struct screen *s = &data->screen;
4477 struct options *oo = wp->window->options;
4478 struct grid_cell gc;
4479 u_int sx, sy, cy, endsx, endsy;
4480 int startrelpos, endrelpos;
4482 window_copy_synchronize_cursor(wme, no_reset);
4484 /* Adjust the selection. */
4485 sx = data->selx;
4486 sy = data->sely;
4487 startrelpos = window_copy_adjust_selection(wme, &sx, &sy);
4489 /* Adjust the end of selection. */
4490 endsx = data->endselx;
4491 endsy = data->endsely;
4492 endrelpos = window_copy_adjust_selection(wme, &endsx, &endsy);
4494 /* Selection is outside of the current screen */
4495 if (startrelpos == endrelpos &&
4496 startrelpos != WINDOW_COPY_REL_POS_ON_SCREEN) {
4497 screen_hide_selection(s);
4498 return (0);
4501 /* Set colours and selection. */
4502 style_apply(&gc, oo, "mode-style", NULL);
4503 gc.flags |= GRID_FLAG_NOPALETTE;
4504 screen_set_selection(s, sx, sy, endsx, endsy, data->rectflag,
4505 data->modekeys, &gc);
4507 if (data->rectflag && may_redraw) {
4509 * Can't rely on the caller to redraw the right lines for
4510 * rectangle selection - find the highest line and the number
4511 * of lines, and redraw just past that in both directions
4513 cy = data->cy;
4514 if (data->cursordrag == CURSORDRAG_ENDSEL) {
4515 if (sy < cy)
4516 window_copy_redraw_lines(wme, sy, cy - sy + 1);
4517 else
4518 window_copy_redraw_lines(wme, cy, sy - cy + 1);
4519 } else {
4520 if (endsy < cy) {
4521 window_copy_redraw_lines(wme, endsy,
4522 cy - endsy + 1);
4523 } else {
4524 window_copy_redraw_lines(wme, cy,
4525 endsy - cy + 1);
4530 return (1);
4533 static void *
4534 window_copy_get_selection(struct window_mode_entry *wme, size_t *len)
4536 struct window_pane *wp = wme->wp;
4537 struct window_copy_mode_data *data = wme->data;
4538 struct screen *s = &data->screen;
4539 char *buf;
4540 size_t off;
4541 u_int i, xx, yy, sx, sy, ex, ey, ey_last;
4542 u_int firstsx, lastex, restex, restsx, selx;
4543 int keys;
4545 if (data->screen.sel == NULL && data->lineflag == LINE_SEL_NONE) {
4546 buf = window_copy_match_at_cursor(data);
4547 if (buf != NULL)
4548 *len = strlen(buf);
4549 else
4550 *len = 0;
4551 return (buf);
4554 buf = xmalloc(1);
4555 off = 0;
4557 *buf = '\0';
4560 * The selection extends from selx,sely to (adjusted) cx,cy on
4561 * the base screen.
4564 /* Find start and end. */
4565 xx = data->endselx;
4566 yy = data->endsely;
4567 if (yy < data->sely || (yy == data->sely && xx < data->selx)) {
4568 sx = xx; sy = yy;
4569 ex = data->selx; ey = data->sely;
4570 } else {
4571 sx = data->selx; sy = data->sely;
4572 ex = xx; ey = yy;
4575 /* Trim ex to end of line. */
4576 ey_last = window_copy_find_length(wme, ey);
4577 if (ex > ey_last)
4578 ex = ey_last;
4581 * Deal with rectangle-copy if necessary; four situations: start of
4582 * first line (firstsx), end of last line (lastex), start (restsx) and
4583 * end (restex) of all other lines.
4585 xx = screen_size_x(s);
4588 * Behave according to mode-keys. If it is emacs, copy like emacs,
4589 * keeping the top-left-most character, and dropping the
4590 * bottom-right-most, regardless of copy direction. If it is vi, also
4591 * keep bottom-right-most character.
4593 keys = options_get_number(wp->window->options, "mode-keys");
4594 if (data->rectflag) {
4596 * Need to ignore the column with the cursor in it, which for
4597 * rectangular copy means knowing which side the cursor is on.
4599 if (data->cursordrag == CURSORDRAG_ENDSEL)
4600 selx = data->selx;
4601 else
4602 selx = data->endselx;
4603 if (selx < data->cx) {
4604 /* Selection start is on the left. */
4605 if (keys == MODEKEY_EMACS) {
4606 lastex = data->cx;
4607 restex = data->cx;
4609 else {
4610 lastex = data->cx + 1;
4611 restex = data->cx + 1;
4613 firstsx = selx;
4614 restsx = selx;
4615 } else {
4616 /* Cursor is on the left. */
4617 lastex = selx + 1;
4618 restex = selx + 1;
4619 firstsx = data->cx;
4620 restsx = data->cx;
4622 } else {
4623 if (keys == MODEKEY_EMACS)
4624 lastex = ex;
4625 else
4626 lastex = ex + 1;
4627 restex = xx;
4628 firstsx = sx;
4629 restsx = 0;
4632 /* Copy the lines. */
4633 for (i = sy; i <= ey; i++) {
4634 window_copy_copy_line(wme, &buf, &off, i,
4635 (i == sy ? firstsx : restsx),
4636 (i == ey ? lastex : restex));
4639 /* Don't bother if no data. */
4640 if (off == 0) {
4641 free(buf);
4642 *len = 0;
4643 return (NULL);
4645 /* Remove final \n (unless at end in vi mode). */
4646 if (keys == MODEKEY_EMACS || lastex <= ey_last) {
4647 if (~grid_get_line(data->backing->grid, ey)->flags &
4648 GRID_LINE_WRAPPED || lastex != ey_last)
4649 off -= 1;
4651 *len = off;
4652 return (buf);
4655 static void
4656 window_copy_copy_buffer(struct window_mode_entry *wme, const char *prefix,
4657 void *buf, size_t len, int set_paste, int set_clip)
4659 struct window_pane *wp = wme->wp;
4660 struct screen_write_ctx ctx;
4662 if (set_clip &&
4663 options_get_number(global_options, "set-clipboard") != 0) {
4664 screen_write_start_pane(&ctx, wp, NULL);
4665 screen_write_setselection(&ctx, "", buf, len);
4666 screen_write_stop(&ctx);
4667 notify_pane("pane-set-clipboard", wp);
4670 if (set_paste)
4671 paste_add(prefix, buf, len);
4674 static void *
4675 window_copy_pipe_run(struct window_mode_entry *wme, struct session *s,
4676 const char *cmd, size_t *len)
4678 void *buf;
4679 struct job *job;
4681 buf = window_copy_get_selection(wme, len);
4682 if (cmd == NULL || *cmd == '\0')
4683 cmd = options_get_string(global_options, "copy-command");
4684 if (cmd != NULL && *cmd != '\0') {
4685 job = job_run(cmd, 0, NULL, NULL, s, NULL, NULL, NULL, NULL,
4686 NULL, JOB_NOWAIT, -1, -1);
4687 bufferevent_write(job_get_event(job), buf, *len);
4689 return (buf);
4692 static void
4693 window_copy_pipe(struct window_mode_entry *wme, struct session *s,
4694 const char *cmd)
4696 size_t len;
4698 window_copy_pipe_run(wme, s, cmd, &len);
4701 static void
4702 window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s,
4703 const char *prefix, const char *cmd, int set_paste, int set_clip)
4705 void *buf;
4706 size_t len;
4708 buf = window_copy_pipe_run(wme, s, cmd, &len);
4709 if (buf != NULL)
4710 window_copy_copy_buffer(wme, prefix, buf, len, set_paste,
4711 set_clip);
4714 static void
4715 window_copy_copy_selection(struct window_mode_entry *wme, const char *prefix,
4716 int set_paste, int set_clip)
4718 char *buf;
4719 size_t len;
4721 buf = window_copy_get_selection(wme, &len);
4722 if (buf != NULL)
4723 window_copy_copy_buffer(wme, prefix, buf, len, set_paste,
4724 set_clip);
4727 static void
4728 window_copy_append_selection(struct window_mode_entry *wme)
4730 struct window_pane *wp = wme->wp;
4731 char *buf;
4732 struct paste_buffer *pb;
4733 const char *bufdata, *bufname = NULL;
4734 size_t len, bufsize;
4735 struct screen_write_ctx ctx;
4737 buf = window_copy_get_selection(wme, &len);
4738 if (buf == NULL)
4739 return;
4741 if (options_get_number(global_options, "set-clipboard") != 0) {
4742 screen_write_start_pane(&ctx, wp, NULL);
4743 screen_write_setselection(&ctx, "", buf, len);
4744 screen_write_stop(&ctx);
4745 notify_pane("pane-set-clipboard", wp);
4748 pb = paste_get_top(&bufname);
4749 if (pb != NULL) {
4750 bufdata = paste_buffer_data(pb, &bufsize);
4751 buf = xrealloc(buf, len + bufsize);
4752 memmove(buf + bufsize, buf, len);
4753 memcpy(buf, bufdata, bufsize);
4754 len += bufsize;
4756 if (paste_set(buf, len, bufname, NULL) != 0)
4757 free(buf);
4760 static void
4761 window_copy_copy_line(struct window_mode_entry *wme, char **buf, size_t *off,
4762 u_int sy, u_int sx, u_int ex)
4764 struct window_copy_mode_data *data = wme->data;
4765 struct grid *gd = data->backing->grid;
4766 struct grid_cell gc;
4767 struct grid_line *gl;
4768 struct utf8_data ud;
4769 u_int i, xx, wrapped = 0;
4770 const char *s;
4772 if (sx > ex)
4773 return;
4776 * Work out if the line was wrapped at the screen edge and all of it is
4777 * on screen.
4779 gl = grid_get_line(gd, sy);
4780 if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx)
4781 wrapped = 1;
4783 /* If the line was wrapped, don't strip spaces (use the full length). */
4784 if (wrapped)
4785 xx = gl->cellsize;
4786 else
4787 xx = window_copy_find_length(wme, sy);
4788 if (ex > xx)
4789 ex = xx;
4790 if (sx > xx)
4791 sx = xx;
4793 if (sx < ex) {
4794 for (i = sx; i < ex; i++) {
4795 grid_get_cell(gd, i, sy, &gc);
4796 if (gc.flags & GRID_FLAG_PADDING)
4797 continue;
4798 utf8_copy(&ud, &gc.data);
4799 if (ud.size == 1 && (gc.attr & GRID_ATTR_CHARSET)) {
4800 s = tty_acs_get(NULL, ud.data[0]);
4801 if (s != NULL && strlen(s) <= sizeof ud.data) {
4802 ud.size = strlen(s);
4803 memcpy(ud.data, s, ud.size);
4807 *buf = xrealloc(*buf, (*off) + ud.size);
4808 memcpy(*buf + *off, ud.data, ud.size);
4809 *off += ud.size;
4813 /* Only add a newline if the line wasn't wrapped. */
4814 if (!wrapped || ex != xx) {
4815 *buf = xrealloc(*buf, (*off) + 1);
4816 (*buf)[(*off)++] = '\n';
4820 static void
4821 window_copy_clear_selection(struct window_mode_entry *wme)
4823 struct window_copy_mode_data *data = wme->data;
4824 u_int px, py;
4826 screen_clear_selection(&data->screen);
4828 data->cursordrag = CURSORDRAG_NONE;
4829 data->lineflag = LINE_SEL_NONE;
4830 data->selflag = SEL_CHAR;
4832 py = screen_hsize(data->backing) + data->cy - data->oy;
4833 px = window_copy_find_length(wme, py);
4834 if (data->cx > px)
4835 window_copy_update_cursor(wme, px, data->cy);
4838 static int
4839 window_copy_in_set(struct window_mode_entry *wme, u_int px, u_int py,
4840 const char *set)
4842 struct window_copy_mode_data *data = wme->data;
4843 struct grid_cell gc;
4845 grid_get_cell(data->backing->grid, px, py, &gc);
4846 if (gc.flags & GRID_FLAG_PADDING)
4847 return (0);
4848 return (utf8_cstrhas(set, &gc.data));
4851 static u_int
4852 window_copy_find_length(struct window_mode_entry *wme, u_int py)
4854 struct window_copy_mode_data *data = wme->data;
4856 return (grid_line_length(data->backing->grid, py));
4859 static void
4860 window_copy_cursor_start_of_line(struct window_mode_entry *wme)
4862 struct window_copy_mode_data *data = wme->data;
4863 struct screen *back_s = data->backing;
4864 struct grid_reader gr;
4865 u_int px, py, oldy, hsize;
4867 px = data->cx;
4868 hsize = screen_hsize(back_s);
4869 py = hsize + data->cy - data->oy;
4870 oldy = data->cy;
4872 grid_reader_start(&gr, back_s->grid, px, py);
4873 grid_reader_cursor_start_of_line(&gr, 1);
4874 grid_reader_get_cursor(&gr, &px, &py);
4875 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
4878 static void
4879 window_copy_cursor_back_to_indentation(struct window_mode_entry *wme)
4881 struct window_copy_mode_data *data = wme->data;
4882 struct screen *back_s = data->backing;
4883 struct grid_reader gr;
4884 u_int px, py, oldy, hsize;
4886 px = data->cx;
4887 hsize = screen_hsize(back_s);
4888 py = hsize + data->cy - data->oy;
4889 oldy = data->cy;
4891 grid_reader_start(&gr, back_s->grid, px, py);
4892 grid_reader_cursor_back_to_indentation(&gr);
4893 grid_reader_get_cursor(&gr, &px, &py);
4894 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
4897 static void
4898 window_copy_cursor_end_of_line(struct window_mode_entry *wme)
4900 struct window_copy_mode_data *data = wme->data;
4901 struct screen *back_s = data->backing;
4902 struct grid_reader gr;
4903 u_int px, py, oldy, hsize;
4905 px = data->cx;
4906 hsize = screen_hsize(back_s);
4907 py = hsize + data->cy - data->oy;
4908 oldy = data->cy;
4910 grid_reader_start(&gr, back_s->grid, px, py);
4911 if (data->screen.sel != NULL && data->rectflag)
4912 grid_reader_cursor_end_of_line(&gr, 1, 1);
4913 else
4914 grid_reader_cursor_end_of_line(&gr, 1, 0);
4915 grid_reader_get_cursor(&gr, &px, &py);
4916 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
4917 data->oy, oldy, px, py, 0);
4920 static void
4921 window_copy_other_end(struct window_mode_entry *wme)
4923 struct window_copy_mode_data *data = wme->data;
4924 struct screen *s = &data->screen;
4925 u_int selx, sely, cy, yy, hsize;
4927 if (s->sel == NULL && data->lineflag == LINE_SEL_NONE)
4928 return;
4930 if (data->lineflag == LINE_SEL_LEFT_RIGHT)
4931 data->lineflag = LINE_SEL_RIGHT_LEFT;
4932 else if (data->lineflag == LINE_SEL_RIGHT_LEFT)
4933 data->lineflag = LINE_SEL_LEFT_RIGHT;
4935 switch (data->cursordrag) {
4936 case CURSORDRAG_NONE:
4937 case CURSORDRAG_SEL:
4938 data->cursordrag = CURSORDRAG_ENDSEL;
4939 break;
4940 case CURSORDRAG_ENDSEL:
4941 data->cursordrag = CURSORDRAG_SEL;
4942 break;
4945 selx = data->endselx;
4946 sely = data->endsely;
4947 if (data->cursordrag == CURSORDRAG_SEL) {
4948 selx = data->selx;
4949 sely = data->sely;
4952 cy = data->cy;
4953 yy = screen_hsize(data->backing) + data->cy - data->oy;
4955 data->cx = selx;
4957 hsize = screen_hsize(data->backing);
4958 if (sely < hsize - data->oy) { /* above */
4959 data->oy = hsize - sely;
4960 data->cy = 0;
4961 } else if (sely > hsize - data->oy + screen_size_y(s)) { /* below */
4962 data->oy = hsize - sely + screen_size_y(s) - 1;
4963 data->cy = screen_size_y(s) - 1;
4964 } else
4965 data->cy = cy + sely - yy;
4967 window_copy_update_selection(wme, 1, 1);
4968 window_copy_redraw_screen(wme);
4971 static void
4972 window_copy_cursor_left(struct window_mode_entry *wme)
4974 struct window_copy_mode_data *data = wme->data;
4975 struct screen *back_s = data->backing;
4976 struct grid_reader gr;
4977 u_int px, py, oldy, hsize;
4979 px = data->cx;
4980 hsize = screen_hsize(back_s);
4981 py = hsize + data->cy - data->oy;
4982 oldy = data->cy;
4984 grid_reader_start(&gr, back_s->grid, px, py);
4985 grid_reader_cursor_left(&gr, 1);
4986 grid_reader_get_cursor(&gr, &px, &py);
4987 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
4990 static void
4991 window_copy_cursor_right(struct window_mode_entry *wme, int all)
4993 struct window_copy_mode_data *data = wme->data;
4994 struct screen *back_s = data->backing;
4995 struct grid_reader gr;
4996 u_int px, py, oldy, hsize;
4998 px = data->cx;
4999 hsize = screen_hsize(back_s);
5000 py = hsize + data->cy - data->oy;
5001 oldy = data->cy;
5003 grid_reader_start(&gr, back_s->grid, px, py);
5004 grid_reader_cursor_right(&gr, 1, all);
5005 grid_reader_get_cursor(&gr, &px, &py);
5006 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
5007 data->oy, oldy, px, py, 0);
5010 static void
5011 window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only)
5013 struct window_copy_mode_data *data = wme->data;
5014 struct screen *s = &data->screen;
5015 u_int ox, oy, px, py;
5016 int norectsel;
5018 norectsel = data->screen.sel == NULL || !data->rectflag;
5019 oy = screen_hsize(data->backing) + data->cy - data->oy;
5020 ox = window_copy_find_length(wme, oy);
5021 if (norectsel && data->cx != ox) {
5022 data->lastcx = data->cx;
5023 data->lastsx = ox;
5026 if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely)
5027 window_copy_other_end(wme);
5029 if (scroll_only || data->cy == 0) {
5030 if (norectsel)
5031 data->cx = data->lastcx;
5032 window_copy_scroll_down(wme, 1);
5033 if (scroll_only) {
5034 if (data->cy == screen_size_y(s) - 1)
5035 window_copy_redraw_lines(wme, data->cy, 1);
5036 else
5037 window_copy_redraw_lines(wme, data->cy, 2);
5039 } else {
5040 if (norectsel) {
5041 window_copy_update_cursor(wme, data->lastcx,
5042 data->cy - 1);
5043 } else
5044 window_copy_update_cursor(wme, data->cx, data->cy - 1);
5045 if (window_copy_update_selection(wme, 1, 0)) {
5046 if (data->cy == screen_size_y(s) - 1)
5047 window_copy_redraw_lines(wme, data->cy, 1);
5048 else
5049 window_copy_redraw_lines(wme, data->cy, 2);
5053 if (norectsel) {
5054 py = screen_hsize(data->backing) + data->cy - data->oy;
5055 px = window_copy_find_length(wme, py);
5056 if ((data->cx >= data->lastsx && data->cx != px) ||
5057 data->cx > px)
5059 window_copy_update_cursor(wme, px, data->cy);
5060 if (window_copy_update_selection(wme, 1, 0))
5061 window_copy_redraw_lines(wme, data->cy, 1);
5065 if (data->lineflag == LINE_SEL_LEFT_RIGHT)
5067 py = screen_hsize(data->backing) + data->cy - data->oy;
5068 if (data->rectflag)
5069 px = screen_size_x(data->backing);
5070 else
5071 px = window_copy_find_length(wme, py);
5072 window_copy_update_cursor(wme, px, data->cy);
5073 if (window_copy_update_selection(wme, 1, 0))
5074 window_copy_redraw_lines(wme, data->cy, 1);
5076 else if (data->lineflag == LINE_SEL_RIGHT_LEFT)
5078 window_copy_update_cursor(wme, 0, data->cy);
5079 if (window_copy_update_selection(wme, 1, 0))
5080 window_copy_redraw_lines(wme, data->cy, 1);
5084 static void
5085 window_copy_cursor_down(struct window_mode_entry *wme, int scroll_only)
5087 struct window_copy_mode_data *data = wme->data;
5088 struct screen *s = &data->screen;
5089 u_int ox, oy, px, py;
5090 int norectsel;
5092 norectsel = data->screen.sel == NULL || !data->rectflag;
5093 oy = screen_hsize(data->backing) + data->cy - data->oy;
5094 ox = window_copy_find_length(wme, oy);
5095 if (norectsel && data->cx != ox) {
5096 data->lastcx = data->cx;
5097 data->lastsx = ox;
5100 if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely)
5101 window_copy_other_end(wme);
5103 if (scroll_only || data->cy == screen_size_y(s) - 1) {
5104 if (norectsel)
5105 data->cx = data->lastcx;
5106 window_copy_scroll_up(wme, 1);
5107 if (scroll_only && data->cy > 0)
5108 window_copy_redraw_lines(wme, data->cy - 1, 2);
5109 } else {
5110 if (norectsel) {
5111 window_copy_update_cursor(wme, data->lastcx,
5112 data->cy + 1);
5113 } else
5114 window_copy_update_cursor(wme, data->cx, data->cy + 1);
5115 if (window_copy_update_selection(wme, 1, 0))
5116 window_copy_redraw_lines(wme, data->cy - 1, 2);
5119 if (norectsel) {
5120 py = screen_hsize(data->backing) + data->cy - data->oy;
5121 px = window_copy_find_length(wme, py);
5122 if ((data->cx >= data->lastsx && data->cx != px) ||
5123 data->cx > px)
5125 window_copy_update_cursor(wme, px, data->cy);
5126 if (window_copy_update_selection(wme, 1, 0))
5127 window_copy_redraw_lines(wme, data->cy, 1);
5131 if (data->lineflag == LINE_SEL_LEFT_RIGHT)
5133 py = screen_hsize(data->backing) + data->cy - data->oy;
5134 if (data->rectflag)
5135 px = screen_size_x(data->backing);
5136 else
5137 px = window_copy_find_length(wme, py);
5138 window_copy_update_cursor(wme, px, data->cy);
5139 if (window_copy_update_selection(wme, 1, 0))
5140 window_copy_redraw_lines(wme, data->cy, 1);
5142 else if (data->lineflag == LINE_SEL_RIGHT_LEFT)
5144 window_copy_update_cursor(wme, 0, data->cy);
5145 if (window_copy_update_selection(wme, 1, 0))
5146 window_copy_redraw_lines(wme, data->cy, 1);
5150 static void
5151 window_copy_cursor_jump(struct window_mode_entry *wme)
5153 struct window_copy_mode_data *data = wme->data;
5154 struct screen *back_s = data->backing;
5155 struct grid_reader gr;
5156 u_int px, py, oldy, hsize;
5158 px = data->cx + 1;
5159 hsize = screen_hsize(back_s);
5160 py = hsize + data->cy - data->oy;
5161 oldy = data->cy;
5163 grid_reader_start(&gr, back_s->grid, px, py);
5164 if (grid_reader_cursor_jump(&gr, data->jumpchar)) {
5165 grid_reader_get_cursor(&gr, &px, &py);
5166 window_copy_acquire_cursor_down(wme, hsize,
5167 screen_size_y(back_s), data->oy, oldy, px, py, 0);
5171 static void
5172 window_copy_cursor_jump_back(struct window_mode_entry *wme)
5174 struct window_copy_mode_data *data = wme->data;
5175 struct screen *back_s = data->backing;
5176 struct grid_reader gr;
5177 u_int px, py, oldy, hsize;
5179 px = data->cx;
5180 hsize = screen_hsize(back_s);
5181 py = hsize + data->cy - data->oy;
5182 oldy = data->cy;
5184 grid_reader_start(&gr, back_s->grid, px, py);
5185 grid_reader_cursor_left(&gr, 0);
5186 if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) {
5187 grid_reader_get_cursor(&gr, &px, &py);
5188 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px,
5189 py);
5193 static void
5194 window_copy_cursor_jump_to(struct window_mode_entry *wme)
5196 struct window_copy_mode_data *data = wme->data;
5197 struct screen *back_s = data->backing;
5198 struct grid_reader gr;
5199 u_int px, py, oldy, hsize;
5201 px = data->cx + 2;
5202 hsize = screen_hsize(back_s);
5203 py = hsize + data->cy - data->oy;
5204 oldy = data->cy;
5206 grid_reader_start(&gr, back_s->grid, px, py);
5207 if (grid_reader_cursor_jump(&gr, data->jumpchar)) {
5208 grid_reader_cursor_left(&gr, 1);
5209 grid_reader_get_cursor(&gr, &px, &py);
5210 window_copy_acquire_cursor_down(wme, hsize,
5211 screen_size_y(back_s), data->oy, oldy, px, py, 0);
5215 static void
5216 window_copy_cursor_jump_to_back(struct window_mode_entry *wme)
5218 struct window_copy_mode_data *data = wme->data;
5219 struct screen *back_s = data->backing;
5220 struct grid_reader gr;
5221 u_int px, py, oldy, hsize;
5223 px = data->cx;
5224 hsize = screen_hsize(back_s);
5225 py = hsize + data->cy - data->oy;
5226 oldy = data->cy;
5228 grid_reader_start(&gr, back_s->grid, px, py);
5229 grid_reader_cursor_left(&gr, 0);
5230 grid_reader_cursor_left(&gr, 0);
5231 if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) {
5232 grid_reader_cursor_right(&gr, 1, 0);
5233 grid_reader_get_cursor(&gr, &px, &py);
5234 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px,
5235 py);
5239 static void
5240 window_copy_cursor_next_word(struct window_mode_entry *wme,
5241 const char *separators)
5243 struct window_copy_mode_data *data = wme->data;
5244 struct screen *back_s = data->backing;
5245 struct grid_reader gr;
5246 u_int px, py, oldy, hsize;
5248 px = data->cx;
5249 hsize = screen_hsize(back_s);
5250 py = hsize + data->cy - data->oy;
5251 oldy = data->cy;
5253 grid_reader_start(&gr, back_s->grid, px, py);
5254 grid_reader_cursor_next_word(&gr, separators);
5255 grid_reader_get_cursor(&gr, &px, &py);
5256 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
5257 data->oy, oldy, px, py, 0);
5260 /* Compute the next place where a word ends. */
5261 static void
5262 window_copy_cursor_next_word_end_pos(struct window_mode_entry *wme,
5263 const char *separators, u_int *ppx, u_int *ppy)
5265 struct window_pane *wp = wme->wp;
5266 struct window_copy_mode_data *data = wme->data;
5267 struct options *oo = wp->window->options;
5268 struct screen *back_s = data->backing;
5269 struct grid_reader gr;
5270 u_int px, py, hsize;
5272 px = data->cx;
5273 hsize = screen_hsize(back_s);
5274 py = hsize + data->cy - data->oy;
5276 grid_reader_start(&gr, back_s->grid, px, py);
5277 if (options_get_number(oo, "mode-keys") == MODEKEY_VI) {
5278 if (!grid_reader_in_set(&gr, WHITESPACE))
5279 grid_reader_cursor_right(&gr, 0, 0);
5280 grid_reader_cursor_next_word_end(&gr, separators);
5281 grid_reader_cursor_left(&gr, 1);
5282 } else
5283 grid_reader_cursor_next_word_end(&gr, separators);
5284 grid_reader_get_cursor(&gr, &px, &py);
5285 *ppx = px;
5286 *ppy = py;
5289 /* Move to the next place where a word ends. */
5290 static void
5291 window_copy_cursor_next_word_end(struct window_mode_entry *wme,
5292 const char *separators, int no_reset)
5294 struct window_pane *wp = wme->wp;
5295 struct window_copy_mode_data *data = wme->data;
5296 struct options *oo = wp->window->options;
5297 struct screen *back_s = data->backing;
5298 struct grid_reader gr;
5299 u_int px, py, oldy, hsize;
5301 px = data->cx;
5302 hsize = screen_hsize(back_s);
5303 py = hsize + data->cy - data->oy;
5304 oldy = data->cy;
5306 grid_reader_start(&gr, back_s->grid, px, py);
5307 if (options_get_number(oo, "mode-keys") == MODEKEY_VI) {
5308 if (!grid_reader_in_set(&gr, WHITESPACE))
5309 grid_reader_cursor_right(&gr, 0, 0);
5310 grid_reader_cursor_next_word_end(&gr, separators);
5311 grid_reader_cursor_left(&gr, 1);
5312 } else
5313 grid_reader_cursor_next_word_end(&gr, separators);
5314 grid_reader_get_cursor(&gr, &px, &py);
5315 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
5316 data->oy, oldy, px, py, no_reset);
5319 /* Compute the previous place where a word begins. */
5320 static void
5321 window_copy_cursor_previous_word_pos(struct window_mode_entry *wme,
5322 const char *separators, u_int *ppx, u_int *ppy)
5324 struct window_copy_mode_data *data = wme->data;
5325 struct screen *back_s = data->backing;
5326 struct grid_reader gr;
5327 u_int px, py, hsize;
5329 px = data->cx;
5330 hsize = screen_hsize(back_s);
5331 py = hsize + data->cy - data->oy;
5333 grid_reader_start(&gr, back_s->grid, px, py);
5334 grid_reader_cursor_previous_word(&gr, separators, /* already= */ 0,
5335 /* stop_at_eol= */ 1);
5336 grid_reader_get_cursor(&gr, &px, &py);
5337 *ppx = px;
5338 *ppy = py;
5341 /* Move to the previous place where a word begins. */
5342 static void
5343 window_copy_cursor_previous_word(struct window_mode_entry *wme,
5344 const char *separators, int already)
5346 struct window_copy_mode_data *data = wme->data;
5347 struct window *w = wme->wp->window;
5348 struct screen *back_s = data->backing;
5349 struct grid_reader gr;
5350 u_int px, py, oldy, hsize;
5351 int stop_at_eol;
5353 if (options_get_number(w->options, "mode-keys") == MODEKEY_EMACS)
5354 stop_at_eol = 1;
5355 else
5356 stop_at_eol = 0;
5358 px = data->cx;
5359 hsize = screen_hsize(back_s);
5360 py = hsize + data->cy - data->oy;
5361 oldy = data->cy;
5363 grid_reader_start(&gr, back_s->grid, px, py);
5364 grid_reader_cursor_previous_word(&gr, separators, already, stop_at_eol);
5365 grid_reader_get_cursor(&gr, &px, &py);
5366 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
5369 static void
5370 window_copy_cursor_prompt(struct window_mode_entry *wme, int direction,
5371 int start_output)
5373 struct window_copy_mode_data *data = wme->data;
5374 struct screen *s = data->backing;
5375 struct grid *gd = s->grid;
5376 u_int end_line;
5377 u_int line = gd->hsize - data->oy + data->cy;
5378 int add, line_flag;
5380 if (start_output)
5381 line_flag = GRID_LINE_START_OUTPUT;
5382 else
5383 line_flag = GRID_LINE_START_PROMPT;
5385 if (direction == 0) { /* up */
5386 add = -1;
5387 end_line = 0;
5388 } else { /* down */
5389 add = 1;
5390 end_line = gd->hsize + gd->sy - 1;
5393 if (line == end_line)
5394 return;
5395 for (;;) {
5396 if (line == end_line)
5397 return;
5398 line += add;
5400 if (grid_get_line(gd, line)->flags & line_flag)
5401 break;
5404 data->cx = 0;
5405 if (line > gd->hsize) {
5406 data->cy = line - gd->hsize;
5407 data->oy = 0;
5408 } else {
5409 data->cy = 0;
5410 data->oy = gd->hsize - line;
5413 window_copy_update_selection(wme, 1, 0);
5414 window_copy_redraw_screen(wme);
5417 static void
5418 window_copy_scroll_up(struct window_mode_entry *wme, u_int ny)
5420 struct window_pane *wp = wme->wp;
5421 struct window_copy_mode_data *data = wme->data;
5422 struct screen *s = &data->screen;
5423 struct screen_write_ctx ctx;
5425 if (data->oy < ny)
5426 ny = data->oy;
5427 if (ny == 0)
5428 return;
5429 data->oy -= ny;
5431 if (data->searchmark != NULL && !data->timeout)
5432 window_copy_search_marks(wme, NULL, data->searchregex, 1);
5433 window_copy_update_selection(wme, 0, 0);
5435 screen_write_start_pane(&ctx, wp, NULL);
5436 screen_write_cursormove(&ctx, 0, 0, 0);
5437 screen_write_deleteline(&ctx, ny, 8);
5438 window_copy_write_lines(wme, &ctx, screen_size_y(s) - ny, ny);
5439 window_copy_write_line(wme, &ctx, 0);
5440 if (screen_size_y(s) > 1)
5441 window_copy_write_line(wme, &ctx, 1);
5442 if (screen_size_y(s) > 3)
5443 window_copy_write_line(wme, &ctx, screen_size_y(s) - 2);
5444 if (s->sel != NULL && screen_size_y(s) > ny)
5445 window_copy_write_line(wme, &ctx, screen_size_y(s) - ny - 1);
5446 screen_write_cursormove(&ctx, data->cx, data->cy, 0);
5447 screen_write_stop(&ctx);
5450 static void
5451 window_copy_scroll_down(struct window_mode_entry *wme, u_int ny)
5453 struct window_pane *wp = wme->wp;
5454 struct window_copy_mode_data *data = wme->data;
5455 struct screen *s = &data->screen;
5456 struct screen_write_ctx ctx;
5458 if (ny > screen_hsize(data->backing))
5459 return;
5461 if (data->oy > screen_hsize(data->backing) - ny)
5462 ny = screen_hsize(data->backing) - data->oy;
5463 if (ny == 0)
5464 return;
5465 data->oy += ny;
5467 if (data->searchmark != NULL && !data->timeout)
5468 window_copy_search_marks(wme, NULL, data->searchregex, 1);
5469 window_copy_update_selection(wme, 0, 0);
5471 screen_write_start_pane(&ctx, wp, NULL);
5472 screen_write_cursormove(&ctx, 0, 0, 0);
5473 screen_write_insertline(&ctx, ny, 8);
5474 window_copy_write_lines(wme, &ctx, 0, ny);
5475 if (s->sel != NULL && screen_size_y(s) > ny)
5476 window_copy_write_line(wme, &ctx, ny);
5477 else if (ny == 1) /* nuke position */
5478 window_copy_write_line(wme, &ctx, 1);
5479 screen_write_cursormove(&ctx, data->cx, data->cy, 0);
5480 screen_write_stop(&ctx);
5483 static void
5484 window_copy_rectangle_set(struct window_mode_entry *wme, int rectflag)
5486 struct window_copy_mode_data *data = wme->data;
5487 u_int px, py;
5489 data->rectflag = rectflag;
5491 py = screen_hsize(data->backing) + data->cy - data->oy;
5492 px = window_copy_find_length(wme, py);
5493 if (data->cx > px)
5494 window_copy_update_cursor(wme, px, data->cy);
5496 window_copy_update_selection(wme, 1, 0);
5497 window_copy_redraw_screen(wme);
5500 static void
5501 window_copy_move_mouse(struct mouse_event *m)
5503 struct window_pane *wp;
5504 struct window_mode_entry *wme;
5505 u_int x, y;
5507 wp = cmd_mouse_pane(m, NULL, NULL);
5508 if (wp == NULL)
5509 return;
5510 wme = TAILQ_FIRST(&wp->modes);
5511 if (wme == NULL)
5512 return;
5513 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode)
5514 return;
5516 if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
5517 return;
5519 window_copy_update_cursor(wme, x, y);
5522 void
5523 window_copy_start_drag(struct client *c, struct mouse_event *m)
5525 struct window_pane *wp;
5526 struct window_mode_entry *wme;
5527 struct window_copy_mode_data *data;
5528 u_int x, y, yg;
5530 if (c == NULL)
5531 return;
5533 wp = cmd_mouse_pane(m, NULL, NULL);
5534 if (wp == NULL)
5535 return;
5536 wme = TAILQ_FIRST(&wp->modes);
5537 if (wme == NULL)
5538 return;
5539 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode)
5540 return;
5542 if (cmd_mouse_at(wp, m, &x, &y, 1) != 0)
5543 return;
5545 c->tty.mouse_drag_update = window_copy_drag_update;
5546 c->tty.mouse_drag_release = window_copy_drag_release;
5548 data = wme->data;
5549 yg = screen_hsize(data->backing) + y - data->oy;
5550 if (x < data->selrx || x > data->endselrx || yg != data->selry)
5551 data->selflag = SEL_CHAR;
5552 switch (data->selflag) {
5553 case SEL_WORD:
5554 if (data->separators != NULL) {
5555 window_copy_update_cursor(wme, x, y);
5556 window_copy_cursor_previous_word_pos(wme,
5557 data->separators, &x, &y);
5558 y -= screen_hsize(data->backing) - data->oy;
5560 window_copy_update_cursor(wme, x, y);
5561 break;
5562 case SEL_LINE:
5563 window_copy_update_cursor(wme, 0, y);
5564 break;
5565 case SEL_CHAR:
5566 window_copy_update_cursor(wme, x, y);
5567 window_copy_start_selection(wme);
5568 break;
5571 window_copy_redraw_screen(wme);
5572 window_copy_drag_update(c, m);
5575 static void
5576 window_copy_drag_update(struct client *c, struct mouse_event *m)
5578 struct window_pane *wp;
5579 struct window_mode_entry *wme;
5580 struct window_copy_mode_data *data;
5581 u_int x, y, old_cx, old_cy;
5582 struct timeval tv = {
5583 .tv_usec = WINDOW_COPY_DRAG_REPEAT_TIME
5586 if (c == NULL)
5587 return;
5589 wp = cmd_mouse_pane(m, NULL, NULL);
5590 if (wp == NULL)
5591 return;
5592 wme = TAILQ_FIRST(&wp->modes);
5593 if (wme == NULL)
5594 return;
5595 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode)
5596 return;
5598 data = wme->data;
5599 evtimer_del(&data->dragtimer);
5601 if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
5602 return;
5603 old_cx = data->cx;
5604 old_cy = data->cy;
5606 window_copy_update_cursor(wme, x, y);
5607 if (window_copy_update_selection(wme, 1, 0))
5608 window_copy_redraw_selection(wme, old_cy);
5609 if (old_cy != data->cy || old_cx == data->cx) {
5610 if (y == 0) {
5611 evtimer_add(&data->dragtimer, &tv);
5612 window_copy_cursor_up(wme, 1);
5613 } else if (y == screen_size_y(&data->screen) - 1) {
5614 evtimer_add(&data->dragtimer, &tv);
5615 window_copy_cursor_down(wme, 1);
5620 static void
5621 window_copy_drag_release(struct client *c, struct mouse_event *m)
5623 struct window_pane *wp;
5624 struct window_mode_entry *wme;
5625 struct window_copy_mode_data *data;
5627 if (c == NULL)
5628 return;
5630 wp = cmd_mouse_pane(m, NULL, NULL);
5631 if (wp == NULL)
5632 return;
5633 wme = TAILQ_FIRST(&wp->modes);
5634 if (wme == NULL)
5635 return;
5636 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode)
5637 return;
5639 data = wme->data;
5640 evtimer_del(&data->dragtimer);
5643 static void
5644 window_copy_jump_to_mark(struct window_mode_entry *wme)
5646 struct window_copy_mode_data *data = wme->data;
5647 u_int tmx, tmy;
5649 tmx = data->cx;
5650 tmy = screen_hsize(data->backing) + data->cy - data->oy;
5651 data->cx = data->mx;
5652 if (data->my < screen_hsize(data->backing)) {
5653 data->cy = 0;
5654 data->oy = screen_hsize(data->backing) - data->my;
5655 } else {
5656 data->cy = data->my - screen_hsize(data->backing);
5657 data->oy = 0;
5659 data->mx = tmx;
5660 data->my = tmy;
5661 data->showmark = 1;
5662 window_copy_update_selection(wme, 0, 0);
5663 window_copy_redraw_screen(wme);
5666 /* Scroll up if the cursor went off the visible screen. */
5667 static void
5668 window_copy_acquire_cursor_up(struct window_mode_entry *wme, u_int hsize,
5669 u_int oy, u_int oldy, u_int px, u_int py)
5671 u_int cy, yy, ny, nd;
5673 yy = hsize - oy;
5674 if (py < yy) {
5675 ny = yy - py;
5676 cy = 0;
5677 nd = 1;
5678 } else {
5679 ny = 0;
5680 cy = py - yy;
5681 nd = oldy - cy + 1;
5683 while (ny > 0) {
5684 window_copy_cursor_up(wme, 1);
5685 ny--;
5687 window_copy_update_cursor(wme, px, cy);
5688 if (window_copy_update_selection(wme, 1, 0))
5689 window_copy_redraw_lines(wme, cy, nd);
5692 /* Scroll down if the cursor went off the visible screen. */
5693 static void
5694 window_copy_acquire_cursor_down(struct window_mode_entry *wme, u_int hsize,
5695 u_int sy, u_int oy, u_int oldy, u_int px, u_int py, int no_reset)
5697 u_int cy, yy, ny, nd;
5699 cy = py - hsize + oy;
5700 yy = sy - 1;
5701 if (cy > yy) {
5702 ny = cy - yy;
5703 oldy = yy;
5704 nd = 1;
5705 } else {
5706 ny = 0;
5707 nd = cy - oldy + 1;
5709 while (ny > 0) {
5710 window_copy_cursor_down(wme, 1);
5711 ny--;
5713 if (cy > yy)
5714 window_copy_update_cursor(wme, px, yy);
5715 else
5716 window_copy_update_cursor(wme, px, cy);
5717 if (window_copy_update_selection(wme, 1, no_reset))
5718 window_copy_redraw_lines(wme, oldy, nd);