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