1 /* $OpenBSD$ */
2
3 /*
4 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
5 *
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.
9 *
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.
17 */
18
19 #include <sys/types.h>
20
21 #include <ctype.h>
22 #include <regex.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <time.h>
26
27 #include "tmux.h"
28
29 struct window_copy_mode_data;
30
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);
98 static void window_copy_pipe(struct window_mode_entry *,
99 struct session *, const char *);
100 static void window_copy_copy_pipe(struct window_mode_entry *,
101 struct session *, const char *, const char *);
102 static void window_copy_copy_selection(struct window_mode_entry *,
103 const char *);
104 static void window_copy_append_selection(struct window_mode_entry *);
105 static void window_copy_clear_selection(struct window_mode_entry *);
106 static void window_copy_copy_line(struct window_mode_entry *, char **,
107 size_t *, u_int, u_int, u_int);
108 static int window_copy_in_set(struct window_mode_entry *, u_int, u_int,
109 const char *);
110 static u_int window_copy_find_length(struct window_mode_entry *, u_int);
111 static void window_copy_cursor_start_of_line(struct window_mode_entry *);
112 static void window_copy_cursor_back_to_indentation(
113 struct window_mode_entry *);
114 static void window_copy_cursor_end_of_line(struct window_mode_entry *);
115 static void window_copy_other_end(struct window_mode_entry *);
116 static void window_copy_cursor_left(struct window_mode_entry *);
117 static void window_copy_cursor_right(struct window_mode_entry *, int);
118 static void window_copy_cursor_up(struct window_mode_entry *, int);
119 static void window_copy_cursor_down(struct window_mode_entry *, int);
120 static void window_copy_cursor_jump(struct window_mode_entry *);
121 static void window_copy_cursor_jump_back(struct window_mode_entry *);
122 static void window_copy_cursor_jump_to(struct window_mode_entry *);
123 static void window_copy_cursor_jump_to_back(struct window_mode_entry *);
124 static void window_copy_cursor_next_word(struct window_mode_entry *,
125 const char *);
126 static void window_copy_cursor_next_word_end_pos(struct window_mode_entry *,
127 const char *, u_int *, u_int *);
128 static void window_copy_cursor_next_word_end(struct window_mode_entry *,
129 const char *, int);
130 static void window_copy_cursor_previous_word_pos(struct window_mode_entry *,
131 const char *, u_int *, u_int *);
132 static void window_copy_cursor_previous_word(struct window_mode_entry *,
133 const char *, int);
134 static void window_copy_cursor_prompt(struct window_mode_entry *, int,
135 const char *);
136 static void window_copy_scroll_up(struct window_mode_entry *, u_int);
137 static void window_copy_scroll_down(struct window_mode_entry *, u_int);
138 static void window_copy_rectangle_set(struct window_mode_entry *, int);
139 static void window_copy_move_mouse(struct mouse_event *);
140 static void window_copy_drag_update(struct client *, struct mouse_event *);
141 static void window_copy_drag_release(struct client *, struct mouse_event *);
142 static void window_copy_jump_to_mark(struct window_mode_entry *);
143 static void window_copy_acquire_cursor_up(struct window_mode_entry *,
144 u_int, u_int, u_int, u_int, u_int);
145 static void window_copy_acquire_cursor_down(struct window_mode_entry *,
146 u_int, u_int, u_int, u_int, u_int, u_int, int);
147
148 const struct window_mode window_copy_mode = {
149 .name = "copy-mode",
150
151 .init = window_copy_init,
152 .free = window_copy_free,
153 .resize = window_copy_resize,
154 .key_table = window_copy_key_table,
155 .command = window_copy_command,
156 .formats = window_copy_formats,
157 };
158
159 const struct window_mode window_view_mode = {
160 .name = "view-mode",
161
162 .init = window_copy_view_init,
163 .free = window_copy_free,
164 .resize = window_copy_resize,
165 .key_table = window_copy_key_table,
166 .command = window_copy_command,
167 .formats = window_copy_formats,
168 };
169
170 enum {
171 WINDOW_COPY_OFF,
172 WINDOW_COPY_SEARCHUP,
173 WINDOW_COPY_SEARCHDOWN,
174 WINDOW_COPY_JUMPFORWARD,
175 WINDOW_COPY_JUMPBACKWARD,
176 WINDOW_COPY_JUMPTOFORWARD,
177 WINDOW_COPY_JUMPTOBACKWARD,
178 };
179
180 enum {
181 WINDOW_COPY_REL_POS_ABOVE,
182 WINDOW_COPY_REL_POS_ON_SCREEN,
183 WINDOW_COPY_REL_POS_BELOW,
184 };
185
186 enum window_copy_cmd_action {
187 WINDOW_COPY_CMD_NOTHING,
188 WINDOW_COPY_CMD_REDRAW,
189 WINDOW_COPY_CMD_CANCEL,
190 };
191
192 enum window_copy_cmd_clear {
193 WINDOW_COPY_CMD_CLEAR_ALWAYS,
194 WINDOW_COPY_CMD_CLEAR_NEVER,
195 WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
196 };
197
198 struct window_copy_cmd_state {
199 struct window_mode_entry *wme;
200 struct args *args;
201 struct mouse_event *m;
202
203 struct client *c;
204 struct session *s;
205 struct winlink *wl;
206 };
207
208 /*
209 * Copy mode's visible screen (the "screen" field) is filled from one of two
210 * sources: the original contents of the pane (used when we actually enter via
211 * the "copy-mode" command, to copy the contents of the current pane), or else
212 * a series of lines containing the output from an output-writing tmux command
213 * (such as any of the "show-*" or "list-*" commands).
214 *
215 * In either case, the full content of the copy-mode grid is pointed at by the
216 * "backing" field, and is copied into "screen" as needed (that is, when
217 * scrolling occurs). When copy-mode is backed by a pane, backing points
218 * directly at that pane's screen structure (&wp->base); when backed by a list
219 * of output-lines from a command, it points at a newly-allocated screen
220 * structure (which is deallocated when the mode ends).
221 */
222 struct window_copy_mode_data {
223 struct screen screen;
224
225 struct screen *backing;
226 int backing_written; /* backing display started */
227 struct screen *writing;
228 struct input_ctx *ictx;
229
230 int viewmode; /* view mode entered */
231
232 u_int oy; /* number of lines scrolled up */
233
234 u_int selx; /* beginning of selection */
235 u_int sely;
236
237 u_int endselx; /* end of selection */
238 u_int endsely;
239
240 enum {
241 CURSORDRAG_NONE, /* selection is independent of cursor */
242 CURSORDRAG_ENDSEL, /* end is synchronized with cursor */
243 CURSORDRAG_SEL, /* start is synchronized with cursor */
244 } cursordrag;
245
246 int modekeys;
247 enum {
248 LINE_SEL_NONE,
249 LINE_SEL_LEFT_RIGHT,
250 LINE_SEL_RIGHT_LEFT,
251 } lineflag; /* line selection mode */
252 int rectflag; /* in rectangle copy mode? */
253 int scroll_exit; /* exit on scroll to end? */
254 int hide_position; /* hide position marker */
255
256 enum {
257 SEL_CHAR, /* select one char at a time */
258 SEL_WORD, /* select one word at a time */
259 SEL_LINE, /* select one line at a time */
260 } selflag;
261
262 const char *separators; /* word separators */
263
264 u_int dx; /* drag start position */
265 u_int dy;
266
267 u_int selrx; /* selection reset positions */
268 u_int selry;
269 u_int endselrx;
270 u_int endselry;
271
272 u_int cx;
273 u_int cy;
274
275 u_int lastcx; /* position in last line w/ content */
276 u_int lastsx; /* size of last line w/ content */
277
278 u_int mx; /* mark position */
279 u_int my;
280 int showmark;
281
282 int searchtype;
283 int searchdirection;
284 int searchregex;
285 char *searchstr;
286 u_char *searchmark;
287 int searchcount;
288 int searchmore;
289 int searchall;
290 int searchx;
291 int searchy;
292 int searcho;
293 u_char searchgen;
294
295 int timeout; /* search has timed out */
296 #define WINDOW_COPY_SEARCH_TIMEOUT 10000
297 #define WINDOW_COPY_SEARCH_ALL_TIMEOUT 200
298 #define WINDOW_COPY_SEARCH_MAX_LINE 2000
299
300 int jumptype;
301 struct utf8_data *jumpchar;
302
303 struct event dragtimer;
304 #define WINDOW_COPY_DRAG_REPEAT_TIME 50000
305 };
306
307 static void
window_copy_scroll_timer(__unused int fd,__unused short events,void * arg)308 window_copy_scroll_timer(__unused int fd, __unused short events, void *arg)
309 {
310 struct window_mode_entry *wme = arg;
311 struct window_pane *wp = wme->wp;
312 struct window_copy_mode_data *data = wme->data;
313 struct timeval tv = {
314 .tv_usec = WINDOW_COPY_DRAG_REPEAT_TIME
315 };
316
317 evtimer_del(&data->dragtimer);
318
319 if (TAILQ_FIRST(&wp->modes) != wme)
320 return;
321
322 if (data->cy == 0) {
323 evtimer_add(&data->dragtimer, &tv);
324 window_copy_cursor_up(wme, 1);
325 } else if (data->cy == screen_size_y(&data->screen) - 1) {
326 evtimer_add(&data->dragtimer, &tv);
327 window_copy_cursor_down(wme, 1);
328 }
329 }
330
331 static struct screen *
window_copy_clone_screen(struct screen * src,struct screen * hint,u_int * cx,u_int * cy,int trim)332 window_copy_clone_screen(struct screen *src, struct screen *hint, u_int *cx,
333 u_int *cy, int trim)
334 {
335 struct screen *dst;
336 const struct grid_line *gl;
337 u_int sy, wx, wy;
338 int reflow;
339
340 dst = xcalloc(1, sizeof *dst);
341
342 sy = screen_hsize(src) + screen_size_y(src);
343 if (trim) {
344 while (sy > screen_hsize(src)) {
345 gl = grid_peek_line(src->grid, sy - 1);
346 if (gl->cellused != 0)
347 break;
348 sy--;
349 }
350 }
351 log_debug("%s: target screen is %ux%u, source %ux%u", __func__,
352 screen_size_x(src), sy, screen_size_x(hint),
353 screen_hsize(src) + screen_size_y(src));
354 screen_init(dst, screen_size_x(src), sy, screen_hlimit(src));
355
356 /*
357 * Ensure history is on for the backing grid so lines are not deleted
358 * during resizing.
359 */
360 dst->grid->flags |= GRID_HISTORY;
361 grid_duplicate_lines(dst->grid, 0, src->grid, 0, sy);
362
363 dst->grid->sy = sy - screen_hsize(src);
364 dst->grid->hsize = screen_hsize(src);
365 dst->grid->hscrolled = src->grid->hscrolled;
366 if (src->cy > dst->grid->sy - 1) {
367 dst->cx = 0;
368 dst->cy = dst->grid->sy - 1;
369 } else {
370 dst->cx = src->cx;
371 dst->cy = src->cy;
372 }
373
374 if (cx != NULL && cy != NULL) {
375 *cx = dst->cx;
376 *cy = screen_hsize(dst) + dst->cy;
377 reflow = (screen_size_x(hint) != screen_size_x(dst));
378 }
379 else
380 reflow = 0;
381 if (reflow)
382 grid_wrap_position(dst->grid, *cx, *cy, &wx, &wy);
383 screen_resize_cursor(dst, screen_size_x(hint), screen_size_y(hint), 1,
384 0, 0);
385 if (reflow)
386 grid_unwrap_position(dst->grid, cx, cy, wx, wy);
387
388 return (dst);
389 }
390
391 static struct window_copy_mode_data *
window_copy_common_init(struct window_mode_entry * wme)392 window_copy_common_init(struct window_mode_entry *wme)
393 {
394 struct window_pane *wp = wme->wp;
395 struct window_copy_mode_data *data;
396 struct screen *base = &wp->base;
397
398 wme->data = data = xcalloc(1, sizeof *data);
399
400 data->cursordrag = CURSORDRAG_NONE;
401 data->lineflag = LINE_SEL_NONE;
402 data->selflag = SEL_CHAR;
403
404 if (wp->searchstr != NULL) {
405 data->searchtype = WINDOW_COPY_SEARCHUP;
406 data->searchregex = wp->searchregex;
407 data->searchstr = xstrdup(wp->searchstr);
408 } else {
409 data->searchtype = WINDOW_COPY_OFF;
410 data->searchregex = 0;
411 data->searchstr = NULL;
412 }
413 data->searchx = data->searchy = data->searcho = -1;
414 data->searchall = 1;
415
416 data->jumptype = WINDOW_COPY_OFF;
417 data->jumpchar = NULL;
418
419 screen_init(&data->screen, screen_size_x(base), screen_size_y(base), 0);
420 data->modekeys = options_get_number(wp->window->options, "mode-keys");
421
422 evtimer_set(&data->dragtimer, window_copy_scroll_timer, wme);
423
424 return (data);
425 }
426
427 static struct screen *
window_copy_init(struct window_mode_entry * wme,__unused struct cmd_find_state * fs,struct args * args)428 window_copy_init(struct window_mode_entry *wme,
429 __unused struct cmd_find_state *fs, struct args *args)
430 {
431 struct window_pane *wp = wme->swp;
432 struct window_copy_mode_data *data;
433 struct screen *base = &wp->base;
434 struct screen_write_ctx ctx;
435 u_int i, cx, cy;
436
437 data = window_copy_common_init(wme);
438 data->backing = window_copy_clone_screen(base, &data->screen, &cx, &cy,
439 wme->swp != wme->wp);
440
441 data->cx = cx;
442 if (cy < screen_hsize(data->backing)) {
443 data->cy = 0;
444 data->oy = screen_hsize(data->backing) - cy;
445 } else {
446 data->cy = cy - screen_hsize(data->backing);
447 data->oy = 0;
448 }
449
450 data->scroll_exit = args_has(args, 'e');
451 data->hide_position = args_has(args, 'H');
452
453 if (base->hyperlinks != NULL)
454 data->screen.hyperlinks = hyperlinks_copy(base->hyperlinks);
455 data->screen.cx = data->cx;
456 data->screen.cy = data->cy;
457 data->mx = data->cx;
458 data->my = screen_hsize(data->backing) + data->cy - data->oy;
459 data->showmark = 0;
460
461 screen_write_start(&ctx, &data->screen);
462 for (i = 0; i < screen_size_y(&data->screen); i++)
463 window_copy_write_line(wme, &ctx, i);
464 screen_write_cursormove(&ctx, data->cx, data->cy, 0);
465 screen_write_stop(&ctx);
466
467 return (&data->screen);
468 }
469
470 static struct screen *
window_copy_view_init(struct window_mode_entry * wme,__unused struct cmd_find_state * fs,__unused struct args * args)471 window_copy_view_init(struct window_mode_entry *wme,
472 __unused struct cmd_find_state *fs, __unused struct args *args)
473 {
474 struct window_pane *wp = wme->wp;
475 struct window_copy_mode_data *data;
476 struct screen *base = &wp->base;
477 u_int sx = screen_size_x(base);
478
479 data = window_copy_common_init(wme);
480 data->viewmode = 1;
481
482 data->backing = xmalloc(sizeof *data->backing);
483 screen_init(data->backing, sx, screen_size_y(base), UINT_MAX);
484 data->writing = xmalloc(sizeof *data->writing);
485 screen_init(data->writing, sx, screen_size_y(base), 0);
486 data->ictx = input_init(NULL, NULL, NULL);
487 data->mx = data->cx;
488 data->my = screen_hsize(data->backing) + data->cy - data->oy;
489 data->showmark = 0;
490
491 return (&data->screen);
492 }
493
494 static void
window_copy_free(struct window_mode_entry * wme)495 window_copy_free(struct window_mode_entry *wme)
496 {
497 struct window_copy_mode_data *data = wme->data;
498
499 evtimer_del(&data->dragtimer);
500
501 free(data->searchmark);
502 free(data->searchstr);
503 free(data->jumpchar);
504
505 if (data->writing != NULL) {
506 screen_free(data->writing);
507 free(data->writing);
508 }
509 if (data->ictx != NULL)
510 input_free(data->ictx);
511 screen_free(data->backing);
512 free(data->backing);
513
514 screen_free(&data->screen);
515 free(data);
516 }
517
518 void
window_copy_add(struct window_pane * wp,int parse,const char * fmt,...)519 window_copy_add(struct window_pane *wp, int parse, const char *fmt, ...)
520 {
521 va_list ap;
522
523 va_start(ap, fmt);
524 window_copy_vadd(wp, parse, fmt, ap);
525 va_end(ap);
526 }
527
528 static void
window_copy_init_ctx_cb(__unused struct screen_write_ctx * ctx,struct tty_ctx * ttyctx)529 window_copy_init_ctx_cb(__unused struct screen_write_ctx *ctx,
530 struct tty_ctx *ttyctx)
531 {
532 memcpy(&ttyctx->defaults, &grid_default_cell, sizeof ttyctx->defaults);
533 ttyctx->palette = NULL;
534 ttyctx->redraw_cb = NULL;
535 ttyctx->set_client_cb = NULL;
536 ttyctx->arg = NULL;
537 }
538
539 void
window_copy_vadd(struct window_pane * wp,int parse,const char * fmt,va_list ap)540 window_copy_vadd(struct window_pane *wp, int parse, const char *fmt, va_list ap)
541 {
542 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
543 struct window_copy_mode_data *data = wme->data;
544 struct screen *backing = data->backing;
545 struct screen *writing = data->writing;
546 struct screen_write_ctx writing_ctx, backing_ctx, ctx;
547 struct grid_cell gc;
548 u_int old_hsize, old_cy;
549 u_int sx = screen_size_x(backing);
550 char *text;
551
552 if (parse) {
553 vasprintf(&text, fmt, ap);
554 screen_write_start(&writing_ctx, writing);
555 screen_write_reset(&writing_ctx);
556 input_parse_screen(data->ictx, writing, window_copy_init_ctx_cb,
557 data, text, strlen(text));
558 free(text);
559 }
560
561 old_hsize = screen_hsize(data->backing);
562 screen_write_start(&backing_ctx, backing);
563 if (data->backing_written) {
564 /*
565 * On the second or later line, do a CRLF before writing
566 * (so it's on a new line).
567 */
568 screen_write_carriagereturn(&backing_ctx);
569 screen_write_linefeed(&backing_ctx, 0, 8);
570 } else
571 data->backing_written = 1;
572 old_cy = backing->cy;
573 if (parse)
574 screen_write_fast_copy(&backing_ctx, writing, 0, 0, sx, 1);
575 else {
576 memcpy(&gc, &grid_default_cell, sizeof gc);
577 screen_write_vnputs(&backing_ctx, 0, &gc, fmt, ap);
578 }
579 screen_write_stop(&backing_ctx);
580
581 data->oy += screen_hsize(data->backing) - old_hsize;
582
583 screen_write_start_pane(&ctx, wp, &data->screen);
584
585 /*
586 * If the history has changed, draw the top line.
587 * (If there's any history at all, it has changed.)
588 */
589 if (screen_hsize(data->backing))
590 window_copy_redraw_lines(wme, 0, 1);
591
592 /* Write the new lines. */
593 window_copy_redraw_lines(wme, old_cy, backing->cy - old_cy + 1);
594
595 screen_write_stop(&ctx);
596 }
597
598 void
window_copy_pageup(struct window_pane * wp,int half_page)599 window_copy_pageup(struct window_pane *wp, int half_page)
600 {
601 window_copy_pageup1(TAILQ_FIRST(&wp->modes), half_page);
602 }
603
604 static void
window_copy_pageup1(struct window_mode_entry * wme,int half_page)605 window_copy_pageup1(struct window_mode_entry *wme, int half_page)
606 {
607 struct window_copy_mode_data *data = wme->data;
608 struct screen *s = &data->screen;
609 u_int n, ox, oy, px, py;
610
611 oy = screen_hsize(data->backing) + data->cy - data->oy;
612 ox = window_copy_find_length(wme, oy);
613
614 if (data->cx != ox) {
615 data->lastcx = data->cx;
616 data->lastsx = ox;
617 }
618 data->cx = data->lastcx;
619
620 n = 1;
621 if (screen_size_y(s) > 2) {
622 if (half_page)
623 n = screen_size_y(s) / 2;
624 else
625 n = screen_size_y(s) - 2;
626 }
627
628 if (data->oy + n > screen_hsize(data->backing)) {
629 data->oy = screen_hsize(data->backing);
630 if (data->cy < n)
631 data->cy = 0;
632 else
633 data->cy -= n;
634 } else
635 data->oy += n;
636
637 if (data->screen.sel == NULL || !data->rectflag) {
638 py = screen_hsize(data->backing) + data->cy - data->oy;
639 px = window_copy_find_length(wme, py);
640 if ((data->cx >= data->lastsx && data->cx != px) ||
641 data->cx > px)
642 window_copy_cursor_end_of_line(wme);
643 }
644
645 if (data->searchmark != NULL && !data->timeout)
646 window_copy_search_marks(wme, NULL, data->searchregex, 1);
647 window_copy_update_selection(wme, 1, 0);
648 window_copy_redraw_screen(wme);
649 }
650
651 void
window_copy_pagedown(struct window_pane * wp,int half_page,int scroll_exit)652 window_copy_pagedown(struct window_pane *wp, int half_page, int scroll_exit)
653 {
654 if (window_copy_pagedown1(TAILQ_FIRST(&wp->modes), half_page,
655 scroll_exit)) {
656 window_pane_reset_mode(wp);
657 return;
658 }
659 }
660
661 static int
window_copy_pagedown1(struct window_mode_entry * wme,int half_page,int scroll_exit)662 window_copy_pagedown1(struct window_mode_entry *wme, int half_page,
663 int scroll_exit)
664 {
665 struct window_copy_mode_data *data = wme->data;
666 struct screen *s = &data->screen;
667 u_int n, ox, oy, px, py;
668
669 oy = screen_hsize(data->backing) + data->cy - data->oy;
670 ox = window_copy_find_length(wme, oy);
671
672 if (data->cx != ox) {
673 data->lastcx = data->cx;
674 data->lastsx = ox;
675 }
676 data->cx = data->lastcx;
677
678 n = 1;
679 if (screen_size_y(s) > 2) {
680 if (half_page)
681 n = screen_size_y(s) / 2;
682 else
683 n = screen_size_y(s) - 2;
684 }
685
686 if (data->oy < n) {
687 data->oy = 0;
688 if (data->cy + (n - data->oy) >= screen_size_y(data->backing))
689 data->cy = screen_size_y(data->backing) - 1;
690 else
691 data->cy += n - data->oy;
692 } else
693 data->oy -= n;
694
695 if (data->screen.sel == NULL || !data->rectflag) {
696 py = screen_hsize(data->backing) + data->cy - data->oy;
697 px = window_copy_find_length(wme, py);
698 if ((data->cx >= data->lastsx && data->cx != px) ||
699 data->cx > px)
700 window_copy_cursor_end_of_line(wme);
701 }
702
703 if (scroll_exit && data->oy == 0)
704 return (1);
705 if (data->searchmark != NULL && !data->timeout)
706 window_copy_search_marks(wme, NULL, data->searchregex, 1);
707 window_copy_update_selection(wme, 1, 0);
708 window_copy_redraw_screen(wme);
709 return (0);
710 }
711
712 static void
window_copy_previous_paragraph(struct window_mode_entry * wme)713 window_copy_previous_paragraph(struct window_mode_entry *wme)
714 {
715 struct window_copy_mode_data *data = wme->data;
716 u_int oy;
717
718 oy = screen_hsize(data->backing) + data->cy - data->oy;
719
720 while (oy > 0 && window_copy_find_length(wme, oy) == 0)
721 oy--;
722
723 while (oy > 0 && window_copy_find_length(wme, oy) > 0)
724 oy--;
725
726 window_copy_scroll_to(wme, 0, oy, 0);
727 }
728
729 static void
window_copy_next_paragraph(struct window_mode_entry * wme)730 window_copy_next_paragraph(struct window_mode_entry *wme)
731 {
732 struct window_copy_mode_data *data = wme->data;
733 struct screen *s = &data->screen;
734 u_int maxy, ox, oy;
735
736 oy = screen_hsize(data->backing) + data->cy - data->oy;
737 maxy = screen_hsize(data->backing) + screen_size_y(s) - 1;
738
739 while (oy < maxy && window_copy_find_length(wme, oy) == 0)
740 oy++;
741
742 while (oy < maxy && window_copy_find_length(wme, oy) > 0)
743 oy++;
744
745 ox = window_copy_find_length(wme, oy);
746 window_copy_scroll_to(wme, ox, oy, 0);
747 }
748
749 char *
window_copy_get_word(struct window_pane * wp,u_int x,u_int y)750 window_copy_get_word(struct window_pane *wp, u_int x, u_int y)
751 {
752 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
753 struct window_copy_mode_data *data = wme->data;
754 struct grid *gd = data->screen.grid;
755
756 return (format_grid_word(gd, x, gd->hsize + y));
757 }
758
759 char *
window_copy_get_line(struct window_pane * wp,u_int y)760 window_copy_get_line(struct window_pane *wp, u_int y)
761 {
762 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
763 struct window_copy_mode_data *data = wme->data;
764 struct grid *gd = data->screen.grid;
765
766 return (format_grid_line(gd, gd->hsize + y));
767 }
768
769 static void *
window_copy_cursor_hyperlink_cb(struct format_tree * ft)770 window_copy_cursor_hyperlink_cb(struct format_tree *ft)
771 {
772 struct window_pane *wp = format_get_pane(ft);
773 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
774 struct window_copy_mode_data *data = wme->data;
775 struct grid *gd = data->screen.grid;
776
777 return (format_grid_hyperlink(gd, data->cx, gd->hsize + data->cy,
778 &data->screen));
779 }
780
781 static void *
window_copy_cursor_word_cb(struct format_tree * ft)782 window_copy_cursor_word_cb(struct format_tree *ft)
783 {
784 struct window_pane *wp = format_get_pane(ft);
785 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
786 struct window_copy_mode_data *data = wme->data;
787
788 return (window_copy_get_word(wp, data->cx, data->cy));
789 }
790
791 static void *
window_copy_cursor_line_cb(struct format_tree * ft)792 window_copy_cursor_line_cb(struct format_tree *ft)
793 {
794 struct window_pane *wp = format_get_pane(ft);
795 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
796 struct window_copy_mode_data *data = wme->data;
797
798 return (window_copy_get_line(wp, data->cy));
799 }
800
801 static void *
window_copy_search_match_cb(struct format_tree * ft)802 window_copy_search_match_cb(struct format_tree *ft)
803 {
804 struct window_pane *wp = format_get_pane(ft);
805 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
806 struct window_copy_mode_data *data = wme->data;
807
808 return (window_copy_match_at_cursor(data));
809 }
810
811 static void
window_copy_formats(struct window_mode_entry * wme,struct format_tree * ft)812 window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft)
813 {
814 struct window_copy_mode_data *data = wme->data;
815
816 format_add(ft, "scroll_position", "%d", data->oy);
817 format_add(ft, "rectangle_toggle", "%d", data->rectflag);
818
819 format_add(ft, "copy_cursor_x", "%d", data->cx);
820 format_add(ft, "copy_cursor_y", "%d", data->cy);
821
822 if (data->screen.sel != NULL) {
823 format_add(ft, "selection_start_x", "%d", data->selx);
824 format_add(ft, "selection_start_y", "%d", data->sely);
825 format_add(ft, "selection_end_x", "%d", data->endselx);
826 format_add(ft, "selection_end_y", "%d", data->endsely);
827
828 if (data->cursordrag != CURSORDRAG_NONE)
829 format_add(ft, "selection_active", "1");
830 else
831 format_add(ft, "selection_active", "0");
832 if (data->endselx != data->selx || data->endsely != data->sely)
833 format_add(ft, "selection_present", "1");
834 else
835 format_add(ft, "selection_present", "0");
836 } else {
837 format_add(ft, "selection_active", "0");
838 format_add(ft, "selection_present", "0");
839 }
840
841 format_add(ft, "search_present", "%d", data->searchmark != NULL);
842 if (data->searchcount != -1) {
843 format_add(ft, "search_count", "%d", data->searchcount);
844 format_add(ft, "search_count_partial", "%d", data->searchmore);
845 }
846 format_add_cb(ft, "search_match", window_copy_search_match_cb);
847
848 format_add_cb(ft, "copy_cursor_word", window_copy_cursor_word_cb);
849 format_add_cb(ft, "copy_cursor_line", window_copy_cursor_line_cb);
850 format_add_cb(ft, "copy_cursor_hyperlink",
851 window_copy_cursor_hyperlink_cb);
852 }
853
854 static void
window_copy_size_changed(struct window_mode_entry * wme)855 window_copy_size_changed(struct window_mode_entry *wme)
856 {
857 struct window_copy_mode_data *data = wme->data;
858 struct screen *s = &data->screen;
859 struct screen_write_ctx ctx;
860 int search = (data->searchmark != NULL);
861
862 window_copy_clear_selection(wme);
863 window_copy_clear_marks(wme);
864
865 screen_write_start(&ctx, s);
866 window_copy_write_lines(wme, &ctx, 0, screen_size_y(s));
867 screen_write_stop(&ctx);
868
869 if (search && !data->timeout)
870 window_copy_search_marks(wme, NULL, data->searchregex, 0);
871 data->searchx = data->cx;
872 data->searchy = data->cy;
873 data->searcho = data->oy;
874 }
875
876 static void
window_copy_resize(struct window_mode_entry * wme,u_int sx,u_int sy)877 window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
878 {
879 struct window_copy_mode_data *data = wme->data;
880 struct screen *s = &data->screen;
881 struct grid *gd = data->backing->grid;
882 u_int cx, cy, wx, wy;
883 int reflow;
884
885 screen_resize(s, sx, sy, 0);
886 cx = data->cx;
887 cy = gd->hsize + data->cy - data->oy;
888 reflow = (gd->sx != sx);
889 if (reflow)
890 grid_wrap_position(gd, cx, cy, &wx, &wy);
891 screen_resize_cursor(data->backing, sx, sy, 1, 0, 0);
892 if (reflow)
893 grid_unwrap_position(gd, &cx, &cy, wx, wy);
894
895 data->cx = cx;
896 if (cy < gd->hsize) {
897 data->cy = 0;
898 data->oy = gd->hsize - cy;
899 } else {
900 data->cy = cy - gd->hsize;
901 data->oy = 0;
902 }
903
904 window_copy_size_changed(wme);
905 window_copy_redraw_screen(wme);
906 }
907
908 static const char *
window_copy_key_table(struct window_mode_entry * wme)909 window_copy_key_table(struct window_mode_entry *wme)
910 {
911 struct window_pane *wp = wme->wp;
912
913 if (options_get_number(wp->window->options, "mode-keys") == MODEKEY_VI)
914 return ("copy-mode-vi");
915 return ("copy-mode");
916 }
917
918 static int
window_copy_expand_search_string(struct window_copy_cmd_state * cs)919 window_copy_expand_search_string(struct window_copy_cmd_state *cs)
920 {
921 struct window_mode_entry *wme = cs->wme;
922 struct window_copy_mode_data *data = wme->data;
923 const char *ss = args_string(cs->args, 1);
924 char *expanded;
925
926 if (ss == NULL || *ss == '\0')
927 return (0);
928
929 if (args_has(cs->args, 'F')) {
930 expanded = format_single(NULL, ss, NULL, NULL, NULL, wme->wp);
931 if (*expanded == '\0') {
932 free(expanded);
933 return (0);
934 }
935 free(data->searchstr);
936 data->searchstr = expanded;
937 } else {
938 free(data->searchstr);
939 data->searchstr = xstrdup(ss);
940 }
941 return (1);
942 }
943
944 static enum window_copy_cmd_action
window_copy_cmd_append_selection(struct window_copy_cmd_state * cs)945 window_copy_cmd_append_selection(struct window_copy_cmd_state *cs)
946 {
947 struct window_mode_entry *wme = cs->wme;
948 struct session *s = cs->s;
949
950 if (s != NULL)
951 window_copy_append_selection(wme);
952 window_copy_clear_selection(wme);
953 return (WINDOW_COPY_CMD_REDRAW);
954 }
955
956 static enum window_copy_cmd_action
window_copy_cmd_append_selection_and_cancel(struct window_copy_cmd_state * cs)957 window_copy_cmd_append_selection_and_cancel(struct window_copy_cmd_state *cs)
958 {
959 struct window_mode_entry *wme = cs->wme;
960 struct session *s = cs->s;
961
962 if (s != NULL)
963 window_copy_append_selection(wme);
964 window_copy_clear_selection(wme);
965 return (WINDOW_COPY_CMD_CANCEL);
966 }
967
968 static enum window_copy_cmd_action
window_copy_cmd_back_to_indentation(struct window_copy_cmd_state * cs)969 window_copy_cmd_back_to_indentation(struct window_copy_cmd_state *cs)
970 {
971 struct window_mode_entry *wme = cs->wme;
972
973 window_copy_cursor_back_to_indentation(wme);
974 return (WINDOW_COPY_CMD_NOTHING);
975 }
976
977 static enum window_copy_cmd_action
window_copy_cmd_begin_selection(struct window_copy_cmd_state * cs)978 window_copy_cmd_begin_selection(struct window_copy_cmd_state *cs)
979 {
980 struct window_mode_entry *wme = cs->wme;
981 struct client *c = cs->c;
982 struct mouse_event *m = cs->m;
983 struct window_copy_mode_data *data = wme->data;
984
985 if (m != NULL) {
986 window_copy_start_drag(c, m);
987 return (WINDOW_COPY_CMD_NOTHING);
988 }
989
990 data->lineflag = LINE_SEL_NONE;
991 data->selflag = SEL_CHAR;
992 window_copy_start_selection(wme);
993 return (WINDOW_COPY_CMD_REDRAW);
994 }
995
996 static enum window_copy_cmd_action
window_copy_cmd_stop_selection(struct window_copy_cmd_state * cs)997 window_copy_cmd_stop_selection(struct window_copy_cmd_state *cs)
998 {
999 struct window_mode_entry *wme = cs->wme;
1000 struct window_copy_mode_data *data = wme->data;
1001
1002 data->cursordrag = CURSORDRAG_NONE;
1003 data->lineflag = LINE_SEL_NONE;
1004 data->selflag = SEL_CHAR;
1005 return (WINDOW_COPY_CMD_NOTHING);
1006 }
1007
1008 static enum window_copy_cmd_action
window_copy_cmd_bottom_line(struct window_copy_cmd_state * cs)1009 window_copy_cmd_bottom_line(struct window_copy_cmd_state *cs)
1010 {
1011 struct window_mode_entry *wme = cs->wme;
1012 struct window_copy_mode_data *data = wme->data;
1013
1014 data->cx = 0;
1015 data->cy = screen_size_y(&data->screen) - 1;
1016
1017 window_copy_update_selection(wme, 1, 0);
1018 return (WINDOW_COPY_CMD_REDRAW);
1019 }
1020
1021 static enum window_copy_cmd_action
window_copy_cmd_cancel(__unused struct window_copy_cmd_state * cs)1022 window_copy_cmd_cancel(__unused struct window_copy_cmd_state *cs)
1023 {
1024 return (WINDOW_COPY_CMD_CANCEL);
1025 }
1026
1027 static enum window_copy_cmd_action
window_copy_cmd_clear_selection(struct window_copy_cmd_state * cs)1028 window_copy_cmd_clear_selection(struct window_copy_cmd_state *cs)
1029 {
1030 struct window_mode_entry *wme = cs->wme;
1031
1032 window_copy_clear_selection(wme);
1033 return (WINDOW_COPY_CMD_REDRAW);
1034 }
1035
1036 static enum window_copy_cmd_action
window_copy_do_copy_end_of_line(struct window_copy_cmd_state * cs,int pipe,int cancel)1037 window_copy_do_copy_end_of_line(struct window_copy_cmd_state *cs, int pipe,
1038 int cancel)
1039 {
1040 struct window_mode_entry *wme = cs->wme;
1041 struct client *c = cs->c;
1042 struct session *s = cs->s;
1043 struct winlink *wl = cs->wl;
1044 struct window_pane *wp = wme->wp;
1045 u_int count = args_count(cs->args);
1046 u_int np = wme->prefix, ocx, ocy, ooy;
1047 struct window_copy_mode_data *data = wme->data;
1048 char *prefix = NULL, *command = NULL;
1049 const char *arg1 = args_string(cs->args, 1);
1050 const char *arg2 = args_string(cs->args, 2);
1051
1052 if (pipe) {
1053 if (count == 3)
1054 prefix = format_single(NULL, arg2, c, s, wl, wp);
1055 if (s != NULL && count > 1 && *arg1 != '\0')
1056 command = format_single(NULL, arg1, c, s, wl, wp);
1057 } else {
1058 if (count == 2)
1059 prefix = format_single(NULL, arg1, c, s, wl, wp);
1060 }
1061
1062 ocx = data->cx;
1063 ocy = data->cy;
1064 ooy = data->oy;
1065
1066 window_copy_start_selection(wme);
1067 for (; np > 1; np--)
1068 window_copy_cursor_down(wme, 0);
1069 window_copy_cursor_end_of_line(wme);
1070
1071 if (s != NULL) {
1072 if (pipe)
1073 window_copy_copy_pipe(wme, s, prefix, command);
1074 else
1075 window_copy_copy_selection(wme, prefix);
1076
1077 if (cancel) {
1078 free(prefix);
1079 free(command);
1080 return (WINDOW_COPY_CMD_CANCEL);
1081 }
1082 }
1083 window_copy_clear_selection(wme);
1084
1085 data->cx = ocx;
1086 data->cy = ocy;
1087 data->oy = ooy;
1088
1089 free(prefix);
1090 free(command);
1091 return (WINDOW_COPY_CMD_REDRAW);
1092 }
1093
1094 static enum window_copy_cmd_action
window_copy_cmd_copy_end_of_line(struct window_copy_cmd_state * cs)1095 window_copy_cmd_copy_end_of_line(struct window_copy_cmd_state *cs)
1096 {
1097 return (window_copy_do_copy_end_of_line(cs, 0, 0));
1098 }
1099
1100 static enum window_copy_cmd_action
window_copy_cmd_copy_end_of_line_and_cancel(struct window_copy_cmd_state * cs)1101 window_copy_cmd_copy_end_of_line_and_cancel(struct window_copy_cmd_state *cs)
1102 {
1103 return (window_copy_do_copy_end_of_line(cs, 0, 1));
1104 }
1105
1106 static enum window_copy_cmd_action
window_copy_cmd_copy_pipe_end_of_line(struct window_copy_cmd_state * cs)1107 window_copy_cmd_copy_pipe_end_of_line(struct window_copy_cmd_state *cs)
1108 {
1109 return (window_copy_do_copy_end_of_line(cs, 1, 0));
1110 }
1111
1112 static enum window_copy_cmd_action
window_copy_cmd_copy_pipe_end_of_line_and_cancel(struct window_copy_cmd_state * cs)1113 window_copy_cmd_copy_pipe_end_of_line_and_cancel(
1114 struct window_copy_cmd_state *cs)
1115 {
1116 return (window_copy_do_copy_end_of_line(cs, 1, 1));
1117 }
1118
1119 static enum window_copy_cmd_action
window_copy_do_copy_line(struct window_copy_cmd_state * cs,int pipe,int cancel)1120 window_copy_do_copy_line(struct window_copy_cmd_state *cs, int pipe, int cancel)
1121 {
1122 struct window_mode_entry *wme = cs->wme;
1123 struct client *c = cs->c;
1124 struct session *s = cs->s;
1125 struct winlink *wl = cs->wl;
1126 struct window_pane *wp = wme->wp;
1127 struct window_copy_mode_data *data = wme->data;
1128 u_int count = args_count(cs->args);
1129 u_int np = wme->prefix, ocx, ocy, ooy;
1130 char *prefix = NULL, *command = NULL;
1131 const char *arg1 = args_string(cs->args, 1);
1132 const char *arg2 = args_string(cs->args, 2);
1133
1134 if (pipe) {
1135 if (count == 3)
1136 prefix = format_single(NULL, arg2, c, s, wl, wp);
1137 if (s != NULL && count > 1 && *arg1 != '\0')
1138 command = format_single(NULL, arg1, c, s, wl, wp);
1139 } else {
1140 if (count == 2)
1141 prefix = format_single(NULL, arg1, c, s, wl, wp);
1142 }
1143
1144 ocx = data->cx;
1145 ocy = data->cy;
1146 ooy = data->oy;
1147
1148 data->selflag = SEL_CHAR;
1149 window_copy_cursor_start_of_line(wme);
1150 window_copy_start_selection(wme);
1151 for (; np > 1; np--)
1152 window_copy_cursor_down(wme, 0);
1153 window_copy_cursor_end_of_line(wme);
1154
1155 if (s != NULL) {
1156 if (pipe)
1157 window_copy_copy_pipe(wme, s, prefix, command);
1158 else
1159 window_copy_copy_selection(wme, prefix);
1160
1161 if (cancel) {
1162 free(prefix);
1163 free(command);
1164 return (WINDOW_COPY_CMD_CANCEL);
1165 }
1166 }
1167 window_copy_clear_selection(wme);
1168
1169 data->cx = ocx;
1170 data->cy = ocy;
1171 data->oy = ooy;
1172
1173 free(prefix);
1174 free(command);
1175 return (WINDOW_COPY_CMD_REDRAW);
1176 }
1177
1178 static enum window_copy_cmd_action
window_copy_cmd_copy_line(struct window_copy_cmd_state * cs)1179 window_copy_cmd_copy_line(struct window_copy_cmd_state *cs)
1180 {
1181 return (window_copy_do_copy_line(cs, 0, 0));
1182 }
1183
1184 static enum window_copy_cmd_action
window_copy_cmd_copy_line_and_cancel(struct window_copy_cmd_state * cs)1185 window_copy_cmd_copy_line_and_cancel(struct window_copy_cmd_state *cs)
1186 {
1187 return (window_copy_do_copy_line(cs, 0, 1));
1188 }
1189
1190 static enum window_copy_cmd_action
window_copy_cmd_copy_pipe_line(struct window_copy_cmd_state * cs)1191 window_copy_cmd_copy_pipe_line(struct window_copy_cmd_state *cs)
1192 {
1193 return (window_copy_do_copy_line(cs, 1, 0));
1194 }
1195
1196 static enum window_copy_cmd_action
window_copy_cmd_copy_pipe_line_and_cancel(struct window_copy_cmd_state * cs)1197 window_copy_cmd_copy_pipe_line_and_cancel(struct window_copy_cmd_state *cs)
1198 {
1199 return (window_copy_do_copy_line(cs, 1, 1));
1200 }
1201
1202 static enum window_copy_cmd_action
window_copy_cmd_copy_selection_no_clear(struct window_copy_cmd_state * cs)1203 window_copy_cmd_copy_selection_no_clear(struct window_copy_cmd_state *cs)
1204 {
1205 struct window_mode_entry *wme = cs->wme;
1206 struct client *c = cs->c;
1207 struct session *s = cs->s;
1208 struct winlink *wl = cs->wl;
1209 struct window_pane *wp = wme->wp;
1210 char *prefix = NULL;
1211 const char *arg1 = args_string(cs->args, 1);
1212
1213 if (arg1 != NULL)
1214 prefix = format_single(NULL, arg1, c, s, wl, wp);
1215
1216 if (s != NULL)
1217 window_copy_copy_selection(wme, prefix);
1218
1219 free(prefix);
1220 return (WINDOW_COPY_CMD_NOTHING);
1221 }
1222
1223 static enum window_copy_cmd_action
window_copy_cmd_copy_selection(struct window_copy_cmd_state * cs)1224 window_copy_cmd_copy_selection(struct window_copy_cmd_state *cs)
1225 {
1226 struct window_mode_entry *wme = cs->wme;
1227
1228 window_copy_cmd_copy_selection_no_clear(cs);
1229 window_copy_clear_selection(wme);
1230 return (WINDOW_COPY_CMD_REDRAW);
1231 }
1232
1233 static enum window_copy_cmd_action
window_copy_cmd_copy_selection_and_cancel(struct window_copy_cmd_state * cs)1234 window_copy_cmd_copy_selection_and_cancel(struct window_copy_cmd_state *cs)
1235 {
1236 struct window_mode_entry *wme = cs->wme;
1237
1238 window_copy_cmd_copy_selection_no_clear(cs);
1239 window_copy_clear_selection(wme);
1240 return (WINDOW_COPY_CMD_CANCEL);
1241 }
1242
1243 static enum window_copy_cmd_action
window_copy_cmd_cursor_down(struct window_copy_cmd_state * cs)1244 window_copy_cmd_cursor_down(struct window_copy_cmd_state *cs)
1245 {
1246 struct window_mode_entry *wme = cs->wme;
1247 u_int np = wme->prefix;
1248
1249 for (; np != 0; np--)
1250 window_copy_cursor_down(wme, 0);
1251 return (WINDOW_COPY_CMD_NOTHING);
1252 }
1253
1254 static enum window_copy_cmd_action
window_copy_cmd_cursor_down_and_cancel(struct window_copy_cmd_state * cs)1255 window_copy_cmd_cursor_down_and_cancel(struct window_copy_cmd_state *cs)
1256 {
1257 struct window_mode_entry *wme = cs->wme;
1258 struct window_copy_mode_data *data = wme->data;
1259 u_int np = wme->prefix, cy;
1260
1261 cy = data->cy;
1262 for (; np != 0; np--)
1263 window_copy_cursor_down(wme, 0);
1264 if (cy == data->cy && data->oy == 0)
1265 return (WINDOW_COPY_CMD_CANCEL);
1266 return (WINDOW_COPY_CMD_NOTHING);
1267 }
1268
1269 static enum window_copy_cmd_action
window_copy_cmd_cursor_left(struct window_copy_cmd_state * cs)1270 window_copy_cmd_cursor_left(struct window_copy_cmd_state *cs)
1271 {
1272 struct window_mode_entry *wme = cs->wme;
1273 u_int np = wme->prefix;
1274
1275 for (; np != 0; np--)
1276 window_copy_cursor_left(wme);
1277 return (WINDOW_COPY_CMD_NOTHING);
1278 }
1279
1280 static enum window_copy_cmd_action
window_copy_cmd_cursor_right(struct window_copy_cmd_state * cs)1281 window_copy_cmd_cursor_right(struct window_copy_cmd_state *cs)
1282 {
1283 struct window_mode_entry *wme = cs->wme;
1284 struct window_copy_mode_data *data = wme->data;
1285 u_int np = wme->prefix;
1286
1287 for (; np != 0; np--) {
1288 window_copy_cursor_right(wme, data->screen.sel != NULL &&
1289 data->rectflag);
1290 }
1291 return (WINDOW_COPY_CMD_NOTHING);
1292 }
1293
1294 /* Scroll line containing the cursor to the given position. */
1295 static enum window_copy_cmd_action
window_copy_cmd_scroll_to(struct window_copy_cmd_state * cs,u_int to)1296 window_copy_cmd_scroll_to(struct window_copy_cmd_state *cs, u_int to)
1297 {
1298 struct window_mode_entry *wme = cs->wme;
1299 struct window_copy_mode_data *data = wme->data;
1300 u_int oy, delta;
1301 int scroll_up; /* >0 up, <0 down */
1302
1303 scroll_up = data->cy - to;
1304 delta = abs(scroll_up);
1305 oy = screen_hsize(data->backing) - data->oy;
1306
1307 /*
1308 * oy is the maximum scroll down amount, while data->oy is the maximum
1309 * scroll up amount.
1310 */
1311 if (scroll_up > 0 && data->oy >= delta) {
1312 window_copy_scroll_up(wme, delta);
1313 data->cy -= delta;
1314 } else if (scroll_up < 0 && oy >= delta) {
1315 window_copy_scroll_down(wme, delta);
1316 data->cy += delta;
1317 }
1318
1319 window_copy_update_selection(wme, 0, 0);
1320 return (WINDOW_COPY_CMD_REDRAW);
1321 }
1322
1323 /* Scroll line containing the cursor to the bottom. */
1324 static enum window_copy_cmd_action
window_copy_cmd_scroll_bottom(struct window_copy_cmd_state * cs)1325 window_copy_cmd_scroll_bottom(struct window_copy_cmd_state *cs)
1326 {
1327 struct window_copy_mode_data *data = cs->wme->data;
1328 u_int bottom;
1329
1330 bottom = screen_size_y(&data->screen) - 1;
1331 return (window_copy_cmd_scroll_to(cs, bottom));
1332 }
1333
1334 /* Scroll line containing the cursor to the middle. */
1335 static enum window_copy_cmd_action
window_copy_cmd_scroll_middle(struct window_copy_cmd_state * cs)1336 window_copy_cmd_scroll_middle(struct window_copy_cmd_state *cs)
1337 {
1338 struct window_copy_mode_data *data = cs->wme->data;
1339 u_int mid_value;
1340
1341 mid_value = (screen_size_y(&data->screen) - 1) / 2;
1342 return (window_copy_cmd_scroll_to(cs, mid_value));
1343 }
1344
1345 /* Scroll line containing the cursor to the top. */
1346 static enum window_copy_cmd_action
window_copy_cmd_scroll_top(struct window_copy_cmd_state * cs)1347 window_copy_cmd_scroll_top(struct window_copy_cmd_state *cs)
1348 {
1349 return (window_copy_cmd_scroll_to(cs, 0));
1350 }
1351
1352 static enum window_copy_cmd_action
window_copy_cmd_cursor_up(struct window_copy_cmd_state * cs)1353 window_copy_cmd_cursor_up(struct window_copy_cmd_state *cs)
1354 {
1355 struct window_mode_entry *wme = cs->wme;
1356 u_int np = wme->prefix;
1357
1358 for (; np != 0; np--)
1359 window_copy_cursor_up(wme, 0);
1360 return (WINDOW_COPY_CMD_NOTHING);
1361 }
1362
1363 static enum window_copy_cmd_action
window_copy_cmd_end_of_line(struct window_copy_cmd_state * cs)1364 window_copy_cmd_end_of_line(struct window_copy_cmd_state *cs)
1365 {
1366 struct window_mode_entry *wme = cs->wme;
1367
1368 window_copy_cursor_end_of_line(wme);
1369 return (WINDOW_COPY_CMD_NOTHING);
1370 }
1371
1372 static enum window_copy_cmd_action
window_copy_cmd_halfpage_down(struct window_copy_cmd_state * cs)1373 window_copy_cmd_halfpage_down(struct window_copy_cmd_state *cs)
1374 {
1375 struct window_mode_entry *wme = cs->wme;
1376 struct window_copy_mode_data *data = wme->data;
1377 u_int np = wme->prefix;
1378
1379 for (; np != 0; np--) {
1380 if (window_copy_pagedown1(wme, 1, data->scroll_exit))
1381 return (WINDOW_COPY_CMD_CANCEL);
1382 }
1383 return (WINDOW_COPY_CMD_NOTHING);
1384 }
1385
1386 static enum window_copy_cmd_action
window_copy_cmd_halfpage_down_and_cancel(struct window_copy_cmd_state * cs)1387 window_copy_cmd_halfpage_down_and_cancel(struct window_copy_cmd_state *cs)
1388 {
1389
1390 struct window_mode_entry *wme = cs->wme;
1391 u_int np = wme->prefix;
1392
1393 for (; np != 0; np--) {
1394 if (window_copy_pagedown1(wme, 1, 1))
1395 return (WINDOW_COPY_CMD_CANCEL);
1396 }
1397 return (WINDOW_COPY_CMD_NOTHING);
1398 }
1399
1400 static enum window_copy_cmd_action
window_copy_cmd_halfpage_up(struct window_copy_cmd_state * cs)1401 window_copy_cmd_halfpage_up(struct window_copy_cmd_state *cs)
1402 {
1403 struct window_mode_entry *wme = cs->wme;
1404 u_int np = wme->prefix;
1405
1406 for (; np != 0; np--)
1407 window_copy_pageup1(wme, 1);
1408 return (WINDOW_COPY_CMD_NOTHING);
1409 }
1410
1411 static enum window_copy_cmd_action
window_copy_cmd_toggle_position(struct window_copy_cmd_state * cs)1412 window_copy_cmd_toggle_position(struct window_copy_cmd_state *cs)
1413 {
1414 struct window_mode_entry *wme = cs->wme;
1415 struct window_copy_mode_data *data = wme->data;
1416
1417 data->hide_position = !data->hide_position;
1418 return (WINDOW_COPY_CMD_REDRAW);
1419 }
1420
1421 static enum window_copy_cmd_action
window_copy_cmd_history_bottom(struct window_copy_cmd_state * cs)1422 window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs)
1423 {
1424 struct window_mode_entry *wme = cs->wme;
1425 struct window_copy_mode_data *data = wme->data;
1426 struct screen *s = data->backing;
1427 u_int oy;
1428
1429 oy = screen_hsize(s) + data->cy - data->oy;
1430 if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely)
1431 window_copy_other_end(wme);
1432
1433 data->cy = screen_size_y(&data->screen) - 1;
1434 data->cx = window_copy_find_length(wme, screen_hsize(s) + data->cy);
1435 data->oy = 0;
1436
1437 if (data->searchmark != NULL && !data->timeout)
1438 window_copy_search_marks(wme, NULL, data->searchregex, 1);
1439 window_copy_update_selection(wme, 1, 0);
1440 return (WINDOW_COPY_CMD_REDRAW);
1441 }
1442
1443 static enum window_copy_cmd_action
window_copy_cmd_history_top(struct window_copy_cmd_state * cs)1444 window_copy_cmd_history_top(struct window_copy_cmd_state *cs)
1445 {
1446 struct window_mode_entry *wme = cs->wme;
1447 struct window_copy_mode_data *data = wme->data;
1448 u_int oy;
1449
1450 oy = screen_hsize(data->backing) + data->cy - data->oy;
1451 if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely)
1452 window_copy_other_end(wme);
1453
1454 data->cy = 0;
1455 data->cx = 0;
1456 data->oy = screen_hsize(data->backing);
1457
1458 if (data->searchmark != NULL && !data->timeout)
1459 window_copy_search_marks(wme, NULL, data->searchregex, 1);
1460 window_copy_update_selection(wme, 1, 0);
1461 return (WINDOW_COPY_CMD_REDRAW);
1462 }
1463
1464 static enum window_copy_cmd_action
window_copy_cmd_jump_again(struct window_copy_cmd_state * cs)1465 window_copy_cmd_jump_again(struct window_copy_cmd_state *cs)
1466 {
1467 struct window_mode_entry *wme = cs->wme;
1468 struct window_copy_mode_data *data = wme->data;
1469 u_int np = wme->prefix;
1470
1471 switch (data->jumptype) {
1472 case WINDOW_COPY_JUMPFORWARD:
1473 for (; np != 0; np--)
1474 window_copy_cursor_jump(wme);
1475 break;
1476 case WINDOW_COPY_JUMPBACKWARD:
1477 for (; np != 0; np--)
1478 window_copy_cursor_jump_back(wme);
1479 break;
1480 case WINDOW_COPY_JUMPTOFORWARD:
1481 for (; np != 0; np--)
1482 window_copy_cursor_jump_to(wme);
1483 break;
1484 case WINDOW_COPY_JUMPTOBACKWARD:
1485 for (; np != 0; np--)
1486 window_copy_cursor_jump_to_back(wme);
1487 break;
1488 }
1489 return (WINDOW_COPY_CMD_NOTHING);
1490 }
1491
1492 static enum window_copy_cmd_action
window_copy_cmd_jump_reverse(struct window_copy_cmd_state * cs)1493 window_copy_cmd_jump_reverse(struct window_copy_cmd_state *cs)
1494 {
1495 struct window_mode_entry *wme = cs->wme;
1496 struct window_copy_mode_data *data = wme->data;
1497 u_int np = wme->prefix;
1498
1499 switch (data->jumptype) {
1500 case WINDOW_COPY_JUMPFORWARD:
1501 for (; np != 0; np--)
1502 window_copy_cursor_jump_back(wme);
1503 break;
1504 case WINDOW_COPY_JUMPBACKWARD:
1505 for (; np != 0; np--)
1506 window_copy_cursor_jump(wme);
1507 break;
1508 case WINDOW_COPY_JUMPTOFORWARD:
1509 for (; np != 0; np--)
1510 window_copy_cursor_jump_to_back(wme);
1511 break;
1512 case WINDOW_COPY_JUMPTOBACKWARD:
1513 for (; np != 0; np--)
1514 window_copy_cursor_jump_to(wme);
1515 break;
1516 }
1517 return (WINDOW_COPY_CMD_NOTHING);
1518 }
1519
1520 static enum window_copy_cmd_action
window_copy_cmd_middle_line(struct window_copy_cmd_state * cs)1521 window_copy_cmd_middle_line(struct window_copy_cmd_state *cs)
1522 {
1523 struct window_mode_entry *wme = cs->wme;
1524 struct window_copy_mode_data *data = wme->data;
1525
1526 data->cx = 0;
1527 data->cy = (screen_size_y(&data->screen) - 1) / 2;
1528
1529 window_copy_update_selection(wme, 1, 0);
1530 return (WINDOW_COPY_CMD_REDRAW);
1531 }
1532
1533 static enum window_copy_cmd_action
window_copy_cmd_previous_matching_bracket(struct window_copy_cmd_state * cs)1534 window_copy_cmd_previous_matching_bracket(struct window_copy_cmd_state *cs)
1535 {
1536 struct window_mode_entry *wme = cs->wme;
1537 u_int np = wme->prefix;
1538 struct window_copy_mode_data *data = wme->data;
1539 struct screen *s = data->backing;
1540 char open[] = "{[(", close[] = "}])";
1541 char tried, found, start, *cp;
1542 u_int px, py, xx, n;
1543 struct grid_cell gc;
1544 int failed;
1545
1546 for (; np != 0; np--) {
1547 /* Get cursor position and line length. */
1548 px = data->cx;
1549 py = screen_hsize(s) + data->cy - data->oy;
1550 xx = window_copy_find_length(wme, py);
1551 if (xx == 0)
1552 break;
1553
1554 /*
1555 * Get the current character. If not on a bracket, try the
1556 * previous. If still not, then behave like previous-word.
1557 */
1558 tried = 0;
1559 retry:
1560 grid_get_cell(s->grid, px, py, &gc);
1561 if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING))
1562 cp = NULL;
1563 else {
1564 found = *gc.data.data;
1565 cp = strchr(close, found);
1566 }
1567 if (cp == NULL) {
1568 if (data->modekeys == MODEKEY_EMACS) {
1569 if (!tried && px > 0) {
1570 px--;
1571 tried = 1;
1572 goto retry;
1573 }
1574 window_copy_cursor_previous_word(wme, close, 1);
1575 }
1576 continue;
1577 }
1578 start = open[cp - close];
1579
1580 /* Walk backward until the matching bracket is reached. */
1581 n = 1;
1582 failed = 0;
1583 do {
1584 if (px == 0) {
1585 if (py == 0) {
1586 failed = 1;
1587 break;
1588 }
1589 do {
1590 py--;
1591 xx = window_copy_find_length(wme, py);
1592 } while (xx == 0 && py > 0);
1593 if (xx == 0 && py == 0) {
1594 failed = 1;
1595 break;
1596 }
1597 px = xx - 1;
1598 } else
1599 px--;
1600
1601 grid_get_cell(s->grid, px, py, &gc);
1602 if (gc.data.size == 1 &&
1603 (~gc.flags & GRID_FLAG_PADDING)) {
1604 if (*gc.data.data == found)
1605 n++;
1606 else if (*gc.data.data == start)
1607 n--;
1608 }
1609 } while (n != 0);
1610
1611 /* Move the cursor to the found location if any. */
1612 if (!failed)
1613 window_copy_scroll_to(wme, px, py, 0);
1614 }
1615
1616 return (WINDOW_COPY_CMD_NOTHING);
1617 }
1618
1619 static enum window_copy_cmd_action
window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state * cs)1620 window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state *cs)
1621 {
1622 struct window_mode_entry *wme = cs->wme;
1623 u_int np = wme->prefix;
1624 struct window_copy_mode_data *data = wme->data;
1625 struct screen *s = data->backing;
1626 char open[] = "{[(", close[] = "}])";
1627 char tried, found, end, *cp;
1628 u_int px, py, xx, yy, sx, sy, n;
1629 struct grid_cell gc;
1630 int failed;
1631 struct grid_line *gl;
1632
1633 for (; np != 0; np--) {
1634 /* Get cursor position and line length. */
1635 px = data->cx;
1636 py = screen_hsize(s) + data->cy - data->oy;
1637 xx = window_copy_find_length(wme, py);
1638 yy = screen_hsize(s) + screen_size_y(s) - 1;
1639 if (xx == 0)
1640 break;
1641
1642 /*
1643 * Get the current character. If not on a bracket, try the
1644 * next. If still not, then behave like next-word.
1645 */
1646 tried = 0;
1647 retry:
1648 grid_get_cell(s->grid, px, py, &gc);
1649 if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING))
1650 cp = NULL;
1651 else {
1652 found = *gc.data.data;
1653
1654 /*
1655 * In vi mode, attempt to move to previous bracket if a
1656 * closing bracket is found first. If this fails,
1657 * return to the original cursor position.
1658 */
1659 cp = strchr(close, found);
1660 if (cp != NULL && data->modekeys == MODEKEY_VI) {
1661 sx = data->cx;
1662 sy = screen_hsize(s) + data->cy - data->oy;
1663
1664 window_copy_scroll_to(wme, px, py, 0);
1665 window_copy_cmd_previous_matching_bracket(cs);
1666
1667 px = data->cx;
1668 py = screen_hsize(s) + data->cy - data->oy;
1669 grid_get_cell(s->grid, px, py, &gc);
1670 if (gc.data.size == 1 &&
1671 (~gc.flags & GRID_FLAG_PADDING) &&
1672 strchr(close, *gc.data.data) != NULL)
1673 window_copy_scroll_to(wme, sx, sy, 0);
1674 break;
1675 }
1676
1677 cp = strchr(open, found);
1678 }
1679 if (cp == NULL) {
1680 if (data->modekeys == MODEKEY_EMACS) {
1681 if (!tried && px <= xx) {
1682 px++;
1683 tried = 1;
1684 goto retry;
1685 }
1686 window_copy_cursor_next_word_end(wme, open, 0);
1687 continue;
1688 }
1689 /* For vi, continue searching for bracket until EOL. */
1690 if (px > xx) {
1691 if (py == yy)
1692 continue;
1693 gl = grid_get_line(s->grid, py);
1694 if (~gl->flags & GRID_LINE_WRAPPED)
1695 continue;
1696 if (gl->cellsize > s->grid->sx)
1697 continue;
1698 px = 0;
1699 py++;
1700 xx = window_copy_find_length(wme, py);
1701 } else
1702 px++;
1703 goto retry;
1704 }
1705 end = close[cp - open];
1706
1707 /* Walk forward until the matching bracket is reached. */
1708 n = 1;
1709 failed = 0;
1710 do {
1711 if (px > xx) {
1712 if (py == yy) {
1713 failed = 1;
1714 break;
1715 }
1716 px = 0;
1717 py++;
1718 xx = window_copy_find_length(wme, py);
1719 } else
1720 px++;
1721
1722 grid_get_cell(s->grid, px, py, &gc);
1723 if (gc.data.size == 1 &&
1724 (~gc.flags & GRID_FLAG_PADDING)) {
1725 if (*gc.data.data == found)
1726 n++;
1727 else if (*gc.data.data == end)
1728 n--;
1729 }
1730 } while (n != 0);
1731
1732 /* Move the cursor to the found location if any. */
1733 if (!failed)
1734 window_copy_scroll_to(wme, px, py, 0);
1735 }
1736
1737 return (WINDOW_COPY_CMD_NOTHING);
1738 }
1739
1740 static enum window_copy_cmd_action
window_copy_cmd_next_paragraph(struct window_copy_cmd_state * cs)1741 window_copy_cmd_next_paragraph(struct window_copy_cmd_state *cs)
1742 {
1743 struct window_mode_entry *wme = cs->wme;
1744 u_int np = wme->prefix;
1745
1746 for (; np != 0; np--)
1747 window_copy_next_paragraph(wme);
1748 return (WINDOW_COPY_CMD_NOTHING);
1749 }
1750
1751 static enum window_copy_cmd_action
window_copy_cmd_next_space(struct window_copy_cmd_state * cs)1752 window_copy_cmd_next_space(struct window_copy_cmd_state *cs)
1753 {
1754 struct window_mode_entry *wme = cs->wme;
1755 u_int np = wme->prefix;
1756
1757 for (; np != 0; np--)
1758 window_copy_cursor_next_word(wme, "");
1759 return (WINDOW_COPY_CMD_NOTHING);
1760 }
1761
1762 static enum window_copy_cmd_action
window_copy_cmd_next_space_end(struct window_copy_cmd_state * cs)1763 window_copy_cmd_next_space_end(struct window_copy_cmd_state *cs)
1764 {
1765 struct window_mode_entry *wme = cs->wme;
1766 u_int np = wme->prefix;
1767
1768 for (; np != 0; np--)
1769 window_copy_cursor_next_word_end(wme, "", 0);
1770 return (WINDOW_COPY_CMD_NOTHING);
1771 }
1772
1773 static enum window_copy_cmd_action
window_copy_cmd_next_word(struct window_copy_cmd_state * cs)1774 window_copy_cmd_next_word(struct window_copy_cmd_state *cs)
1775 {
1776 struct window_mode_entry *wme = cs->wme;
1777 u_int np = wme->prefix;
1778 const char *separators;
1779
1780 separators = options_get_string(cs->s->options, "word-separators");
1781
1782 for (; np != 0; np--)
1783 window_copy_cursor_next_word(wme, separators);
1784 return (WINDOW_COPY_CMD_NOTHING);
1785 }
1786
1787 static enum window_copy_cmd_action
window_copy_cmd_next_word_end(struct window_copy_cmd_state * cs)1788 window_copy_cmd_next_word_end(struct window_copy_cmd_state *cs)
1789 {
1790 struct window_mode_entry *wme = cs->wme;
1791 u_int np = wme->prefix;
1792 const char *separators;
1793
1794 separators = options_get_string(cs->s->options, "word-separators");
1795
1796 for (; np != 0; np--)
1797 window_copy_cursor_next_word_end(wme, separators, 0);
1798 return (WINDOW_COPY_CMD_NOTHING);
1799 }
1800
1801 static enum window_copy_cmd_action
window_copy_cmd_other_end(struct window_copy_cmd_state * cs)1802 window_copy_cmd_other_end(struct window_copy_cmd_state *cs)
1803 {
1804 struct window_mode_entry *wme = cs->wme;
1805 u_int np = wme->prefix;
1806 struct window_copy_mode_data *data = wme->data;
1807
1808 data->selflag = SEL_CHAR;
1809 if ((np % 2) != 0)
1810 window_copy_other_end(wme);
1811 return (WINDOW_COPY_CMD_NOTHING);
1812 }
1813
1814 static enum window_copy_cmd_action
window_copy_cmd_page_down(struct window_copy_cmd_state * cs)1815 window_copy_cmd_page_down(struct window_copy_cmd_state *cs)
1816 {
1817 struct window_mode_entry *wme = cs->wme;
1818 struct window_copy_mode_data *data = wme->data;
1819 u_int np = wme->prefix;
1820
1821 for (; np != 0; np--) {
1822 if (window_copy_pagedown1(wme, 0, data->scroll_exit))
1823 return (WINDOW_COPY_CMD_CANCEL);
1824 }
1825 return (WINDOW_COPY_CMD_NOTHING);
1826 }
1827
1828 static enum window_copy_cmd_action
window_copy_cmd_page_down_and_cancel(struct window_copy_cmd_state * cs)1829 window_copy_cmd_page_down_and_cancel(struct window_copy_cmd_state *cs)
1830 {
1831 struct window_mode_entry *wme = cs->wme;
1832 u_int np = wme->prefix;
1833
1834 for (; np != 0; np--) {
1835 if (window_copy_pagedown1(wme, 0, 1))
1836 return (WINDOW_COPY_CMD_CANCEL);
1837 }
1838 return (WINDOW_COPY_CMD_NOTHING);
1839 }
1840
1841 static enum window_copy_cmd_action
window_copy_cmd_page_up(struct window_copy_cmd_state * cs)1842 window_copy_cmd_page_up(struct window_copy_cmd_state *cs)
1843 {
1844 struct window_mode_entry *wme = cs->wme;
1845 u_int np = wme->prefix;
1846
1847 for (; np != 0; np--)
1848 window_copy_pageup1(wme, 0);
1849 return (WINDOW_COPY_CMD_NOTHING);
1850 }
1851
1852 static enum window_copy_cmd_action
window_copy_cmd_previous_paragraph(struct window_copy_cmd_state * cs)1853 window_copy_cmd_previous_paragraph(struct window_copy_cmd_state *cs)
1854 {
1855 struct window_mode_entry *wme = cs->wme;
1856 u_int np = wme->prefix;
1857
1858 for (; np != 0; np--)
1859 window_copy_previous_paragraph(wme);
1860 return (WINDOW_COPY_CMD_NOTHING);
1861 }
1862
1863 static enum window_copy_cmd_action
window_copy_cmd_previous_space(struct window_copy_cmd_state * cs)1864 window_copy_cmd_previous_space(struct window_copy_cmd_state *cs)
1865 {
1866 struct window_mode_entry *wme = cs->wme;
1867 u_int np = wme->prefix;
1868
1869 for (; np != 0; np--)
1870 window_copy_cursor_previous_word(wme, "", 1);
1871 return (WINDOW_COPY_CMD_NOTHING);
1872 }
1873
1874 static enum window_copy_cmd_action
window_copy_cmd_previous_word(struct window_copy_cmd_state * cs)1875 window_copy_cmd_previous_word(struct window_copy_cmd_state *cs)
1876 {
1877 struct window_mode_entry *wme = cs->wme;
1878 u_int np = wme->prefix;
1879 const char *separators;
1880
1881 separators = options_get_string(cs->s->options, "word-separators");
1882
1883 for (; np != 0; np--)
1884 window_copy_cursor_previous_word(wme, separators, 1);
1885 return (WINDOW_COPY_CMD_NOTHING);
1886 }
1887
1888 static enum window_copy_cmd_action
window_copy_cmd_rectangle_on(struct window_copy_cmd_state * cs)1889 window_copy_cmd_rectangle_on(struct window_copy_cmd_state *cs)
1890 {
1891 struct window_mode_entry *wme = cs->wme;
1892 struct window_copy_mode_data *data = wme->data;
1893
1894 data->lineflag = LINE_SEL_NONE;
1895 window_copy_rectangle_set(wme, 1);
1896
1897 return (WINDOW_COPY_CMD_NOTHING);
1898 }
1899
1900 static enum window_copy_cmd_action
window_copy_cmd_rectangle_off(struct window_copy_cmd_state * cs)1901 window_copy_cmd_rectangle_off(struct window_copy_cmd_state *cs)
1902 {
1903 struct window_mode_entry *wme = cs->wme;
1904 struct window_copy_mode_data *data = wme->data;
1905
1906 data->lineflag = LINE_SEL_NONE;
1907 window_copy_rectangle_set(wme, 0);
1908
1909 return (WINDOW_COPY_CMD_NOTHING);
1910 }
1911
1912 static enum window_copy_cmd_action
window_copy_cmd_rectangle_toggle(struct window_copy_cmd_state * cs)1913 window_copy_cmd_rectangle_toggle(struct window_copy_cmd_state *cs)
1914 {
1915 struct window_mode_entry *wme = cs->wme;
1916 struct window_copy_mode_data *data = wme->data;
1917
1918 data->lineflag = LINE_SEL_NONE;
1919 window_copy_rectangle_set(wme, !data->rectflag);
1920
1921 return (WINDOW_COPY_CMD_NOTHING);
1922 }
1923
1924 static enum window_copy_cmd_action
window_copy_cmd_scroll_down(struct window_copy_cmd_state * cs)1925 window_copy_cmd_scroll_down(struct window_copy_cmd_state *cs)
1926 {
1927 struct window_mode_entry *wme = cs->wme;
1928 struct window_copy_mode_data *data = wme->data;
1929 u_int np = wme->prefix;
1930
1931 for (; np != 0; np--)
1932 window_copy_cursor_down(wme, 1);
1933 if (data->scroll_exit && data->oy == 0)
1934 return (WINDOW_COPY_CMD_CANCEL);
1935 return (WINDOW_COPY_CMD_NOTHING);
1936 }
1937
1938 static enum window_copy_cmd_action
window_copy_cmd_scroll_down_and_cancel(struct window_copy_cmd_state * cs)1939 window_copy_cmd_scroll_down_and_cancel(struct window_copy_cmd_state *cs)
1940 {
1941 struct window_mode_entry *wme = cs->wme;
1942 struct window_copy_mode_data *data = wme->data;
1943 u_int np = wme->prefix;
1944
1945 for (; np != 0; np--)
1946 window_copy_cursor_down(wme, 1);
1947 if (data->oy == 0)
1948 return (WINDOW_COPY_CMD_CANCEL);
1949 return (WINDOW_COPY_CMD_NOTHING);
1950 }
1951
1952 static enum window_copy_cmd_action
window_copy_cmd_scroll_up(struct window_copy_cmd_state * cs)1953 window_copy_cmd_scroll_up(struct window_copy_cmd_state *cs)
1954 {
1955 struct window_mode_entry *wme = cs->wme;
1956 u_int np = wme->prefix;
1957
1958 for (; np != 0; np--)
1959 window_copy_cursor_up(wme, 1);
1960 return (WINDOW_COPY_CMD_NOTHING);
1961 }
1962
1963 static enum window_copy_cmd_action
window_copy_cmd_search_again(struct window_copy_cmd_state * cs)1964 window_copy_cmd_search_again(struct window_copy_cmd_state *cs)
1965 {
1966 struct window_mode_entry *wme = cs->wme;
1967 struct window_copy_mode_data *data = wme->data;
1968 u_int np = wme->prefix;
1969
1970 if (data->searchtype == WINDOW_COPY_SEARCHUP) {
1971 for (; np != 0; np--)
1972 window_copy_search_up(wme, data->searchregex);
1973 } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
1974 for (; np != 0; np--)
1975 window_copy_search_down(wme, data->searchregex);
1976 }
1977 return (WINDOW_COPY_CMD_NOTHING);
1978 }
1979
1980 static enum window_copy_cmd_action
window_copy_cmd_search_reverse(struct window_copy_cmd_state * cs)1981 window_copy_cmd_search_reverse(struct window_copy_cmd_state *cs)
1982 {
1983 struct window_mode_entry *wme = cs->wme;
1984 struct window_copy_mode_data *data = wme->data;
1985 u_int np = wme->prefix;
1986
1987 if (data->searchtype == WINDOW_COPY_SEARCHUP) {
1988 for (; np != 0; np--)
1989 window_copy_search_down(wme, data->searchregex);
1990 } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
1991 for (; np != 0; np--)
1992 window_copy_search_up(wme, data->searchregex);
1993 }
1994 return (WINDOW_COPY_CMD_NOTHING);
1995 }
1996
1997 static enum window_copy_cmd_action
window_copy_cmd_select_line(struct window_copy_cmd_state * cs)1998 window_copy_cmd_select_line(struct window_copy_cmd_state *cs)
1999 {
2000 struct window_mode_entry *wme = cs->wme;
2001 struct window_copy_mode_data *data = wme->data;
2002 u_int np = wme->prefix;
2003
2004 data->lineflag = LINE_SEL_LEFT_RIGHT;
2005 data->rectflag = 0;
2006 data->selflag = SEL_LINE;
2007 data->dx = data->cx;
2008 data->dy = screen_hsize(data->backing) + data->cy - data->oy;
2009
2010 window_copy_cursor_start_of_line(wme);
2011 data->selrx = data->cx;
2012 data->selry = screen_hsize(data->backing) + data->cy - data->oy;
2013 data->endselry = data->selry;
2014 window_copy_start_selection(wme);
2015 window_copy_cursor_end_of_line(wme);
2016 data->endselry = screen_hsize(data->backing) + data->cy - data->oy;
2017 data->endselrx = window_copy_find_length(wme, data->endselry);
2018 for (; np > 1; np--) {
2019 window_copy_cursor_down(wme, 0);
2020 window_copy_cursor_end_of_line(wme);
2021 }
2022
2023 return (WINDOW_COPY_CMD_REDRAW);
2024 }
2025
2026 static enum window_copy_cmd_action
window_copy_cmd_select_word(struct window_copy_cmd_state * cs)2027 window_copy_cmd_select_word(struct window_copy_cmd_state *cs)
2028 {
2029 struct window_mode_entry *wme = cs->wme;
2030 struct options *session_options = cs->s->options;
2031 struct window_copy_mode_data *data = wme->data;
2032 u_int px, py, nextx, nexty;
2033
2034 data->lineflag = LINE_SEL_LEFT_RIGHT;
2035 data->rectflag = 0;
2036 data->selflag = SEL_WORD;
2037 data->dx = data->cx;
2038 data->dy = screen_hsize(data->backing) + data->cy - data->oy;
2039
2040 data->separators = options_get_string(session_options,
2041 "word-separators");
2042 window_copy_cursor_previous_word(wme, data->separators, 0);
2043 px = data->cx;
2044 py = screen_hsize(data->backing) + data->cy - data->oy;
2045 data->selrx = px;
2046 data->selry = py;
2047 window_copy_start_selection(wme);
2048
2049 /* Handle single character words. */
2050 nextx = px + 1;
2051 nexty = py;
2052 if (grid_get_line(data->backing->grid, nexty)->flags &
2053 GRID_LINE_WRAPPED && nextx > screen_size_x(data->backing) - 1) {
2054 nextx = 0;
2055 nexty++;
2056 }
2057 if (px >= window_copy_find_length(wme, py) ||
2058 !window_copy_in_set(wme, nextx, nexty, WHITESPACE))
2059 window_copy_cursor_next_word_end(wme, data->separators, 1);
2060 else {
2061 window_copy_update_cursor(wme, px, data->cy);
2062 if (window_copy_update_selection(wme, 1, 1))
2063 window_copy_redraw_lines(wme, data->cy, 1);
2064 }
2065 data->endselrx = data->cx;
2066 data->endselry = screen_hsize(data->backing) + data->cy - data->oy;
2067 if (data->dy > data->endselry) {
2068 data->dy = data->endselry;
2069 data->dx = data->endselrx;
2070 } else if (data->dx > data->endselrx)
2071 data->dx = data->endselrx;
2072
2073 return (WINDOW_COPY_CMD_REDRAW);
2074 }
2075
2076 static enum window_copy_cmd_action
window_copy_cmd_set_mark(struct window_copy_cmd_state * cs)2077 window_copy_cmd_set_mark(struct window_copy_cmd_state *cs)
2078 {
2079 struct window_copy_mode_data *data = cs->wme->data;
2080
2081 data->mx = data->cx;
2082 data->my = screen_hsize(data->backing) + data->cy - data->oy;
2083 data->showmark = 1;
2084 return (WINDOW_COPY_CMD_REDRAW);
2085 }
2086
2087 static enum window_copy_cmd_action
window_copy_cmd_start_of_line(struct window_copy_cmd_state * cs)2088 window_copy_cmd_start_of_line(struct window_copy_cmd_state *cs)
2089 {
2090 struct window_mode_entry *wme = cs->wme;
2091
2092 window_copy_cursor_start_of_line(wme);
2093 return (WINDOW_COPY_CMD_NOTHING);
2094 }
2095
2096 static enum window_copy_cmd_action
window_copy_cmd_top_line(struct window_copy_cmd_state * cs)2097 window_copy_cmd_top_line(struct window_copy_cmd_state *cs)
2098 {
2099 struct window_mode_entry *wme = cs->wme;
2100 struct window_copy_mode_data *data = wme->data;
2101
2102 data->cx = 0;
2103 data->cy = 0;
2104
2105 window_copy_update_selection(wme, 1, 0);
2106 return (WINDOW_COPY_CMD_REDRAW);
2107 }
2108
2109 static enum window_copy_cmd_action
window_copy_cmd_copy_pipe_no_clear(struct window_copy_cmd_state * cs)2110 window_copy_cmd_copy_pipe_no_clear(struct window_copy_cmd_state *cs)
2111 {
2112 struct window_mode_entry *wme = cs->wme;
2113 struct client *c = cs->c;
2114 struct session *s = cs->s;
2115 struct winlink *wl = cs->wl;
2116 struct window_pane *wp = wme->wp;
2117 char *command = NULL, *prefix = NULL;
2118 const char *arg1 = args_string(cs->args, 1);
2119 const char *arg2 = args_string(cs->args, 2);
2120
2121 if (arg2 != NULL)
2122 prefix = format_single(NULL, arg2, c, s, wl, wp);
2123
2124 if (s != NULL && arg1 != NULL && *arg1 != '\0')
2125 command = format_single(NULL, arg1, c, s, wl, wp);
2126 window_copy_copy_pipe(wme, s, prefix, command);
2127 free(command);
2128
2129 free(prefix);
2130 return (WINDOW_COPY_CMD_NOTHING);
2131 }
2132
2133 static enum window_copy_cmd_action
window_copy_cmd_copy_pipe(struct window_copy_cmd_state * cs)2134 window_copy_cmd_copy_pipe(struct window_copy_cmd_state *cs)
2135 {
2136 struct window_mode_entry *wme = cs->wme;
2137
2138 window_copy_cmd_copy_pipe_no_clear(cs);
2139 window_copy_clear_selection(wme);
2140 return (WINDOW_COPY_CMD_REDRAW);
2141 }
2142
2143 static enum window_copy_cmd_action
window_copy_cmd_copy_pipe_and_cancel(struct window_copy_cmd_state * cs)2144 window_copy_cmd_copy_pipe_and_cancel(struct window_copy_cmd_state *cs)
2145 {
2146 struct window_mode_entry *wme = cs->wme;
2147
2148 window_copy_cmd_copy_pipe_no_clear(cs);
2149 window_copy_clear_selection(wme);
2150 return (WINDOW_COPY_CMD_CANCEL);
2151 }
2152
2153 static enum window_copy_cmd_action
window_copy_cmd_pipe_no_clear(struct window_copy_cmd_state * cs)2154 window_copy_cmd_pipe_no_clear(struct window_copy_cmd_state *cs)
2155 {
2156 struct window_mode_entry *wme = cs->wme;
2157 struct client *c = cs->c;
2158 struct session *s = cs->s;
2159 struct winlink *wl = cs->wl;
2160 struct window_pane *wp = wme->wp;
2161 char *command = NULL;
2162 const char *arg1 = args_string(cs->args, 1);
2163
2164 if (s != NULL && arg1 != NULL && *arg1 != '\0')
2165 command = format_single(NULL, arg1, c, s, wl, wp);
2166 window_copy_pipe(wme, s, command);
2167 free(command);
2168
2169 return (WINDOW_COPY_CMD_NOTHING);
2170 }
2171
2172 static enum window_copy_cmd_action
window_copy_cmd_pipe(struct window_copy_cmd_state * cs)2173 window_copy_cmd_pipe(struct window_copy_cmd_state *cs)
2174 {
2175 struct window_mode_entry *wme = cs->wme;
2176
2177 window_copy_cmd_pipe_no_clear(cs);
2178 window_copy_clear_selection(wme);
2179 return (WINDOW_COPY_CMD_REDRAW);
2180 }
2181
2182 static enum window_copy_cmd_action
window_copy_cmd_pipe_and_cancel(struct window_copy_cmd_state * cs)2183 window_copy_cmd_pipe_and_cancel(struct window_copy_cmd_state *cs)
2184 {
2185 struct window_mode_entry *wme = cs->wme;
2186
2187 window_copy_cmd_pipe_no_clear(cs);
2188 window_copy_clear_selection(wme);
2189 return (WINDOW_COPY_CMD_CANCEL);
2190 }
2191
2192 static enum window_copy_cmd_action
window_copy_cmd_goto_line(struct window_copy_cmd_state * cs)2193 window_copy_cmd_goto_line(struct window_copy_cmd_state *cs)
2194 {
2195 struct window_mode_entry *wme = cs->wme;
2196 const char *arg1 = args_string(cs->args, 1);
2197
2198 if (*arg1 != '\0')
2199 window_copy_goto_line(wme, arg1);
2200 return (WINDOW_COPY_CMD_NOTHING);
2201 }
2202
2203 static enum window_copy_cmd_action
window_copy_cmd_jump_backward(struct window_copy_cmd_state * cs)2204 window_copy_cmd_jump_backward(struct window_copy_cmd_state *cs)
2205 {
2206 struct window_mode_entry *wme = cs->wme;
2207 struct window_copy_mode_data *data = wme->data;
2208 u_int np = wme->prefix;
2209 const char *arg1 = args_string(cs->args, 1);
2210
2211 if (*arg1 != '\0') {
2212 data->jumptype = WINDOW_COPY_JUMPBACKWARD;
2213 free(data->jumpchar);
2214 data->jumpchar = utf8_fromcstr(arg1);
2215 for (; np != 0; np--)
2216 window_copy_cursor_jump_back(wme);
2217 }
2218 return (WINDOW_COPY_CMD_NOTHING);
2219 }
2220
2221 static enum window_copy_cmd_action
window_copy_cmd_jump_forward(struct window_copy_cmd_state * cs)2222 window_copy_cmd_jump_forward(struct window_copy_cmd_state *cs)
2223 {
2224 struct window_mode_entry *wme = cs->wme;
2225 struct window_copy_mode_data *data = wme->data;
2226 u_int np = wme->prefix;
2227 const char *arg1 = args_string(cs->args, 1);
2228
2229 if (*arg1 != '\0') {
2230 data->jumptype = WINDOW_COPY_JUMPFORWARD;
2231 free(data->jumpchar);
2232 data->jumpchar = utf8_fromcstr(arg1);
2233 for (; np != 0; np--)
2234 window_copy_cursor_jump(wme);
2235 }
2236 return (WINDOW_COPY_CMD_NOTHING);
2237 }
2238
2239 static enum window_copy_cmd_action
window_copy_cmd_jump_to_backward(struct window_copy_cmd_state * cs)2240 window_copy_cmd_jump_to_backward(struct window_copy_cmd_state *cs)
2241 {
2242 struct window_mode_entry *wme = cs->wme;
2243 struct window_copy_mode_data *data = wme->data;
2244 u_int np = wme->prefix;
2245 const char *arg1 = args_string(cs->args, 1);
2246
2247 if (*arg1 != '\0') {
2248 data->jumptype = WINDOW_COPY_JUMPTOBACKWARD;
2249 free(data->jumpchar);
2250 data->jumpchar = utf8_fromcstr(arg1);
2251 for (; np != 0; np--)
2252 window_copy_cursor_jump_to_back(wme);
2253 }
2254 return (WINDOW_COPY_CMD_NOTHING);
2255 }
2256
2257 static enum window_copy_cmd_action
window_copy_cmd_jump_to_forward(struct window_copy_cmd_state * cs)2258 window_copy_cmd_jump_to_forward(struct window_copy_cmd_state *cs)
2259 {
2260 struct window_mode_entry *wme = cs->wme;
2261 struct window_copy_mode_data *data = wme->data;
2262 u_int np = wme->prefix;
2263 const char *arg1 = args_string(cs->args, 1);
2264
2265 if (*arg1 != '\0') {
2266 data->jumptype = WINDOW_COPY_JUMPTOFORWARD;
2267 free(data->jumpchar);
2268 data->jumpchar = utf8_fromcstr(arg1);
2269 for (; np != 0; np--)
2270 window_copy_cursor_jump_to(wme);
2271 }
2272 return (WINDOW_COPY_CMD_NOTHING);
2273 }
2274
2275 static enum window_copy_cmd_action
window_copy_cmd_jump_to_mark(struct window_copy_cmd_state * cs)2276 window_copy_cmd_jump_to_mark(struct window_copy_cmd_state *cs)
2277 {
2278 struct window_mode_entry *wme = cs->wme;
2279
2280 window_copy_jump_to_mark(wme);
2281 return (WINDOW_COPY_CMD_NOTHING);
2282 }
2283
2284 static enum window_copy_cmd_action
window_copy_cmd_next_prompt(struct window_copy_cmd_state * cs)2285 window_copy_cmd_next_prompt(struct window_copy_cmd_state *cs)
2286 {
2287 struct window_mode_entry *wme = cs->wme;
2288 const char *arg1 = args_string(cs->args, 1);
2289
2290 window_copy_cursor_prompt(wme, 1, arg1);
2291 return (WINDOW_COPY_CMD_NOTHING);
2292 }
2293
2294 static enum window_copy_cmd_action
window_copy_cmd_previous_prompt(struct window_copy_cmd_state * cs)2295 window_copy_cmd_previous_prompt(struct window_copy_cmd_state *cs)
2296 {
2297 struct window_mode_entry *wme = cs->wme;
2298 const char *arg1 = args_string(cs->args, 1);
2299
2300 window_copy_cursor_prompt(wme, 0, arg1);
2301 return (WINDOW_COPY_CMD_NOTHING);
2302 }
2303
2304 static enum window_copy_cmd_action
window_copy_cmd_search_backward(struct window_copy_cmd_state * cs)2305 window_copy_cmd_search_backward(struct window_copy_cmd_state *cs)
2306 {
2307 struct window_mode_entry *wme = cs->wme;
2308 struct window_copy_mode_data *data = wme->data;
2309 u_int np = wme->prefix;
2310
2311 if (!window_copy_expand_search_string(cs))
2312 return (WINDOW_COPY_CMD_NOTHING);
2313
2314 if (data->searchstr != NULL) {
2315 data->searchtype = WINDOW_COPY_SEARCHUP;
2316 data->searchregex = 1;
2317 data->timeout = 0;
2318 for (; np != 0; np--)
2319 window_copy_search_up(wme, 1);
2320 }
2321 return (WINDOW_COPY_CMD_NOTHING);
2322 }
2323
2324 static enum window_copy_cmd_action
window_copy_cmd_search_backward_text(struct window_copy_cmd_state * cs)2325 window_copy_cmd_search_backward_text(struct window_copy_cmd_state *cs)
2326 {
2327 struct window_mode_entry *wme = cs->wme;
2328 struct window_copy_mode_data *data = wme->data;
2329 u_int np = wme->prefix;
2330
2331 if (!window_copy_expand_search_string(cs))
2332 return (WINDOW_COPY_CMD_NOTHING);
2333
2334 if (data->searchstr != NULL) {
2335 data->searchtype = WINDOW_COPY_SEARCHUP;
2336 data->searchregex = 0;
2337 data->timeout = 0;
2338 for (; np != 0; np--)
2339 window_copy_search_up(wme, 0);
2340 }
2341 return (WINDOW_COPY_CMD_NOTHING);
2342 }
2343
2344 static enum window_copy_cmd_action
window_copy_cmd_search_forward(struct window_copy_cmd_state * cs)2345 window_copy_cmd_search_forward(struct window_copy_cmd_state *cs)
2346 {
2347 struct window_mode_entry *wme = cs->wme;
2348 struct window_copy_mode_data *data = wme->data;
2349 u_int np = wme->prefix;
2350
2351 if (!window_copy_expand_search_string(cs))
2352 return (WINDOW_COPY_CMD_NOTHING);
2353
2354 if (data->searchstr != NULL) {
2355 data->searchtype = WINDOW_COPY_SEARCHDOWN;
2356 data->searchregex = 1;
2357 data->timeout = 0;
2358 for (; np != 0; np--)
2359 window_copy_search_down(wme, 1);
2360 }
2361 return (WINDOW_COPY_CMD_NOTHING);
2362 }
2363
2364 static enum window_copy_cmd_action
window_copy_cmd_search_forward_text(struct window_copy_cmd_state * cs)2365 window_copy_cmd_search_forward_text(struct window_copy_cmd_state *cs)
2366 {
2367 struct window_mode_entry *wme = cs->wme;
2368 struct window_copy_mode_data *data = wme->data;
2369 u_int np = wme->prefix;
2370
2371 if (!window_copy_expand_search_string(cs))
2372 return (WINDOW_COPY_CMD_NOTHING);
2373
2374 if (data->searchstr != NULL) {
2375 data->searchtype = WINDOW_COPY_SEARCHDOWN;
2376 data->searchregex = 0;
2377 data->timeout = 0;
2378 for (; np != 0; np--)
2379 window_copy_search_down(wme, 0);
2380 }
2381 return (WINDOW_COPY_CMD_NOTHING);
2382 }
2383
2384 static enum window_copy_cmd_action
window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state * cs)2385 window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs)
2386 {
2387 struct window_mode_entry *wme = cs->wme;
2388 struct window_copy_mode_data *data = wme->data;
2389 const char *arg1 = args_string(cs->args, 1);
2390 const char *ss = data->searchstr;
2391 char prefix;
2392 enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING;
2393
2394 data->timeout = 0;
2395
2396 log_debug("%s: %s", __func__, arg1);
2397
2398 prefix = *arg1++;
2399 if (data->searchx == -1 || data->searchy == -1) {
2400 data->searchx = data->cx;
2401 data->searchy = data->cy;
2402 data->searcho = data->oy;
2403 } else if (ss != NULL && strcmp(arg1, ss) != 0) {
2404 data->cx = data->searchx;
2405 data->cy = data->searchy;
2406 data->oy = data->searcho;
2407 action = WINDOW_COPY_CMD_REDRAW;
2408 }
2409 if (*arg1 == '\0') {
2410 window_copy_clear_marks(wme);
2411 return (WINDOW_COPY_CMD_REDRAW);
2412 }
2413 switch (prefix) {
2414 case '=':
2415 case '-':
2416 data->searchtype = WINDOW_COPY_SEARCHUP;
2417 data->searchregex = 0;
2418 free(data->searchstr);
2419 data->searchstr = xstrdup(arg1);
2420 if (!window_copy_search_up(wme, 0)) {
2421 window_copy_clear_marks(wme);
2422 return (WINDOW_COPY_CMD_REDRAW);
2423 }
2424 break;
2425 case '+':
2426 data->searchtype = WINDOW_COPY_SEARCHDOWN;
2427 data->searchregex = 0;
2428 free(data->searchstr);
2429 data->searchstr = xstrdup(arg1);
2430 if (!window_copy_search_down(wme, 0)) {
2431 window_copy_clear_marks(wme);
2432 return (WINDOW_COPY_CMD_REDRAW);
2433 }
2434 break;
2435 }
2436 return (action);
2437 }
2438
2439 static enum window_copy_cmd_action
window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state * cs)2440 window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs)
2441 {
2442 struct window_mode_entry *wme = cs->wme;
2443 struct window_copy_mode_data *data = wme->data;
2444 const char *arg1 = args_string(cs->args, 1);
2445 const char *ss = data->searchstr;
2446 char prefix;
2447 enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING;
2448
2449 data->timeout = 0;
2450
2451 log_debug("%s: %s", __func__, arg1);
2452
2453 prefix = *arg1++;
2454 if (data->searchx == -1 || data->searchy == -1) {
2455 data->searchx = data->cx;
2456 data->searchy = data->cy;
2457 data->searcho = data->oy;
2458 } else if (ss != NULL && strcmp(arg1, ss) != 0) {
2459 data->cx = data->searchx;
2460 data->cy = data->searchy;
2461 data->oy = data->searcho;
2462 action = WINDOW_COPY_CMD_REDRAW;
2463 }
2464 if (*arg1 == '\0') {
2465 window_copy_clear_marks(wme);
2466 return (WINDOW_COPY_CMD_REDRAW);
2467 }
2468 switch (prefix) {
2469 case '=':
2470 case '+':
2471 data->searchtype = WINDOW_COPY_SEARCHDOWN;
2472 data->searchregex = 0;
2473 free(data->searchstr);
2474 data->searchstr = xstrdup(arg1);
2475 if (!window_copy_search_down(wme, 0)) {
2476 window_copy_clear_marks(wme);
2477 return (WINDOW_COPY_CMD_REDRAW);
2478 }
2479 break;
2480 case '-':
2481 data->searchtype = WINDOW_COPY_SEARCHUP;
2482 data->searchregex = 0;
2483 free(data->searchstr);
2484 data->searchstr = xstrdup(arg1);
2485 if (!window_copy_search_up(wme, 0)) {
2486 window_copy_clear_marks(wme);
2487 return (WINDOW_COPY_CMD_REDRAW);
2488 }
2489 }
2490 return (action);
2491 }
2492
2493 static enum window_copy_cmd_action
window_copy_cmd_refresh_from_pane(struct window_copy_cmd_state * cs)2494 window_copy_cmd_refresh_from_pane(struct window_copy_cmd_state *cs)
2495 {
2496 struct window_mode_entry *wme = cs->wme;
2497 struct window_pane *wp = wme->swp;
2498 struct window_copy_mode_data *data = wme->data;
2499
2500 if (data->viewmode)
2501 return (WINDOW_COPY_CMD_NOTHING);
2502
2503 screen_free(data->backing);
2504 free(data->backing);
2505 data->backing = window_copy_clone_screen(&wp->base, &data->screen, NULL,
2506 NULL, wme->swp != wme->wp);
2507
2508 window_copy_size_changed(wme);
2509 return (WINDOW_COPY_CMD_REDRAW);
2510 }
2511
2512 static const struct {
2513 const char *command;
2514 u_int minargs;
2515 u_int maxargs;
2516 enum window_copy_cmd_clear clear;
2517 enum window_copy_cmd_action (*f)(struct window_copy_cmd_state *);
2518 } window_copy_cmd_table[] = {
2519 { .command = "append-selection",
2520 .minargs = 0,
2521 .maxargs = 0,
2522 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2523 .f = window_copy_cmd_append_selection
2524 },
2525 { .command = "append-selection-and-cancel",
2526 .minargs = 0,
2527 .maxargs = 0,
2528 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2529 .f = window_copy_cmd_append_selection_and_cancel
2530 },
2531 { .command = "back-to-indentation",
2532 .minargs = 0,
2533 .maxargs = 0,
2534 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2535 .f = window_copy_cmd_back_to_indentation
2536 },
2537 { .command = "begin-selection",
2538 .minargs = 0,
2539 .maxargs = 0,
2540 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2541 .f = window_copy_cmd_begin_selection
2542 },
2543 { .command = "bottom-line",
2544 .minargs = 0,
2545 .maxargs = 0,
2546 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2547 .f = window_copy_cmd_bottom_line
2548 },
2549 { .command = "cancel",
2550 .minargs = 0,
2551 .maxargs = 0,
2552 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2553 .f = window_copy_cmd_cancel
2554 },
2555 { .command = "clear-selection",
2556 .minargs = 0,
2557 .maxargs = 0,
2558 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2559 .f = window_copy_cmd_clear_selection
2560 },
2561 { .command = "copy-end-of-line",
2562 .minargs = 0,
2563 .maxargs = 1,
2564 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2565 .f = window_copy_cmd_copy_end_of_line
2566 },
2567 { .command = "copy-end-of-line-and-cancel",
2568 .minargs = 0,
2569 .maxargs = 1,
2570 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2571 .f = window_copy_cmd_copy_end_of_line_and_cancel
2572 },
2573 { .command = "copy-pipe-end-of-line",
2574 .minargs = 0,
2575 .maxargs = 2,
2576 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2577 .f = window_copy_cmd_copy_pipe_end_of_line
2578 },
2579 { .command = "copy-pipe-end-of-line-and-cancel",
2580 .minargs = 0,
2581 .maxargs = 2,
2582 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2583 .f = window_copy_cmd_copy_pipe_end_of_line_and_cancel
2584 },
2585 { .command = "copy-line",
2586 .minargs = 0,
2587 .maxargs = 1,
2588 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2589 .f = window_copy_cmd_copy_line
2590 },
2591 { .command = "copy-line-and-cancel",
2592 .minargs = 0,
2593 .maxargs = 1,
2594 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2595 .f = window_copy_cmd_copy_line_and_cancel
2596 },
2597 { .command = "copy-pipe-line",
2598 .minargs = 0,
2599 .maxargs = 2,
2600 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2601 .f = window_copy_cmd_copy_pipe_line
2602 },
2603 { .command = "copy-pipe-line-and-cancel",
2604 .minargs = 0,
2605 .maxargs = 2,
2606 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2607 .f = window_copy_cmd_copy_pipe_line_and_cancel
2608 },
2609 { .command = "copy-pipe-no-clear",
2610 .minargs = 0,
2611 .maxargs = 2,
2612 .clear = WINDOW_COPY_CMD_CLEAR_NEVER,
2613 .f = window_copy_cmd_copy_pipe_no_clear
2614 },
2615 { .command = "copy-pipe",
2616 .minargs = 0,
2617 .maxargs = 2,
2618 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2619 .f = window_copy_cmd_copy_pipe
2620 },
2621 { .command = "copy-pipe-and-cancel",
2622 .minargs = 0,
2623 .maxargs = 2,
2624 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2625 .f = window_copy_cmd_copy_pipe_and_cancel
2626 },
2627 { .command = "copy-selection-no-clear",
2628 .minargs = 0,
2629 .maxargs = 1,
2630 .clear = WINDOW_COPY_CMD_CLEAR_NEVER,
2631 .f = window_copy_cmd_copy_selection_no_clear
2632 },
2633 { .command = "copy-selection",
2634 .minargs = 0,
2635 .maxargs = 1,
2636 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2637 .f = window_copy_cmd_copy_selection
2638 },
2639 { .command = "copy-selection-and-cancel",
2640 .minargs = 0,
2641 .maxargs = 1,
2642 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2643 .f = window_copy_cmd_copy_selection_and_cancel
2644 },
2645 { .command = "cursor-down",
2646 .minargs = 0,
2647 .maxargs = 0,
2648 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2649 .f = window_copy_cmd_cursor_down
2650 },
2651 { .command = "cursor-down-and-cancel",
2652 .minargs = 0,
2653 .maxargs = 0,
2654 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2655 .f = window_copy_cmd_cursor_down_and_cancel
2656 },
2657 { .command = "cursor-left",
2658 .minargs = 0,
2659 .maxargs = 0,
2660 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2661 .f = window_copy_cmd_cursor_left
2662 },
2663 { .command = "cursor-right",
2664 .minargs = 0,
2665 .maxargs = 0,
2666 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2667 .f = window_copy_cmd_cursor_right
2668 },
2669 { .command = "cursor-up",
2670 .minargs = 0,
2671 .maxargs = 0,
2672 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2673 .f = window_copy_cmd_cursor_up
2674 },
2675 { .command = "end-of-line",
2676 .minargs = 0,
2677 .maxargs = 0,
2678 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2679 .f = window_copy_cmd_end_of_line
2680 },
2681 { .command = "goto-line",
2682 .minargs = 1,
2683 .maxargs = 1,
2684 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2685 .f = window_copy_cmd_goto_line
2686 },
2687 { .command = "halfpage-down",
2688 .minargs = 0,
2689 .maxargs = 0,
2690 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2691 .f = window_copy_cmd_halfpage_down
2692 },
2693 { .command = "halfpage-down-and-cancel",
2694 .minargs = 0,
2695 .maxargs = 0,
2696 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2697 .f = window_copy_cmd_halfpage_down_and_cancel
2698 },
2699 { .command = "halfpage-up",
2700 .minargs = 0,
2701 .maxargs = 0,
2702 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2703 .f = window_copy_cmd_halfpage_up
2704 },
2705 { .command = "history-bottom",
2706 .minargs = 0,
2707 .maxargs = 0,
2708 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2709 .f = window_copy_cmd_history_bottom
2710 },
2711 { .command = "history-top",
2712 .minargs = 0,
2713 .maxargs = 0,
2714 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2715 .f = window_copy_cmd_history_top
2716 },
2717 { .command = "jump-again",
2718 .minargs = 0,
2719 .maxargs = 0,
2720 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2721 .f = window_copy_cmd_jump_again
2722 },
2723 { .command = "jump-backward",
2724 .minargs = 1,
2725 .maxargs = 1,
2726 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2727 .f = window_copy_cmd_jump_backward
2728 },
2729 { .command = "jump-forward",
2730 .minargs = 1,
2731 .maxargs = 1,
2732 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2733 .f = window_copy_cmd_jump_forward
2734 },
2735 { .command = "jump-reverse",
2736 .minargs = 0,
2737 .maxargs = 0,
2738 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2739 .f = window_copy_cmd_jump_reverse
2740 },
2741 { .command = "jump-to-backward",
2742 .minargs = 1,
2743 .maxargs = 1,
2744 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2745 .f = window_copy_cmd_jump_to_backward
2746 },
2747 { .command = "jump-to-forward",
2748 .minargs = 1,
2749 .maxargs = 1,
2750 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2751 .f = window_copy_cmd_jump_to_forward
2752 },
2753 { .command = "jump-to-mark",
2754 .minargs = 0,
2755 .maxargs = 0,
2756 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2757 .f = window_copy_cmd_jump_to_mark
2758 },
2759 { .command = "next-prompt",
2760 .minargs = 0,
2761 .maxargs = 1,
2762 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2763 .f = window_copy_cmd_next_prompt
2764 },
2765 { .command = "previous-prompt",
2766 .minargs = 0,
2767 .maxargs = 1,
2768 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2769 .f = window_copy_cmd_previous_prompt
2770 },
2771 { .command = "middle-line",
2772 .minargs = 0,
2773 .maxargs = 0,
2774 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2775 .f = window_copy_cmd_middle_line
2776 },
2777 { .command = "next-matching-bracket",
2778 .minargs = 0,
2779 .maxargs = 0,
2780 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2781 .f = window_copy_cmd_next_matching_bracket
2782 },
2783 { .command = "next-paragraph",
2784 .minargs = 0,
2785 .maxargs = 0,
2786 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2787 .f = window_copy_cmd_next_paragraph
2788 },
2789 { .command = "next-space",
2790 .minargs = 0,
2791 .maxargs = 0,
2792 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2793 .f = window_copy_cmd_next_space
2794 },
2795 { .command = "next-space-end",
2796 .minargs = 0,
2797 .maxargs = 0,
2798 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2799 .f = window_copy_cmd_next_space_end
2800 },
2801 { .command = "next-word",
2802 .minargs = 0,
2803 .maxargs = 0,
2804 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2805 .f = window_copy_cmd_next_word
2806 },
2807 { .command = "next-word-end",
2808 .minargs = 0,
2809 .maxargs = 0,
2810 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2811 .f = window_copy_cmd_next_word_end
2812 },
2813 { .command = "other-end",
2814 .minargs = 0,
2815 .maxargs = 0,
2816 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2817 .f = window_copy_cmd_other_end
2818 },
2819 { .command = "page-down",
2820 .minargs = 0,
2821 .maxargs = 0,
2822 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2823 .f = window_copy_cmd_page_down
2824 },
2825 { .command = "page-down-and-cancel",
2826 .minargs = 0,
2827 .maxargs = 0,
2828 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2829 .f = window_copy_cmd_page_down_and_cancel
2830 },
2831 { .command = "page-up",
2832 .minargs = 0,
2833 .maxargs = 0,
2834 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2835 .f = window_copy_cmd_page_up
2836 },
2837 { .command = "pipe-no-clear",
2838 .minargs = 0,
2839 .maxargs = 1,
2840 .clear = WINDOW_COPY_CMD_CLEAR_NEVER,
2841 .f = window_copy_cmd_pipe_no_clear
2842 },
2843 { .command = "pipe",
2844 .minargs = 0,
2845 .maxargs = 1,
2846 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2847 .f = window_copy_cmd_pipe
2848 },
2849 { .command = "pipe-and-cancel",
2850 .minargs = 0,
2851 .maxargs = 1,
2852 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2853 .f = window_copy_cmd_pipe_and_cancel
2854 },
2855 { .command = "previous-matching-bracket",
2856 .minargs = 0,
2857 .maxargs = 0,
2858 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2859 .f = window_copy_cmd_previous_matching_bracket
2860 },
2861 { .command = "previous-paragraph",
2862 .minargs = 0,
2863 .maxargs = 0,
2864 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2865 .f = window_copy_cmd_previous_paragraph
2866 },
2867 { .command = "previous-space",
2868 .minargs = 0,
2869 .maxargs = 0,
2870 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2871 .f = window_copy_cmd_previous_space
2872 },
2873 { .command = "previous-word",
2874 .minargs = 0,
2875 .maxargs = 0,
2876 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2877 .f = window_copy_cmd_previous_word
2878 },
2879 { .command = "rectangle-on",
2880 .minargs = 0,
2881 .maxargs = 0,
2882 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2883 .f = window_copy_cmd_rectangle_on
2884 },
2885 { .command = "rectangle-off",
2886 .minargs = 0,
2887 .maxargs = 0,
2888 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2889 .f = window_copy_cmd_rectangle_off
2890 },
2891 { .command = "rectangle-toggle",
2892 .minargs = 0,
2893 .maxargs = 0,
2894 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2895 .f = window_copy_cmd_rectangle_toggle
2896 },
2897 { .command = "refresh-from-pane",
2898 .minargs = 0,
2899 .maxargs = 0,
2900 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2901 .f = window_copy_cmd_refresh_from_pane
2902 },
2903 { .command = "scroll-bottom",
2904 .minargs = 0,
2905 .maxargs = 0,
2906 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2907 .f = window_copy_cmd_scroll_bottom
2908 },
2909 { .command = "scroll-down",
2910 .minargs = 0,
2911 .maxargs = 0,
2912 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2913 .f = window_copy_cmd_scroll_down
2914 },
2915 { .command = "scroll-down-and-cancel",
2916 .minargs = 0,
2917 .maxargs = 0,
2918 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2919 .f = window_copy_cmd_scroll_down_and_cancel
2920 },
2921 { .command = "scroll-middle",
2922 .minargs = 0,
2923 .maxargs = 0,
2924 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2925 .f = window_copy_cmd_scroll_middle
2926 },
2927 { .command = "scroll-top",
2928 .minargs = 0,
2929 .maxargs = 0,
2930 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2931 .f = window_copy_cmd_scroll_top
2932 },
2933 { .command = "scroll-up",
2934 .minargs = 0,
2935 .maxargs = 0,
2936 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2937 .f = window_copy_cmd_scroll_up
2938 },
2939 { .command = "search-again",
2940 .minargs = 0,
2941 .maxargs = 0,
2942 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2943 .f = window_copy_cmd_search_again
2944 },
2945 { .command = "search-backward",
2946 .minargs = 0,
2947 .maxargs = 1,
2948 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2949 .f = window_copy_cmd_search_backward
2950 },
2951 { .command = "search-backward-text",
2952 .minargs = 0,
2953 .maxargs = 1,
2954 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2955 .f = window_copy_cmd_search_backward_text
2956 },
2957 { .command = "search-backward-incremental",
2958 .minargs = 1,
2959 .maxargs = 1,
2960 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2961 .f = window_copy_cmd_search_backward_incremental
2962 },
2963 { .command = "search-forward",
2964 .minargs = 0,
2965 .maxargs = 1,
2966 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2967 .f = window_copy_cmd_search_forward
2968 },
2969 { .command = "search-forward-text",
2970 .minargs = 0,
2971 .maxargs = 1,
2972 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2973 .f = window_copy_cmd_search_forward_text
2974 },
2975 { .command = "search-forward-incremental",
2976 .minargs = 1,
2977 .maxargs = 1,
2978 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2979 .f = window_copy_cmd_search_forward_incremental
2980 },
2981 { .command = "search-reverse",
2982 .minargs = 0,
2983 .maxargs = 0,
2984 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2985 .f = window_copy_cmd_search_reverse
2986 },
2987 { .command = "select-line",
2988 .minargs = 0,
2989 .maxargs = 0,
2990 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2991 .f = window_copy_cmd_select_line
2992 },
2993 { .command = "select-word",
2994 .minargs = 0,
2995 .maxargs = 0,
2996 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2997 .f = window_copy_cmd_select_word
2998 },
2999 { .command = "set-mark",
3000 .minargs = 0,
3001 .maxargs = 0,
3002 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
3003 .f = window_copy_cmd_set_mark
3004 },
3005 { .command = "start-of-line",
3006 .minargs = 0,
3007 .maxargs = 0,
3008 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
3009 .f = window_copy_cmd_start_of_line
3010 },
3011 { .command = "stop-selection",
3012 .minargs = 0,
3013 .maxargs = 0,
3014 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
3015 .f = window_copy_cmd_stop_selection
3016 },
3017 { .command = "toggle-position",
3018 .minargs = 0,
3019 .maxargs = 0,
3020 .clear = WINDOW_COPY_CMD_CLEAR_NEVER,
3021 .f = window_copy_cmd_toggle_position
3022 },
3023 { .command = "top-line",
3024 .minargs = 0,
3025 .maxargs = 0,
3026 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
3027 .f = window_copy_cmd_top_line
3028 }
3029 };
3030
3031 static void
window_copy_command(struct window_mode_entry * wme,struct client * c,struct session * s,struct winlink * wl,struct args * args,struct mouse_event * m)3032 window_copy_command(struct window_mode_entry *wme, struct client *c,
3033 struct session *s, struct winlink *wl, struct args *args,
3034 struct mouse_event *m)
3035 {
3036 struct window_copy_mode_data *data = wme->data;
3037 struct window_copy_cmd_state cs;
3038 enum window_copy_cmd_action action;
3039 enum window_copy_cmd_clear clear = WINDOW_COPY_CMD_CLEAR_NEVER;
3040 const char *command;
3041 u_int i, count = args_count(args);
3042 int keys;
3043
3044 if (count == 0)
3045 return;
3046 command = args_string(args, 0);
3047
3048 if (m != NULL && m->valid && !MOUSE_WHEEL(m->b))
3049 window_copy_move_mouse(m);
3050
3051 cs.wme = wme;
3052 cs.args = args;
3053 cs.m = m;
3054
3055 cs.c = c;
3056 cs.s = s;
3057 cs.wl = wl;
3058
3059 action = WINDOW_COPY_CMD_NOTHING;
3060 for (i = 0; i < nitems(window_copy_cmd_table); i++) {
3061 if (strcmp(window_copy_cmd_table[i].command, command) == 0) {
3062 if (count - 1 < window_copy_cmd_table[i].minargs ||
3063 count - 1 > window_copy_cmd_table[i].maxargs)
3064 break;
3065 clear = window_copy_cmd_table[i].clear;
3066 action = window_copy_cmd_table[i].f(&cs);
3067 break;
3068 }
3069 }
3070
3071 if (strncmp(command, "search-", 7) != 0 && data->searchmark != NULL) {
3072 keys = options_get_number(wme->wp->window->options, "mode-keys");
3073 if (clear == WINDOW_COPY_CMD_CLEAR_EMACS_ONLY &&
3074 keys == MODEKEY_VI)
3075 clear = WINDOW_COPY_CMD_CLEAR_NEVER;
3076 if (clear != WINDOW_COPY_CMD_CLEAR_NEVER) {
3077 window_copy_clear_marks(wme);
3078 data->searchx = data->searchy = -1;
3079 }
3080 if (action == WINDOW_COPY_CMD_NOTHING)
3081 action = WINDOW_COPY_CMD_REDRAW;
3082 }
3083 wme->prefix = 1;
3084
3085 if (action == WINDOW_COPY_CMD_CANCEL)
3086 window_pane_reset_mode(wme->wp);
3087 else if (action == WINDOW_COPY_CMD_REDRAW)
3088 window_copy_redraw_screen(wme);
3089 }
3090
3091 static void
window_copy_scroll_to(struct window_mode_entry * wme,u_int px,u_int py,int no_redraw)3092 window_copy_scroll_to(struct window_mode_entry *wme, u_int px, u_int py,
3093 int no_redraw)
3094 {
3095 struct window_copy_mode_data *data = wme->data;
3096 struct grid *gd = data->backing->grid;
3097 u_int offset, gap;
3098
3099 data->cx = px;
3100
3101 if (py >= gd->hsize - data->oy && py < gd->hsize - data->oy + gd->sy)
3102 data->cy = py - (gd->hsize - data->oy);
3103 else {
3104 gap = gd->sy / 4;
3105 if (py < gd->sy) {
3106 offset = 0;
3107 data->cy = py;
3108 } else if (py > gd->hsize + gd->sy - gap) {
3109 offset = gd->hsize;
3110 data->cy = py - gd->hsize;
3111 } else {
3112 offset = py + gap - gd->sy;
3113 data->cy = py - offset;
3114 }
3115 data->oy = gd->hsize - offset;
3116 }
3117
3118 if (!no_redraw && data->searchmark != NULL && !data->timeout)
3119 window_copy_search_marks(wme, NULL, data->searchregex, 1);
3120 window_copy_update_selection(wme, 1, 0);
3121 if (!no_redraw)
3122 window_copy_redraw_screen(wme);
3123 }
3124
3125 static int
window_copy_search_compare(struct grid * gd,u_int px,u_int py,struct grid * sgd,u_int spx,int cis)3126 window_copy_search_compare(struct grid *gd, u_int px, u_int py,
3127 struct grid *sgd, u_int spx, int cis)
3128 {
3129 struct grid_cell gc, sgc;
3130 const struct utf8_data *ud, *sud;
3131
3132 grid_get_cell(gd, px, py, &gc);
3133 ud = &gc.data;
3134 grid_get_cell(sgd, spx, 0, &sgc);
3135 sud = &sgc.data;
3136
3137 if (ud->size != sud->size || ud->width != sud->width)
3138 return (0);
3139
3140 if (cis && ud->size == 1)
3141 return (tolower(ud->data[0]) == sud->data[0]);
3142
3143 return (memcmp(ud->data, sud->data, ud->size) == 0);
3144 }
3145
3146 static int
window_copy_search_lr(struct grid * gd,struct grid * sgd,u_int * ppx,u_int py,u_int first,u_int last,int cis)3147 window_copy_search_lr(struct grid *gd, struct grid *sgd, u_int *ppx, u_int py,
3148 u_int first, u_int last, int cis)
3149 {
3150 u_int ax, bx, px, pywrap, endline;
3151 int matched;
3152 struct grid_line *gl;
3153
3154 endline = gd->hsize + gd->sy - 1;
3155 for (ax = first; ax < last; ax++) {
3156 for (bx = 0; bx < sgd->sx; bx++) {
3157 px = ax + bx;
3158 pywrap = py;
3159 /* Wrap line. */
3160 while (px >= gd->sx && pywrap < endline) {
3161 gl = grid_get_line(gd, pywrap);
3162 if (~gl->flags & GRID_LINE_WRAPPED)
3163 break;
3164 px -= gd->sx;
3165 pywrap++;
3166 }
3167 /* We have run off the end of the grid. */
3168 if (px >= gd->sx)
3169 break;
3170 matched = window_copy_search_compare(gd, px, pywrap,
3171 sgd, bx, cis);
3172 if (!matched)
3173 break;
3174 }
3175 if (bx == sgd->sx) {
3176 *ppx = ax;
3177 return (1);
3178 }
3179 }
3180 return (0);
3181 }
3182
3183 static int
window_copy_search_rl(struct grid * gd,struct grid * sgd,u_int * ppx,u_int py,u_int first,u_int last,int cis)3184 window_copy_search_rl(struct grid *gd,
3185 struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis)
3186 {
3187 u_int ax, bx, px, pywrap, endline;
3188 int matched;
3189 struct grid_line *gl;
3190
3191 endline = gd->hsize + gd->sy - 1;
3192 for (ax = last; ax > first; ax--) {
3193 for (bx = 0; bx < sgd->sx; bx++) {
3194 px = ax - 1 + bx;
3195 pywrap = py;
3196 /* Wrap line. */
3197 while (px >= gd->sx && pywrap < endline) {
3198 gl = grid_get_line(gd, pywrap);
3199 if (~gl->flags & GRID_LINE_WRAPPED)
3200 break;
3201 px -= gd->sx;
3202 pywrap++;
3203 }
3204 /* We have run off the end of the grid. */
3205 if (px >= gd->sx)
3206 break;
3207 matched = window_copy_search_compare(gd, px, pywrap,
3208 sgd, bx, cis);
3209 if (!matched)
3210 break;
3211 }
3212 if (bx == sgd->sx) {
3213 *ppx = ax - 1;
3214 return (1);
3215 }
3216 }
3217 return (0);
3218 }
3219
3220 static int
window_copy_search_lr_regex(struct grid * gd,u_int * ppx,u_int * psx,u_int py,u_int first,u_int last,regex_t * reg)3221 window_copy_search_lr_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py,
3222 u_int first, u_int last, regex_t *reg)
3223 {
3224 int eflags = 0;
3225 u_int endline, foundx, foundy, len, pywrap, size = 1;
3226 char *buf;
3227 regmatch_t regmatch;
3228 struct grid_line *gl;
3229
3230 /*
3231 * This can happen during search if the last match was the last
3232 * character on a line.
3233 */
3234 if (first >= last)
3235 return (0);
3236
3237 /* Set flags for regex search. */
3238 if (first != 0)
3239 eflags |= REG_NOTBOL;
3240
3241 /* Need to look at the entire string. */
3242 buf = xmalloc(size);
3243 buf[0] = '\0';
3244 buf = window_copy_stringify(gd, py, first, gd->sx, buf, &size);
3245 len = gd->sx - first;
3246 endline = gd->hsize + gd->sy - 1;
3247 pywrap = py;
3248 while (buf != NULL &&
3249 pywrap <= endline &&
3250 len < WINDOW_COPY_SEARCH_MAX_LINE) {
3251 gl = grid_get_line(gd, pywrap);
3252 if (~gl->flags & GRID_LINE_WRAPPED)
3253 break;
3254 pywrap++;
3255 buf = window_copy_stringify(gd, pywrap, 0, gd->sx, buf, &size);
3256 len += gd->sx;
3257 }
3258
3259 if (regexec(reg, buf, 1, ®match, eflags) == 0 &&
3260 regmatch.rm_so != regmatch.rm_eo) {
3261 foundx = first;
3262 foundy = py;
3263 window_copy_cstrtocellpos(gd, len, &foundx, &foundy,
3264 buf + regmatch.rm_so);
3265 if (foundy == py && foundx < last) {
3266 *ppx = foundx;
3267 len -= foundx - first;
3268 window_copy_cstrtocellpos(gd, len, &foundx, &foundy,
3269 buf + regmatch.rm_eo);
3270 *psx = foundx;
3271 while (foundy > py) {
3272 *psx += gd->sx;
3273 foundy--;
3274 }
3275 *psx -= *ppx;
3276 free(buf);
3277 return (1);
3278 }
3279 }
3280
3281 free(buf);
3282 *ppx = 0;
3283 *psx = 0;
3284 return (0);
3285 }
3286
3287 static int
window_copy_search_rl_regex(struct grid * gd,u_int * ppx,u_int * psx,u_int py,u_int first,u_int last,regex_t * reg)3288 window_copy_search_rl_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py,
3289 u_int first, u_int last, regex_t *reg)
3290 {
3291 int eflags = 0;
3292 u_int endline, len, pywrap, size = 1;
3293 char *buf;
3294 struct grid_line *gl;
3295
3296 /* Set flags for regex search. */
3297 if (first != 0)
3298 eflags |= REG_NOTBOL;
3299
3300 /* Need to look at the entire string. */
3301 buf = xmalloc(size);
3302 buf[0] = '\0';
3303 buf = window_copy_stringify(gd, py, first, gd->sx, buf, &size);
3304 len = gd->sx - first;
3305 endline = gd->hsize + gd->sy - 1;
3306 pywrap = py;
3307 while (buf != NULL &&
3308 pywrap <= endline &&
3309 len < WINDOW_COPY_SEARCH_MAX_LINE) {
3310 gl = grid_get_line(gd, pywrap);
3311 if (~gl->flags & GRID_LINE_WRAPPED)
3312 break;
3313 pywrap++;
3314 buf = window_copy_stringify(gd, pywrap, 0, gd->sx, buf, &size);
3315 len += gd->sx;
3316 }
3317
3318 if (window_copy_last_regex(gd, py, first, last, len, ppx, psx, buf,
3319 reg, eflags))
3320 {
3321 free(buf);
3322 return (1);
3323 }
3324
3325 free(buf);
3326 *ppx = 0;
3327 *psx = 0;
3328 return (0);
3329 }
3330
3331 static const char *
window_copy_cellstring(const struct grid_line * gl,u_int px,size_t * size,int * allocated)3332 window_copy_cellstring(const struct grid_line *gl, u_int px, size_t *size,
3333 int *allocated)
3334 {
3335 static struct utf8_data ud;
3336 struct grid_cell_entry *gce;
3337 char *copy;
3338
3339 if (px >= gl->cellsize) {
3340 *size = 1;
3341 *allocated = 0;
3342 return (" ");
3343 }
3344
3345 gce = &gl->celldata[px];
3346 if (gce->flags & GRID_FLAG_PADDING) {
3347 *size = 0;
3348 *allocated = 0;
3349 return (NULL);
3350 }
3351 if (~gce->flags & GRID_FLAG_EXTENDED) {
3352 *size = 1;
3353 *allocated = 0;
3354 return (const char *)(&gce->data.data);
3355 }
3356
3357 utf8_to_data(gl->extddata[gce->offset].data, &ud);
3358 if (ud.size == 0) {
3359 *size = 0;
3360 *allocated = 0;
3361 return (NULL);
3362 }
3363 *size = ud.size;
3364 *allocated = 1;
3365
3366 copy = xmalloc(ud.size);
3367 memcpy(copy, ud.data, ud.size);
3368 return (copy);
3369 }
3370
3371 /* Find last match in given range. */
3372 static int
window_copy_last_regex(struct grid * gd,u_int py,u_int first,u_int last,u_int len,u_int * ppx,u_int * psx,const char * buf,const regex_t * preg,int eflags)3373 window_copy_last_regex(struct grid *gd, u_int py, u_int first, u_int last,
3374 u_int len, u_int *ppx, u_int *psx, const char *buf, const regex_t *preg,
3375 int eflags)
3376 {
3377 u_int foundx, foundy, oldx, px = 0, savepx, savesx = 0;
3378 regmatch_t regmatch;
3379
3380 foundx = first;
3381 foundy = py;
3382 oldx = first;
3383 while (regexec(preg, buf + px, 1, ®match, eflags) == 0) {
3384 if (regmatch.rm_so == regmatch.rm_eo)
3385 break;
3386 window_copy_cstrtocellpos(gd, len, &foundx, &foundy,
3387 buf + px + regmatch.rm_so);
3388 if (foundy > py || foundx >= last)
3389 break;
3390 len -= foundx - oldx;
3391 savepx = foundx;
3392 window_copy_cstrtocellpos(gd, len, &foundx, &foundy,
3393 buf + px + regmatch.rm_eo);
3394 if (foundy > py || foundx >= last) {
3395 *ppx = savepx;
3396 *psx = foundx;
3397 while (foundy > py) {
3398 *psx += gd->sx;
3399 foundy--;
3400 }
3401 *psx -= *ppx;
3402 return (1);
3403 } else {
3404 savesx = foundx - savepx;
3405 len -= savesx;
3406 oldx = foundx;
3407 }
3408 px += regmatch.rm_eo;
3409 }
3410
3411 if (savesx > 0) {
3412 *ppx = savepx;
3413 *psx = savesx;
3414 return (1);
3415 } else {
3416 *ppx = 0;
3417 *psx = 0;
3418 return (0);
3419 }
3420 }
3421
3422 /* Stringify line and append to input buffer. Caller frees. */
3423 static char *
window_copy_stringify(struct grid * gd,u_int py,u_int first,u_int last,char * buf,u_int * size)3424 window_copy_stringify(struct grid *gd, u_int py, u_int first, u_int last,
3425 char *buf, u_int *size)
3426 {
3427 u_int ax, bx, newsize = *size;
3428 const struct grid_line *gl;
3429 const char *d;
3430 size_t bufsize = 1024, dlen;
3431 int allocated;
3432
3433 while (bufsize < newsize)
3434 bufsize *= 2;
3435 buf = xrealloc(buf, bufsize);
3436
3437 gl = grid_peek_line(gd, py);
3438 bx = *size - 1;
3439 for (ax = first; ax < last; ax++) {
3440 d = window_copy_cellstring(gl, ax, &dlen, &allocated);
3441 newsize += dlen;
3442 while (bufsize < newsize) {
3443 bufsize *= 2;
3444 buf = xrealloc(buf, bufsize);
3445 }
3446 if (dlen == 1)
3447 buf[bx++] = *d;
3448 else {
3449 memcpy(buf + bx, d, dlen);
3450 bx += dlen;
3451 }
3452 if (allocated)
3453 free(__UNCONST(d));
3454 }
3455 buf[newsize - 1] = '\0';
3456
3457 *size = newsize;
3458 return (buf);
3459 }
3460
3461 /* Map start of C string containing UTF-8 data to grid cell position. */
3462 static void
window_copy_cstrtocellpos(struct grid * gd,u_int ncells,u_int * ppx,u_int * ppy,const char * str)3463 window_copy_cstrtocellpos(struct grid *gd, u_int ncells, u_int *ppx, u_int *ppy,
3464 const char *str)
3465 {
3466 u_int cell, ccell, px, pywrap, pos, len;
3467 int match;
3468 const struct grid_line *gl;
3469 const char *d;
3470 size_t dlen;
3471 struct {
3472 const char *d;
3473 size_t dlen;
3474 int allocated;
3475 } *cells;
3476
3477 /* Populate the array of cell data. */
3478 cells = xreallocarray(NULL, ncells, sizeof cells[0]);
3479 cell = 0;
3480 px = *ppx;
3481 pywrap = *ppy;
3482 gl = grid_peek_line(gd, pywrap);
3483 while (cell < ncells) {
3484 cells[cell].d = window_copy_cellstring(gl, px,
3485 &cells[cell].dlen, &cells[cell].allocated);
3486 cell++;
3487 px++;
3488 if (px == gd->sx) {
3489 px = 0;
3490 pywrap++;
3491 gl = grid_peek_line(gd, pywrap);
3492 }
3493 }
3494
3495 /* Locate starting cell. */
3496 cell = 0;
3497 len = strlen(str);
3498 while (cell < ncells) {
3499 ccell = cell;
3500 pos = 0;
3501 match = 1;
3502 while (ccell < ncells) {
3503 if (str[pos] == '\0') {
3504 match = 0;
3505 break;
3506 }
3507 d = cells[ccell].d;
3508 dlen = cells[ccell].dlen;
3509 if (dlen == 1) {
3510 if (str[pos] != *d) {
3511 match = 0;
3512 break;
3513 }
3514 pos++;
3515 } else {
3516 if (dlen > len - pos)
3517 dlen = len - pos;
3518 if (memcmp(str + pos, d, dlen) != 0) {
3519 match = 0;
3520 break;
3521 }
3522 pos += dlen;
3523 }
3524 ccell++;
3525 }
3526 if (match)
3527 break;
3528 cell++;
3529 }
3530
3531 /* If not found this will be one past the end. */
3532 px = *ppx + cell;
3533 pywrap = *ppy;
3534 while (px >= gd->sx) {
3535 px -= gd->sx;
3536 pywrap++;
3537 }
3538
3539 *ppx = px;
3540 *ppy = pywrap;
3541
3542 /* Free cell data. */
3543 for (cell = 0; cell < ncells; cell++) {
3544 if (cells[cell].allocated)
3545 free(__UNCONST(cells[cell].d));
3546 }
3547 free(cells);
3548 }
3549
3550 static void
window_copy_move_left(struct screen * s,u_int * fx,u_int * fy,int wrapflag)3551 window_copy_move_left(struct screen *s, u_int *fx, u_int *fy, int wrapflag)
3552 {
3553 if (*fx == 0) { /* left */
3554 if (*fy == 0) { /* top */
3555 if (wrapflag) {
3556 *fx = screen_size_x(s) - 1;
3557 *fy = screen_hsize(s) + screen_size_y(s) - 1;
3558 }
3559 return;
3560 }
3561 *fx = screen_size_x(s) - 1;
3562 *fy = *fy - 1;
3563 } else
3564 *fx = *fx - 1;
3565 }
3566
3567 static void
window_copy_move_right(struct screen * s,u_int * fx,u_int * fy,int wrapflag)3568 window_copy_move_right(struct screen *s, u_int *fx, u_int *fy, int wrapflag)
3569 {
3570 if (*fx == screen_size_x(s) - 1) { /* right */
3571 if (*fy == screen_hsize(s) + screen_size_y(s) - 1) { /* bottom */
3572 if (wrapflag) {
3573 *fx = 0;
3574 *fy = 0;
3575 }
3576 return;
3577 }
3578 *fx = 0;
3579 *fy = *fy + 1;
3580 } else
3581 *fx = *fx + 1;
3582 }
3583
3584 static int
window_copy_is_lowercase(const char * ptr)3585 window_copy_is_lowercase(const char *ptr)
3586 {
3587 while (*ptr != '\0') {
3588 if (*ptr != tolower((u_char)*ptr))
3589 return (0);
3590 ++ptr;
3591 }
3592 return (1);
3593 }
3594
3595 /*
3596 * Handle backward wrapped regex searches with overlapping matches. In this case
3597 * find the longest overlapping match from previous wrapped lines.
3598 */
3599 static void
window_copy_search_back_overlap(struct grid * gd,regex_t * preg,u_int * ppx,u_int * psx,u_int * ppy,u_int endline)3600 window_copy_search_back_overlap(struct grid *gd, regex_t *preg, u_int *ppx,
3601 u_int *psx, u_int *ppy, u_int endline)
3602 {
3603 u_int endx, endy, oldendx, oldendy, px, py, sx;
3604 int found = 1;
3605
3606 oldendx = *ppx + *psx;
3607 oldendy = *ppy - 1;
3608 while (oldendx > gd->sx - 1) {
3609 oldendx -= gd->sx;
3610 oldendy++;
3611 }
3612 endx = oldendx;
3613 endy = oldendy;
3614 px = *ppx;
3615 py = *ppy;
3616 while (found && px == 0 && py - 1 > endline &&
3617 grid_get_line(gd, py - 2)->flags & GRID_LINE_WRAPPED &&
3618 endx == oldendx && endy == oldendy) {
3619 py--;
3620 found = window_copy_search_rl_regex(gd, &px, &sx, py - 1, 0,
3621 gd->sx, preg);
3622 if (found) {
3623 endx = px + sx;
3624 endy = py - 1;
3625 while (endx > gd->sx - 1) {
3626 endx -= gd->sx;
3627 endy++;
3628 }
3629 if (endx == oldendx && endy == oldendy) {
3630 *ppx = px;
3631 *ppy = py;
3632 }
3633 }
3634 }
3635 }
3636
3637 /*
3638 * Search for text stored in sgd starting from position fx,fy up to endline. If
3639 * found, jump to it. If cis then ignore case. The direction is 0 for searching
3640 * up, down otherwise. If wrap then go to begin/end of grid and try again if
3641 * not found.
3642 */
3643 static int
window_copy_search_jump(struct window_mode_entry * wme,struct grid * gd,struct grid * sgd,u_int fx,u_int fy,u_int endline,int cis,int wrap,int direction,int regex)3644 window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd,
3645 struct grid *sgd, u_int fx, u_int fy, u_int endline, int cis, int wrap,
3646 int direction, int regex)
3647 {
3648 u_int i, px, sx, ssize = 1;
3649 int found = 0, cflags = REG_EXTENDED;
3650 char *sbuf;
3651 regex_t reg;
3652
3653 if (regex) {
3654 sbuf = xmalloc(ssize);
3655 sbuf[0] = '\0';
3656 sbuf = window_copy_stringify(sgd, 0, 0, sgd->sx, sbuf, &ssize);
3657 if (cis)
3658 cflags |= REG_ICASE;
3659 if (regcomp(®, sbuf, cflags) != 0) {
3660 free(sbuf);
3661 return (0);
3662 }
3663 free(sbuf);
3664 }
3665
3666 if (direction) {
3667 for (i = fy; i <= endline; i++) {
3668 if (regex) {
3669 found = window_copy_search_lr_regex(gd,
3670 &px, &sx, i, fx, gd->sx, ®);
3671 } else {
3672 found = window_copy_search_lr(gd, sgd,
3673 &px, i, fx, gd->sx, cis);
3674 }
3675 if (found)
3676 break;
3677 fx = 0;
3678 }
3679 } else {
3680 for (i = fy + 1; endline < i; i--) {
3681 if (regex) {
3682 found = window_copy_search_rl_regex(gd,
3683 &px, &sx, i - 1, 0, fx + 1, ®);
3684 if (found) {
3685 window_copy_search_back_overlap(gd,
3686 ®, &px, &sx, &i, endline);
3687 }
3688 } else {
3689 found = window_copy_search_rl(gd, sgd,
3690 &px, i - 1, 0, fx + 1, cis);
3691 }
3692 if (found) {
3693 i--;
3694 break;
3695 }
3696 fx = gd->sx - 1;
3697 }
3698 }
3699 if (regex)
3700 regfree(®);
3701
3702 if (found) {
3703 window_copy_scroll_to(wme, px, i, 1);
3704 return (1);
3705 }
3706 if (wrap) {
3707 return (window_copy_search_jump(wme, gd, sgd,
3708 direction ? 0 : gd->sx - 1,
3709 direction ? 0 : gd->hsize + gd->sy - 1, fy, cis, 0,
3710 direction, regex));
3711 }
3712 return (0);
3713 }
3714
3715 static void
window_copy_move_after_search_mark(struct window_copy_mode_data * data,u_int * fx,u_int * fy,int wrapflag)3716 window_copy_move_after_search_mark(struct window_copy_mode_data *data,
3717 u_int *fx, u_int *fy, int wrapflag)
3718 {
3719 struct screen *s = data->backing;
3720 u_int at, start;
3721
3722 if (window_copy_search_mark_at(data, *fx, *fy, &start) == 0 &&
3723 data->searchmark[start] != 0) {
3724 while (window_copy_search_mark_at(data, *fx, *fy, &at) == 0) {
3725 if (data->searchmark[at] != data->searchmark[start])
3726 break;
3727 /* Stop if not wrapping and at the end of the grid. */
3728 if (!wrapflag &&
3729 *fx == screen_size_x(s) - 1 &&
3730 *fy == screen_hsize(s) + screen_size_y(s) - 1)
3731 break;
3732
3733 window_copy_move_right(s, fx, fy, wrapflag);
3734 }
3735 }
3736 }
3737
3738 /*
3739 * Search in for text searchstr. If direction is 0 then search up, otherwise
3740 * down.
3741 */
3742 static int
window_copy_search(struct window_mode_entry * wme,int direction,int regex)3743 window_copy_search(struct window_mode_entry *wme, int direction, int regex)
3744 {
3745 struct window_pane *wp = wme->wp;
3746 struct window_copy_mode_data *data = wme->data;
3747 struct screen *s = data->backing, ss;
3748 struct screen_write_ctx ctx;
3749 struct grid *gd = s->grid;
3750 const char *str = data->searchstr;
3751 u_int at, endline, fx, fy, start;
3752 int cis, found, keys, visible_only;
3753 int wrapflag;
3754
3755 if (regex && str[strcspn(str, "^$*+()?[].\\")] == '\0')
3756 regex = 0;
3757
3758 data->searchdirection = direction;
3759
3760 if (data->timeout)
3761 return (0);
3762
3763 if (data->searchall || wp->searchstr == NULL ||
3764 wp->searchregex != regex) {
3765 visible_only = 0;
3766 data->searchall = 0;
3767 } else
3768 visible_only = (strcmp(wp->searchstr, str) == 0);
3769 if (visible_only == 0 && data->searchmark != NULL)
3770 window_copy_clear_marks(wme);
3771 free(wp->searchstr);
3772 wp->searchstr = xstrdup(str);
3773 wp->searchregex = regex;
3774
3775 fx = data->cx;
3776 fy = screen_hsize(data->backing) - data->oy + data->cy;
3777
3778 screen_init(&ss, screen_write_strlen("%s", str), 1, 0);
3779 screen_write_start(&ctx, &ss);
3780 screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", str);
3781 screen_write_stop(&ctx);
3782
3783 wrapflag = options_get_number(wp->window->options, "wrap-search");
3784 cis = window_copy_is_lowercase(str);
3785
3786 keys = options_get_number(wp->window->options, "mode-keys");
3787
3788 if (direction) {
3789 /*
3790 * Behave according to mode-keys. If it is emacs, search forward
3791 * leaves the cursor after the match. If it is vi, the cursor
3792 * remains at the beginning of the match, regardless of
3793 * direction, which means that we need to start the next search
3794 * after the term the cursor is currently on when searching
3795 * forward.
3796 */
3797 if (keys == MODEKEY_VI) {
3798 if (data->searchmark != NULL)
3799 window_copy_move_after_search_mark(data, &fx,
3800 &fy, wrapflag);
3801 else {
3802 /*
3803 * When there are no search marks, start the
3804 * search after the current cursor position.
3805 */
3806 window_copy_move_right(s, &fx, &fy, wrapflag);
3807 }
3808 }
3809 endline = gd->hsize + gd->sy - 1;
3810 } else {
3811 window_copy_move_left(s, &fx, &fy, wrapflag);
3812 endline = 0;
3813 }
3814
3815 found = window_copy_search_jump(wme, gd, ss.grid, fx, fy, endline, cis,
3816 wrapflag, direction, regex);
3817 if (found) {
3818 window_copy_search_marks(wme, &ss, regex, visible_only);
3819 fx = data->cx;
3820 fy = screen_hsize(data->backing) - data->oy + data->cy;
3821
3822 /*
3823 * When searching forward, if the cursor is not at the beginning
3824 * of the mark, search again.
3825 */
3826 if (direction &&
3827 window_copy_search_mark_at(data, fx, fy, &at) == 0 &&
3828 at > 0 &&
3829 data->searchmark != NULL &&
3830 data->searchmark[at] == data->searchmark[at - 1]) {
3831 window_copy_move_after_search_mark(data, &fx, &fy,
3832 wrapflag);
3833 window_copy_search_jump(wme, gd, ss.grid, fx,
3834 fy, endline, cis, wrapflag, direction,
3835 regex);
3836 fx = data->cx;
3837 fy = screen_hsize(data->backing) - data->oy + data->cy;
3838 }
3839
3840 if (direction) {
3841 /*
3842 * When in Emacs mode, position the cursor just after
3843 * the mark.
3844 */
3845 if (keys == MODEKEY_EMACS) {
3846 window_copy_move_after_search_mark(data, &fx,
3847 &fy, wrapflag);
3848 data->cx = fx;
3849 data->cy = fy - screen_hsize(data->backing) +
3850 data-> oy;
3851 }
3852 } else {
3853 /*
3854 * When searching backward, position the cursor at the
3855 * beginning of the mark.
3856 */
3857 if (window_copy_search_mark_at(data, fx, fy,
3858 &start) == 0) {
3859 while (window_copy_search_mark_at(data, fx, fy,
3860 &at) == 0 &&
3861 data->searchmark != NULL &&
3862 data->searchmark[at] ==
3863 data->searchmark[start]) {
3864 data->cx = fx;
3865 data->cy = fy -
3866 screen_hsize(data->backing) +
3867 data-> oy;
3868 if (at == 0)
3869 break;
3870
3871 window_copy_move_left(s, &fx, &fy, 0);
3872 }
3873 }
3874 }
3875 }
3876 window_copy_redraw_screen(wme);
3877
3878 screen_free(&ss);
3879 return (found);
3880 }
3881
3882 static void
window_copy_visible_lines(struct window_copy_mode_data * data,u_int * start,u_int * end)3883 window_copy_visible_lines(struct window_copy_mode_data *data, u_int *start,
3884 u_int *end)
3885 {
3886 struct grid *gd = data->backing->grid;
3887 const struct grid_line *gl;
3888
3889 for (*start = gd->hsize - data->oy; *start > 0; (*start)--) {
3890 gl = grid_peek_line(gd, (*start) - 1);
3891 if (~gl->flags & GRID_LINE_WRAPPED)
3892 break;
3893 }
3894 *end = gd->hsize - data->oy + gd->sy;
3895 }
3896
3897 static int
window_copy_search_mark_at(struct window_copy_mode_data * data,u_int px,u_int py,u_int * at)3898 window_copy_search_mark_at(struct window_copy_mode_data *data, u_int px,
3899 u_int py, u_int *at)
3900 {
3901 struct screen *s = data->backing;
3902 struct grid *gd = s->grid;
3903
3904 if (py < gd->hsize - data->oy)
3905 return (-1);
3906 if (py > gd->hsize - data->oy + gd->sy - 1)
3907 return (-1);
3908 *at = ((py - (gd->hsize - data->oy)) * gd->sx) + px;
3909 return (0);
3910 }
3911
3912 static int
window_copy_search_marks(struct window_mode_entry * wme,struct screen * ssp,int regex,int visible_only)3913 window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp,
3914 int regex, int visible_only)
3915 {
3916 struct window_copy_mode_data *data = wme->data;
3917 struct screen *s = data->backing, ss;
3918 struct screen_write_ctx ctx;
3919 struct grid *gd = s->grid;
3920 int found, cis, stopped = 0;
3921 int cflags = REG_EXTENDED;
3922 u_int px, py, i, b, nfound = 0, width;
3923 u_int ssize = 1, start, end;
3924 char *sbuf;
3925 regex_t reg;
3926 uint64_t stop = 0, tstart, t;
3927
3928 if (ssp == NULL) {
3929 width = screen_write_strlen("%s", data->searchstr);
3930 screen_init(&ss, width, 1, 0);
3931 screen_write_start(&ctx, &ss);
3932 screen_write_nputs(&ctx, -1, &grid_default_cell, "%s",
3933 data->searchstr);
3934 screen_write_stop(&ctx);
3935 ssp = &ss;
3936 } else
3937 width = screen_size_x(ssp);
3938
3939 cis = window_copy_is_lowercase(data->searchstr);
3940
3941 if (regex) {
3942 sbuf = xmalloc(ssize);
3943 sbuf[0] = '\0';
3944 sbuf = window_copy_stringify(ssp->grid, 0, 0, ssp->grid->sx,
3945 sbuf, &ssize);
3946 if (cis)
3947 cflags |= REG_ICASE;
3948 if (regcomp(®, sbuf, cflags) != 0) {
3949 free(sbuf);
3950 return (0);
3951 }
3952 free(sbuf);
3953 }
3954 tstart = get_timer();
3955
3956 if (visible_only)
3957 window_copy_visible_lines(data, &start, &end);
3958 else {
3959 start = 0;
3960 end = gd->hsize + gd->sy;
3961 stop = get_timer() + WINDOW_COPY_SEARCH_ALL_TIMEOUT;
3962 }
3963
3964 again:
3965 free(data->searchmark);
3966 data->searchmark = xcalloc(gd->sx, gd->sy);
3967 data->searchgen = 1;
3968
3969 for (py = start; py < end; py++) {
3970 px = 0;
3971 for (;;) {
3972 if (regex) {
3973 found = window_copy_search_lr_regex(gd,
3974 &px, &width, py, px, gd->sx, ®);
3975 if (!found)
3976 break;
3977 } else {
3978 found = window_copy_search_lr(gd, ssp->grid,
3979 &px, py, px, gd->sx, cis);
3980 if (!found)
3981 break;
3982 }
3983 nfound++;
3984
3985 if (window_copy_search_mark_at(data, px, py, &b) == 0) {
3986 if (b + width > gd->sx * gd->sy)
3987 width = (gd->sx * gd->sy) - b;
3988 for (i = b; i < b + width; i++) {
3989 if (data->searchmark[i] != 0)
3990 continue;
3991 data->searchmark[i] = data->searchgen;
3992 }
3993 if (data->searchgen == UCHAR_MAX)
3994 data->searchgen = 1;
3995 else
3996 data->searchgen++;
3997 }
3998 px += width;
3999 }
4000
4001 t = get_timer();
4002 if (t - tstart > WINDOW_COPY_SEARCH_TIMEOUT) {
4003 data->timeout = 1;
4004 break;
4005 }
4006 if (stop != 0 && t > stop) {
4007 stopped = 1;
4008 break;
4009 }
4010 }
4011 if (data->timeout) {
4012 window_copy_clear_marks(wme);
4013 goto out;
4014 }
4015
4016 if (stopped && stop != 0) {
4017 /* Try again but just the visible context. */
4018 window_copy_visible_lines(data, &start, &end);
4019 stop = 0;
4020 goto again;
4021 }
4022
4023 if (!visible_only) {
4024 if (stopped) {
4025 if (nfound > 1000)
4026 data->searchcount = 1000;
4027 else if (nfound > 100)
4028 data->searchcount = 100;
4029 else if (nfound > 10)
4030 data->searchcount = 10;
4031 else
4032 data->searchcount = -1;
4033 data->searchmore = 1;
4034 } else {
4035 data->searchcount = nfound;
4036 data->searchmore = 0;
4037 }
4038 }
4039
4040 out:
4041 if (ssp == &ss)
4042 screen_free(&ss);
4043 if (regex)
4044 regfree(®);
4045 return (1);
4046 }
4047
4048 static void
window_copy_clear_marks(struct window_mode_entry * wme)4049 window_copy_clear_marks(struct window_mode_entry *wme)
4050 {
4051 struct window_copy_mode_data *data = wme->data;
4052
4053 free(data->searchmark);
4054 data->searchmark = NULL;
4055 }
4056
4057 static int
window_copy_search_up(struct window_mode_entry * wme,int regex)4058 window_copy_search_up(struct window_mode_entry *wme, int regex)
4059 {
4060 return (window_copy_search(wme, 0, regex));
4061 }
4062
4063 static int
window_copy_search_down(struct window_mode_entry * wme,int regex)4064 window_copy_search_down(struct window_mode_entry *wme, int regex)
4065 {
4066 return (window_copy_search(wme, 1, regex));
4067 }
4068
4069 static void
window_copy_goto_line(struct window_mode_entry * wme,const char * linestr)4070 window_copy_goto_line(struct window_mode_entry *wme, const char *linestr)
4071 {
4072 struct window_copy_mode_data *data = wme->data;
4073 const char *errstr;
4074 int lineno;
4075
4076 lineno = strtonum(linestr, -1, INT_MAX, &errstr);
4077 if (errstr != NULL)
4078 return;
4079 if (lineno < 0 || (u_int)lineno > screen_hsize(data->backing))
4080 lineno = screen_hsize(data->backing);
4081
4082 data->oy = lineno;
4083 window_copy_update_selection(wme, 1, 0);
4084 window_copy_redraw_screen(wme);
4085 }
4086
4087 static void
window_copy_match_start_end(struct window_copy_mode_data * data,u_int at,u_int * start,u_int * end)4088 window_copy_match_start_end(struct window_copy_mode_data *data, u_int at,
4089 u_int *start, u_int *end)
4090 {
4091 struct grid *gd = data->backing->grid;
4092 u_int last = (gd->sy * gd->sx) - 1;
4093 u_char mark = data->searchmark[at];
4094
4095 *start = *end = at;
4096 while (*start != 0 && data->searchmark[*start] == mark)
4097 (*start)--;
4098 if (data->searchmark[*start] != mark)
4099 (*start)++;
4100 while (*end != last && data->searchmark[*end] == mark)
4101 (*end)++;
4102 if (data->searchmark[*end] != mark)
4103 (*end)--;
4104 }
4105
4106 static char *
window_copy_match_at_cursor(struct window_copy_mode_data * data)4107 window_copy_match_at_cursor(struct window_copy_mode_data *data)
4108 {
4109 struct grid *gd = data->backing->grid;
4110 struct grid_cell gc;
4111 u_int at, start, end, cy, px, py;
4112 u_int sx = screen_size_x(data->backing);
4113 char *buf = NULL;
4114 size_t len = 0;
4115
4116 if (data->searchmark == NULL)
4117 return (NULL);
4118
4119 cy = screen_hsize(data->backing) - data->oy + data->cy;
4120 if (window_copy_search_mark_at(data, data->cx, cy, &at) != 0)
4121 return (NULL);
4122 if (data->searchmark[at] == 0) {
4123 /* Allow one position after the match. */
4124 if (at == 0 || data->searchmark[--at] == 0)
4125 return (NULL);
4126 }
4127 window_copy_match_start_end(data, at, &start, &end);
4128
4129 /*
4130 * Cells will not be set in the marked array unless they are valid text
4131 * and wrapping will be taken care of, so we can just copy.
4132 */
4133 for (at = start; at <= end; at++) {
4134 py = at / sx;
4135 px = at - (py * sx);
4136
4137 grid_get_cell(gd, px, gd->hsize + py - data->oy, &gc);
4138 buf = xrealloc(buf, len + gc.data.size + 1);
4139 memcpy(buf + len, gc.data.data, gc.data.size);
4140 len += gc.data.size;
4141 }
4142 if (len != 0)
4143 buf[len] = '\0';
4144 return (buf);
4145 }
4146
4147 static void
window_copy_update_style(struct window_mode_entry * wme,u_int fx,u_int fy,struct grid_cell * gc,const struct grid_cell * mgc,const struct grid_cell * cgc,const struct grid_cell * mkgc)4148 window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy,
4149 struct grid_cell *gc, const struct grid_cell *mgc,
4150 const struct grid_cell *cgc, const struct grid_cell *mkgc)
4151 {
4152 struct window_pane *wp = wme->wp;
4153 struct window_copy_mode_data *data = wme->data;
4154 u_int mark, start, end, cy, cursor, current;
4155 int inv = 0, found = 0;
4156 int keys;
4157
4158 if (data->showmark && fy == data->my) {
4159 gc->attr = mkgc->attr;
4160 if (fx == data->mx)
4161 inv = 1;
4162 if (inv) {
4163 gc->fg = mkgc->bg;
4164 gc->bg = mkgc->fg;
4165 }
4166 else {
4167 gc->fg = mkgc->fg;
4168 gc->bg = mkgc->bg;
4169 }
4170 }
4171
4172 if (data->searchmark == NULL)
4173 return;
4174
4175 if (window_copy_search_mark_at(data, fx, fy, ¤t) != 0)
4176 return;
4177 mark = data->searchmark[current];
4178 if (mark == 0)
4179 return;
4180
4181 cy = screen_hsize(data->backing) - data->oy + data->cy;
4182 if (window_copy_search_mark_at(data, data->cx, cy, &cursor) == 0) {
4183 keys = options_get_number(wp->window->options, "mode-keys");
4184 if (cursor != 0 &&
4185 keys == MODEKEY_EMACS &&
4186 data->searchdirection) {
4187 if (data->searchmark[cursor - 1] == mark) {
4188 cursor--;
4189 found = 1;
4190 }
4191 } else if (data->searchmark[cursor] == mark)
4192 found = 1;
4193 if (found) {
4194 window_copy_match_start_end(data, cursor, &start, &end);
4195 if (current >= start && current <= end) {
4196 gc->attr = cgc->attr;
4197 if (inv) {
4198 gc->fg = cgc->bg;
4199 gc->bg = cgc->fg;
4200 }
4201 else {
4202 gc->fg = cgc->fg;
4203 gc->bg = cgc->bg;
4204 }
4205 return;
4206 }
4207 }
4208 }
4209
4210 gc->attr = mgc->attr;
4211 if (inv) {
4212 gc->fg = mgc->bg;
4213 gc->bg = mgc->fg;
4214 }
4215 else {
4216 gc->fg = mgc->fg;
4217 gc->bg = mgc->bg;
4218 }
4219 }
4220
4221 static void
window_copy_write_one(struct window_mode_entry * wme,struct screen_write_ctx * ctx,u_int py,u_int fy,u_int nx,const struct grid_cell * mgc,const struct grid_cell * cgc,const struct grid_cell * mkgc)4222 window_copy_write_one(struct window_mode_entry *wme,
4223 struct screen_write_ctx *ctx, u_int py, u_int fy, u_int nx,
4224 const struct grid_cell *mgc, const struct grid_cell *cgc,
4225 const struct grid_cell *mkgc)
4226 {
4227 struct window_copy_mode_data *data = wme->data;
4228 struct grid *gd = data->backing->grid;
4229 struct grid_cell gc;
4230 u_int fx;
4231
4232 screen_write_cursormove(ctx, 0, py, 0);
4233 for (fx = 0; fx < nx; fx++) {
4234 grid_get_cell(gd, fx, fy, &gc);
4235 if (fx + gc.data.width <= nx) {
4236 window_copy_update_style(wme, fx, fy, &gc, mgc, cgc,
4237 mkgc);
4238 screen_write_cell(ctx, &gc);
4239 }
4240 }
4241 }
4242
4243 static void
window_copy_write_line(struct window_mode_entry * wme,struct screen_write_ctx * ctx,u_int py)4244 window_copy_write_line(struct window_mode_entry *wme,
4245 struct screen_write_ctx *ctx, u_int py)
4246 {
4247 struct window_pane *wp = wme->wp;
4248 struct window_copy_mode_data *data = wme->data;
4249 struct screen *s = &data->screen;
4250 struct options *oo = wp->window->options;
4251 struct grid_line *gl;
4252 struct grid_cell gc, mgc, cgc, mkgc;
4253 char hdr[512], tmp[256], *t;
4254 size_t size = 0;
4255 u_int hsize = screen_hsize(data->backing);
4256
4257 style_apply(&gc, oo, "mode-style", NULL);
4258 gc.flags |= GRID_FLAG_NOPALETTE;
4259 style_apply(&mgc, oo, "copy-mode-match-style", NULL);
4260 mgc.flags |= GRID_FLAG_NOPALETTE;
4261 style_apply(&cgc, oo, "copy-mode-current-match-style", NULL);
4262 cgc.flags |= GRID_FLAG_NOPALETTE;
4263 style_apply(&mkgc, oo, "copy-mode-mark-style", NULL);
4264 mkgc.flags |= GRID_FLAG_NOPALETTE;
4265
4266 if (py == 0 && s->rupper < s->rlower && !data->hide_position) {
4267 gl = grid_get_line(data->backing->grid, hsize - data->oy);
4268 if (gl->time == 0)
4269 xsnprintf(tmp, sizeof tmp, "[%u/%u]", data->oy, hsize);
4270 else {
4271 t = format_pretty_time(gl->time, 1);
4272 xsnprintf(tmp, sizeof tmp, "%s [%u/%u]", t, data->oy,
4273 hsize);
4274 free(t);
4275 }
4276
4277 if (data->searchmark == NULL) {
4278 if (data->timeout) {
4279 size = xsnprintf(hdr, sizeof hdr,
4280 "(timed out) %s", tmp);
4281 } else
4282 size = xsnprintf(hdr, sizeof hdr, "%s", tmp);
4283 } else {
4284 if (data->searchcount == -1)
4285 size = xsnprintf(hdr, sizeof hdr, "%s", tmp);
4286 else {
4287 size = xsnprintf(hdr, sizeof hdr,
4288 "(%d%s results) %s", data->searchcount,
4289 data->searchmore ? "+" : "", tmp);
4290 }
4291 }
4292 if (size > screen_size_x(s))
4293 size = screen_size_x(s);
4294 screen_write_cursormove(ctx, screen_size_x(s) - size, 0, 0);
4295 screen_write_puts(ctx, &gc, "%s", hdr);
4296 } else
4297 size = 0;
4298
4299 if (size < screen_size_x(s)) {
4300 window_copy_write_one(wme, ctx, py, hsize - data->oy + py,
4301 screen_size_x(s) - size, &mgc, &cgc, &mkgc);
4302 }
4303
4304 if (py == data->cy && data->cx == screen_size_x(s)) {
4305 screen_write_cursormove(ctx, screen_size_x(s) - 1, py, 0);
4306 screen_write_putc(ctx, &grid_default_cell, '$');
4307 }
4308 }
4309
4310 static void
window_copy_write_lines(struct window_mode_entry * wme,struct screen_write_ctx * ctx,u_int py,u_int ny)4311 window_copy_write_lines(struct window_mode_entry *wme,
4312 struct screen_write_ctx *ctx, u_int py, u_int ny)
4313 {
4314 u_int yy;
4315
4316 for (yy = py; yy < py + ny; yy++)
4317 window_copy_write_line(wme, ctx, py);
4318 }
4319
4320 static void
window_copy_redraw_selection(struct window_mode_entry * wme,u_int old_y)4321 window_copy_redraw_selection(struct window_mode_entry *wme, u_int old_y)
4322 {
4323 struct window_copy_mode_data *data = wme->data;
4324 struct grid *gd = data->backing->grid;
4325 u_int new_y, start, end;
4326
4327 new_y = data->cy;
4328 if (old_y <= new_y) {
4329 start = old_y;
4330 end = new_y;
4331 } else {
4332 start = new_y;
4333 end = old_y;
4334 }
4335
4336 /*
4337 * In word selection mode the first word on the line below the cursor
4338 * might be selected, so add this line to the redraw area.
4339 */
4340 if (data->selflag == SEL_WORD) {
4341 /* Last grid line in data coordinates. */
4342 if (end < gd->sy + data->oy - 1)
4343 end++;
4344 }
4345 window_copy_redraw_lines(wme, start, end - start + 1);
4346 }
4347
4348 static void
window_copy_redraw_lines(struct window_mode_entry * wme,u_int py,u_int ny)4349 window_copy_redraw_lines(struct window_mode_entry *wme, u_int py, u_int ny)
4350 {
4351 struct window_pane *wp = wme->wp;
4352 struct window_copy_mode_data *data = wme->data;
4353 struct screen_write_ctx ctx;
4354 u_int i;
4355
4356 screen_write_start_pane(&ctx, wp, NULL);
4357 for (i = py; i < py + ny; i++)
4358 window_copy_write_line(wme, &ctx, i);
4359 screen_write_cursormove(&ctx, data->cx, data->cy, 0);
4360 screen_write_stop(&ctx);
4361 }
4362
4363 static void
window_copy_redraw_screen(struct window_mode_entry * wme)4364 window_copy_redraw_screen(struct window_mode_entry *wme)
4365 {
4366 struct window_copy_mode_data *data = wme->data;
4367
4368 window_copy_redraw_lines(wme, 0, screen_size_y(&data->screen));
4369 }
4370
4371 static void
window_copy_synchronize_cursor_end(struct window_mode_entry * wme,int begin,int no_reset)4372 window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin,
4373 int no_reset)
4374 {
4375 struct window_copy_mode_data *data = wme->data;
4376 u_int xx, yy;
4377
4378 xx = data->cx;
4379 yy = screen_hsize(data->backing) + data->cy - data->oy;
4380 switch (data->selflag) {
4381 case SEL_WORD:
4382 if (no_reset)
4383 break;
4384 begin = 0;
4385 if (data->dy > yy || (data->dy == yy && data->dx > xx)) {
4386 /* Right to left selection. */
4387 window_copy_cursor_previous_word_pos(wme,
4388 data->separators, &xx, &yy);
4389 begin = 1;
4390
4391 /* Reset the end. */
4392 data->endselx = data->endselrx;
4393 data->endsely = data->endselry;
4394 } else {
4395 /* Left to right selection. */
4396 if (xx >= window_copy_find_length(wme, yy) ||
4397 !window_copy_in_set(wme, xx + 1, yy, WHITESPACE)) {
4398 window_copy_cursor_next_word_end_pos(wme,
4399 data->separators, &xx, &yy);
4400 }
4401
4402 /* Reset the start. */
4403 data->selx = data->selrx;
4404 data->sely = data->selry;
4405 }
4406 break;
4407 case SEL_LINE:
4408 if (no_reset)
4409 break;
4410 begin = 0;
4411 if (data->dy > yy) {
4412 /* Right to left selection. */
4413 xx = 0;
4414 begin = 1;
4415
4416 /* Reset the end. */
4417 data->endselx = data->endselrx;
4418 data->endsely = data->endselry;
4419 } else {
4420 /* Left to right selection. */
4421 if (yy < data->endselry)
4422 yy = data->endselry;
4423 xx = window_copy_find_length(wme, yy);
4424
4425 /* Reset the start. */
4426 data->selx = data->selrx;
4427 data->sely = data->selry;
4428 }
4429 break;
4430 case SEL_CHAR:
4431 break;
4432 }
4433 if (begin) {
4434 data->selx = xx;
4435 data->sely = yy;
4436 } else {
4437 data->endselx = xx;
4438 data->endsely = yy;
4439 }
4440 }
4441
4442 static void
window_copy_synchronize_cursor(struct window_mode_entry * wme,int no_reset)4443 window_copy_synchronize_cursor(struct window_mode_entry *wme, int no_reset)
4444 {
4445 struct window_copy_mode_data *data = wme->data;
4446
4447 switch (data->cursordrag) {
4448 case CURSORDRAG_ENDSEL:
4449 window_copy_synchronize_cursor_end(wme, 0, no_reset);
4450 break;
4451 case CURSORDRAG_SEL:
4452 window_copy_synchronize_cursor_end(wme, 1, no_reset);
4453 break;
4454 case CURSORDRAG_NONE:
4455 break;
4456 }
4457 }
4458
4459 static void
window_copy_update_cursor(struct window_mode_entry * wme,u_int cx,u_int cy)4460 window_copy_update_cursor(struct window_mode_entry *wme, u_int cx, u_int cy)
4461 {
4462 struct window_pane *wp = wme->wp;
4463 struct window_copy_mode_data *data = wme->data;
4464 struct screen *s = &data->screen;
4465 struct screen_write_ctx ctx;
4466 u_int old_cx, old_cy;
4467
4468 old_cx = data->cx; old_cy = data->cy;
4469 data->cx = cx; data->cy = cy;
4470 if (old_cx == screen_size_x(s))
4471 window_copy_redraw_lines(wme, old_cy, 1);
4472 if (data->cx == screen_size_x(s))
4473 window_copy_redraw_lines(wme, data->cy, 1);
4474 else {
4475 screen_write_start_pane(&ctx, wp, NULL);
4476 screen_write_cursormove(&ctx, data->cx, data->cy, 0);
4477 screen_write_stop(&ctx);
4478 }
4479 }
4480
4481 static void
window_copy_start_selection(struct window_mode_entry * wme)4482 window_copy_start_selection(struct window_mode_entry *wme)
4483 {
4484 struct window_copy_mode_data *data = wme->data;
4485
4486 data->selx = data->cx;
4487 data->sely = screen_hsize(data->backing) + data->cy - data->oy;
4488
4489 data->endselx = data->selx;
4490 data->endsely = data->sely;
4491
4492 data->cursordrag = CURSORDRAG_ENDSEL;
4493
4494 window_copy_set_selection(wme, 1, 0);
4495 }
4496
4497 static int
window_copy_adjust_selection(struct window_mode_entry * wme,u_int * selx,u_int * sely)4498 window_copy_adjust_selection(struct window_mode_entry *wme, u_int *selx,
4499 u_int *sely)
4500 {
4501 struct window_copy_mode_data *data = wme->data;
4502 struct screen *s = &data->screen;
4503 u_int sx, sy, ty;
4504 int relpos;
4505
4506 sx = *selx;
4507 sy = *sely;
4508
4509 ty = screen_hsize(data->backing) - data->oy;
4510 if (sy < ty) {
4511 relpos = WINDOW_COPY_REL_POS_ABOVE;
4512 if (!data->rectflag)
4513 sx = 0;
4514 sy = 0;
4515 } else if (sy > ty + screen_size_y(s) - 1) {
4516 relpos = WINDOW_COPY_REL_POS_BELOW;
4517 if (!data->rectflag)
4518 sx = screen_size_x(s) - 1;
4519 sy = screen_size_y(s) - 1;
4520 } else {
4521 relpos = WINDOW_COPY_REL_POS_ON_SCREEN;
4522 sy -= ty;
4523 }
4524
4525 *selx = sx;
4526 *sely = sy;
4527 return (relpos);
4528 }
4529
4530 static int
window_copy_update_selection(struct window_mode_entry * wme,int may_redraw,int no_reset)4531 window_copy_update_selection(struct window_mode_entry *wme, int may_redraw,
4532 int no_reset)
4533 {
4534 struct window_copy_mode_data *data = wme->data;
4535 struct screen *s = &data->screen;
4536
4537 if (s->sel == NULL && data->lineflag == LINE_SEL_NONE)
4538 return (0);
4539 return (window_copy_set_selection(wme, may_redraw, no_reset));
4540 }
4541
4542 static int
window_copy_set_selection(struct window_mode_entry * wme,int may_redraw,int no_reset)4543 window_copy_set_selection(struct window_mode_entry *wme, int may_redraw,
4544 int no_reset)
4545 {
4546 struct window_pane *wp = wme->wp;
4547 struct window_copy_mode_data *data = wme->data;
4548 struct screen *s = &data->screen;
4549 struct options *oo = wp->window->options;
4550 struct grid_cell gc;
4551 u_int sx, sy, cy, endsx, endsy;
4552 int startrelpos, endrelpos;
4553
4554 window_copy_synchronize_cursor(wme, no_reset);
4555
4556 /* Adjust the selection. */
4557 sx = data->selx;
4558 sy = data->sely;
4559 startrelpos = window_copy_adjust_selection(wme, &sx, &sy);
4560
4561 /* Adjust the end of selection. */
4562 endsx = data->endselx;
4563 endsy = data->endsely;
4564 endrelpos = window_copy_adjust_selection(wme, &endsx, &endsy);
4565
4566 /* Selection is outside of the current screen */
4567 if (startrelpos == endrelpos &&
4568 startrelpos != WINDOW_COPY_REL_POS_ON_SCREEN) {
4569 screen_hide_selection(s);
4570 return (0);
4571 }
4572
4573 /* Set colours and selection. */
4574 style_apply(&gc, oo, "mode-style", NULL);
4575 gc.flags |= GRID_FLAG_NOPALETTE;
4576 screen_set_selection(s, sx, sy, endsx, endsy, data->rectflag,
4577 data->modekeys, &gc);
4578
4579 if (data->rectflag && may_redraw) {
4580 /*
4581 * Can't rely on the caller to redraw the right lines for
4582 * rectangle selection - find the highest line and the number
4583 * of lines, and redraw just past that in both directions
4584 */
4585 cy = data->cy;
4586 if (data->cursordrag == CURSORDRAG_ENDSEL) {
4587 if (sy < cy)
4588 window_copy_redraw_lines(wme, sy, cy - sy + 1);
4589 else
4590 window_copy_redraw_lines(wme, cy, sy - cy + 1);
4591 } else {
4592 if (endsy < cy) {
4593 window_copy_redraw_lines(wme, endsy,
4594 cy - endsy + 1);
4595 } else {
4596 window_copy_redraw_lines(wme, cy,
4597 endsy - cy + 1);
4598 }
4599 }
4600 }
4601
4602 return (1);
4603 }
4604
4605 static void *
window_copy_get_selection(struct window_mode_entry * wme,size_t * len)4606 window_copy_get_selection(struct window_mode_entry *wme, size_t *len)
4607 {
4608 struct window_pane *wp = wme->wp;
4609 struct window_copy_mode_data *data = wme->data;
4610 struct screen *s = &data->screen;
4611 char *buf;
4612 size_t off;
4613 u_int i, xx, yy, sx, sy, ex, ey, ey_last;
4614 u_int firstsx, lastex, restex, restsx, selx;
4615 int keys;
4616
4617 if (data->screen.sel == NULL && data->lineflag == LINE_SEL_NONE) {
4618 buf = window_copy_match_at_cursor(data);
4619 if (buf != NULL)
4620 *len = strlen(buf);
4621 else
4622 *len = 0;
4623 return (buf);
4624 }
4625
4626 buf = xmalloc(1);
4627 off = 0;
4628
4629 *buf = '\0';
4630
4631 /*
4632 * The selection extends from selx,sely to (adjusted) cx,cy on
4633 * the base screen.
4634 */
4635
4636 /* Find start and end. */
4637 xx = data->endselx;
4638 yy = data->endsely;
4639 if (yy < data->sely || (yy == data->sely && xx < data->selx)) {
4640 sx = xx; sy = yy;
4641 ex = data->selx; ey = data->sely;
4642 } else {
4643 sx = data->selx; sy = data->sely;
4644 ex = xx; ey = yy;
4645 }
4646
4647 /* Trim ex to end of line. */
4648 ey_last = window_copy_find_length(wme, ey);
4649 if (ex > ey_last)
4650 ex = ey_last;
4651
4652 /*
4653 * Deal with rectangle-copy if necessary; four situations: start of
4654 * first line (firstsx), end of last line (lastex), start (restsx) and
4655 * end (restex) of all other lines.
4656 */
4657 xx = screen_size_x(s);
4658
4659 /*
4660 * Behave according to mode-keys. If it is emacs, copy like emacs,
4661 * keeping the top-left-most character, and dropping the
4662 * bottom-right-most, regardless of copy direction. If it is vi, also
4663 * keep bottom-right-most character.
4664 */
4665 keys = options_get_number(wp->window->options, "mode-keys");
4666 if (data->rectflag) {
4667 /*
4668 * Need to ignore the column with the cursor in it, which for
4669 * rectangular copy means knowing which side the cursor is on.
4670 */
4671 if (data->cursordrag == CURSORDRAG_ENDSEL)
4672 selx = data->selx;
4673 else
4674 selx = data->endselx;
4675 if (selx < data->cx) {
4676 /* Selection start is on the left. */
4677 if (keys == MODEKEY_EMACS) {
4678 lastex = data->cx;
4679 restex = data->cx;
4680 }
4681 else {
4682 lastex = data->cx + 1;
4683 restex = data->cx + 1;
4684 }
4685 firstsx = selx;
4686 restsx = selx;
4687 } else {
4688 /* Cursor is on the left. */
4689 lastex = selx + 1;
4690 restex = selx + 1;
4691 firstsx = data->cx;
4692 restsx = data->cx;
4693 }
4694 } else {
4695 if (keys == MODEKEY_EMACS)
4696 lastex = ex;
4697 else
4698 lastex = ex + 1;
4699 restex = xx;
4700 firstsx = sx;
4701 restsx = 0;
4702 }
4703
4704 /* Copy the lines. */
4705 for (i = sy; i <= ey; i++) {
4706 window_copy_copy_line(wme, &buf, &off, i,
4707 (i == sy ? firstsx : restsx),
4708 (i == ey ? lastex : restex));
4709 }
4710
4711 /* Don't bother if no data. */
4712 if (off == 0) {
4713 free(buf);
4714 *len = 0;
4715 return (NULL);
4716 }
4717 /* Remove final \n (unless at end in vi mode). */
4718 if (keys == MODEKEY_EMACS || lastex <= ey_last) {
4719 if (~grid_get_line(data->backing->grid, ey)->flags &
4720 GRID_LINE_WRAPPED || lastex != ey_last)
4721 off -= 1;
4722 }
4723 *len = off;
4724 return (buf);
4725 }
4726
4727 static void
window_copy_copy_buffer(struct window_mode_entry * wme,const char * prefix,void * buf,size_t len)4728 window_copy_copy_buffer(struct window_mode_entry *wme, const char *prefix,
4729 void *buf, size_t len)
4730 {
4731 struct window_pane *wp = wme->wp;
4732 struct screen_write_ctx ctx;
4733
4734 if (options_get_number(global_options, "set-clipboard") != 0) {
4735 screen_write_start_pane(&ctx, wp, NULL);
4736 screen_write_setselection(&ctx, "", buf, len);
4737 screen_write_stop(&ctx);
4738 notify_pane("pane-set-clipboard", wp);
4739 }
4740
4741 paste_add(prefix, buf, len);
4742 }
4743
4744 static void *
window_copy_pipe_run(struct window_mode_entry * wme,struct session * s,const char * cmd,size_t * len)4745 window_copy_pipe_run(struct window_mode_entry *wme, struct session *s,
4746 const char *cmd, size_t *len)
4747 {
4748 void *buf;
4749 struct job *job;
4750
4751 buf = window_copy_get_selection(wme, len);
4752 if (cmd == NULL || *cmd == '\0')
4753 cmd = options_get_string(global_options, "copy-command");
4754 if (cmd != NULL && *cmd != '\0') {
4755 job = job_run(cmd, 0, NULL, NULL, s, NULL, NULL, NULL, NULL,
4756 NULL, JOB_NOWAIT, -1, -1);
4757 bufferevent_write(job_get_event(job), buf, *len);
4758 }
4759 return (buf);
4760 }
4761
4762 static void
window_copy_pipe(struct window_mode_entry * wme,struct session * s,const char * cmd)4763 window_copy_pipe(struct window_mode_entry *wme, struct session *s,
4764 const char *cmd)
4765 {
4766 size_t len;
4767
4768 window_copy_pipe_run(wme, s, cmd, &len);
4769 }
4770
4771 static void
window_copy_copy_pipe(struct window_mode_entry * wme,struct session * s,const char * prefix,const char * cmd)4772 window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s,
4773 const char *prefix, const char *cmd)
4774 {
4775 void *buf;
4776 size_t len;
4777
4778 buf = window_copy_pipe_run(wme, s, cmd, &len);
4779 if (buf != NULL)
4780 window_copy_copy_buffer(wme, prefix, buf, len);
4781 }
4782
4783 static void
window_copy_copy_selection(struct window_mode_entry * wme,const char * prefix)4784 window_copy_copy_selection(struct window_mode_entry *wme, const char *prefix)
4785 {
4786 char *buf;
4787 size_t len;
4788
4789 buf = window_copy_get_selection(wme, &len);
4790 if (buf != NULL)
4791 window_copy_copy_buffer(wme, prefix, buf, len);
4792 }
4793
4794 static void
window_copy_append_selection(struct window_mode_entry * wme)4795 window_copy_append_selection(struct window_mode_entry *wme)
4796 {
4797 struct window_pane *wp = wme->wp;
4798 char *buf;
4799 struct paste_buffer *pb;
4800 const char *bufdata, *bufname = NULL;
4801 size_t len, bufsize;
4802 struct screen_write_ctx ctx;
4803
4804 buf = window_copy_get_selection(wme, &len);
4805 if (buf == NULL)
4806 return;
4807
4808 if (options_get_number(global_options, "set-clipboard") != 0) {
4809 screen_write_start_pane(&ctx, wp, NULL);
4810 screen_write_setselection(&ctx, "", (u_char *)buf, len);
4811 screen_write_stop(&ctx);
4812 notify_pane("pane-set-clipboard", wp);
4813 }
4814
4815 pb = paste_get_top(&bufname);
4816 if (pb != NULL) {
4817 bufdata = paste_buffer_data(pb, &bufsize);
4818 buf = xrealloc(buf, len + bufsize);
4819 memmove(buf + bufsize, buf, len);
4820 memcpy(buf, bufdata, bufsize);
4821 len += bufsize;
4822 }
4823 if (paste_set(buf, len, bufname, NULL) != 0)
4824 free(buf);
4825 }
4826
4827 static void
window_copy_copy_line(struct window_mode_entry * wme,char ** buf,size_t * off,u_int sy,u_int sx,u_int ex)4828 window_copy_copy_line(struct window_mode_entry *wme, char **buf, size_t *off,
4829 u_int sy, u_int sx, u_int ex)
4830 {
4831 struct window_copy_mode_data *data = wme->data;
4832 struct grid *gd = data->backing->grid;
4833 struct grid_cell gc;
4834 struct grid_line *gl;
4835 struct utf8_data ud;
4836 u_int i, xx, wrapped = 0;
4837 const char *s;
4838
4839 if (sx > ex)
4840 return;
4841
4842 /*
4843 * Work out if the line was wrapped at the screen edge and all of it is
4844 * on screen.
4845 */
4846 gl = grid_get_line(gd, sy);
4847 if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx)
4848 wrapped = 1;
4849
4850 /* If the line was wrapped, don't strip spaces (use the full length). */
4851 if (wrapped)
4852 xx = gl->cellsize;
4853 else
4854 xx = window_copy_find_length(wme, sy);
4855 if (ex > xx)
4856 ex = xx;
4857 if (sx > xx)
4858 sx = xx;
4859
4860 if (sx < ex) {
4861 for (i = sx; i < ex; i++) {
4862 grid_get_cell(gd, i, sy, &gc);
4863 if (gc.flags & GRID_FLAG_PADDING)
4864 continue;
4865 utf8_copy(&ud, &gc.data);
4866 if (ud.size == 1 && (gc.attr & GRID_ATTR_CHARSET)) {
4867 s = tty_acs_get(NULL, ud.data[0]);
4868 if (s != NULL && strlen(s) <= sizeof ud.data) {
4869 ud.size = strlen(s);
4870 memcpy(ud.data, s, ud.size);
4871 }
4872 }
4873
4874 *buf = xrealloc(*buf, (*off) + ud.size);
4875 memcpy(*buf + *off, ud.data, ud.size);
4876 *off += ud.size;
4877 }
4878 }
4879
4880 /* Only add a newline if the line wasn't wrapped. */
4881 if (!wrapped || ex != xx) {
4882 *buf = xrealloc(*buf, (*off) + 1);
4883 (*buf)[(*off)++] = '\n';
4884 }
4885 }
4886
4887 static void
window_copy_clear_selection(struct window_mode_entry * wme)4888 window_copy_clear_selection(struct window_mode_entry *wme)
4889 {
4890 struct window_copy_mode_data *data = wme->data;
4891 u_int px, py;
4892
4893 screen_clear_selection(&data->screen);
4894
4895 data->cursordrag = CURSORDRAG_NONE;
4896 data->lineflag = LINE_SEL_NONE;
4897 data->selflag = SEL_CHAR;
4898
4899 py = screen_hsize(data->backing) + data->cy - data->oy;
4900 px = window_copy_find_length(wme, py);
4901 if (data->cx > px)
4902 window_copy_update_cursor(wme, px, data->cy);
4903 }
4904
4905 static int
window_copy_in_set(struct window_mode_entry * wme,u_int px,u_int py,const char * set)4906 window_copy_in_set(struct window_mode_entry *wme, u_int px, u_int py,
4907 const char *set)
4908 {
4909 struct window_copy_mode_data *data = wme->data;
4910 struct grid_cell gc;
4911
4912 grid_get_cell(data->backing->grid, px, py, &gc);
4913 if (gc.flags & GRID_FLAG_PADDING)
4914 return (0);
4915 return (utf8_cstrhas(set, &gc.data));
4916 }
4917
4918 static u_int
window_copy_find_length(struct window_mode_entry * wme,u_int py)4919 window_copy_find_length(struct window_mode_entry *wme, u_int py)
4920 {
4921 struct window_copy_mode_data *data = wme->data;
4922
4923 return (grid_line_length(data->backing->grid, py));
4924 }
4925
4926 static void
window_copy_cursor_start_of_line(struct window_mode_entry * wme)4927 window_copy_cursor_start_of_line(struct window_mode_entry *wme)
4928 {
4929 struct window_copy_mode_data *data = wme->data;
4930 struct screen *back_s = data->backing;
4931 struct grid_reader gr;
4932 u_int px, py, oldy, hsize;
4933
4934 px = data->cx;
4935 hsize = screen_hsize(back_s);
4936 py = hsize + data->cy - data->oy;
4937 oldy = data->cy;
4938
4939 grid_reader_start(&gr, back_s->grid, px, py);
4940 grid_reader_cursor_start_of_line(&gr, 1);
4941 grid_reader_get_cursor(&gr, &px, &py);
4942 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
4943 }
4944
4945 static void
window_copy_cursor_back_to_indentation(struct window_mode_entry * wme)4946 window_copy_cursor_back_to_indentation(struct window_mode_entry *wme)
4947 {
4948 struct window_copy_mode_data *data = wme->data;
4949 struct screen *back_s = data->backing;
4950 struct grid_reader gr;
4951 u_int px, py, oldy, hsize;
4952
4953 px = data->cx;
4954 hsize = screen_hsize(back_s);
4955 py = hsize + data->cy - data->oy;
4956 oldy = data->cy;
4957
4958 grid_reader_start(&gr, back_s->grid, px, py);
4959 grid_reader_cursor_back_to_indentation(&gr);
4960 grid_reader_get_cursor(&gr, &px, &py);
4961 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
4962 }
4963
4964 static void
window_copy_cursor_end_of_line(struct window_mode_entry * wme)4965 window_copy_cursor_end_of_line(struct window_mode_entry *wme)
4966 {
4967 struct window_copy_mode_data *data = wme->data;
4968 struct screen *back_s = data->backing;
4969 struct grid_reader gr;
4970 u_int px, py, oldy, hsize;
4971
4972 px = data->cx;
4973 hsize = screen_hsize(back_s);
4974 py = hsize + data->cy - data->oy;
4975 oldy = data->cy;
4976
4977 grid_reader_start(&gr, back_s->grid, px, py);
4978 if (data->screen.sel != NULL && data->rectflag)
4979 grid_reader_cursor_end_of_line(&gr, 1, 1);
4980 else
4981 grid_reader_cursor_end_of_line(&gr, 1, 0);
4982 grid_reader_get_cursor(&gr, &px, &py);
4983 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
4984 data->oy, oldy, px, py, 0);
4985 }
4986
4987 static void
window_copy_other_end(struct window_mode_entry * wme)4988 window_copy_other_end(struct window_mode_entry *wme)
4989 {
4990 struct window_copy_mode_data *data = wme->data;
4991 struct screen *s = &data->screen;
4992 u_int selx, sely, cy, yy, hsize;
4993
4994 if (s->sel == NULL && data->lineflag == LINE_SEL_NONE)
4995 return;
4996
4997 if (data->lineflag == LINE_SEL_LEFT_RIGHT)
4998 data->lineflag = LINE_SEL_RIGHT_LEFT;
4999 else if (data->lineflag == LINE_SEL_RIGHT_LEFT)
5000 data->lineflag = LINE_SEL_LEFT_RIGHT;
5001
5002 switch (data->cursordrag) {
5003 case CURSORDRAG_NONE:
5004 case CURSORDRAG_SEL:
5005 data->cursordrag = CURSORDRAG_ENDSEL;
5006 break;
5007 case CURSORDRAG_ENDSEL:
5008 data->cursordrag = CURSORDRAG_SEL;
5009 break;
5010 }
5011
5012 selx = data->endselx;
5013 sely = data->endsely;
5014 if (data->cursordrag == CURSORDRAG_SEL) {
5015 selx = data->selx;
5016 sely = data->sely;
5017 }
5018
5019 cy = data->cy;
5020 yy = screen_hsize(data->backing) + data->cy - data->oy;
5021
5022 data->cx = selx;
5023
5024 hsize = screen_hsize(data->backing);
5025 if (sely < hsize - data->oy) { /* above */
5026 data->oy = hsize - sely;
5027 data->cy = 0;
5028 } else if (sely > hsize - data->oy + screen_size_y(s)) { /* below */
5029 data->oy = hsize - sely + screen_size_y(s) - 1;
5030 data->cy = screen_size_y(s) - 1;
5031 } else
5032 data->cy = cy + sely - yy;
5033
5034 window_copy_update_selection(wme, 1, 1);
5035 window_copy_redraw_screen(wme);
5036 }
5037
5038 static void
window_copy_cursor_left(struct window_mode_entry * wme)5039 window_copy_cursor_left(struct window_mode_entry *wme)
5040 {
5041 struct window_copy_mode_data *data = wme->data;
5042 struct screen *back_s = data->backing;
5043 struct grid_reader gr;
5044 u_int px, py, oldy, hsize;
5045
5046 px = data->cx;
5047 hsize = screen_hsize(back_s);
5048 py = hsize + data->cy - data->oy;
5049 oldy = data->cy;
5050
5051 grid_reader_start(&gr, back_s->grid, px, py);
5052 grid_reader_cursor_left(&gr, 1);
5053 grid_reader_get_cursor(&gr, &px, &py);
5054 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
5055 }
5056
5057 static void
window_copy_cursor_right(struct window_mode_entry * wme,int all)5058 window_copy_cursor_right(struct window_mode_entry *wme, int all)
5059 {
5060 struct window_copy_mode_data *data = wme->data;
5061 struct screen *back_s = data->backing;
5062 struct grid_reader gr;
5063 u_int px, py, oldy, hsize;
5064
5065 px = data->cx;
5066 hsize = screen_hsize(back_s);
5067 py = hsize + data->cy - data->oy;
5068 oldy = data->cy;
5069
5070 grid_reader_start(&gr, back_s->grid, px, py);
5071 grid_reader_cursor_right(&gr, 1, all);
5072 grid_reader_get_cursor(&gr, &px, &py);
5073 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
5074 data->oy, oldy, px, py, 0);
5075 }
5076
5077 static void
window_copy_cursor_up(struct window_mode_entry * wme,int scroll_only)5078 window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only)
5079 {
5080 struct window_copy_mode_data *data = wme->data;
5081 struct screen *s = &data->screen;
5082 u_int ox, oy, px, py;
5083 int norectsel;
5084
5085 norectsel = data->screen.sel == NULL || !data->rectflag;
5086 oy = screen_hsize(data->backing) + data->cy - data->oy;
5087 ox = window_copy_find_length(wme, oy);
5088 if (norectsel && data->cx != ox) {
5089 data->lastcx = data->cx;
5090 data->lastsx = ox;
5091 }
5092
5093 if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely)
5094 window_copy_other_end(wme);
5095
5096 if (scroll_only || data->cy == 0) {
5097 if (norectsel)
5098 data->cx = data->lastcx;
5099 window_copy_scroll_down(wme, 1);
5100 if (scroll_only) {
5101 if (data->cy == screen_size_y(s) - 1)
5102 window_copy_redraw_lines(wme, data->cy, 1);
5103 else
5104 window_copy_redraw_lines(wme, data->cy, 2);
5105 }
5106 } else {
5107 if (norectsel) {
5108 window_copy_update_cursor(wme, data->lastcx,
5109 data->cy - 1);
5110 } else
5111 window_copy_update_cursor(wme, data->cx, data->cy - 1);
5112 if (window_copy_update_selection(wme, 1, 0)) {
5113 if (data->cy == screen_size_y(s) - 1)
5114 window_copy_redraw_lines(wme, data->cy, 1);
5115 else
5116 window_copy_redraw_lines(wme, data->cy, 2);
5117 }
5118 }
5119
5120 if (norectsel) {
5121 py = screen_hsize(data->backing) + data->cy - data->oy;
5122 px = window_copy_find_length(wme, py);
5123 if ((data->cx >= data->lastsx && data->cx != px) ||
5124 data->cx > px)
5125 {
5126 window_copy_update_cursor(wme, px, data->cy);
5127 if (window_copy_update_selection(wme, 1, 0))
5128 window_copy_redraw_lines(wme, data->cy, 1);
5129 }
5130 }
5131
5132 if (data->lineflag == LINE_SEL_LEFT_RIGHT)
5133 {
5134 py = screen_hsize(data->backing) + data->cy - data->oy;
5135 if (data->rectflag)
5136 px = screen_size_x(data->backing);
5137 else
5138 px = window_copy_find_length(wme, py);
5139 window_copy_update_cursor(wme, px, data->cy);
5140 if (window_copy_update_selection(wme, 1, 0))
5141 window_copy_redraw_lines(wme, data->cy, 1);
5142 }
5143 else if (data->lineflag == LINE_SEL_RIGHT_LEFT)
5144 {
5145 window_copy_update_cursor(wme, 0, data->cy);
5146 if (window_copy_update_selection(wme, 1, 0))
5147 window_copy_redraw_lines(wme, data->cy, 1);
5148 }
5149 }
5150
5151 static void
window_copy_cursor_down(struct window_mode_entry * wme,int scroll_only)5152 window_copy_cursor_down(struct window_mode_entry *wme, int scroll_only)
5153 {
5154 struct window_copy_mode_data *data = wme->data;
5155 struct screen *s = &data->screen;
5156 u_int ox, oy, px, py;
5157 int norectsel;
5158
5159 norectsel = data->screen.sel == NULL || !data->rectflag;
5160 oy = screen_hsize(data->backing) + data->cy - data->oy;
5161 ox = window_copy_find_length(wme, oy);
5162 if (norectsel && data->cx != ox) {
5163 data->lastcx = data->cx;
5164 data->lastsx = ox;
5165 }
5166
5167 if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely)
5168 window_copy_other_end(wme);
5169
5170 if (scroll_only || data->cy == screen_size_y(s) - 1) {
5171 if (norectsel)
5172 data->cx = data->lastcx;
5173 window_copy_scroll_up(wme, 1);
5174 if (scroll_only && data->cy > 0)
5175 window_copy_redraw_lines(wme, data->cy - 1, 2);
5176 } else {
5177 if (norectsel) {
5178 window_copy_update_cursor(wme, data->lastcx,
5179 data->cy + 1);
5180 } else
5181 window_copy_update_cursor(wme, data->cx, data->cy + 1);
5182 if (window_copy_update_selection(wme, 1, 0))
5183 window_copy_redraw_lines(wme, data->cy - 1, 2);
5184 }
5185
5186 if (norectsel) {
5187 py = screen_hsize(data->backing) + data->cy - data->oy;
5188 px = window_copy_find_length(wme, py);
5189 if ((data->cx >= data->lastsx && data->cx != px) ||
5190 data->cx > px)
5191 {
5192 window_copy_update_cursor(wme, px, data->cy);
5193 if (window_copy_update_selection(wme, 1, 0))
5194 window_copy_redraw_lines(wme, data->cy, 1);
5195 }
5196 }
5197
5198 if (data->lineflag == LINE_SEL_LEFT_RIGHT)
5199 {
5200 py = screen_hsize(data->backing) + data->cy - data->oy;
5201 if (data->rectflag)
5202 px = screen_size_x(data->backing);
5203 else
5204 px = window_copy_find_length(wme, py);
5205 window_copy_update_cursor(wme, px, data->cy);
5206 if (window_copy_update_selection(wme, 1, 0))
5207 window_copy_redraw_lines(wme, data->cy, 1);
5208 }
5209 else if (data->lineflag == LINE_SEL_RIGHT_LEFT)
5210 {
5211 window_copy_update_cursor(wme, 0, data->cy);
5212 if (window_copy_update_selection(wme, 1, 0))
5213 window_copy_redraw_lines(wme, data->cy, 1);
5214 }
5215 }
5216
5217 static void
window_copy_cursor_jump(struct window_mode_entry * wme)5218 window_copy_cursor_jump(struct window_mode_entry *wme)
5219 {
5220 struct window_copy_mode_data *data = wme->data;
5221 struct screen *back_s = data->backing;
5222 struct grid_reader gr;
5223 u_int px, py, oldy, hsize;
5224
5225 px = data->cx + 1;
5226 hsize = screen_hsize(back_s);
5227 py = hsize + data->cy - data->oy;
5228 oldy = data->cy;
5229
5230 grid_reader_start(&gr, back_s->grid, px, py);
5231 if (grid_reader_cursor_jump(&gr, data->jumpchar)) {
5232 grid_reader_get_cursor(&gr, &px, &py);
5233 window_copy_acquire_cursor_down(wme, hsize,
5234 screen_size_y(back_s), data->oy, oldy, px, py, 0);
5235 }
5236 }
5237
5238 static void
window_copy_cursor_jump_back(struct window_mode_entry * wme)5239 window_copy_cursor_jump_back(struct window_mode_entry *wme)
5240 {
5241 struct window_copy_mode_data *data = wme->data;
5242 struct screen *back_s = data->backing;
5243 struct grid_reader gr;
5244 u_int px, py, oldy, hsize;
5245
5246 px = data->cx;
5247 hsize = screen_hsize(back_s);
5248 py = hsize + data->cy - data->oy;
5249 oldy = data->cy;
5250
5251 grid_reader_start(&gr, back_s->grid, px, py);
5252 grid_reader_cursor_left(&gr, 0);
5253 if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) {
5254 grid_reader_get_cursor(&gr, &px, &py);
5255 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px,
5256 py);
5257 }
5258 }
5259
5260 static void
window_copy_cursor_jump_to(struct window_mode_entry * wme)5261 window_copy_cursor_jump_to(struct window_mode_entry *wme)
5262 {
5263 struct window_copy_mode_data *data = wme->data;
5264 struct screen *back_s = data->backing;
5265 struct grid_reader gr;
5266 u_int px, py, oldy, hsize;
5267
5268 px = data->cx + 2;
5269 hsize = screen_hsize(back_s);
5270 py = hsize + data->cy - data->oy;
5271 oldy = data->cy;
5272
5273 grid_reader_start(&gr, back_s->grid, px, py);
5274 if (grid_reader_cursor_jump(&gr, data->jumpchar)) {
5275 grid_reader_cursor_left(&gr, 1);
5276 grid_reader_get_cursor(&gr, &px, &py);
5277 window_copy_acquire_cursor_down(wme, hsize,
5278 screen_size_y(back_s), data->oy, oldy, px, py, 0);
5279 }
5280 }
5281
5282 static void
window_copy_cursor_jump_to_back(struct window_mode_entry * wme)5283 window_copy_cursor_jump_to_back(struct window_mode_entry *wme)
5284 {
5285 struct window_copy_mode_data *data = wme->data;
5286 struct screen *back_s = data->backing;
5287 struct grid_reader gr;
5288 u_int px, py, oldy, hsize;
5289
5290 px = data->cx;
5291 hsize = screen_hsize(back_s);
5292 py = hsize + data->cy - data->oy;
5293 oldy = data->cy;
5294
5295 grid_reader_start(&gr, back_s->grid, px, py);
5296 grid_reader_cursor_left(&gr, 0);
5297 grid_reader_cursor_left(&gr, 0);
5298 if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) {
5299 grid_reader_cursor_right(&gr, 1, 0);
5300 grid_reader_get_cursor(&gr, &px, &py);
5301 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px,
5302 py);
5303 }
5304 }
5305
5306 static void
window_copy_cursor_next_word(struct window_mode_entry * wme,const char * separators)5307 window_copy_cursor_next_word(struct window_mode_entry *wme,
5308 const char *separators)
5309 {
5310 struct window_copy_mode_data *data = wme->data;
5311 struct screen *back_s = data->backing;
5312 struct grid_reader gr;
5313 u_int px, py, oldy, hsize;
5314
5315 px = data->cx;
5316 hsize = screen_hsize(back_s);
5317 py = hsize + data->cy - data->oy;
5318 oldy = data->cy;
5319
5320 grid_reader_start(&gr, back_s->grid, px, py);
5321 grid_reader_cursor_next_word(&gr, separators);
5322 grid_reader_get_cursor(&gr, &px, &py);
5323 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
5324 data->oy, oldy, px, py, 0);
5325 }
5326
5327 /* Compute the next place where a word ends. */
5328 static void
window_copy_cursor_next_word_end_pos(struct window_mode_entry * wme,const char * separators,u_int * ppx,u_int * ppy)5329 window_copy_cursor_next_word_end_pos(struct window_mode_entry *wme,
5330 const char *separators, u_int *ppx, u_int *ppy)
5331 {
5332 struct window_pane *wp = wme->wp;
5333 struct window_copy_mode_data *data = wme->data;
5334 struct options *oo = wp->window->options;
5335 struct screen *back_s = data->backing;
5336 struct grid_reader gr;
5337 u_int px, py, hsize;
5338
5339 px = data->cx;
5340 hsize = screen_hsize(back_s);
5341 py = hsize + data->cy - data->oy;
5342
5343 grid_reader_start(&gr, back_s->grid, px, py);
5344 if (options_get_number(oo, "mode-keys") == MODEKEY_VI) {
5345 if (!grid_reader_in_set(&gr, WHITESPACE))
5346 grid_reader_cursor_right(&gr, 0, 0);
5347 grid_reader_cursor_next_word_end(&gr, separators);
5348 grid_reader_cursor_left(&gr, 1);
5349 } else
5350 grid_reader_cursor_next_word_end(&gr, separators);
5351 grid_reader_get_cursor(&gr, &px, &py);
5352 *ppx = px;
5353 *ppy = py;
5354 }
5355
5356 /* Move to the next place where a word ends. */
5357 static void
window_copy_cursor_next_word_end(struct window_mode_entry * wme,const char * separators,int no_reset)5358 window_copy_cursor_next_word_end(struct window_mode_entry *wme,
5359 const char *separators, int no_reset)
5360 {
5361 struct window_pane *wp = wme->wp;
5362 struct window_copy_mode_data *data = wme->data;
5363 struct options *oo = wp->window->options;
5364 struct screen *back_s = data->backing;
5365 struct grid_reader gr;
5366 u_int px, py, oldy, hsize;
5367
5368 px = data->cx;
5369 hsize = screen_hsize(back_s);
5370 py = hsize + data->cy - data->oy;
5371 oldy = data->cy;
5372
5373 grid_reader_start(&gr, back_s->grid, px, py);
5374 if (options_get_number(oo, "mode-keys") == MODEKEY_VI) {
5375 if (!grid_reader_in_set(&gr, WHITESPACE))
5376 grid_reader_cursor_right(&gr, 0, 0);
5377 grid_reader_cursor_next_word_end(&gr, separators);
5378 grid_reader_cursor_left(&gr, 1);
5379 } else
5380 grid_reader_cursor_next_word_end(&gr, separators);
5381 grid_reader_get_cursor(&gr, &px, &py);
5382 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
5383 data->oy, oldy, px, py, no_reset);
5384 }
5385
5386 /* Compute the previous place where a word begins. */
5387 static void
window_copy_cursor_previous_word_pos(struct window_mode_entry * wme,const char * separators,u_int * ppx,u_int * ppy)5388 window_copy_cursor_previous_word_pos(struct window_mode_entry *wme,
5389 const char *separators, u_int *ppx, u_int *ppy)
5390 {
5391 struct window_copy_mode_data *data = wme->data;
5392 struct screen *back_s = data->backing;
5393 struct grid_reader gr;
5394 u_int px, py, hsize;
5395
5396 px = data->cx;
5397 hsize = screen_hsize(back_s);
5398 py = hsize + data->cy - data->oy;
5399
5400 grid_reader_start(&gr, back_s->grid, px, py);
5401 grid_reader_cursor_previous_word(&gr, separators, /* already= */ 0,
5402 /* stop_at_eol= */ 1);
5403 grid_reader_get_cursor(&gr, &px, &py);
5404 *ppx = px;
5405 *ppy = py;
5406 }
5407
5408 /* Move to the previous place where a word begins. */
5409 static void
window_copy_cursor_previous_word(struct window_mode_entry * wme,const char * separators,int already)5410 window_copy_cursor_previous_word(struct window_mode_entry *wme,
5411 const char *separators, int already)
5412 {
5413 struct window_copy_mode_data *data = wme->data;
5414 struct window *w = wme->wp->window;
5415 struct screen *back_s = data->backing;
5416 struct grid_reader gr;
5417 u_int px, py, oldy, hsize;
5418 int stop_at_eol;
5419
5420 if (options_get_number(w->options, "mode-keys") == MODEKEY_EMACS)
5421 stop_at_eol = 1;
5422 else
5423 stop_at_eol = 0;
5424
5425 px = data->cx;
5426 hsize = screen_hsize(back_s);
5427 py = hsize + data->cy - data->oy;
5428 oldy = data->cy;
5429
5430 grid_reader_start(&gr, back_s->grid, px, py);
5431 grid_reader_cursor_previous_word(&gr, separators, already, stop_at_eol);
5432 grid_reader_get_cursor(&gr, &px, &py);
5433 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
5434 }
5435
5436 static void
window_copy_cursor_prompt(struct window_mode_entry * wme,int direction,const char * args)5437 window_copy_cursor_prompt(struct window_mode_entry *wme, int direction,
5438 const char *args)
5439 {
5440 struct window_copy_mode_data *data = wme->data;
5441 struct screen *s = data->backing;
5442 struct grid *gd = s->grid;
5443 u_int end_line;
5444 u_int line = gd->hsize - data->oy + data->cy;
5445 int add, line_flag;
5446
5447 if (args != NULL && strcmp(args, "-o") == 0)
5448 line_flag = GRID_LINE_START_OUTPUT;
5449 else
5450 line_flag = GRID_LINE_START_PROMPT;
5451
5452 if (direction == 0) { /* up */
5453 add = -1;
5454 end_line = 0;
5455 } else { /* down */
5456 add = 1;
5457 end_line = gd->hsize + gd->sy - 1;
5458 }
5459
5460 if (line == end_line)
5461 return;
5462 for (;;) {
5463 if (line == end_line)
5464 return;
5465 line += add;
5466
5467 if (grid_get_line(gd, line)->flags & line_flag)
5468 break;
5469 }
5470
5471 data->cx = 0;
5472 if (line > gd->hsize) {
5473 data->cy = line - gd->hsize;
5474 data->oy = 0;
5475 } else {
5476 data->cy = 0;
5477 data->oy = gd->hsize - line;
5478 }
5479
5480 window_copy_update_selection(wme, 1, 0);
5481 window_copy_redraw_screen(wme);
5482 }
5483
5484 static void
window_copy_scroll_up(struct window_mode_entry * wme,u_int ny)5485 window_copy_scroll_up(struct window_mode_entry *wme, u_int ny)
5486 {
5487 struct window_pane *wp = wme->wp;
5488 struct window_copy_mode_data *data = wme->data;
5489 struct screen *s = &data->screen;
5490 struct screen_write_ctx ctx;
5491
5492 if (data->oy < ny)
5493 ny = data->oy;
5494 if (ny == 0)
5495 return;
5496 data->oy -= ny;
5497
5498 if (data->searchmark != NULL && !data->timeout)
5499 window_copy_search_marks(wme, NULL, data->searchregex, 1);
5500 window_copy_update_selection(wme, 0, 0);
5501
5502 screen_write_start_pane(&ctx, wp, NULL);
5503 screen_write_cursormove(&ctx, 0, 0, 0);
5504 screen_write_deleteline(&ctx, ny, 8);
5505 window_copy_write_lines(wme, &ctx, screen_size_y(s) - ny, ny);
5506 window_copy_write_line(wme, &ctx, 0);
5507 if (screen_size_y(s) > 1)
5508 window_copy_write_line(wme, &ctx, 1);
5509 if (screen_size_y(s) > 3)
5510 window_copy_write_line(wme, &ctx, screen_size_y(s) - 2);
5511 if (s->sel != NULL && screen_size_y(s) > ny)
5512 window_copy_write_line(wme, &ctx, screen_size_y(s) - ny - 1);
5513 screen_write_cursormove(&ctx, data->cx, data->cy, 0);
5514 screen_write_stop(&ctx);
5515 }
5516
5517 static void
window_copy_scroll_down(struct window_mode_entry * wme,u_int ny)5518 window_copy_scroll_down(struct window_mode_entry *wme, u_int ny)
5519 {
5520 struct window_pane *wp = wme->wp;
5521 struct window_copy_mode_data *data = wme->data;
5522 struct screen *s = &data->screen;
5523 struct screen_write_ctx ctx;
5524
5525 if (ny > screen_hsize(data->backing))
5526 return;
5527
5528 if (data->oy > screen_hsize(data->backing) - ny)
5529 ny = screen_hsize(data->backing) - data->oy;
5530 if (ny == 0)
5531 return;
5532 data->oy += ny;
5533
5534 if (data->searchmark != NULL && !data->timeout)
5535 window_copy_search_marks(wme, NULL, data->searchregex, 1);
5536 window_copy_update_selection(wme, 0, 0);
5537
5538 screen_write_start_pane(&ctx, wp, NULL);
5539 screen_write_cursormove(&ctx, 0, 0, 0);
5540 screen_write_insertline(&ctx, ny, 8);
5541 window_copy_write_lines(wme, &ctx, 0, ny);
5542 if (s->sel != NULL && screen_size_y(s) > ny)
5543 window_copy_write_line(wme, &ctx, ny);
5544 else if (ny == 1) /* nuke position */
5545 window_copy_write_line(wme, &ctx, 1);
5546 screen_write_cursormove(&ctx, data->cx, data->cy, 0);
5547 screen_write_stop(&ctx);
5548 }
5549
5550 static void
window_copy_rectangle_set(struct window_mode_entry * wme,int rectflag)5551 window_copy_rectangle_set(struct window_mode_entry *wme, int rectflag)
5552 {
5553 struct window_copy_mode_data *data = wme->data;
5554 u_int px, py;
5555
5556 data->rectflag = rectflag;
5557
5558 py = screen_hsize(data->backing) + data->cy - data->oy;
5559 px = window_copy_find_length(wme, py);
5560 if (data->cx > px)
5561 window_copy_update_cursor(wme, px, data->cy);
5562
5563 window_copy_update_selection(wme, 1, 0);
5564 window_copy_redraw_screen(wme);
5565 }
5566
5567 static void
window_copy_move_mouse(struct mouse_event * m)5568 window_copy_move_mouse(struct mouse_event *m)
5569 {
5570 struct window_pane *wp;
5571 struct window_mode_entry *wme;
5572 u_int x, y;
5573
5574 wp = cmd_mouse_pane(m, NULL, NULL);
5575 if (wp == NULL)
5576 return;
5577 wme = TAILQ_FIRST(&wp->modes);
5578 if (wme == NULL)
5579 return;
5580 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode)
5581 return;
5582
5583 if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
5584 return;
5585
5586 window_copy_update_cursor(wme, x, y);
5587 }
5588
5589 void
window_copy_start_drag(struct client * c,struct mouse_event * m)5590 window_copy_start_drag(struct client *c, struct mouse_event *m)
5591 {
5592 struct window_pane *wp;
5593 struct window_mode_entry *wme;
5594 struct window_copy_mode_data *data;
5595 u_int x, y, yg;
5596
5597 if (c == NULL)
5598 return;
5599
5600 wp = cmd_mouse_pane(m, NULL, NULL);
5601 if (wp == NULL)
5602 return;
5603 wme = TAILQ_FIRST(&wp->modes);
5604 if (wme == NULL)
5605 return;
5606 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode)
5607 return;
5608
5609 if (cmd_mouse_at(wp, m, &x, &y, 1) != 0)
5610 return;
5611
5612 c->tty.mouse_drag_update = window_copy_drag_update;
5613 c->tty.mouse_drag_release = window_copy_drag_release;
5614
5615 data = wme->data;
5616 yg = screen_hsize(data->backing) + y - data->oy;
5617 if (x < data->selrx || x > data->endselrx || yg != data->selry)
5618 data->selflag = SEL_CHAR;
5619 switch (data->selflag) {
5620 case SEL_WORD:
5621 if (data->separators != NULL) {
5622 window_copy_update_cursor(wme, x, y);
5623 window_copy_cursor_previous_word_pos(wme,
5624 data->separators, &x, &y);
5625 y -= screen_hsize(data->backing) - data->oy;
5626 }
5627 window_copy_update_cursor(wme, x, y);
5628 break;
5629 case SEL_LINE:
5630 window_copy_update_cursor(wme, 0, y);
5631 break;
5632 case SEL_CHAR:
5633 window_copy_update_cursor(wme, x, y);
5634 window_copy_start_selection(wme);
5635 break;
5636 }
5637
5638 window_copy_redraw_screen(wme);
5639 window_copy_drag_update(c, m);
5640 }
5641
5642 static void
window_copy_drag_update(struct client * c,struct mouse_event * m)5643 window_copy_drag_update(struct client *c, struct mouse_event *m)
5644 {
5645 struct window_pane *wp;
5646 struct window_mode_entry *wme;
5647 struct window_copy_mode_data *data;
5648 u_int x, y, old_cx, old_cy;
5649 struct timeval tv = {
5650 .tv_usec = WINDOW_COPY_DRAG_REPEAT_TIME
5651 };
5652
5653 if (c == NULL)
5654 return;
5655
5656 wp = cmd_mouse_pane(m, NULL, NULL);
5657 if (wp == NULL)
5658 return;
5659 wme = TAILQ_FIRST(&wp->modes);
5660 if (wme == NULL)
5661 return;
5662 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode)
5663 return;
5664
5665 data = wme->data;
5666 evtimer_del(&data->dragtimer);
5667
5668 if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
5669 return;
5670 old_cx = data->cx;
5671 old_cy = data->cy;
5672
5673 window_copy_update_cursor(wme, x, y);
5674 if (window_copy_update_selection(wme, 1, 0))
5675 window_copy_redraw_selection(wme, old_cy);
5676 if (old_cy != data->cy || old_cx == data->cx) {
5677 if (y == 0) {
5678 evtimer_add(&data->dragtimer, &tv);
5679 window_copy_cursor_up(wme, 1);
5680 } else if (y == screen_size_y(&data->screen) - 1) {
5681 evtimer_add(&data->dragtimer, &tv);
5682 window_copy_cursor_down(wme, 1);
5683 }
5684 }
5685 }
5686
5687 static void
window_copy_drag_release(struct client * c,struct mouse_event * m)5688 window_copy_drag_release(struct client *c, struct mouse_event *m)
5689 {
5690 struct window_pane *wp;
5691 struct window_mode_entry *wme;
5692 struct window_copy_mode_data *data;
5693
5694 if (c == NULL)
5695 return;
5696
5697 wp = cmd_mouse_pane(m, NULL, NULL);
5698 if (wp == NULL)
5699 return;
5700 wme = TAILQ_FIRST(&wp->modes);
5701 if (wme == NULL)
5702 return;
5703 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode)
5704 return;
5705
5706 data = wme->data;
5707 evtimer_del(&data->dragtimer);
5708 }
5709
5710 static void
window_copy_jump_to_mark(struct window_mode_entry * wme)5711 window_copy_jump_to_mark(struct window_mode_entry *wme)
5712 {
5713 struct window_copy_mode_data *data = wme->data;
5714 u_int tmx, tmy;
5715
5716 tmx = data->cx;
5717 tmy = screen_hsize(data->backing) + data->cy - data->oy;
5718 data->cx = data->mx;
5719 if (data->my < screen_hsize(data->backing)) {
5720 data->cy = 0;
5721 data->oy = screen_hsize(data->backing) - data->my;
5722 } else {
5723 data->cy = data->my - screen_hsize(data->backing);
5724 data->oy = 0;
5725 }
5726 data->mx = tmx;
5727 data->my = tmy;
5728 data->showmark = 1;
5729 window_copy_update_selection(wme, 0, 0);
5730 window_copy_redraw_screen(wme);
5731 }
5732
5733 /* Scroll up if the cursor went off the visible screen. */
5734 static void
window_copy_acquire_cursor_up(struct window_mode_entry * wme,u_int hsize,u_int oy,u_int oldy,u_int px,u_int py)5735 window_copy_acquire_cursor_up(struct window_mode_entry *wme, u_int hsize,
5736 u_int oy, u_int oldy, u_int px, u_int py)
5737 {
5738 u_int cy, yy, ny, nd;
5739
5740 yy = hsize - oy;
5741 if (py < yy) {
5742 ny = yy - py;
5743 cy = 0;
5744 nd = 1;
5745 } else {
5746 ny = 0;
5747 cy = py - yy;
5748 nd = oldy - cy + 1;
5749 }
5750 while (ny > 0) {
5751 window_copy_cursor_up(wme, 1);
5752 ny--;
5753 }
5754 window_copy_update_cursor(wme, px, cy);
5755 if (window_copy_update_selection(wme, 1, 0))
5756 window_copy_redraw_lines(wme, cy, nd);
5757 }
5758
5759 /* Scroll down if the cursor went off the visible screen. */
5760 static void
window_copy_acquire_cursor_down(struct window_mode_entry * wme,u_int hsize,u_int sy,u_int oy,u_int oldy,u_int px,u_int py,int no_reset)5761 window_copy_acquire_cursor_down(struct window_mode_entry *wme, u_int hsize,
5762 u_int sy, u_int oy, u_int oldy, u_int px, u_int py, int no_reset)
5763 {
5764 u_int cy, yy, ny, nd;
5765
5766 cy = py - hsize + oy;
5767 yy = sy - 1;
5768 if (cy > yy) {
5769 ny = cy - yy;
5770 oldy = yy;
5771 nd = 1;
5772 } else {
5773 ny = 0;
5774 nd = cy - oldy + 1;
5775 }
5776 while (ny > 0) {
5777 window_copy_cursor_down(wme, 1);
5778 ny--;
5779 }
5780 if (cy > yy)
5781 window_copy_update_cursor(wme, px, yy);
5782 else
5783 window_copy_update_cursor(wme, px, cy);
5784 if (window_copy_update_selection(wme, 1, no_reset))
5785 window_copy_redraw_lines(wme, oldy, nd);
5786 }
5787