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