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