1 /* $OpenBSD: window-copy.c,v 1.322 2021/04/05 08:43:48 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 *, int, 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 *ws; /* 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 u_int np = wme->prefix; 1106 1107 for (; np != 0; np--) 1108 window_copy_cursor_right(wme, 0); 1109 return (WINDOW_COPY_CMD_NOTHING); 1110 } 1111 1112 static enum window_copy_cmd_action 1113 window_copy_cmd_cursor_up(struct window_copy_cmd_state *cs) 1114 { 1115 struct window_mode_entry *wme = cs->wme; 1116 u_int np = wme->prefix; 1117 1118 for (; np != 0; np--) 1119 window_copy_cursor_up(wme, 0); 1120 return (WINDOW_COPY_CMD_NOTHING); 1121 } 1122 1123 static enum window_copy_cmd_action 1124 window_copy_cmd_end_of_line(struct window_copy_cmd_state *cs) 1125 { 1126 struct window_mode_entry *wme = cs->wme; 1127 1128 window_copy_cursor_end_of_line(wme); 1129 return (WINDOW_COPY_CMD_NOTHING); 1130 } 1131 1132 static enum window_copy_cmd_action 1133 window_copy_cmd_halfpage_down(struct window_copy_cmd_state *cs) 1134 { 1135 struct window_mode_entry *wme = cs->wme; 1136 struct window_copy_mode_data *data = wme->data; 1137 u_int np = wme->prefix; 1138 1139 for (; np != 0; np--) { 1140 if (window_copy_pagedown(wme, 1, data->scroll_exit)) 1141 return (WINDOW_COPY_CMD_CANCEL); 1142 } 1143 return (WINDOW_COPY_CMD_NOTHING); 1144 } 1145 1146 static enum window_copy_cmd_action 1147 window_copy_cmd_halfpage_down_and_cancel(struct window_copy_cmd_state *cs) 1148 { 1149 1150 struct window_mode_entry *wme = cs->wme; 1151 u_int np = wme->prefix; 1152 1153 for (; np != 0; np--) { 1154 if (window_copy_pagedown(wme, 1, 1)) 1155 return (WINDOW_COPY_CMD_CANCEL); 1156 } 1157 return (WINDOW_COPY_CMD_NOTHING); 1158 } 1159 1160 static enum window_copy_cmd_action 1161 window_copy_cmd_halfpage_up(struct window_copy_cmd_state *cs) 1162 { 1163 struct window_mode_entry *wme = cs->wme; 1164 u_int np = wme->prefix; 1165 1166 for (; np != 0; np--) 1167 window_copy_pageup1(wme, 1); 1168 return (WINDOW_COPY_CMD_NOTHING); 1169 } 1170 1171 static enum window_copy_cmd_action 1172 window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs) 1173 { 1174 struct window_mode_entry *wme = cs->wme; 1175 struct window_copy_mode_data *data = wme->data; 1176 struct screen *s = data->backing; 1177 u_int oy; 1178 1179 oy = screen_hsize(s) + data->cy - data->oy; 1180 if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely) 1181 window_copy_other_end(wme); 1182 1183 data->cy = screen_size_y(&data->screen) - 1; 1184 data->cx = window_copy_find_length(wme, screen_hsize(s) + data->cy); 1185 data->oy = 0; 1186 1187 if (data->searchmark != NULL && !data->timeout) 1188 window_copy_search_marks(wme, NULL, data->searchregex, 1); 1189 window_copy_update_selection(wme, 1, 0); 1190 return (WINDOW_COPY_CMD_REDRAW); 1191 } 1192 1193 static enum window_copy_cmd_action 1194 window_copy_cmd_history_top(struct window_copy_cmd_state *cs) 1195 { 1196 struct window_mode_entry *wme = cs->wme; 1197 struct window_copy_mode_data *data = wme->data; 1198 u_int oy; 1199 1200 oy = screen_hsize(data->backing) + data->cy - data->oy; 1201 if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely) 1202 window_copy_other_end(wme); 1203 1204 data->cy = 0; 1205 data->cx = 0; 1206 data->oy = screen_hsize(data->backing); 1207 1208 if (data->searchmark != NULL && !data->timeout) 1209 window_copy_search_marks(wme, NULL, data->searchregex, 1); 1210 window_copy_update_selection(wme, 1, 0); 1211 return (WINDOW_COPY_CMD_REDRAW); 1212 } 1213 1214 static enum window_copy_cmd_action 1215 window_copy_cmd_jump_again(struct window_copy_cmd_state *cs) 1216 { 1217 struct window_mode_entry *wme = cs->wme; 1218 struct window_copy_mode_data *data = wme->data; 1219 u_int np = wme->prefix; 1220 1221 switch (data->jumptype) { 1222 case WINDOW_COPY_JUMPFORWARD: 1223 for (; np != 0; np--) 1224 window_copy_cursor_jump(wme); 1225 break; 1226 case WINDOW_COPY_JUMPBACKWARD: 1227 for (; np != 0; np--) 1228 window_copy_cursor_jump_back(wme); 1229 break; 1230 case WINDOW_COPY_JUMPTOFORWARD: 1231 for (; np != 0; np--) 1232 window_copy_cursor_jump_to(wme); 1233 break; 1234 case WINDOW_COPY_JUMPTOBACKWARD: 1235 for (; np != 0; np--) 1236 window_copy_cursor_jump_to_back(wme); 1237 break; 1238 } 1239 return (WINDOW_COPY_CMD_NOTHING); 1240 } 1241 1242 static enum window_copy_cmd_action 1243 window_copy_cmd_jump_reverse(struct window_copy_cmd_state *cs) 1244 { 1245 struct window_mode_entry *wme = cs->wme; 1246 struct window_copy_mode_data *data = wme->data; 1247 u_int np = wme->prefix; 1248 1249 switch (data->jumptype) { 1250 case WINDOW_COPY_JUMPFORWARD: 1251 for (; np != 0; np--) 1252 window_copy_cursor_jump_back(wme); 1253 break; 1254 case WINDOW_COPY_JUMPBACKWARD: 1255 for (; np != 0; np--) 1256 window_copy_cursor_jump(wme); 1257 break; 1258 case WINDOW_COPY_JUMPTOFORWARD: 1259 for (; np != 0; np--) 1260 window_copy_cursor_jump_to_back(wme); 1261 break; 1262 case WINDOW_COPY_JUMPTOBACKWARD: 1263 for (; np != 0; np--) 1264 window_copy_cursor_jump_to(wme); 1265 break; 1266 } 1267 return (WINDOW_COPY_CMD_NOTHING); 1268 } 1269 1270 static enum window_copy_cmd_action 1271 window_copy_cmd_middle_line(struct window_copy_cmd_state *cs) 1272 { 1273 struct window_mode_entry *wme = cs->wme; 1274 struct window_copy_mode_data *data = wme->data; 1275 1276 data->cx = 0; 1277 data->cy = (screen_size_y(&data->screen) - 1) / 2; 1278 1279 window_copy_update_selection(wme, 1, 0); 1280 return (WINDOW_COPY_CMD_REDRAW); 1281 } 1282 1283 static enum window_copy_cmd_action 1284 window_copy_cmd_previous_matching_bracket(struct window_copy_cmd_state *cs) 1285 { 1286 struct window_mode_entry *wme = cs->wme; 1287 u_int np = wme->prefix; 1288 struct window_copy_mode_data *data = wme->data; 1289 struct screen *s = data->backing; 1290 char open[] = "{[(", close[] = "}])"; 1291 char tried, found, start, *cp; 1292 u_int px, py, xx, n; 1293 struct grid_cell gc; 1294 int failed; 1295 1296 for (; np != 0; np--) { 1297 /* Get cursor position and line length. */ 1298 px = data->cx; 1299 py = screen_hsize(s) + data->cy - data->oy; 1300 xx = window_copy_find_length(wme, py); 1301 if (xx == 0) 1302 break; 1303 1304 /* 1305 * Get the current character. If not on a bracket, try the 1306 * previous. If still not, then behave like previous-word. 1307 */ 1308 tried = 0; 1309 retry: 1310 grid_get_cell(s->grid, px, py, &gc); 1311 if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING)) 1312 cp = NULL; 1313 else { 1314 found = *gc.data.data; 1315 cp = strchr(close, found); 1316 } 1317 if (cp == NULL) { 1318 if (data->modekeys == MODEKEY_EMACS) { 1319 if (!tried && px > 0) { 1320 px--; 1321 tried = 1; 1322 goto retry; 1323 } 1324 window_copy_cursor_previous_word(wme, "}]) ", 1); 1325 } 1326 continue; 1327 } 1328 start = open[cp - close]; 1329 1330 /* Walk backward until the matching bracket is reached. */ 1331 n = 1; 1332 failed = 0; 1333 do { 1334 if (px == 0) { 1335 if (py == 0) { 1336 failed = 1; 1337 break; 1338 } 1339 do { 1340 py--; 1341 xx = window_copy_find_length(wme, py); 1342 } while (xx == 0 && py > 0); 1343 if (xx == 0 && py == 0) { 1344 failed = 1; 1345 break; 1346 } 1347 px = xx - 1; 1348 } else 1349 px--; 1350 1351 grid_get_cell(s->grid, px, py, &gc); 1352 if (gc.data.size == 1 && 1353 (~gc.flags & GRID_FLAG_PADDING)) { 1354 if (*gc.data.data == found) 1355 n++; 1356 else if (*gc.data.data == start) 1357 n--; 1358 } 1359 } while (n != 0); 1360 1361 /* Move the cursor to the found location if any. */ 1362 if (!failed) 1363 window_copy_scroll_to(wme, px, py, 0); 1364 } 1365 1366 return (WINDOW_COPY_CMD_NOTHING); 1367 } 1368 1369 static enum window_copy_cmd_action 1370 window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state *cs) 1371 { 1372 struct window_mode_entry *wme = cs->wme; 1373 u_int np = wme->prefix; 1374 struct window_copy_mode_data *data = wme->data; 1375 struct screen *s = data->backing; 1376 char open[] = "{[(", close[] = "}])"; 1377 char tried, found, end, *cp; 1378 u_int px, py, xx, yy, sx, sy, n; 1379 struct grid_cell gc; 1380 int failed; 1381 struct grid_line *gl; 1382 1383 for (; np != 0; np--) { 1384 /* Get cursor position and line length. */ 1385 px = data->cx; 1386 py = screen_hsize(s) + data->cy - data->oy; 1387 xx = window_copy_find_length(wme, py); 1388 yy = screen_hsize(s) + screen_size_y(s) - 1; 1389 if (xx == 0) 1390 break; 1391 1392 /* 1393 * Get the current character. If not on a bracket, try the 1394 * next. If still not, then behave like next-word. 1395 */ 1396 tried = 0; 1397 retry: 1398 grid_get_cell(s->grid, px, py, &gc); 1399 if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING)) 1400 cp = NULL; 1401 else { 1402 found = *gc.data.data; 1403 1404 /* 1405 * In vi mode, attempt to move to previous bracket if a 1406 * closing bracket is found first. If this fails, 1407 * return to the original cursor position. 1408 */ 1409 cp = strchr(close, found); 1410 if (cp != NULL && data->modekeys == MODEKEY_VI) { 1411 sx = data->cx; 1412 sy = screen_hsize(s) + data->cy - data->oy; 1413 1414 window_copy_scroll_to(wme, px, py, 0); 1415 window_copy_cmd_previous_matching_bracket(cs); 1416 1417 px = data->cx; 1418 py = screen_hsize(s) + data->cy - data->oy; 1419 grid_get_cell(s->grid, px, py, &gc); 1420 if (gc.data.size == 1 && 1421 (~gc.flags & GRID_FLAG_PADDING) && 1422 strchr(close, *gc.data.data) != NULL) 1423 window_copy_scroll_to(wme, sx, sy, 0); 1424 break; 1425 } 1426 1427 cp = strchr(open, found); 1428 } 1429 if (cp == NULL) { 1430 if (data->modekeys == MODEKEY_EMACS) { 1431 if (!tried && px <= xx) { 1432 px++; 1433 tried = 1; 1434 goto retry; 1435 } 1436 window_copy_cursor_next_word_end(wme, "{[( ", 1437 0); 1438 continue; 1439 } 1440 /* For vi, continue searching for bracket until EOL. */ 1441 if (px > xx) { 1442 if (py == yy) 1443 continue; 1444 gl = grid_get_line(s->grid, py); 1445 if (~gl->flags & GRID_LINE_WRAPPED) 1446 continue; 1447 if (gl->cellsize > s->grid->sx) 1448 continue; 1449 px = 0; 1450 py++; 1451 xx = window_copy_find_length(wme, py); 1452 } else 1453 px++; 1454 goto retry; 1455 } 1456 end = close[cp - open]; 1457 1458 /* Walk forward until the matching bracket is reached. */ 1459 n = 1; 1460 failed = 0; 1461 do { 1462 if (px > xx) { 1463 if (py == yy) { 1464 failed = 1; 1465 break; 1466 } 1467 px = 0; 1468 py++; 1469 xx = window_copy_find_length(wme, py); 1470 } else 1471 px++; 1472 1473 grid_get_cell(s->grid, px, py, &gc); 1474 if (gc.data.size == 1 && 1475 (~gc.flags & GRID_FLAG_PADDING)) { 1476 if (*gc.data.data == found) 1477 n++; 1478 else if (*gc.data.data == end) 1479 n--; 1480 } 1481 } while (n != 0); 1482 1483 /* Move the cursor to the found location if any. */ 1484 if (!failed) 1485 window_copy_scroll_to(wme, px, py, 0); 1486 } 1487 1488 return (WINDOW_COPY_CMD_NOTHING); 1489 } 1490 1491 static enum window_copy_cmd_action 1492 window_copy_cmd_next_paragraph(struct window_copy_cmd_state *cs) 1493 { 1494 struct window_mode_entry *wme = cs->wme; 1495 u_int np = wme->prefix; 1496 1497 for (; np != 0; np--) 1498 window_copy_next_paragraph(wme); 1499 return (WINDOW_COPY_CMD_NOTHING); 1500 } 1501 1502 static enum window_copy_cmd_action 1503 window_copy_cmd_next_space(struct window_copy_cmd_state *cs) 1504 { 1505 struct window_mode_entry *wme = cs->wme; 1506 u_int np = wme->prefix; 1507 1508 for (; np != 0; np--) 1509 window_copy_cursor_next_word(wme, " "); 1510 return (WINDOW_COPY_CMD_NOTHING); 1511 } 1512 1513 static enum window_copy_cmd_action 1514 window_copy_cmd_next_space_end(struct window_copy_cmd_state *cs) 1515 { 1516 struct window_mode_entry *wme = cs->wme; 1517 u_int np = wme->prefix; 1518 1519 for (; np != 0; np--) 1520 window_copy_cursor_next_word_end(wme, " ", 0); 1521 return (WINDOW_COPY_CMD_NOTHING); 1522 } 1523 1524 static enum window_copy_cmd_action 1525 window_copy_cmd_next_word(struct window_copy_cmd_state *cs) 1526 { 1527 struct window_mode_entry *wme = cs->wme; 1528 struct session *s = cs->s; 1529 u_int np = wme->prefix; 1530 const char *ws; 1531 1532 ws = options_get_string(s->options, "word-separators"); 1533 for (; np != 0; np--) 1534 window_copy_cursor_next_word(wme, ws); 1535 return (WINDOW_COPY_CMD_NOTHING); 1536 } 1537 1538 static enum window_copy_cmd_action 1539 window_copy_cmd_next_word_end(struct window_copy_cmd_state *cs) 1540 { 1541 struct window_mode_entry *wme = cs->wme; 1542 struct session *s = cs->s; 1543 u_int np = wme->prefix; 1544 const char *ws; 1545 1546 ws = options_get_string(s->options, "word-separators"); 1547 for (; np != 0; np--) 1548 window_copy_cursor_next_word_end(wme, ws, 0); 1549 return (WINDOW_COPY_CMD_NOTHING); 1550 } 1551 1552 static enum window_copy_cmd_action 1553 window_copy_cmd_other_end(struct window_copy_cmd_state *cs) 1554 { 1555 struct window_mode_entry *wme = cs->wme; 1556 u_int np = wme->prefix; 1557 struct window_copy_mode_data *data = wme->data; 1558 1559 data->selflag = SEL_CHAR; 1560 if ((np % 2) != 0) 1561 window_copy_other_end(wme); 1562 return (WINDOW_COPY_CMD_NOTHING); 1563 } 1564 1565 static enum window_copy_cmd_action 1566 window_copy_cmd_page_down(struct window_copy_cmd_state *cs) 1567 { 1568 struct window_mode_entry *wme = cs->wme; 1569 struct window_copy_mode_data *data = wme->data; 1570 u_int np = wme->prefix; 1571 1572 for (; np != 0; np--) { 1573 if (window_copy_pagedown(wme, 0, data->scroll_exit)) 1574 return (WINDOW_COPY_CMD_CANCEL); 1575 } 1576 return (WINDOW_COPY_CMD_NOTHING); 1577 } 1578 1579 static enum window_copy_cmd_action 1580 window_copy_cmd_page_down_and_cancel(struct window_copy_cmd_state *cs) 1581 { 1582 struct window_mode_entry *wme = cs->wme; 1583 u_int np = wme->prefix; 1584 1585 for (; np != 0; np--) { 1586 if (window_copy_pagedown(wme, 0, 1)) 1587 return (WINDOW_COPY_CMD_CANCEL); 1588 } 1589 return (WINDOW_COPY_CMD_NOTHING); 1590 } 1591 1592 static enum window_copy_cmd_action 1593 window_copy_cmd_page_up(struct window_copy_cmd_state *cs) 1594 { 1595 struct window_mode_entry *wme = cs->wme; 1596 u_int np = wme->prefix; 1597 1598 for (; np != 0; np--) 1599 window_copy_pageup1(wme, 0); 1600 return (WINDOW_COPY_CMD_NOTHING); 1601 } 1602 1603 static enum window_copy_cmd_action 1604 window_copy_cmd_previous_paragraph(struct window_copy_cmd_state *cs) 1605 { 1606 struct window_mode_entry *wme = cs->wme; 1607 u_int np = wme->prefix; 1608 1609 for (; np != 0; np--) 1610 window_copy_previous_paragraph(wme); 1611 return (WINDOW_COPY_CMD_NOTHING); 1612 } 1613 1614 static enum window_copy_cmd_action 1615 window_copy_cmd_previous_space(struct window_copy_cmd_state *cs) 1616 { 1617 struct window_mode_entry *wme = cs->wme; 1618 u_int np = wme->prefix; 1619 1620 for (; np != 0; np--) 1621 window_copy_cursor_previous_word(wme, " ", 1); 1622 return (WINDOW_COPY_CMD_NOTHING); 1623 } 1624 1625 static enum window_copy_cmd_action 1626 window_copy_cmd_previous_word(struct window_copy_cmd_state *cs) 1627 { 1628 struct window_mode_entry *wme = cs->wme; 1629 struct session *s = cs->s; 1630 u_int np = wme->prefix; 1631 const char *ws; 1632 1633 ws = options_get_string(s->options, "word-separators"); 1634 for (; np != 0; np--) 1635 window_copy_cursor_previous_word(wme, ws, 1); 1636 return (WINDOW_COPY_CMD_NOTHING); 1637 } 1638 1639 static enum window_copy_cmd_action 1640 window_copy_cmd_rectangle_on(struct window_copy_cmd_state *cs) 1641 { 1642 struct window_mode_entry *wme = cs->wme; 1643 struct window_copy_mode_data *data = wme->data; 1644 1645 data->lineflag = LINE_SEL_NONE; 1646 window_copy_rectangle_set(wme, 1); 1647 1648 return (WINDOW_COPY_CMD_NOTHING); 1649 } 1650 1651 static enum window_copy_cmd_action 1652 window_copy_cmd_rectangle_off(struct window_copy_cmd_state *cs) 1653 { 1654 struct window_mode_entry *wme = cs->wme; 1655 struct window_copy_mode_data *data = wme->data; 1656 1657 data->lineflag = LINE_SEL_NONE; 1658 window_copy_rectangle_set(wme, 0); 1659 1660 return (WINDOW_COPY_CMD_NOTHING); 1661 } 1662 1663 static enum window_copy_cmd_action 1664 window_copy_cmd_rectangle_toggle(struct window_copy_cmd_state *cs) 1665 { 1666 struct window_mode_entry *wme = cs->wme; 1667 struct window_copy_mode_data *data = wme->data; 1668 1669 data->lineflag = LINE_SEL_NONE; 1670 window_copy_rectangle_set(wme, !data->rectflag); 1671 1672 return (WINDOW_COPY_CMD_NOTHING); 1673 } 1674 1675 static enum window_copy_cmd_action 1676 window_copy_cmd_scroll_down(struct window_copy_cmd_state *cs) 1677 { 1678 struct window_mode_entry *wme = cs->wme; 1679 struct window_copy_mode_data *data = wme->data; 1680 u_int np = wme->prefix; 1681 1682 for (; np != 0; np--) 1683 window_copy_cursor_down(wme, 1); 1684 if (data->scroll_exit && data->oy == 0) 1685 return (WINDOW_COPY_CMD_CANCEL); 1686 return (WINDOW_COPY_CMD_NOTHING); 1687 } 1688 1689 static enum window_copy_cmd_action 1690 window_copy_cmd_scroll_down_and_cancel(struct window_copy_cmd_state *cs) 1691 { 1692 struct window_mode_entry *wme = cs->wme; 1693 struct window_copy_mode_data *data = wme->data; 1694 u_int np = wme->prefix; 1695 1696 for (; np != 0; np--) 1697 window_copy_cursor_down(wme, 1); 1698 if (data->oy == 0) 1699 return (WINDOW_COPY_CMD_CANCEL); 1700 return (WINDOW_COPY_CMD_NOTHING); 1701 } 1702 1703 static enum window_copy_cmd_action 1704 window_copy_cmd_scroll_up(struct window_copy_cmd_state *cs) 1705 { 1706 struct window_mode_entry *wme = cs->wme; 1707 u_int np = wme->prefix; 1708 1709 for (; np != 0; np--) 1710 window_copy_cursor_up(wme, 1); 1711 return (WINDOW_COPY_CMD_NOTHING); 1712 } 1713 1714 static enum window_copy_cmd_action 1715 window_copy_cmd_search_again(struct window_copy_cmd_state *cs) 1716 { 1717 struct window_mode_entry *wme = cs->wme; 1718 struct window_copy_mode_data *data = wme->data; 1719 u_int np = wme->prefix; 1720 1721 if (data->searchtype == WINDOW_COPY_SEARCHUP) { 1722 for (; np != 0; np--) 1723 window_copy_search_up(wme, data->searchregex); 1724 } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { 1725 for (; np != 0; np--) 1726 window_copy_search_down(wme, data->searchregex); 1727 } 1728 return (WINDOW_COPY_CMD_NOTHING); 1729 } 1730 1731 static enum window_copy_cmd_action 1732 window_copy_cmd_search_reverse(struct window_copy_cmd_state *cs) 1733 { 1734 struct window_mode_entry *wme = cs->wme; 1735 struct window_copy_mode_data *data = wme->data; 1736 u_int np = wme->prefix; 1737 1738 if (data->searchtype == WINDOW_COPY_SEARCHUP) { 1739 for (; np != 0; np--) 1740 window_copy_search_down(wme, data->searchregex); 1741 } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { 1742 for (; np != 0; np--) 1743 window_copy_search_up(wme, data->searchregex); 1744 } 1745 return (WINDOW_COPY_CMD_NOTHING); 1746 } 1747 1748 static enum window_copy_cmd_action 1749 window_copy_cmd_select_line(struct window_copy_cmd_state *cs) 1750 { 1751 struct window_mode_entry *wme = cs->wme; 1752 struct window_copy_mode_data *data = wme->data; 1753 u_int np = wme->prefix; 1754 1755 data->lineflag = LINE_SEL_LEFT_RIGHT; 1756 data->rectflag = 0; 1757 data->selflag = SEL_LINE; 1758 data->dx = data->cx; 1759 data->dy = screen_hsize(data->backing) + data->cy - data->oy; 1760 1761 window_copy_cursor_start_of_line(wme); 1762 data->selrx = data->cx; 1763 data->selry = screen_hsize(data->backing) + data->cy - data->oy; 1764 data->endselry = data->selry; 1765 window_copy_start_selection(wme); 1766 window_copy_cursor_end_of_line(wme); 1767 data->endselry = screen_hsize(data->backing) + data->cy - data->oy; 1768 data->endselrx = window_copy_find_length(wme, data->endselry); 1769 for (; np > 1; np--) { 1770 window_copy_cursor_down(wme, 0); 1771 window_copy_cursor_end_of_line(wme); 1772 } 1773 1774 return (WINDOW_COPY_CMD_REDRAW); 1775 } 1776 1777 static enum window_copy_cmd_action 1778 window_copy_cmd_select_word(struct window_copy_cmd_state *cs) 1779 { 1780 struct window_mode_entry *wme = cs->wme; 1781 struct session *s = cs->s; 1782 struct window_copy_mode_data *data = wme->data; 1783 u_int px, py, nextx, nexty; 1784 1785 data->lineflag = LINE_SEL_LEFT_RIGHT; 1786 data->rectflag = 0; 1787 data->selflag = SEL_WORD; 1788 data->dx = data->cx; 1789 data->dy = screen_hsize(data->backing) + data->cy - data->oy; 1790 1791 data->ws = options_get_string(s->options, "word-separators"); 1792 window_copy_cursor_previous_word(wme, data->ws, 0); 1793 px = data->cx; 1794 py = screen_hsize(data->backing) + data->cy - data->oy; 1795 data->selrx = px; 1796 data->selry = py; 1797 window_copy_start_selection(wme); 1798 1799 /* Handle single character words. */ 1800 nextx = px + 1; 1801 nexty = py; 1802 if (grid_get_line(data->backing->grid, nexty)->flags & 1803 GRID_LINE_WRAPPED && nextx > screen_size_x(data->backing) - 1) { 1804 nextx = 0; 1805 nexty++; 1806 } 1807 if (px >= window_copy_find_length(wme, py) || 1808 !window_copy_in_set(wme, nextx, nexty, data->ws)) 1809 window_copy_cursor_next_word_end(wme, data->ws, 1); 1810 else { 1811 window_copy_update_cursor(wme, px, data->cy); 1812 if (window_copy_update_selection(wme, 1, 1)) 1813 window_copy_redraw_lines(wme, data->cy, 1); 1814 } 1815 data->endselrx = data->cx; 1816 data->endselry = screen_hsize(data->backing) + data->cy - data->oy; 1817 if (data->dy > data->endselry) { 1818 data->dy = data->endselry; 1819 data->dx = data->endselrx; 1820 } else if (data->dx > data->endselrx) 1821 data->dx = data->endselrx; 1822 1823 return (WINDOW_COPY_CMD_REDRAW); 1824 } 1825 1826 static enum window_copy_cmd_action 1827 window_copy_cmd_set_mark(struct window_copy_cmd_state *cs) 1828 { 1829 struct window_copy_mode_data *data = cs->wme->data; 1830 1831 data->mx = data->cx; 1832 data->my = screen_hsize(data->backing) + data->cy - data->oy; 1833 data->showmark = 1; 1834 return (WINDOW_COPY_CMD_REDRAW); 1835 } 1836 1837 static enum window_copy_cmd_action 1838 window_copy_cmd_start_of_line(struct window_copy_cmd_state *cs) 1839 { 1840 struct window_mode_entry *wme = cs->wme; 1841 1842 window_copy_cursor_start_of_line(wme); 1843 return (WINDOW_COPY_CMD_NOTHING); 1844 } 1845 1846 static enum window_copy_cmd_action 1847 window_copy_cmd_top_line(struct window_copy_cmd_state *cs) 1848 { 1849 struct window_mode_entry *wme = cs->wme; 1850 struct window_copy_mode_data *data = wme->data; 1851 1852 data->cx = 0; 1853 data->cy = 0; 1854 1855 window_copy_update_selection(wme, 1, 0); 1856 return (WINDOW_COPY_CMD_REDRAW); 1857 } 1858 1859 static enum window_copy_cmd_action 1860 window_copy_cmd_copy_pipe_no_clear(struct window_copy_cmd_state *cs) 1861 { 1862 struct window_mode_entry *wme = cs->wme; 1863 struct client *c = cs->c; 1864 struct session *s = cs->s; 1865 struct winlink *wl = cs->wl; 1866 struct window_pane *wp = wme->wp; 1867 char *command = NULL; 1868 char *prefix = NULL; 1869 1870 if (cs->args->argc == 3) 1871 prefix = format_single(NULL, cs->args->argv[2], c, s, wl, wp); 1872 1873 if (s != NULL && cs->args->argc > 1 && *cs->args->argv[1] != '\0') 1874 command = format_single(NULL, cs->args->argv[1], c, s, wl, wp); 1875 window_copy_copy_pipe(wme, s, prefix, command); 1876 free(command); 1877 1878 free(prefix); 1879 return (WINDOW_COPY_CMD_NOTHING); 1880 } 1881 1882 static enum window_copy_cmd_action 1883 window_copy_cmd_copy_pipe(struct window_copy_cmd_state *cs) 1884 { 1885 struct window_mode_entry *wme = cs->wme; 1886 1887 window_copy_cmd_copy_pipe_no_clear(cs); 1888 window_copy_clear_selection(wme); 1889 return (WINDOW_COPY_CMD_REDRAW); 1890 } 1891 1892 static enum window_copy_cmd_action 1893 window_copy_cmd_copy_pipe_and_cancel(struct window_copy_cmd_state *cs) 1894 { 1895 struct window_mode_entry *wme = cs->wme; 1896 1897 window_copy_cmd_copy_pipe_no_clear(cs); 1898 window_copy_clear_selection(wme); 1899 return (WINDOW_COPY_CMD_CANCEL); 1900 } 1901 1902 static enum window_copy_cmd_action 1903 window_copy_cmd_pipe_no_clear(struct window_copy_cmd_state *cs) 1904 { 1905 struct window_mode_entry *wme = cs->wme; 1906 struct client *c = cs->c; 1907 struct session *s = cs->s; 1908 struct winlink *wl = cs->wl; 1909 struct window_pane *wp = wme->wp; 1910 char *command = NULL; 1911 1912 if (s != NULL && cs->args->argc > 1 && *cs->args->argv[1] != '\0') 1913 command = format_single(NULL, cs->args->argv[1], c, s, wl, wp); 1914 window_copy_pipe(wme, s, command); 1915 free(command); 1916 1917 return (WINDOW_COPY_CMD_NOTHING); 1918 } 1919 1920 static enum window_copy_cmd_action 1921 window_copy_cmd_pipe(struct window_copy_cmd_state *cs) 1922 { 1923 struct window_mode_entry *wme = cs->wme; 1924 1925 window_copy_cmd_pipe_no_clear(cs); 1926 window_copy_clear_selection(wme); 1927 return (WINDOW_COPY_CMD_REDRAW); 1928 } 1929 1930 static enum window_copy_cmd_action 1931 window_copy_cmd_pipe_and_cancel(struct window_copy_cmd_state *cs) 1932 { 1933 struct window_mode_entry *wme = cs->wme; 1934 1935 window_copy_cmd_pipe_no_clear(cs); 1936 window_copy_clear_selection(wme); 1937 return (WINDOW_COPY_CMD_CANCEL); 1938 } 1939 1940 static enum window_copy_cmd_action 1941 window_copy_cmd_goto_line(struct window_copy_cmd_state *cs) 1942 { 1943 struct window_mode_entry *wme = cs->wme; 1944 const char *argument = cs->args->argv[1]; 1945 1946 if (*argument != '\0') 1947 window_copy_goto_line(wme, argument); 1948 return (WINDOW_COPY_CMD_NOTHING); 1949 } 1950 1951 static enum window_copy_cmd_action 1952 window_copy_cmd_jump_backward(struct window_copy_cmd_state *cs) 1953 { 1954 struct window_mode_entry *wme = cs->wme; 1955 struct window_copy_mode_data *data = wme->data; 1956 u_int np = wme->prefix; 1957 const char *argument = cs->args->argv[1]; 1958 1959 if (*argument != '\0') { 1960 data->jumptype = WINDOW_COPY_JUMPBACKWARD; 1961 free(data->jumpchar); 1962 data->jumpchar = utf8_fromcstr(argument); 1963 for (; np != 0; np--) 1964 window_copy_cursor_jump_back(wme); 1965 } 1966 return (WINDOW_COPY_CMD_NOTHING); 1967 } 1968 1969 static enum window_copy_cmd_action 1970 window_copy_cmd_jump_forward(struct window_copy_cmd_state *cs) 1971 { 1972 struct window_mode_entry *wme = cs->wme; 1973 struct window_copy_mode_data *data = wme->data; 1974 u_int np = wme->prefix; 1975 const char *argument = cs->args->argv[1]; 1976 1977 if (*argument != '\0') { 1978 data->jumptype = WINDOW_COPY_JUMPFORWARD; 1979 free(data->jumpchar); 1980 data->jumpchar = utf8_fromcstr(argument); 1981 for (; np != 0; np--) 1982 window_copy_cursor_jump(wme); 1983 } 1984 return (WINDOW_COPY_CMD_NOTHING); 1985 } 1986 1987 static enum window_copy_cmd_action 1988 window_copy_cmd_jump_to_backward(struct window_copy_cmd_state *cs) 1989 { 1990 struct window_mode_entry *wme = cs->wme; 1991 struct window_copy_mode_data *data = wme->data; 1992 u_int np = wme->prefix; 1993 const char *argument = cs->args->argv[1]; 1994 1995 if (*argument != '\0') { 1996 data->jumptype = WINDOW_COPY_JUMPTOBACKWARD; 1997 free(data->jumpchar); 1998 data->jumpchar = utf8_fromcstr(argument); 1999 for (; np != 0; np--) 2000 window_copy_cursor_jump_to_back(wme); 2001 } 2002 return (WINDOW_COPY_CMD_NOTHING); 2003 } 2004 2005 static enum window_copy_cmd_action 2006 window_copy_cmd_jump_to_forward(struct window_copy_cmd_state *cs) 2007 { 2008 struct window_mode_entry *wme = cs->wme; 2009 struct window_copy_mode_data *data = wme->data; 2010 u_int np = wme->prefix; 2011 const char *argument = cs->args->argv[1]; 2012 2013 if (*argument != '\0') { 2014 data->jumptype = WINDOW_COPY_JUMPTOFORWARD; 2015 free(data->jumpchar); 2016 data->jumpchar = utf8_fromcstr(argument); 2017 for (; np != 0; np--) 2018 window_copy_cursor_jump_to(wme); 2019 } 2020 return (WINDOW_COPY_CMD_NOTHING); 2021 } 2022 2023 static enum window_copy_cmd_action 2024 window_copy_cmd_jump_to_mark(struct window_copy_cmd_state *cs) 2025 { 2026 struct window_mode_entry *wme = cs->wme; 2027 2028 window_copy_jump_to_mark(wme); 2029 return (WINDOW_COPY_CMD_NOTHING); 2030 } 2031 2032 static enum window_copy_cmd_action 2033 window_copy_cmd_search_backward(struct window_copy_cmd_state *cs) 2034 { 2035 struct window_mode_entry *wme = cs->wme; 2036 struct window_copy_mode_data *data = wme->data; 2037 u_int np = wme->prefix; 2038 2039 if (!window_copy_expand_search_string(cs)) 2040 return (WINDOW_COPY_CMD_NOTHING); 2041 2042 if (data->searchstr != NULL) { 2043 data->searchtype = WINDOW_COPY_SEARCHUP; 2044 data->searchregex = 1; 2045 data->timeout = 0; 2046 for (; np != 0; np--) 2047 window_copy_search_up(wme, 1); 2048 } 2049 return (WINDOW_COPY_CMD_NOTHING); 2050 } 2051 2052 static enum window_copy_cmd_action 2053 window_copy_cmd_search_backward_text(struct window_copy_cmd_state *cs) 2054 { 2055 struct window_mode_entry *wme = cs->wme; 2056 struct window_copy_mode_data *data = wme->data; 2057 u_int np = wme->prefix; 2058 2059 if (!window_copy_expand_search_string(cs)) 2060 return (WINDOW_COPY_CMD_NOTHING); 2061 2062 if (data->searchstr != NULL) { 2063 data->searchtype = WINDOW_COPY_SEARCHUP; 2064 data->searchregex = 0; 2065 data->timeout = 0; 2066 for (; np != 0; np--) 2067 window_copy_search_up(wme, 0); 2068 } 2069 return (WINDOW_COPY_CMD_NOTHING); 2070 } 2071 2072 static enum window_copy_cmd_action 2073 window_copy_cmd_search_forward(struct window_copy_cmd_state *cs) 2074 { 2075 struct window_mode_entry *wme = cs->wme; 2076 struct window_copy_mode_data *data = wme->data; 2077 u_int np = wme->prefix; 2078 2079 if (!window_copy_expand_search_string(cs)) 2080 return (WINDOW_COPY_CMD_NOTHING); 2081 2082 if (data->searchstr != NULL) { 2083 data->searchtype = WINDOW_COPY_SEARCHDOWN; 2084 data->searchregex = 1; 2085 data->timeout = 0; 2086 for (; np != 0; np--) 2087 window_copy_search_down(wme, 1); 2088 } 2089 return (WINDOW_COPY_CMD_NOTHING); 2090 } 2091 2092 static enum window_copy_cmd_action 2093 window_copy_cmd_search_forward_text(struct window_copy_cmd_state *cs) 2094 { 2095 struct window_mode_entry *wme = cs->wme; 2096 struct window_copy_mode_data *data = wme->data; 2097 u_int np = wme->prefix; 2098 2099 if (!window_copy_expand_search_string(cs)) 2100 return (WINDOW_COPY_CMD_NOTHING); 2101 2102 if (data->searchstr != NULL) { 2103 data->searchtype = WINDOW_COPY_SEARCHDOWN; 2104 data->searchregex = 0; 2105 data->timeout = 0; 2106 for (; np != 0; np--) 2107 window_copy_search_down(wme, 0); 2108 } 2109 return (WINDOW_COPY_CMD_NOTHING); 2110 } 2111 2112 static enum window_copy_cmd_action 2113 window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) 2114 { 2115 struct window_mode_entry *wme = cs->wme; 2116 struct window_copy_mode_data *data = wme->data; 2117 const char *argument = cs->args->argv[1]; 2118 const char *ss = data->searchstr; 2119 char prefix; 2120 enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING; 2121 2122 data->timeout = 0; 2123 2124 log_debug ("%s: %s", __func__, argument); 2125 2126 prefix = *argument++; 2127 if (data->searchx == -1 || data->searchy == -1) { 2128 data->searchx = data->cx; 2129 data->searchy = data->cy; 2130 data->searcho = data->oy; 2131 } else if (ss != NULL && strcmp(argument, ss) != 0) { 2132 data->cx = data->searchx; 2133 data->cy = data->searchy; 2134 data->oy = data->searcho; 2135 action = WINDOW_COPY_CMD_REDRAW; 2136 } 2137 if (*argument == '\0') { 2138 window_copy_clear_marks(wme); 2139 return (WINDOW_COPY_CMD_REDRAW); 2140 } 2141 switch (prefix) { 2142 case '=': 2143 case '-': 2144 data->searchtype = WINDOW_COPY_SEARCHUP; 2145 data->searchregex = 0; 2146 free(data->searchstr); 2147 data->searchstr = xstrdup(argument); 2148 if (!window_copy_search_up(wme, 0)) { 2149 window_copy_clear_marks(wme); 2150 return (WINDOW_COPY_CMD_REDRAW); 2151 } 2152 break; 2153 case '+': 2154 data->searchtype = WINDOW_COPY_SEARCHDOWN; 2155 data->searchregex = 0; 2156 free(data->searchstr); 2157 data->searchstr = xstrdup(argument); 2158 if (!window_copy_search_down(wme, 0)) { 2159 window_copy_clear_marks(wme); 2160 return (WINDOW_COPY_CMD_REDRAW); 2161 } 2162 break; 2163 } 2164 return (action); 2165 } 2166 2167 static enum window_copy_cmd_action 2168 window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) 2169 { 2170 struct window_mode_entry *wme = cs->wme; 2171 struct window_copy_mode_data *data = wme->data; 2172 const char *argument = cs->args->argv[1]; 2173 const char *ss = data->searchstr; 2174 char prefix; 2175 enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING; 2176 2177 data->timeout = 0; 2178 2179 log_debug ("%s: %s", __func__, argument); 2180 2181 prefix = *argument++; 2182 if (data->searchx == -1 || data->searchy == -1) { 2183 data->searchx = data->cx; 2184 data->searchy = data->cy; 2185 data->searcho = data->oy; 2186 } else if (ss != NULL && strcmp(argument, ss) != 0) { 2187 data->cx = data->searchx; 2188 data->cy = data->searchy; 2189 data->oy = data->searcho; 2190 action = WINDOW_COPY_CMD_REDRAW; 2191 } 2192 if (*argument == '\0') { 2193 window_copy_clear_marks(wme); 2194 return (WINDOW_COPY_CMD_REDRAW); 2195 } 2196 switch (prefix) { 2197 case '=': 2198 case '+': 2199 data->searchtype = WINDOW_COPY_SEARCHDOWN; 2200 data->searchregex = 0; 2201 free(data->searchstr); 2202 data->searchstr = xstrdup(argument); 2203 if (!window_copy_search_down(wme, 0)) { 2204 window_copy_clear_marks(wme); 2205 return (WINDOW_COPY_CMD_REDRAW); 2206 } 2207 break; 2208 case '-': 2209 data->searchtype = WINDOW_COPY_SEARCHUP; 2210 data->searchregex = 0; 2211 free(data->searchstr); 2212 data->searchstr = xstrdup(argument); 2213 if (!window_copy_search_up(wme, 0)) { 2214 window_copy_clear_marks(wme); 2215 return (WINDOW_COPY_CMD_REDRAW); 2216 } 2217 } 2218 return (action); 2219 } 2220 2221 static enum window_copy_cmd_action 2222 window_copy_cmd_refresh_from_pane(struct window_copy_cmd_state *cs) 2223 { 2224 struct window_mode_entry *wme = cs->wme; 2225 struct window_pane *wp = wme->swp; 2226 struct window_copy_mode_data *data = wme->data; 2227 2228 if (data->viewmode) 2229 return (WINDOW_COPY_CMD_NOTHING); 2230 2231 screen_free(data->backing); 2232 free(data->backing); 2233 data->backing = window_copy_clone_screen(&wp->base, &data->screen, NULL, 2234 NULL, wme->swp != wme->wp); 2235 2236 window_copy_size_changed(wme); 2237 return (WINDOW_COPY_CMD_REDRAW); 2238 } 2239 2240 static const struct { 2241 const char *command; 2242 int minargs; 2243 int maxargs; 2244 enum window_copy_cmd_clear clear; 2245 enum window_copy_cmd_action (*f)(struct window_copy_cmd_state *); 2246 } window_copy_cmd_table[] = { 2247 { "append-selection", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2248 window_copy_cmd_append_selection }, 2249 { "append-selection-and-cancel", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2250 window_copy_cmd_append_selection_and_cancel }, 2251 { "back-to-indentation", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2252 window_copy_cmd_back_to_indentation }, 2253 { "begin-selection", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2254 window_copy_cmd_begin_selection }, 2255 { "bottom-line", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2256 window_copy_cmd_bottom_line }, 2257 { "cancel", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2258 window_copy_cmd_cancel }, 2259 { "clear-selection", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2260 window_copy_cmd_clear_selection }, 2261 { "copy-end-of-line", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2262 window_copy_cmd_copy_end_of_line }, 2263 { "copy-line", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2264 window_copy_cmd_copy_line }, 2265 { "copy-pipe-no-clear", 0, 2, WINDOW_COPY_CMD_CLEAR_NEVER, 2266 window_copy_cmd_copy_pipe_no_clear }, 2267 { "copy-pipe", 0, 2, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2268 window_copy_cmd_copy_pipe }, 2269 { "copy-pipe-and-cancel", 0, 2, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2270 window_copy_cmd_copy_pipe_and_cancel }, 2271 { "copy-selection-no-clear", 0, 1, WINDOW_COPY_CMD_CLEAR_NEVER, 2272 window_copy_cmd_copy_selection_no_clear }, 2273 { "copy-selection", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2274 window_copy_cmd_copy_selection }, 2275 { "copy-selection-and-cancel", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2276 window_copy_cmd_copy_selection_and_cancel }, 2277 { "cursor-down", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2278 window_copy_cmd_cursor_down }, 2279 { "cursor-down-and-cancel", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2280 window_copy_cmd_cursor_down_and_cancel }, 2281 { "cursor-left", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2282 window_copy_cmd_cursor_left }, 2283 { "cursor-right", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2284 window_copy_cmd_cursor_right }, 2285 { "cursor-up", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2286 window_copy_cmd_cursor_up }, 2287 { "end-of-line", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2288 window_copy_cmd_end_of_line }, 2289 { "goto-line", 1, 1, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2290 window_copy_cmd_goto_line }, 2291 { "halfpage-down", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2292 window_copy_cmd_halfpage_down }, 2293 { "halfpage-down-and-cancel", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2294 window_copy_cmd_halfpage_down_and_cancel }, 2295 { "halfpage-up", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2296 window_copy_cmd_halfpage_up }, 2297 { "history-bottom", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2298 window_copy_cmd_history_bottom }, 2299 { "history-top", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2300 window_copy_cmd_history_top }, 2301 { "jump-again", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2302 window_copy_cmd_jump_again }, 2303 { "jump-backward", 1, 1, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2304 window_copy_cmd_jump_backward }, 2305 { "jump-forward", 1, 1, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2306 window_copy_cmd_jump_forward }, 2307 { "jump-reverse", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2308 window_copy_cmd_jump_reverse }, 2309 { "jump-to-backward", 1, 1, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2310 window_copy_cmd_jump_to_backward }, 2311 { "jump-to-forward", 1, 1, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2312 window_copy_cmd_jump_to_forward }, 2313 { "jump-to-mark", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2314 window_copy_cmd_jump_to_mark }, 2315 { "middle-line", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2316 window_copy_cmd_middle_line }, 2317 { "next-matching-bracket", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2318 window_copy_cmd_next_matching_bracket }, 2319 { "next-paragraph", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2320 window_copy_cmd_next_paragraph }, 2321 { "next-space", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2322 window_copy_cmd_next_space }, 2323 { "next-space-end", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2324 window_copy_cmd_next_space_end }, 2325 { "next-word", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2326 window_copy_cmd_next_word }, 2327 { "next-word-end", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2328 window_copy_cmd_next_word_end }, 2329 { "other-end", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2330 window_copy_cmd_other_end }, 2331 { "page-down", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2332 window_copy_cmd_page_down }, 2333 { "page-down-and-cancel", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2334 window_copy_cmd_page_down_and_cancel }, 2335 { "page-up", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2336 window_copy_cmd_page_up }, 2337 { "pipe-no-clear", 0, 1, WINDOW_COPY_CMD_CLEAR_NEVER, 2338 window_copy_cmd_pipe_no_clear }, 2339 { "pipe", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2340 window_copy_cmd_pipe }, 2341 { "pipe-and-cancel", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2342 window_copy_cmd_pipe_and_cancel }, 2343 { "previous-matching-bracket", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2344 window_copy_cmd_previous_matching_bracket }, 2345 { "previous-paragraph", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2346 window_copy_cmd_previous_paragraph }, 2347 { "previous-space", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2348 window_copy_cmd_previous_space }, 2349 { "previous-word", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2350 window_copy_cmd_previous_word }, 2351 { "rectangle-on", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2352 window_copy_cmd_rectangle_on }, 2353 { "rectangle-off", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2354 window_copy_cmd_rectangle_off }, 2355 { "rectangle-toggle", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2356 window_copy_cmd_rectangle_toggle }, 2357 { "refresh-from-pane", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2358 window_copy_cmd_refresh_from_pane }, 2359 { "scroll-down", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2360 window_copy_cmd_scroll_down }, 2361 { "scroll-down-and-cancel", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2362 window_copy_cmd_scroll_down_and_cancel }, 2363 { "scroll-up", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2364 window_copy_cmd_scroll_up }, 2365 { "search-again", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2366 window_copy_cmd_search_again }, 2367 { "search-backward", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2368 window_copy_cmd_search_backward }, 2369 { "search-backward-text", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2370 window_copy_cmd_search_backward_text }, 2371 { "search-backward-incremental", 1, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2372 window_copy_cmd_search_backward_incremental }, 2373 { "search-forward", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2374 window_copy_cmd_search_forward }, 2375 { "search-forward-text", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2376 window_copy_cmd_search_forward_text }, 2377 { "search-forward-incremental", 1, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2378 window_copy_cmd_search_forward_incremental }, 2379 { "search-reverse", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2380 window_copy_cmd_search_reverse }, 2381 { "select-line", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2382 window_copy_cmd_select_line }, 2383 { "select-word", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2384 window_copy_cmd_select_word }, 2385 { "set-mark", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2386 window_copy_cmd_set_mark }, 2387 { "start-of-line", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2388 window_copy_cmd_start_of_line }, 2389 { "stop-selection", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2390 window_copy_cmd_stop_selection }, 2391 { "top-line", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2392 window_copy_cmd_top_line }, 2393 }; 2394 2395 static void 2396 window_copy_command(struct window_mode_entry *wme, struct client *c, 2397 struct session *s, struct winlink *wl, struct args *args, 2398 struct mouse_event *m) 2399 { 2400 struct window_copy_mode_data *data = wme->data; 2401 struct window_copy_cmd_state cs; 2402 enum window_copy_cmd_action action; 2403 enum window_copy_cmd_clear clear = WINDOW_COPY_CMD_CLEAR_NEVER; 2404 const char *command; 2405 u_int i; 2406 int keys; 2407 2408 if (args->argc == 0) 2409 return; 2410 command = args->argv[0]; 2411 2412 if (m != NULL && m->valid && !MOUSE_WHEEL(m->b)) 2413 window_copy_move_mouse(m); 2414 2415 cs.wme = wme; 2416 cs.args = args; 2417 cs.m = m; 2418 2419 cs.c = c; 2420 cs.s = s; 2421 cs.wl = wl; 2422 2423 action = WINDOW_COPY_CMD_NOTHING; 2424 for (i = 0; i < nitems(window_copy_cmd_table); i++) { 2425 if (strcmp(window_copy_cmd_table[i].command, command) == 0) { 2426 if (args->argc - 1 < window_copy_cmd_table[i].minargs || 2427 args->argc - 1 > window_copy_cmd_table[i].maxargs) 2428 break; 2429 clear = window_copy_cmd_table[i].clear; 2430 action = window_copy_cmd_table[i].f (&cs); 2431 break; 2432 } 2433 } 2434 2435 if (strncmp(command, "search-", 7) != 0 && data->searchmark != NULL) { 2436 keys = options_get_number(wme->wp->window->options, "mode-keys"); 2437 if (clear == WINDOW_COPY_CMD_CLEAR_EMACS_ONLY && 2438 keys == MODEKEY_VI) 2439 clear = WINDOW_COPY_CMD_CLEAR_NEVER; 2440 if (clear != WINDOW_COPY_CMD_CLEAR_NEVER) { 2441 window_copy_clear_marks(wme); 2442 data->searchx = data->searchy = -1; 2443 } 2444 if (action == WINDOW_COPY_CMD_NOTHING) 2445 action = WINDOW_COPY_CMD_REDRAW; 2446 } 2447 wme->prefix = 1; 2448 2449 if (action == WINDOW_COPY_CMD_CANCEL) 2450 window_pane_reset_mode(wme->wp); 2451 else if (action == WINDOW_COPY_CMD_REDRAW) 2452 window_copy_redraw_screen(wme); 2453 } 2454 2455 static void 2456 window_copy_scroll_to(struct window_mode_entry *wme, u_int px, u_int py, 2457 int no_redraw) 2458 { 2459 struct window_copy_mode_data *data = wme->data; 2460 struct grid *gd = data->backing->grid; 2461 u_int offset, gap; 2462 2463 data->cx = px; 2464 2465 if (py >= gd->hsize - data->oy && py < gd->hsize - data->oy + gd->sy) 2466 data->cy = py - (gd->hsize - data->oy); 2467 else { 2468 gap = gd->sy / 4; 2469 if (py < gd->sy) { 2470 offset = 0; 2471 data->cy = py; 2472 } else if (py > gd->hsize + gd->sy - gap) { 2473 offset = gd->hsize; 2474 data->cy = py - gd->hsize; 2475 } else { 2476 offset = py + gap - gd->sy; 2477 data->cy = py - offset; 2478 } 2479 data->oy = gd->hsize - offset; 2480 } 2481 2482 if (!no_redraw && data->searchmark != NULL && !data->timeout) 2483 window_copy_search_marks(wme, NULL, data->searchregex, 1); 2484 window_copy_update_selection(wme, 1, 0); 2485 if (!no_redraw) 2486 window_copy_redraw_screen(wme); 2487 } 2488 2489 static int 2490 window_copy_search_compare(struct grid *gd, u_int px, u_int py, 2491 struct grid *sgd, u_int spx, int cis) 2492 { 2493 struct grid_cell gc, sgc; 2494 const struct utf8_data *ud, *sud; 2495 2496 grid_get_cell(gd, px, py, &gc); 2497 ud = &gc.data; 2498 grid_get_cell(sgd, spx, 0, &sgc); 2499 sud = &sgc.data; 2500 2501 if (ud->size != sud->size || ud->width != sud->width) 2502 return (0); 2503 2504 if (cis && ud->size == 1) 2505 return (tolower(ud->data[0]) == sud->data[0]); 2506 2507 return (memcmp(ud->data, sud->data, ud->size) == 0); 2508 } 2509 2510 static int 2511 window_copy_search_lr(struct grid *gd, struct grid *sgd, u_int *ppx, u_int py, 2512 u_int first, u_int last, int cis) 2513 { 2514 u_int ax, bx, px, pywrap, endline; 2515 int matched; 2516 struct grid_line *gl; 2517 2518 endline = gd->hsize + gd->sy - 1; 2519 for (ax = first; ax < last; ax++) { 2520 for (bx = 0; bx < sgd->sx; bx++) { 2521 px = ax + bx; 2522 pywrap = py; 2523 /* Wrap line. */ 2524 while (px >= gd->sx && pywrap < endline) { 2525 gl = grid_get_line(gd, pywrap); 2526 if (~gl->flags & GRID_LINE_WRAPPED) 2527 break; 2528 px -= gd->sx; 2529 pywrap++; 2530 } 2531 /* We have run off the end of the grid. */ 2532 if (px >= gd->sx) 2533 break; 2534 matched = window_copy_search_compare(gd, px, pywrap, 2535 sgd, bx, cis); 2536 if (!matched) 2537 break; 2538 } 2539 if (bx == sgd->sx) { 2540 *ppx = ax; 2541 return (1); 2542 } 2543 } 2544 return (0); 2545 } 2546 2547 static int 2548 window_copy_search_rl(struct grid *gd, 2549 struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis) 2550 { 2551 u_int ax, bx, px, pywrap, endline; 2552 int matched; 2553 struct grid_line *gl; 2554 2555 endline = gd->hsize + gd->sy - 1; 2556 for (ax = last; ax > first; ax--) { 2557 for (bx = 0; bx < sgd->sx; bx++) { 2558 px = ax - 1 + bx; 2559 pywrap = py; 2560 /* Wrap line. */ 2561 while (px >= gd->sx && pywrap < endline) { 2562 gl = grid_get_line(gd, pywrap); 2563 if (~gl->flags & GRID_LINE_WRAPPED) 2564 break; 2565 px -= gd->sx; 2566 pywrap++; 2567 } 2568 /* We have run off the end of the grid. */ 2569 if (px >= gd->sx) 2570 break; 2571 matched = window_copy_search_compare(gd, px, pywrap, 2572 sgd, bx, cis); 2573 if (!matched) 2574 break; 2575 } 2576 if (bx == sgd->sx) { 2577 *ppx = ax - 1; 2578 return (1); 2579 } 2580 } 2581 return (0); 2582 } 2583 2584 static int 2585 window_copy_search_lr_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py, 2586 u_int first, u_int last, regex_t *reg) 2587 { 2588 int eflags = 0; 2589 u_int endline, foundx, foundy, len, pywrap, size = 1; 2590 char *buf; 2591 regmatch_t regmatch; 2592 struct grid_line *gl; 2593 2594 /* 2595 * This can happen during search if the last match was the last 2596 * character on a line. 2597 */ 2598 if (first >= last) 2599 return (0); 2600 2601 /* Set flags for regex search. */ 2602 if (first != 0) 2603 eflags |= REG_NOTBOL; 2604 2605 /* Need to look at the entire string. */ 2606 buf = xmalloc(size); 2607 buf[0] = '\0'; 2608 buf = window_copy_stringify(gd, py, first, gd->sx, buf, &size); 2609 len = gd->sx - first; 2610 endline = gd->hsize + gd->sy - 1; 2611 pywrap = py; 2612 while (buf != NULL && pywrap <= endline) { 2613 gl = grid_get_line(gd, pywrap); 2614 if (~gl->flags & GRID_LINE_WRAPPED) 2615 break; 2616 pywrap++; 2617 buf = window_copy_stringify(gd, pywrap, 0, gd->sx, buf, &size); 2618 len += gd->sx; 2619 } 2620 2621 if (regexec(reg, buf, 1, ®match, eflags) == 0 && 2622 regmatch.rm_so != regmatch.rm_eo) { 2623 foundx = first; 2624 foundy = py; 2625 window_copy_cstrtocellpos(gd, len, &foundx, &foundy, 2626 buf + regmatch.rm_so); 2627 if (foundy == py && foundx < last) { 2628 *ppx = foundx; 2629 len -= foundx - first; 2630 window_copy_cstrtocellpos(gd, len, &foundx, &foundy, 2631 buf + regmatch.rm_eo); 2632 *psx = foundx; 2633 while (foundy > py) { 2634 *psx += gd->sx; 2635 foundy--; 2636 } 2637 *psx -= *ppx; 2638 free(buf); 2639 return (1); 2640 } 2641 } 2642 2643 free(buf); 2644 *ppx = 0; 2645 *psx = 0; 2646 return (0); 2647 } 2648 2649 static int 2650 window_copy_search_rl_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py, 2651 u_int first, u_int last, regex_t *reg) 2652 { 2653 int eflags = 0; 2654 u_int endline, len, pywrap, size = 1; 2655 char *buf; 2656 struct grid_line *gl; 2657 2658 /* Set flags for regex search. */ 2659 if (first != 0) 2660 eflags |= REG_NOTBOL; 2661 2662 /* Need to look at the entire string. */ 2663 buf = xmalloc(size); 2664 buf[0] = '\0'; 2665 buf = window_copy_stringify(gd, py, first, gd->sx, buf, &size); 2666 len = gd->sx - first; 2667 endline = gd->hsize + gd->sy - 1; 2668 pywrap = py; 2669 while (buf != NULL && (pywrap <= endline)) { 2670 gl = grid_get_line(gd, pywrap); 2671 if (~gl->flags & GRID_LINE_WRAPPED) 2672 break; 2673 pywrap++; 2674 buf = window_copy_stringify(gd, pywrap, 0, gd->sx, buf, &size); 2675 len += gd->sx; 2676 } 2677 2678 if (window_copy_last_regex(gd, py, first, last, len, ppx, psx, buf, 2679 reg, eflags)) 2680 { 2681 free(buf); 2682 return (1); 2683 } 2684 2685 free(buf); 2686 *ppx = 0; 2687 *psx = 0; 2688 return (0); 2689 } 2690 2691 static const char * 2692 window_copy_cellstring(const struct grid_line *gl, u_int px, size_t *size, 2693 int *allocated) 2694 { 2695 static struct utf8_data ud; 2696 struct grid_cell_entry *gce; 2697 char *copy; 2698 2699 if (px >= gl->cellsize) { 2700 *size = 1; 2701 *allocated = 0; 2702 return (" "); 2703 } 2704 2705 gce = &gl->celldata[px]; 2706 if (gce->flags & GRID_FLAG_PADDING) { 2707 *size = 0; 2708 *allocated = 0; 2709 return (NULL); 2710 } 2711 if (~gce->flags & GRID_FLAG_EXTENDED) { 2712 *size = 1; 2713 *allocated = 0; 2714 return (&gce->data.data); 2715 } 2716 2717 utf8_to_data(gl->extddata[gce->offset].data, &ud); 2718 *size = ud.size; 2719 *allocated = 1; 2720 2721 copy = xmalloc(ud.size); 2722 memcpy(copy, ud.data, ud.size); 2723 return (copy); 2724 } 2725 2726 /* Find last match in given range. */ 2727 static int 2728 window_copy_last_regex(struct grid *gd, u_int py, u_int first, u_int last, 2729 u_int len, u_int *ppx, u_int *psx, const char *buf, const regex_t *preg, 2730 int eflags) 2731 { 2732 u_int foundx, foundy, oldx, px = 0, savepx, savesx = 0; 2733 regmatch_t regmatch; 2734 2735 foundx = first; 2736 foundy = py; 2737 oldx = first; 2738 while (regexec(preg, buf + px, 1, ®match, eflags) == 0) { 2739 if (regmatch.rm_so == regmatch.rm_eo) 2740 break; 2741 window_copy_cstrtocellpos(gd, len, &foundx, &foundy, 2742 buf + px + regmatch.rm_so); 2743 if (foundy > py || foundx >= last) 2744 break; 2745 len -= foundx - oldx; 2746 savepx = foundx; 2747 window_copy_cstrtocellpos(gd, len, &foundx, &foundy, 2748 buf + px + regmatch.rm_eo); 2749 if (foundy > py || foundx >= last) { 2750 *ppx = savepx; 2751 *psx = foundx; 2752 while (foundy > py) { 2753 *psx += gd->sx; 2754 foundy--; 2755 } 2756 *psx -= *ppx; 2757 return (1); 2758 } else { 2759 savesx = foundx - savepx; 2760 len -= savesx; 2761 oldx = foundx; 2762 } 2763 px += regmatch.rm_eo; 2764 } 2765 2766 if (savesx > 0) { 2767 *ppx = savepx; 2768 *psx = savesx; 2769 return (1); 2770 } else { 2771 *ppx = 0; 2772 *psx = 0; 2773 return (0); 2774 } 2775 } 2776 2777 /* Stringify line and append to input buffer. Caller frees. */ 2778 static char * 2779 window_copy_stringify(struct grid *gd, u_int py, u_int first, u_int last, 2780 char *buf, u_int *size) 2781 { 2782 u_int ax, bx, newsize = *size; 2783 const struct grid_line *gl; 2784 const char *d; 2785 size_t bufsize = 1024, dlen; 2786 int allocated; 2787 2788 while (bufsize < newsize) 2789 bufsize *= 2; 2790 buf = xrealloc(buf, bufsize); 2791 2792 gl = grid_peek_line(gd, py); 2793 bx = *size - 1; 2794 for (ax = first; ax < last; ax++) { 2795 d = window_copy_cellstring(gl, ax, &dlen, &allocated); 2796 newsize += dlen; 2797 while (bufsize < newsize) { 2798 bufsize *= 2; 2799 buf = xrealloc(buf, bufsize); 2800 } 2801 if (dlen == 1) 2802 buf[bx++] = *d; 2803 else { 2804 memcpy(buf + bx, d, dlen); 2805 bx += dlen; 2806 } 2807 if (allocated) 2808 free((void *)d); 2809 } 2810 buf[newsize - 1] = '\0'; 2811 2812 *size = newsize; 2813 return (buf); 2814 } 2815 2816 /* Map start of C string containing UTF-8 data to grid cell position. */ 2817 static void 2818 window_copy_cstrtocellpos(struct grid *gd, u_int ncells, u_int *ppx, u_int *ppy, 2819 const char *str) 2820 { 2821 u_int cell, ccell, px, pywrap, pos, len; 2822 int match; 2823 const struct grid_line *gl; 2824 const char *d; 2825 size_t dlen; 2826 struct { 2827 const char *d; 2828 size_t dlen; 2829 int allocated; 2830 } *cells; 2831 2832 /* Populate the array of cell data. */ 2833 cells = xreallocarray(NULL, ncells, sizeof cells[0]); 2834 cell = 0; 2835 px = *ppx; 2836 pywrap = *ppy; 2837 gl = grid_peek_line(gd, pywrap); 2838 while (cell < ncells) { 2839 cells[cell].d = window_copy_cellstring(gl, px, 2840 &cells[cell].dlen, &cells[cell].allocated); 2841 cell++; 2842 px++; 2843 if (px == gd->sx) { 2844 px = 0; 2845 pywrap++; 2846 gl = grid_peek_line(gd, pywrap); 2847 } 2848 } 2849 2850 /* Locate starting cell. */ 2851 cell = 0; 2852 len = strlen(str); 2853 while (cell < ncells) { 2854 ccell = cell; 2855 pos = 0; 2856 match = 1; 2857 while (ccell < ncells) { 2858 if (str[pos] == '\0') { 2859 match = 0; 2860 break; 2861 } 2862 d = cells[ccell].d; 2863 dlen = cells[ccell].dlen; 2864 if (dlen == 1) { 2865 if (str[pos] != *d) { 2866 match = 0; 2867 break; 2868 } 2869 pos++; 2870 } else { 2871 if (dlen > len - pos) 2872 dlen = len - pos; 2873 if (memcmp(str + pos, d, dlen) != 0) { 2874 match = 0; 2875 break; 2876 } 2877 pos += dlen; 2878 } 2879 ccell++; 2880 } 2881 if (match) 2882 break; 2883 cell++; 2884 } 2885 2886 /* If not found this will be one past the end. */ 2887 px = *ppx + cell; 2888 pywrap = *ppy; 2889 while (px >= gd->sx) { 2890 px -= gd->sx; 2891 pywrap++; 2892 } 2893 2894 *ppx = px; 2895 *ppy = pywrap; 2896 2897 /* Free cell data. */ 2898 for (cell = 0; cell < ncells; cell++) { 2899 if (cells[cell].allocated) 2900 free((void *)cells[cell].d); 2901 } 2902 free(cells); 2903 } 2904 2905 static void 2906 window_copy_move_left(struct screen *s, u_int *fx, u_int *fy, int wrapflag) 2907 { 2908 if (*fx == 0) { /* left */ 2909 if (*fy == 0) { /* top */ 2910 if (wrapflag) { 2911 *fx = screen_size_x(s) - 1; 2912 *fy = screen_hsize(s) + screen_size_y(s) - 1; 2913 } 2914 return; 2915 } 2916 *fx = screen_size_x(s) - 1; 2917 *fy = *fy - 1; 2918 } else 2919 *fx = *fx - 1; 2920 } 2921 2922 static void 2923 window_copy_move_right(struct screen *s, u_int *fx, u_int *fy, int wrapflag) 2924 { 2925 if (*fx == screen_size_x(s) - 1) { /* right */ 2926 if (*fy == screen_hsize(s) + screen_size_y(s) - 1) { /* bottom */ 2927 if (wrapflag) { 2928 *fx = 0; 2929 *fy = 0; 2930 } 2931 return; 2932 } 2933 *fx = 0; 2934 *fy = *fy + 1; 2935 } else 2936 *fx = *fx + 1; 2937 } 2938 2939 static int 2940 window_copy_is_lowercase(const char *ptr) 2941 { 2942 while (*ptr != '\0') { 2943 if (*ptr != tolower((u_char)*ptr)) 2944 return (0); 2945 ++ptr; 2946 } 2947 return (1); 2948 } 2949 2950 /* 2951 * Handle backward wrapped regex searches with overlapping matches. In this case 2952 * find the longest overlapping match from previous wrapped lines. 2953 */ 2954 static void 2955 window_copy_search_back_overlap(struct grid *gd, regex_t *preg, u_int *ppx, 2956 u_int *psx, u_int *ppy, u_int endline) 2957 { 2958 u_int endx, endy, oldendx, oldendy, px, py, sx; 2959 int found = 1; 2960 2961 oldendx = *ppx + *psx; 2962 oldendy = *ppy - 1; 2963 while (oldendx > gd->sx - 1) { 2964 oldendx -= gd->sx; 2965 oldendy++; 2966 } 2967 endx = oldendx; 2968 endy = oldendy; 2969 px = *ppx; 2970 py = *ppy; 2971 while (found && px == 0 && py - 1 > endline && 2972 grid_get_line(gd, py - 2)->flags & GRID_LINE_WRAPPED && 2973 endx == oldendx && endy == oldendy) { 2974 py--; 2975 found = window_copy_search_rl_regex(gd, &px, &sx, py - 1, 0, 2976 gd->sx, preg); 2977 if (found) { 2978 endx = px + sx; 2979 endy = py - 1; 2980 while (endx > gd->sx - 1) { 2981 endx -= gd->sx; 2982 endy++; 2983 } 2984 if (endx == oldendx && endy == oldendy) { 2985 *ppx = px; 2986 *ppy = py; 2987 } 2988 } 2989 } 2990 } 2991 2992 /* 2993 * Search for text stored in sgd starting from position fx,fy up to endline. If 2994 * found, jump to it. If cis then ignore case. The direction is 0 for searching 2995 * up, down otherwise. If wrap then go to begin/end of grid and try again if 2996 * not found. 2997 */ 2998 static int 2999 window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, 3000 struct grid *sgd, u_int fx, u_int fy, u_int endline, int cis, int wrap, 3001 int direction, int regex) 3002 { 3003 u_int i, px, sx, ssize = 1; 3004 int found = 0, cflags = REG_EXTENDED; 3005 char *sbuf; 3006 regex_t reg; 3007 3008 if (regex) { 3009 sbuf = xmalloc(ssize); 3010 sbuf[0] = '\0'; 3011 sbuf = window_copy_stringify(sgd, 0, 0, sgd->sx, sbuf, &ssize); 3012 if (cis) 3013 cflags |= REG_ICASE; 3014 if (regcomp(®, sbuf, cflags) != 0) { 3015 free(sbuf); 3016 return (0); 3017 } 3018 free(sbuf); 3019 } 3020 3021 if (direction) { 3022 for (i = fy; i <= endline; i++) { 3023 if (regex) { 3024 found = window_copy_search_lr_regex(gd, 3025 &px, &sx, i, fx, gd->sx, ®); 3026 } else { 3027 found = window_copy_search_lr(gd, sgd, 3028 &px, i, fx, gd->sx, cis); 3029 } 3030 if (found) 3031 break; 3032 fx = 0; 3033 } 3034 } else { 3035 for (i = fy + 1; endline < i; i--) { 3036 if (regex) { 3037 found = window_copy_search_rl_regex(gd, 3038 &px, &sx, i - 1, 0, fx + 1, ®); 3039 if (found) { 3040 window_copy_search_back_overlap(gd, 3041 ®, &px, &sx, &i, endline); 3042 } 3043 } else { 3044 found = window_copy_search_rl(gd, sgd, 3045 &px, i - 1, 0, fx + 1, cis); 3046 } 3047 if (found) { 3048 i--; 3049 break; 3050 } 3051 fx = gd->sx - 1; 3052 } 3053 } 3054 if (regex) 3055 regfree(®); 3056 3057 if (found) { 3058 window_copy_scroll_to(wme, px, i, 1); 3059 return (1); 3060 } 3061 if (wrap) { 3062 return (window_copy_search_jump(wme, gd, sgd, 3063 direction ? 0 : gd->sx - 1, 3064 direction ? 0 : gd->hsize + gd->sy - 1, fy, cis, 0, 3065 direction, regex)); 3066 } 3067 return (0); 3068 } 3069 3070 static void 3071 window_copy_move_after_search_mark(struct window_copy_mode_data *data, 3072 u_int *fx, u_int *fy, int wrapflag) 3073 { 3074 struct screen *s = data->backing; 3075 u_int at, start; 3076 3077 if (window_copy_search_mark_at(data, *fx, *fy, &start) == 0 && 3078 data->searchmark[start] != 0) { 3079 while (window_copy_search_mark_at(data, *fx, *fy, &at) == 0) { 3080 if (data->searchmark[at] != data->searchmark[start]) 3081 break; 3082 /* Stop if not wrapping and at the end of the grid. */ 3083 if (!wrapflag && 3084 *fx == screen_size_x(s) - 1 && 3085 *fy == screen_hsize(s) + screen_size_y(s) - 1) 3086 break; 3087 3088 window_copy_move_right(s, fx, fy, wrapflag); 3089 } 3090 } 3091 } 3092 3093 /* 3094 * Search in for text searchstr. If direction is 0 then search up, otherwise 3095 * down. 3096 */ 3097 static int 3098 window_copy_search(struct window_mode_entry *wme, int direction, int regex) 3099 { 3100 struct window_pane *wp = wme->wp; 3101 struct window_copy_mode_data *data = wme->data; 3102 struct screen *s = data->backing, ss; 3103 struct screen_write_ctx ctx; 3104 struct grid *gd = s->grid; 3105 const char *str = data->searchstr; 3106 u_int at, endline, fx, fy, start; 3107 int cis, found, keys, visible_only; 3108 int wrapflag; 3109 3110 if (regex && str[strcspn(str, "^$*+()?[].\\")] == '\0') 3111 regex = 0; 3112 3113 data->searchdirection = direction; 3114 3115 if (data->timeout) 3116 return (0); 3117 3118 if (data->searchall || wp->searchstr == NULL || 3119 wp->searchregex != regex) { 3120 visible_only = 0; 3121 data->searchall = 0; 3122 } else 3123 visible_only = (strcmp(wp->searchstr, str) == 0); 3124 free(wp->searchstr); 3125 wp->searchstr = xstrdup(str); 3126 wp->searchregex = regex; 3127 3128 fx = data->cx; 3129 fy = screen_hsize(data->backing) - data->oy + data->cy; 3130 3131 screen_init(&ss, screen_write_strlen("%s", str), 1, 0); 3132 screen_write_start(&ctx, &ss); 3133 screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", str); 3134 screen_write_stop(&ctx); 3135 3136 wrapflag = options_get_number(wp->window->options, "wrap-search"); 3137 cis = window_copy_is_lowercase(str); 3138 3139 keys = options_get_number(wp->window->options, "mode-keys"); 3140 3141 if (direction) { 3142 /* 3143 * Behave according to mode-keys. If it is emacs, search forward 3144 * leaves the cursor after the match. If it is vi, the cursor 3145 * remains at the beginning of the match, regardless of 3146 * direction, which means that we need to start the next search 3147 * after the term the cursor is currently on when searching 3148 * forward. 3149 */ 3150 if (keys == MODEKEY_VI) { 3151 if (data->searchmark != NULL) 3152 window_copy_move_after_search_mark(data, &fx, 3153 &fy, wrapflag); 3154 else { 3155 /* 3156 * When there are no search marks, start the 3157 * search after the current cursor position. 3158 */ 3159 window_copy_move_right(s, &fx, &fy, wrapflag); 3160 } 3161 } 3162 endline = gd->hsize + gd->sy - 1; 3163 } 3164 else { 3165 window_copy_move_left(s, &fx, &fy, wrapflag); 3166 endline = 0; 3167 } 3168 3169 found = window_copy_search_jump(wme, gd, ss.grid, fx, fy, endline, cis, 3170 wrapflag, direction, regex); 3171 if (found) { 3172 window_copy_search_marks(wme, &ss, regex, visible_only); 3173 fx = data->cx; 3174 fy = screen_hsize(data->backing) - data->oy + data->cy; 3175 3176 /* 3177 * When searching forward, if the cursor is not at the beginning 3178 * of the mark, search again. 3179 */ 3180 if (direction && 3181 window_copy_search_mark_at(data, fx, fy, &at) == 0 && 3182 at > 0 && 3183 data->searchmark[at] == data->searchmark[at - 1]) { 3184 window_copy_move_after_search_mark(data, &fx, &fy, 3185 wrapflag); 3186 window_copy_search_jump(wme, gd, ss.grid, fx, 3187 fy, endline, cis, wrapflag, direction, 3188 regex); 3189 fx = data->cx; 3190 fy = screen_hsize(data->backing) - data->oy + data->cy; 3191 } 3192 3193 if (direction) { 3194 /* 3195 * When in Emacs mode, position the cursor just after 3196 * the mark. 3197 */ 3198 if (keys == MODEKEY_EMACS) { 3199 window_copy_move_after_search_mark(data, &fx, 3200 &fy, wrapflag); 3201 data->cx = fx; 3202 data->cy = fy - screen_hsize(data->backing) + 3203 data-> oy; 3204 } 3205 } 3206 else { 3207 /* 3208 * When searching backward, position the cursor at the 3209 * beginning of the mark. 3210 */ 3211 if (window_copy_search_mark_at(data, fx, fy, 3212 &start) == 0) { 3213 while (window_copy_search_mark_at(data, fx, fy, 3214 &at) == 0 && 3215 data->searchmark[at] == 3216 data->searchmark[start]) { 3217 data->cx = fx; 3218 data->cy = fy - 3219 screen_hsize(data->backing) + 3220 data-> oy; 3221 if (at == 0) 3222 break; 3223 3224 window_copy_move_left(s, &fx, &fy, 0); 3225 } 3226 } 3227 } 3228 } 3229 window_copy_redraw_screen(wme); 3230 3231 screen_free(&ss); 3232 return (found); 3233 } 3234 3235 static void 3236 window_copy_visible_lines(struct window_copy_mode_data *data, u_int *start, 3237 u_int *end) 3238 { 3239 struct grid *gd = data->backing->grid; 3240 const struct grid_line *gl; 3241 3242 for (*start = gd->hsize - data->oy; *start > 0; (*start)--) { 3243 gl = grid_peek_line(gd, (*start) - 1); 3244 if (~gl->flags & GRID_LINE_WRAPPED) 3245 break; 3246 } 3247 *end = gd->hsize - data->oy + gd->sy; 3248 } 3249 3250 static int 3251 window_copy_search_mark_at(struct window_copy_mode_data *data, u_int px, 3252 u_int py, u_int *at) 3253 { 3254 struct screen *s = data->backing; 3255 struct grid *gd = s->grid; 3256 3257 if (py < gd->hsize - data->oy) 3258 return (-1); 3259 if (py > gd->hsize - data->oy + gd->sy - 1) 3260 return (-1); 3261 *at = ((py - (gd->hsize - data->oy)) * gd->sx) + px; 3262 return (0); 3263 } 3264 3265 static int 3266 window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, 3267 int regex, int visible_only) 3268 { 3269 struct window_copy_mode_data *data = wme->data; 3270 struct screen *s = data->backing, ss; 3271 struct screen_write_ctx ctx; 3272 struct grid *gd = s->grid; 3273 int found, cis, stopped = 0; 3274 int cflags = REG_EXTENDED; 3275 u_int px, py, i, b, nfound = 0, width; 3276 u_int ssize = 1, start, end; 3277 char *sbuf; 3278 regex_t reg; 3279 uint64_t stop = 0, tstart, t; 3280 3281 if (ssp == NULL) { 3282 width = screen_write_strlen("%s", data->searchstr); 3283 screen_init(&ss, width, 1, 0); 3284 screen_write_start(&ctx, &ss); 3285 screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", 3286 data->searchstr); 3287 screen_write_stop(&ctx); 3288 ssp = &ss; 3289 } else 3290 width = screen_size_x(ssp); 3291 3292 cis = window_copy_is_lowercase(data->searchstr); 3293 3294 if (regex) { 3295 sbuf = xmalloc(ssize); 3296 sbuf[0] = '\0'; 3297 sbuf = window_copy_stringify(ssp->grid, 0, 0, ssp->grid->sx, 3298 sbuf, &ssize); 3299 if (cis) 3300 cflags |= REG_ICASE; 3301 if (regcomp(®, sbuf, cflags) != 0) { 3302 free(sbuf); 3303 return (0); 3304 } 3305 free(sbuf); 3306 } 3307 tstart = get_timer(); 3308 3309 if (visible_only) 3310 window_copy_visible_lines(data, &start, &end); 3311 else { 3312 start = 0; 3313 end = gd->hsize + gd->sy; 3314 stop = get_timer() + WINDOW_COPY_SEARCH_ALL_TIMEOUT; 3315 } 3316 3317 again: 3318 free(data->searchmark); 3319 data->searchmark = xcalloc(gd->sx, gd->sy); 3320 data->searchgen = 1; 3321 3322 for (py = start; py < end; py++) { 3323 px = 0; 3324 for (;;) { 3325 if (regex) { 3326 found = window_copy_search_lr_regex(gd, 3327 &px, &width, py, px, gd->sx, ®); 3328 if (!found) 3329 break; 3330 } else { 3331 found = window_copy_search_lr(gd, ssp->grid, 3332 &px, py, px, gd->sx, cis); 3333 if (!found) 3334 break; 3335 } 3336 nfound++; 3337 3338 if (window_copy_search_mark_at(data, px, py, &b) == 0) { 3339 if (b + width > gd->sx * gd->sy) 3340 width = (gd->sx * gd->sy) - b; 3341 for (i = b; i < b + width; i++) { 3342 if (data->searchmark[i] != 0) 3343 continue; 3344 data->searchmark[i] = data->searchgen; 3345 } 3346 if (data->searchgen == UCHAR_MAX) 3347 data->searchgen = 1; 3348 else 3349 data->searchgen++; 3350 } 3351 px += width; 3352 } 3353 3354 t = get_timer(); 3355 if (t - tstart > WINDOW_COPY_SEARCH_TIMEOUT) { 3356 data->timeout = 1; 3357 break; 3358 } 3359 if (stop != 0 && t > stop) { 3360 stopped = 1; 3361 break; 3362 } 3363 } 3364 if (data->timeout) { 3365 window_copy_clear_marks(wme); 3366 goto out; 3367 } 3368 3369 if (stopped && stop != 0) { 3370 /* Try again but just the visible context. */ 3371 window_copy_visible_lines(data, &start, &end); 3372 stop = 0; 3373 goto again; 3374 } 3375 3376 if (!visible_only) { 3377 if (stopped) { 3378 if (nfound > 1000) 3379 data->searchcount = 1000; 3380 else if (nfound > 100) 3381 data->searchcount = 100; 3382 else if (nfound > 10) 3383 data->searchcount = 10; 3384 else 3385 data->searchcount = -1; 3386 data->searchmore = 1; 3387 } else { 3388 data->searchcount = nfound; 3389 data->searchmore = 0; 3390 } 3391 } 3392 3393 out: 3394 if (ssp == &ss) 3395 screen_free(&ss); 3396 if (regex) 3397 regfree(®); 3398 return (1); 3399 } 3400 3401 static void 3402 window_copy_clear_marks(struct window_mode_entry *wme) 3403 { 3404 struct window_copy_mode_data *data = wme->data; 3405 3406 free(data->searchmark); 3407 data->searchmark = NULL; 3408 } 3409 3410 static int 3411 window_copy_search_up(struct window_mode_entry *wme, int regex) 3412 { 3413 return (window_copy_search(wme, 0, regex)); 3414 } 3415 3416 static int 3417 window_copy_search_down(struct window_mode_entry *wme, int regex) 3418 { 3419 return (window_copy_search(wme, 1, regex)); 3420 } 3421 3422 static void 3423 window_copy_goto_line(struct window_mode_entry *wme, const char *linestr) 3424 { 3425 struct window_copy_mode_data *data = wme->data; 3426 const char *errstr; 3427 int lineno; 3428 3429 lineno = strtonum(linestr, -1, INT_MAX, &errstr); 3430 if (errstr != NULL) 3431 return; 3432 if (lineno < 0 || (u_int)lineno > screen_hsize(data->backing)) 3433 lineno = screen_hsize(data->backing); 3434 3435 data->oy = lineno; 3436 window_copy_update_selection(wme, 1, 0); 3437 window_copy_redraw_screen(wme); 3438 } 3439 3440 static void 3441 window_copy_match_start_end(struct window_copy_mode_data *data, u_int at, 3442 u_int *start, u_int *end) 3443 { 3444 struct grid *gd = data->backing->grid; 3445 u_int last = (gd->sy * gd->sx) - 1; 3446 u_char mark = data->searchmark[at]; 3447 3448 *start = *end = at; 3449 while (*start != 0 && data->searchmark[*start] == mark) 3450 (*start)--; 3451 if (data->searchmark[*start] != mark) 3452 (*start)++; 3453 while (*end != last && data->searchmark[*end] == mark) 3454 (*end)++; 3455 if (data->searchmark[*end] != mark) 3456 (*end)--; 3457 } 3458 3459 static char * 3460 window_copy_match_at_cursor(struct window_copy_mode_data *data) 3461 { 3462 struct grid *gd = data->backing->grid; 3463 struct grid_cell gc; 3464 u_int at, start, end, cy, px, py; 3465 u_int sx = screen_size_x(data->backing); 3466 char *buf = NULL; 3467 size_t len = 0; 3468 3469 if (data->searchmark == NULL) 3470 return (NULL); 3471 3472 cy = screen_hsize(data->backing) - data->oy + data->cy; 3473 if (window_copy_search_mark_at(data, data->cx, cy, &at) != 0) 3474 return (NULL); 3475 if (data->searchmark[at] == 0) { 3476 /* Allow one position after the match. */ 3477 if (at == 0 || data->searchmark[--at] == 0) 3478 return (NULL); 3479 } 3480 window_copy_match_start_end(data, at, &start, &end); 3481 3482 /* 3483 * Cells will not be set in the marked array unless they are valid text 3484 * and wrapping will be taken care of, so we can just copy. 3485 */ 3486 for (at = start; at <= end; at++) { 3487 py = at / sx; 3488 px = at - (py * sx); 3489 3490 grid_get_cell(gd, px, gd->hsize + py - data->oy, &gc); 3491 buf = xrealloc(buf, len + gc.data.size + 1); 3492 memcpy(buf + len, gc.data.data, gc.data.size); 3493 len += gc.data.size; 3494 } 3495 if (len != 0) 3496 buf[len] = '\0'; 3497 return (buf); 3498 } 3499 3500 static void 3501 window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy, 3502 struct grid_cell *gc, const struct grid_cell *mgc, 3503 const struct grid_cell *cgc, const struct grid_cell *mkgc) 3504 { 3505 struct window_pane *wp = wme->wp; 3506 struct window_copy_mode_data *data = wme->data; 3507 u_int mark, start, end, cy, cursor, current; 3508 int inv = 0, found = 0; 3509 int keys; 3510 3511 if (data->showmark && fy == data->my) { 3512 gc->attr = mkgc->attr; 3513 if (fx == data->mx) 3514 inv = 1; 3515 if (inv) { 3516 gc->fg = mkgc->bg; 3517 gc->bg = mkgc->fg; 3518 } 3519 else { 3520 gc->fg = mkgc->fg; 3521 gc->bg = mkgc->bg; 3522 } 3523 } 3524 3525 if (data->searchmark == NULL) 3526 return; 3527 3528 if (window_copy_search_mark_at(data, fx, fy, ¤t) != 0) 3529 return; 3530 mark = data->searchmark[current]; 3531 if (mark == 0) 3532 return; 3533 3534 cy = screen_hsize(data->backing) - data->oy + data->cy; 3535 if (window_copy_search_mark_at(data, data->cx, cy, &cursor) == 0) { 3536 keys = options_get_number(wp->window->options, "mode-keys"); 3537 if (cursor != 0 && 3538 keys == MODEKEY_EMACS && 3539 data->searchdirection) { 3540 if (data->searchmark[cursor - 1] == mark) { 3541 cursor--; 3542 found = 1; 3543 } 3544 } else if (data->searchmark[cursor] == mark) 3545 found = 1; 3546 if (found) { 3547 window_copy_match_start_end(data, cursor, &start, &end); 3548 if (current >= start && current <= end) { 3549 gc->attr = cgc->attr; 3550 if (inv) { 3551 gc->fg = cgc->bg; 3552 gc->bg = cgc->fg; 3553 } 3554 else { 3555 gc->fg = cgc->fg; 3556 gc->bg = cgc->bg; 3557 } 3558 return; 3559 } 3560 } 3561 } 3562 3563 gc->attr = mgc->attr; 3564 if (inv) { 3565 gc->fg = mgc->bg; 3566 gc->bg = mgc->fg; 3567 } 3568 else { 3569 gc->fg = mgc->fg; 3570 gc->bg = mgc->bg; 3571 } 3572 } 3573 3574 static void 3575 window_copy_write_one(struct window_mode_entry *wme, 3576 struct screen_write_ctx *ctx, u_int py, u_int fy, u_int nx, 3577 const struct grid_cell *mgc, const struct grid_cell *cgc, 3578 const struct grid_cell *mkgc) 3579 { 3580 struct window_copy_mode_data *data = wme->data; 3581 struct grid *gd = data->backing->grid; 3582 struct grid_cell gc; 3583 u_int fx; 3584 3585 screen_write_cursormove(ctx, 0, py, 0); 3586 for (fx = 0; fx < nx; fx++) { 3587 grid_get_cell(gd, fx, fy, &gc); 3588 if (fx + gc.data.width <= nx) { 3589 window_copy_update_style(wme, fx, fy, &gc, mgc, cgc, 3590 mkgc); 3591 screen_write_cell(ctx, &gc); 3592 } 3593 } 3594 } 3595 3596 static void 3597 window_copy_write_line(struct window_mode_entry *wme, 3598 struct screen_write_ctx *ctx, u_int py) 3599 { 3600 struct window_pane *wp = wme->wp; 3601 struct window_copy_mode_data *data = wme->data; 3602 struct screen *s = &data->screen; 3603 struct options *oo = wp->window->options; 3604 struct grid_cell gc, mgc, cgc, mkgc; 3605 char hdr[512]; 3606 size_t size = 0; 3607 u_int hsize = screen_hsize(data->backing); 3608 3609 style_apply(&gc, oo, "mode-style", NULL); 3610 gc.flags |= GRID_FLAG_NOPALETTE; 3611 style_apply(&mgc, oo, "copy-mode-match-style", NULL); 3612 mgc.flags |= GRID_FLAG_NOPALETTE; 3613 style_apply(&cgc, oo, "copy-mode-current-match-style", NULL); 3614 cgc.flags |= GRID_FLAG_NOPALETTE; 3615 style_apply(&mkgc, oo, "copy-mode-mark-style", NULL); 3616 mkgc.flags |= GRID_FLAG_NOPALETTE; 3617 3618 if (py == 0 && s->rupper < s->rlower && !data->hide_position) { 3619 if (data->searchmark == NULL) { 3620 if (data->timeout) { 3621 size = xsnprintf(hdr, sizeof hdr, 3622 "(timed out) [%u/%u]", data->oy, hsize); 3623 } else { 3624 size = xsnprintf(hdr, sizeof hdr, 3625 "[%u/%u]", data->oy, hsize); 3626 } 3627 } else { 3628 if (data->searchcount == -1) { 3629 size = xsnprintf(hdr, sizeof hdr, 3630 "[%u/%u]", data->oy, hsize); 3631 } else { 3632 size = xsnprintf(hdr, sizeof hdr, 3633 "(%d%s results) [%u/%u]", data->searchcount, 3634 data->searchmore ? "+" : "", data->oy, 3635 hsize); 3636 } 3637 } 3638 if (size > screen_size_x(s)) 3639 size = screen_size_x(s); 3640 screen_write_cursormove(ctx, screen_size_x(s) - size, 0, 0); 3641 screen_write_puts(ctx, &gc, "%s", hdr); 3642 } else 3643 size = 0; 3644 3645 if (size < screen_size_x(s)) { 3646 window_copy_write_one(wme, ctx, py, hsize - data->oy + py, 3647 screen_size_x(s) - size, &mgc, &cgc, &mkgc); 3648 } 3649 3650 if (py == data->cy && data->cx == screen_size_x(s)) { 3651 screen_write_cursormove(ctx, screen_size_x(s) - 1, py, 0); 3652 screen_write_putc(ctx, &grid_default_cell, '$'); 3653 } 3654 } 3655 3656 static void 3657 window_copy_write_lines(struct window_mode_entry *wme, 3658 struct screen_write_ctx *ctx, u_int py, u_int ny) 3659 { 3660 u_int yy; 3661 3662 for (yy = py; yy < py + ny; yy++) 3663 window_copy_write_line(wme, ctx, py); 3664 } 3665 3666 static void 3667 window_copy_redraw_selection(struct window_mode_entry *wme, u_int old_y) 3668 { 3669 struct window_copy_mode_data *data = wme->data; 3670 struct grid *gd = data->backing->grid; 3671 u_int new_y, start, end; 3672 3673 new_y = data->cy; 3674 if (old_y <= new_y) { 3675 start = old_y; 3676 end = new_y; 3677 } else { 3678 start = new_y; 3679 end = old_y; 3680 } 3681 3682 /* 3683 * In word selection mode the first word on the line below the cursor 3684 * might be selected, so add this line to the redraw area. 3685 */ 3686 if (data->selflag == SEL_WORD) { 3687 /* Last grid line in data coordinates. */ 3688 if (end < gd->sy + data->oy - 1) 3689 end++; 3690 } 3691 window_copy_redraw_lines(wme, start, end - start + 1); 3692 } 3693 3694 static void 3695 window_copy_redraw_lines(struct window_mode_entry *wme, u_int py, u_int ny) 3696 { 3697 struct window_pane *wp = wme->wp; 3698 struct window_copy_mode_data *data = wme->data; 3699 struct screen_write_ctx ctx; 3700 u_int i; 3701 3702 screen_write_start_pane(&ctx, wp, NULL); 3703 for (i = py; i < py + ny; i++) 3704 window_copy_write_line(wme, &ctx, i); 3705 screen_write_cursormove(&ctx, data->cx, data->cy, 0); 3706 screen_write_stop(&ctx); 3707 } 3708 3709 static void 3710 window_copy_redraw_screen(struct window_mode_entry *wme) 3711 { 3712 struct window_copy_mode_data *data = wme->data; 3713 3714 window_copy_redraw_lines(wme, 0, screen_size_y(&data->screen)); 3715 } 3716 3717 static void 3718 window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin, 3719 int no_reset) 3720 { 3721 struct window_copy_mode_data *data = wme->data; 3722 u_int xx, yy; 3723 3724 xx = data->cx; 3725 yy = screen_hsize(data->backing) + data->cy - data->oy; 3726 switch (data->selflag) { 3727 case SEL_WORD: 3728 if (no_reset) 3729 break; 3730 begin = 0; 3731 if (data->dy > yy || (data->dy == yy && data->dx > xx)) { 3732 /* Right to left selection. */ 3733 window_copy_cursor_previous_word_pos(wme, data->ws, 0, 3734 &xx, &yy); 3735 begin = 1; 3736 3737 /* Reset the end. */ 3738 data->endselx = data->endselrx; 3739 data->endsely = data->endselry; 3740 } else { 3741 /* Left to right selection. */ 3742 if (xx >= window_copy_find_length(wme, yy) || 3743 !window_copy_in_set(wme, xx + 1, yy, data->ws)) 3744 window_copy_cursor_next_word_end_pos(wme, 3745 data->ws, &xx, &yy); 3746 3747 /* Reset the start. */ 3748 data->selx = data->selrx; 3749 data->sely = data->selry; 3750 } 3751 break; 3752 case SEL_LINE: 3753 if (no_reset) 3754 break; 3755 begin = 0; 3756 if (data->dy > yy) { 3757 /* Right to left selection. */ 3758 xx = 0; 3759 begin = 1; 3760 3761 /* Reset the end. */ 3762 data->endselx = data->endselrx; 3763 data->endsely = data->endselry; 3764 } else { 3765 /* Left to right selection. */ 3766 if (yy < data->endselry) 3767 yy = data->endselry; 3768 xx = window_copy_find_length(wme, yy); 3769 3770 /* Reset the start. */ 3771 data->selx = data->selrx; 3772 data->sely = data->selry; 3773 } 3774 break; 3775 case SEL_CHAR: 3776 break; 3777 } 3778 if (begin) { 3779 data->selx = xx; 3780 data->sely = yy; 3781 } else { 3782 data->endselx = xx; 3783 data->endsely = yy; 3784 } 3785 } 3786 3787 static void 3788 window_copy_synchronize_cursor(struct window_mode_entry *wme, int no_reset) 3789 { 3790 struct window_copy_mode_data *data = wme->data; 3791 3792 switch (data->cursordrag) { 3793 case CURSORDRAG_ENDSEL: 3794 window_copy_synchronize_cursor_end(wme, 0, no_reset); 3795 break; 3796 case CURSORDRAG_SEL: 3797 window_copy_synchronize_cursor_end(wme, 1, no_reset); 3798 break; 3799 case CURSORDRAG_NONE: 3800 break; 3801 } 3802 } 3803 3804 static void 3805 window_copy_update_cursor(struct window_mode_entry *wme, u_int cx, u_int cy) 3806 { 3807 struct window_pane *wp = wme->wp; 3808 struct window_copy_mode_data *data = wme->data; 3809 struct screen *s = &data->screen; 3810 struct screen_write_ctx ctx; 3811 u_int old_cx, old_cy; 3812 3813 old_cx = data->cx; old_cy = data->cy; 3814 data->cx = cx; data->cy = cy; 3815 if (old_cx == screen_size_x(s)) 3816 window_copy_redraw_lines(wme, old_cy, 1); 3817 if (data->cx == screen_size_x(s)) 3818 window_copy_redraw_lines(wme, data->cy, 1); 3819 else { 3820 screen_write_start_pane(&ctx, wp, NULL); 3821 screen_write_cursormove(&ctx, data->cx, data->cy, 0); 3822 screen_write_stop(&ctx); 3823 } 3824 } 3825 3826 static void 3827 window_copy_start_selection(struct window_mode_entry *wme) 3828 { 3829 struct window_copy_mode_data *data = wme->data; 3830 3831 data->selx = data->cx; 3832 data->sely = screen_hsize(data->backing) + data->cy - data->oy; 3833 3834 data->endselx = data->selx; 3835 data->endsely = data->sely; 3836 3837 data->cursordrag = CURSORDRAG_ENDSEL; 3838 3839 window_copy_set_selection(wme, 1, 0); 3840 } 3841 3842 static int 3843 window_copy_adjust_selection(struct window_mode_entry *wme, u_int *selx, 3844 u_int *sely) 3845 { 3846 struct window_copy_mode_data *data = wme->data; 3847 struct screen *s = &data->screen; 3848 u_int sx, sy, ty; 3849 int relpos; 3850 3851 sx = *selx; 3852 sy = *sely; 3853 3854 ty = screen_hsize(data->backing) - data->oy; 3855 if (sy < ty) { 3856 relpos = WINDOW_COPY_REL_POS_ABOVE; 3857 if (!data->rectflag) 3858 sx = 0; 3859 sy = 0; 3860 } else if (sy > ty + screen_size_y(s) - 1) { 3861 relpos = WINDOW_COPY_REL_POS_BELOW; 3862 if (!data->rectflag) 3863 sx = screen_size_x(s) - 1; 3864 sy = screen_size_y(s) - 1; 3865 } else { 3866 relpos = WINDOW_COPY_REL_POS_ON_SCREEN; 3867 sy -= ty; 3868 } 3869 3870 *selx = sx; 3871 *sely = sy; 3872 return (relpos); 3873 } 3874 3875 static int 3876 window_copy_update_selection(struct window_mode_entry *wme, int may_redraw, 3877 int no_reset) 3878 { 3879 struct window_copy_mode_data *data = wme->data; 3880 struct screen *s = &data->screen; 3881 3882 if (s->sel == NULL && data->lineflag == LINE_SEL_NONE) 3883 return (0); 3884 return (window_copy_set_selection(wme, may_redraw, no_reset)); 3885 } 3886 3887 static int 3888 window_copy_set_selection(struct window_mode_entry *wme, int may_redraw, 3889 int no_reset) 3890 { 3891 struct window_pane *wp = wme->wp; 3892 struct window_copy_mode_data *data = wme->data; 3893 struct screen *s = &data->screen; 3894 struct options *oo = wp->window->options; 3895 struct grid_cell gc; 3896 u_int sx, sy, cy, endsx, endsy; 3897 int startrelpos, endrelpos; 3898 3899 window_copy_synchronize_cursor(wme, no_reset); 3900 3901 /* Adjust the selection. */ 3902 sx = data->selx; 3903 sy = data->sely; 3904 startrelpos = window_copy_adjust_selection(wme, &sx, &sy); 3905 3906 /* Adjust the end of selection. */ 3907 endsx = data->endselx; 3908 endsy = data->endsely; 3909 endrelpos = window_copy_adjust_selection(wme, &endsx, &endsy); 3910 3911 /* Selection is outside of the current screen */ 3912 if (startrelpos == endrelpos && 3913 startrelpos != WINDOW_COPY_REL_POS_ON_SCREEN) { 3914 screen_hide_selection(s); 3915 return (0); 3916 } 3917 3918 /* Set colours and selection. */ 3919 style_apply(&gc, oo, "mode-style", NULL); 3920 gc.flags |= GRID_FLAG_NOPALETTE; 3921 screen_set_selection(s, sx, sy, endsx, endsy, data->rectflag, 3922 data->modekeys, &gc); 3923 3924 if (data->rectflag && may_redraw) { 3925 /* 3926 * Can't rely on the caller to redraw the right lines for 3927 * rectangle selection - find the highest line and the number 3928 * of lines, and redraw just past that in both directions 3929 */ 3930 cy = data->cy; 3931 if (data->cursordrag == CURSORDRAG_ENDSEL) { 3932 if (sy < cy) 3933 window_copy_redraw_lines(wme, sy, cy - sy + 1); 3934 else 3935 window_copy_redraw_lines(wme, cy, sy - cy + 1); 3936 } else { 3937 if (endsy < cy) { 3938 window_copy_redraw_lines(wme, endsy, 3939 cy - endsy + 1); 3940 } else { 3941 window_copy_redraw_lines(wme, cy, 3942 endsy - cy + 1); 3943 } 3944 } 3945 } 3946 3947 return (1); 3948 } 3949 3950 static void * 3951 window_copy_get_selection(struct window_mode_entry *wme, size_t *len) 3952 { 3953 struct window_pane *wp = wme->wp; 3954 struct window_copy_mode_data *data = wme->data; 3955 struct screen *s = &data->screen; 3956 char *buf; 3957 size_t off; 3958 u_int i, xx, yy, sx, sy, ex, ey, ey_last; 3959 u_int firstsx, lastex, restex, restsx, selx; 3960 int keys; 3961 3962 if (data->screen.sel == NULL && data->lineflag == LINE_SEL_NONE) { 3963 buf = window_copy_match_at_cursor(data); 3964 if (buf != NULL) 3965 *len = strlen(buf); 3966 else 3967 *len = 0; 3968 return (buf); 3969 } 3970 3971 buf = xmalloc(1); 3972 off = 0; 3973 3974 *buf = '\0'; 3975 3976 /* 3977 * The selection extends from selx,sely to (adjusted) cx,cy on 3978 * the base screen. 3979 */ 3980 3981 /* Find start and end. */ 3982 xx = data->endselx; 3983 yy = data->endsely; 3984 if (yy < data->sely || (yy == data->sely && xx < data->selx)) { 3985 sx = xx; sy = yy; 3986 ex = data->selx; ey = data->sely; 3987 } else { 3988 sx = data->selx; sy = data->sely; 3989 ex = xx; ey = yy; 3990 } 3991 3992 /* Trim ex to end of line. */ 3993 ey_last = window_copy_find_length(wme, ey); 3994 if (ex > ey_last) 3995 ex = ey_last; 3996 3997 /* 3998 * Deal with rectangle-copy if necessary; four situations: start of 3999 * first line (firstsx), end of last line (lastex), start (restsx) and 4000 * end (restex) of all other lines. 4001 */ 4002 xx = screen_size_x(s); 4003 4004 /* 4005 * Behave according to mode-keys. If it is emacs, copy like emacs, 4006 * keeping the top-left-most character, and dropping the 4007 * bottom-right-most, regardless of copy direction. If it is vi, also 4008 * keep bottom-right-most character. 4009 */ 4010 keys = options_get_number(wp->window->options, "mode-keys"); 4011 if (data->rectflag) { 4012 /* 4013 * Need to ignore the column with the cursor in it, which for 4014 * rectangular copy means knowing which side the cursor is on. 4015 */ 4016 if (data->cursordrag == CURSORDRAG_ENDSEL) 4017 selx = data->selx; 4018 else 4019 selx = data->endselx; 4020 if (selx < data->cx) { 4021 /* Selection start is on the left. */ 4022 if (keys == MODEKEY_EMACS) { 4023 lastex = data->cx; 4024 restex = data->cx; 4025 } 4026 else { 4027 lastex = data->cx + 1; 4028 restex = data->cx + 1; 4029 } 4030 firstsx = selx; 4031 restsx = selx; 4032 } else { 4033 /* Cursor is on the left. */ 4034 lastex = selx + 1; 4035 restex = selx + 1; 4036 firstsx = data->cx; 4037 restsx = data->cx; 4038 } 4039 } else { 4040 if (keys == MODEKEY_EMACS) 4041 lastex = ex; 4042 else 4043 lastex = ex + 1; 4044 restex = xx; 4045 firstsx = sx; 4046 restsx = 0; 4047 } 4048 4049 /* Copy the lines. */ 4050 for (i = sy; i <= ey; i++) { 4051 window_copy_copy_line(wme, &buf, &off, i, 4052 (i == sy ? firstsx : restsx), 4053 (i == ey ? lastex : restex)); 4054 } 4055 4056 /* Don't bother if no data. */ 4057 if (off == 0) { 4058 free(buf); 4059 *len = 0; 4060 return (NULL); 4061 } 4062 /* Remove final \n (unless at end in vi mode). */ 4063 if (keys == MODEKEY_EMACS || lastex <= ey_last) { 4064 if (~grid_get_line(data->backing->grid, ey)->flags & 4065 GRID_LINE_WRAPPED || lastex != ey_last) 4066 off -= 1; 4067 } 4068 *len = off; 4069 return (buf); 4070 } 4071 4072 static void 4073 window_copy_copy_buffer(struct window_mode_entry *wme, const char *prefix, 4074 void *buf, size_t len) 4075 { 4076 struct window_pane *wp = wme->wp; 4077 struct screen_write_ctx ctx; 4078 4079 if (options_get_number(global_options, "set-clipboard") != 0) { 4080 screen_write_start_pane(&ctx, wp, NULL); 4081 screen_write_setselection(&ctx, buf, len); 4082 screen_write_stop(&ctx); 4083 notify_pane("pane-set-clipboard", wp); 4084 } 4085 4086 paste_add(prefix, buf, len); 4087 } 4088 4089 static void * 4090 window_copy_pipe_run(struct window_mode_entry *wme, struct session *s, 4091 const char *cmd, size_t *len) 4092 { 4093 void *buf; 4094 struct job *job; 4095 4096 buf = window_copy_get_selection(wme, len); 4097 if (cmd == NULL || *cmd == '\0') 4098 cmd = options_get_string(global_options, "copy-command"); 4099 if (cmd != NULL && *cmd != '\0') { 4100 job = job_run(cmd, 0, NULL, s, NULL, NULL, NULL, NULL, NULL, 4101 JOB_NOWAIT, -1, -1); 4102 bufferevent_write(job_get_event(job), buf, *len); 4103 } 4104 return (buf); 4105 } 4106 4107 static void 4108 window_copy_pipe(struct window_mode_entry *wme, struct session *s, 4109 const char *cmd) 4110 { 4111 size_t len; 4112 4113 window_copy_pipe_run(wme, s, cmd, &len); 4114 } 4115 4116 static void 4117 window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s, 4118 const char *prefix, const char *cmd) 4119 { 4120 void *buf; 4121 size_t len; 4122 4123 buf = window_copy_pipe_run(wme, s, cmd, &len); 4124 if (buf != NULL) 4125 window_copy_copy_buffer(wme, prefix, buf, len); 4126 } 4127 4128 static void 4129 window_copy_copy_selection(struct window_mode_entry *wme, const char *prefix) 4130 { 4131 char *buf; 4132 size_t len; 4133 4134 buf = window_copy_get_selection(wme, &len); 4135 if (buf != NULL) 4136 window_copy_copy_buffer(wme, prefix, buf, len); 4137 } 4138 4139 static void 4140 window_copy_append_selection(struct window_mode_entry *wme) 4141 { 4142 struct window_pane *wp = wme->wp; 4143 char *buf; 4144 struct paste_buffer *pb; 4145 const char *bufdata, *bufname = NULL; 4146 size_t len, bufsize; 4147 struct screen_write_ctx ctx; 4148 4149 buf = window_copy_get_selection(wme, &len); 4150 if (buf == NULL) 4151 return; 4152 4153 if (options_get_number(global_options, "set-clipboard") != 0) { 4154 screen_write_start_pane(&ctx, wp, NULL); 4155 screen_write_setselection(&ctx, buf, len); 4156 screen_write_stop(&ctx); 4157 notify_pane("pane-set-clipboard", wp); 4158 } 4159 4160 pb = paste_get_top(&bufname); 4161 if (pb != NULL) { 4162 bufdata = paste_buffer_data(pb, &bufsize); 4163 buf = xrealloc(buf, len + bufsize); 4164 memmove(buf + bufsize, buf, len); 4165 memcpy(buf, bufdata, bufsize); 4166 len += bufsize; 4167 } 4168 if (paste_set(buf, len, bufname, NULL) != 0) 4169 free(buf); 4170 } 4171 4172 static void 4173 window_copy_copy_line(struct window_mode_entry *wme, char **buf, size_t *off, 4174 u_int sy, u_int sx, u_int ex) 4175 { 4176 struct window_copy_mode_data *data = wme->data; 4177 struct grid *gd = data->backing->grid; 4178 struct grid_cell gc; 4179 struct grid_line *gl; 4180 struct utf8_data ud; 4181 u_int i, xx, wrapped = 0; 4182 const char *s; 4183 4184 if (sx > ex) 4185 return; 4186 4187 /* 4188 * Work out if the line was wrapped at the screen edge and all of it is 4189 * on screen. 4190 */ 4191 gl = grid_get_line(gd, sy); 4192 if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx) 4193 wrapped = 1; 4194 4195 /* If the line was wrapped, don't strip spaces (use the full length). */ 4196 if (wrapped) 4197 xx = gl->cellsize; 4198 else 4199 xx = window_copy_find_length(wme, sy); 4200 if (ex > xx) 4201 ex = xx; 4202 if (sx > xx) 4203 sx = xx; 4204 4205 if (sx < ex) { 4206 for (i = sx; i < ex; i++) { 4207 grid_get_cell(gd, i, sy, &gc); 4208 if (gc.flags & GRID_FLAG_PADDING) 4209 continue; 4210 utf8_copy(&ud, &gc.data); 4211 if (ud.size == 1 && (gc.attr & GRID_ATTR_CHARSET)) { 4212 s = tty_acs_get(NULL, ud.data[0]); 4213 if (s != NULL && strlen(s) <= sizeof ud.data) { 4214 ud.size = strlen(s); 4215 memcpy(ud.data, s, ud.size); 4216 } 4217 } 4218 4219 *buf = xrealloc(*buf, (*off) + ud.size); 4220 memcpy(*buf + *off, ud.data, ud.size); 4221 *off += ud.size; 4222 } 4223 } 4224 4225 /* Only add a newline if the line wasn't wrapped. */ 4226 if (!wrapped || ex != xx) { 4227 *buf = xrealloc(*buf, (*off) + 1); 4228 (*buf)[(*off)++] = '\n'; 4229 } 4230 } 4231 4232 static void 4233 window_copy_clear_selection(struct window_mode_entry *wme) 4234 { 4235 struct window_copy_mode_data *data = wme->data; 4236 u_int px, py; 4237 4238 screen_clear_selection(&data->screen); 4239 4240 data->cursordrag = CURSORDRAG_NONE; 4241 data->lineflag = LINE_SEL_NONE; 4242 data->selflag = SEL_CHAR; 4243 4244 py = screen_hsize(data->backing) + data->cy - data->oy; 4245 px = window_copy_find_length(wme, py); 4246 if (data->cx > px) 4247 window_copy_update_cursor(wme, px, data->cy); 4248 } 4249 4250 static int 4251 window_copy_in_set(struct window_mode_entry *wme, u_int px, u_int py, 4252 const char *set) 4253 { 4254 struct window_copy_mode_data *data = wme->data; 4255 struct grid_cell gc; 4256 4257 grid_get_cell(data->backing->grid, px, py, &gc); 4258 if (gc.flags & GRID_FLAG_PADDING) 4259 return (0); 4260 return (utf8_cstrhas(set, &gc.data)); 4261 } 4262 4263 static u_int 4264 window_copy_find_length(struct window_mode_entry *wme, u_int py) 4265 { 4266 struct window_copy_mode_data *data = wme->data; 4267 4268 return (grid_line_length(data->backing->grid, py)); 4269 } 4270 4271 static void 4272 window_copy_cursor_start_of_line(struct window_mode_entry *wme) 4273 { 4274 struct window_copy_mode_data *data = wme->data; 4275 struct screen *back_s = data->backing; 4276 struct grid_reader gr; 4277 u_int px, py, oldy, hsize; 4278 4279 px = data->cx; 4280 hsize = screen_hsize(back_s); 4281 py = hsize + data->cy - data->oy; 4282 oldy = data->cy; 4283 4284 grid_reader_start(&gr, back_s->grid, px, py); 4285 grid_reader_cursor_start_of_line(&gr, 1); 4286 grid_reader_get_cursor(&gr, &px, &py); 4287 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py); 4288 } 4289 4290 static void 4291 window_copy_cursor_back_to_indentation(struct window_mode_entry *wme) 4292 { 4293 struct window_copy_mode_data *data = wme->data; 4294 struct screen *back_s = data->backing; 4295 struct grid_reader gr; 4296 u_int px, py, oldy, hsize; 4297 4298 px = data->cx; 4299 hsize = screen_hsize(back_s); 4300 py = hsize + data->cy - data->oy; 4301 oldy = data->cy; 4302 4303 grid_reader_start(&gr, back_s->grid, px, py); 4304 grid_reader_cursor_back_to_indentation(&gr); 4305 grid_reader_get_cursor(&gr, &px, &py); 4306 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py); 4307 } 4308 4309 static void 4310 window_copy_cursor_end_of_line(struct window_mode_entry *wme) 4311 { 4312 struct window_copy_mode_data *data = wme->data; 4313 struct screen *back_s = data->backing; 4314 struct grid_reader gr; 4315 u_int px, py, oldy, hsize; 4316 4317 px = data->cx; 4318 hsize = screen_hsize(back_s); 4319 py = hsize + data->cy - data->oy; 4320 oldy = data->cy; 4321 4322 grid_reader_start(&gr, back_s->grid, px, py); 4323 if (data->screen.sel != NULL && data->rectflag) 4324 grid_reader_cursor_end_of_line(&gr, 1, 1); 4325 else 4326 grid_reader_cursor_end_of_line(&gr, 1, 0); 4327 grid_reader_get_cursor(&gr, &px, &py); 4328 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s), 4329 data->oy, oldy, px, py, 0); 4330 } 4331 4332 static void 4333 window_copy_other_end(struct window_mode_entry *wme) 4334 { 4335 struct window_copy_mode_data *data = wme->data; 4336 struct screen *s = &data->screen; 4337 u_int selx, sely, cy, yy, hsize; 4338 4339 if (s->sel == NULL && data->lineflag == LINE_SEL_NONE) 4340 return; 4341 4342 if (data->lineflag == LINE_SEL_LEFT_RIGHT) 4343 data->lineflag = LINE_SEL_RIGHT_LEFT; 4344 else if (data->lineflag == LINE_SEL_RIGHT_LEFT) 4345 data->lineflag = LINE_SEL_LEFT_RIGHT; 4346 4347 switch (data->cursordrag) { 4348 case CURSORDRAG_NONE: 4349 case CURSORDRAG_SEL: 4350 data->cursordrag = CURSORDRAG_ENDSEL; 4351 break; 4352 case CURSORDRAG_ENDSEL: 4353 data->cursordrag = CURSORDRAG_SEL; 4354 break; 4355 } 4356 4357 selx = data->endselx; 4358 sely = data->endsely; 4359 if (data->cursordrag == CURSORDRAG_SEL) { 4360 selx = data->selx; 4361 sely = data->sely; 4362 } 4363 4364 cy = data->cy; 4365 yy = screen_hsize(data->backing) + data->cy - data->oy; 4366 4367 data->cx = selx; 4368 4369 hsize = screen_hsize(data->backing); 4370 if (sely < hsize - data->oy) { /* above */ 4371 data->oy = hsize - sely; 4372 data->cy = 0; 4373 } else if (sely > hsize - data->oy + screen_size_y(s)) { /* below */ 4374 data->oy = hsize - sely + screen_size_y(s) - 1; 4375 data->cy = screen_size_y(s) - 1; 4376 } else 4377 data->cy = cy + sely - yy; 4378 4379 window_copy_update_selection(wme, 1, 1); 4380 window_copy_redraw_screen(wme); 4381 } 4382 4383 static void 4384 window_copy_cursor_left(struct window_mode_entry *wme) 4385 { 4386 struct window_copy_mode_data *data = wme->data; 4387 struct screen *back_s = data->backing; 4388 struct grid_reader gr; 4389 u_int px, py, oldy, hsize; 4390 4391 px = data->cx; 4392 hsize = screen_hsize(back_s); 4393 py = hsize + data->cy - data->oy; 4394 oldy = data->cy; 4395 4396 grid_reader_start(&gr, back_s->grid, px, py); 4397 grid_reader_cursor_left(&gr, 1); 4398 grid_reader_get_cursor(&gr, &px, &py); 4399 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py); 4400 } 4401 4402 static void 4403 window_copy_cursor_right(struct window_mode_entry *wme, int all) 4404 { 4405 struct window_copy_mode_data *data = wme->data; 4406 struct screen *back_s = data->backing; 4407 struct grid_reader gr; 4408 u_int px, py, oldy, hsize; 4409 4410 px = data->cx; 4411 hsize = screen_hsize(back_s); 4412 py = hsize + data->cy - data->oy; 4413 oldy = data->cy; 4414 4415 grid_reader_start(&gr, back_s->grid, px, py); 4416 grid_reader_cursor_right(&gr, 1, all); 4417 grid_reader_get_cursor(&gr, &px, &py); 4418 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s), 4419 data->oy, oldy, px, py, 0); 4420 } 4421 4422 static void 4423 window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only) 4424 { 4425 struct window_copy_mode_data *data = wme->data; 4426 struct screen *s = &data->screen; 4427 u_int ox, oy, px, py; 4428 4429 oy = screen_hsize(data->backing) + data->cy - data->oy; 4430 ox = window_copy_find_length(wme, oy); 4431 if (data->cx != ox) { 4432 data->lastcx = data->cx; 4433 data->lastsx = ox; 4434 } 4435 4436 if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely) 4437 window_copy_other_end(wme); 4438 4439 if (scroll_only || data->cy == 0) { 4440 data->cx = data->lastcx; 4441 window_copy_scroll_down(wme, 1); 4442 if (scroll_only) { 4443 if (data->cy == screen_size_y(s) - 1) 4444 window_copy_redraw_lines(wme, data->cy, 1); 4445 else 4446 window_copy_redraw_lines(wme, data->cy, 2); 4447 } 4448 } else { 4449 window_copy_update_cursor(wme, data->lastcx, data->cy - 1); 4450 if (window_copy_update_selection(wme, 1, 0)) { 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 } 4457 4458 if (data->screen.sel == NULL || !data->rectflag) { 4459 py = screen_hsize(data->backing) + data->cy - data->oy; 4460 px = window_copy_find_length(wme, py); 4461 if ((data->cx >= data->lastsx && data->cx != px) || 4462 data->cx > px) 4463 { 4464 window_copy_update_cursor(wme, px, data->cy); 4465 if (window_copy_update_selection(wme, 1, 0)) 4466 window_copy_redraw_lines(wme, data->cy, 1); 4467 } 4468 } 4469 4470 if (data->lineflag == LINE_SEL_LEFT_RIGHT) 4471 { 4472 py = screen_hsize(data->backing) + data->cy - data->oy; 4473 if (data->rectflag) 4474 px = screen_size_x(data->backing); 4475 else 4476 px = window_copy_find_length(wme, py); 4477 window_copy_update_cursor(wme, px, data->cy); 4478 if (window_copy_update_selection(wme, 1, 0)) 4479 window_copy_redraw_lines(wme, data->cy, 1); 4480 } 4481 else if (data->lineflag == LINE_SEL_RIGHT_LEFT) 4482 { 4483 window_copy_update_cursor(wme, 0, data->cy); 4484 if (window_copy_update_selection(wme, 1, 0)) 4485 window_copy_redraw_lines(wme, data->cy, 1); 4486 } 4487 } 4488 4489 static void 4490 window_copy_cursor_down(struct window_mode_entry *wme, int scroll_only) 4491 { 4492 struct window_copy_mode_data *data = wme->data; 4493 struct screen *s = &data->screen; 4494 u_int ox, oy, px, py; 4495 4496 oy = screen_hsize(data->backing) + data->cy - data->oy; 4497 ox = window_copy_find_length(wme, oy); 4498 if (data->cx != ox) { 4499 data->lastcx = data->cx; 4500 data->lastsx = ox; 4501 } 4502 4503 if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely) 4504 window_copy_other_end(wme); 4505 4506 if (scroll_only || data->cy == screen_size_y(s) - 1) { 4507 data->cx = data->lastcx; 4508 window_copy_scroll_up(wme, 1); 4509 if (scroll_only && data->cy > 0) 4510 window_copy_redraw_lines(wme, data->cy - 1, 2); 4511 } else { 4512 window_copy_update_cursor(wme, data->lastcx, data->cy + 1); 4513 if (window_copy_update_selection(wme, 1, 0)) 4514 window_copy_redraw_lines(wme, data->cy - 1, 2); 4515 } 4516 4517 if (data->screen.sel == NULL || !data->rectflag) { 4518 py = screen_hsize(data->backing) + data->cy - data->oy; 4519 px = window_copy_find_length(wme, py); 4520 if ((data->cx >= data->lastsx && data->cx != px) || 4521 data->cx > px) 4522 { 4523 window_copy_update_cursor(wme, px, data->cy); 4524 if (window_copy_update_selection(wme, 1, 0)) 4525 window_copy_redraw_lines(wme, data->cy, 1); 4526 } 4527 } 4528 4529 if (data->lineflag == LINE_SEL_LEFT_RIGHT) 4530 { 4531 py = screen_hsize(data->backing) + data->cy - data->oy; 4532 if (data->rectflag) 4533 px = screen_size_x(data->backing); 4534 else 4535 px = window_copy_find_length(wme, py); 4536 window_copy_update_cursor(wme, px, data->cy); 4537 if (window_copy_update_selection(wme, 1, 0)) 4538 window_copy_redraw_lines(wme, data->cy, 1); 4539 } 4540 else if (data->lineflag == LINE_SEL_RIGHT_LEFT) 4541 { 4542 window_copy_update_cursor(wme, 0, data->cy); 4543 if (window_copy_update_selection(wme, 1, 0)) 4544 window_copy_redraw_lines(wme, data->cy, 1); 4545 } 4546 } 4547 4548 static void 4549 window_copy_cursor_jump(struct window_mode_entry *wme) 4550 { 4551 struct window_copy_mode_data *data = wme->data; 4552 struct screen *back_s = data->backing; 4553 struct grid_reader gr; 4554 u_int px, py, oldy, hsize; 4555 4556 px = data->cx + 1; 4557 hsize = screen_hsize(back_s); 4558 py = hsize + data->cy - data->oy; 4559 oldy = data->cy; 4560 4561 grid_reader_start(&gr, back_s->grid, px, py); 4562 if (grid_reader_cursor_jump(&gr, data->jumpchar)) { 4563 grid_reader_get_cursor(&gr, &px, &py); 4564 window_copy_acquire_cursor_down(wme, hsize, 4565 screen_size_y(back_s), data->oy, oldy, px, py, 0); 4566 } 4567 } 4568 4569 static void 4570 window_copy_cursor_jump_back(struct window_mode_entry *wme) 4571 { 4572 struct window_copy_mode_data *data = wme->data; 4573 struct screen *back_s = data->backing; 4574 struct grid_reader gr; 4575 u_int px, py, oldy, hsize; 4576 4577 px = data->cx; 4578 hsize = screen_hsize(back_s); 4579 py = hsize + data->cy - data->oy; 4580 oldy = data->cy; 4581 4582 grid_reader_start(&gr, back_s->grid, px, py); 4583 grid_reader_cursor_left(&gr, 0); 4584 if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) { 4585 grid_reader_get_cursor(&gr, &px, &py); 4586 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, 4587 py); 4588 } 4589 } 4590 4591 static void 4592 window_copy_cursor_jump_to(struct window_mode_entry *wme) 4593 { 4594 struct window_copy_mode_data *data = wme->data; 4595 struct screen *back_s = data->backing; 4596 struct grid_reader gr; 4597 u_int px, py, oldy, hsize; 4598 4599 px = data->cx + 2; 4600 hsize = screen_hsize(back_s); 4601 py = hsize + data->cy - data->oy; 4602 oldy = data->cy; 4603 4604 grid_reader_start(&gr, back_s->grid, px, py); 4605 if (grid_reader_cursor_jump(&gr, data->jumpchar)) { 4606 grid_reader_cursor_left(&gr, 1); 4607 grid_reader_get_cursor(&gr, &px, &py); 4608 window_copy_acquire_cursor_down(wme, hsize, 4609 screen_size_y(back_s), data->oy, oldy, px, py, 0); 4610 } 4611 } 4612 4613 static void 4614 window_copy_cursor_jump_to_back(struct window_mode_entry *wme) 4615 { 4616 struct window_copy_mode_data *data = wme->data; 4617 struct screen *back_s = data->backing; 4618 struct grid_reader gr; 4619 u_int px, py, oldy, hsize; 4620 4621 px = data->cx; 4622 hsize = screen_hsize(back_s); 4623 py = hsize + data->cy - data->oy; 4624 oldy = data->cy; 4625 4626 grid_reader_start(&gr, back_s->grid, px, py); 4627 grid_reader_cursor_left(&gr, 0); 4628 grid_reader_cursor_left(&gr, 0); 4629 if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) { 4630 grid_reader_cursor_right(&gr, 1, 0); 4631 grid_reader_get_cursor(&gr, &px, &py); 4632 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, 4633 py); 4634 } 4635 } 4636 4637 static void 4638 window_copy_cursor_next_word(struct window_mode_entry *wme, 4639 const char *separators) 4640 { 4641 struct window_copy_mode_data *data = wme->data; 4642 struct screen *back_s = data->backing; 4643 struct grid_reader gr; 4644 u_int px, py, oldy, hsize; 4645 4646 px = data->cx; 4647 hsize = screen_hsize(back_s); 4648 py = hsize + data->cy - data->oy; 4649 oldy = data->cy; 4650 4651 grid_reader_start(&gr, back_s->grid, px, py); 4652 grid_reader_cursor_next_word(&gr, separators); 4653 grid_reader_get_cursor(&gr, &px, &py); 4654 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s), 4655 data->oy, oldy, px, py, 0); 4656 } 4657 4658 /* Compute the next place where a word ends. */ 4659 static void 4660 window_copy_cursor_next_word_end_pos(struct window_mode_entry *wme, 4661 const char *separators, u_int *ppx, u_int *ppy) 4662 { 4663 struct window_pane *wp = wme->wp; 4664 struct window_copy_mode_data *data = wme->data; 4665 struct options *oo = wp->window->options; 4666 struct screen *back_s = data->backing; 4667 struct grid_reader gr; 4668 u_int px, py, hsize; 4669 int keys; 4670 4671 px = data->cx; 4672 hsize = screen_hsize(back_s); 4673 py = hsize + data->cy - data->oy; 4674 4675 grid_reader_start(&gr, back_s->grid, px, py); 4676 keys = options_get_number(oo, "mode-keys"); 4677 if (keys == MODEKEY_VI && !grid_reader_in_set(&gr, separators)) 4678 grid_reader_cursor_right(&gr, 0, 0); 4679 grid_reader_cursor_next_word_end(&gr, separators); 4680 if (keys == MODEKEY_VI) 4681 grid_reader_cursor_left(&gr, 1); 4682 grid_reader_get_cursor(&gr, &px, &py); 4683 *ppx = px; 4684 *ppy = py; 4685 } 4686 4687 /* Move to the next place where a word ends. */ 4688 static void 4689 window_copy_cursor_next_word_end(struct window_mode_entry *wme, 4690 const char *separators, int no_reset) 4691 { 4692 struct window_pane *wp = wme->wp; 4693 struct window_copy_mode_data *data = wme->data; 4694 struct options *oo = wp->window->options; 4695 struct screen *back_s = data->backing; 4696 struct grid_reader gr; 4697 u_int px, py, oldy, hsize; 4698 int keys; 4699 4700 px = data->cx; 4701 hsize = screen_hsize(back_s); 4702 py = hsize + data->cy - data->oy; 4703 oldy = data->cy; 4704 4705 grid_reader_start(&gr, back_s->grid, px, py); 4706 keys = options_get_number(oo, "mode-keys"); 4707 if (keys == MODEKEY_VI && !grid_reader_in_set(&gr, separators)) 4708 grid_reader_cursor_right(&gr, 0, 0); 4709 grid_reader_cursor_next_word_end(&gr, separators); 4710 if (keys == MODEKEY_VI) 4711 grid_reader_cursor_left(&gr, 1); 4712 grid_reader_get_cursor(&gr, &px, &py); 4713 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s), 4714 data->oy, oldy, px, py, no_reset); 4715 } 4716 4717 /* Compute the previous place where a word begins. */ 4718 static void 4719 window_copy_cursor_previous_word_pos(struct window_mode_entry *wme, 4720 const char *separators, int already, u_int *ppx, u_int *ppy) 4721 { 4722 struct window_copy_mode_data *data = wme->data; 4723 struct screen *back_s = data->backing; 4724 struct grid_reader gr; 4725 u_int px, py, hsize; 4726 4727 px = data->cx; 4728 hsize = screen_hsize(back_s); 4729 py = hsize + data->cy - data->oy; 4730 4731 grid_reader_start(&gr, back_s->grid, px, py); 4732 grid_reader_cursor_previous_word(&gr, separators, already); 4733 grid_reader_get_cursor(&gr, &px, &py); 4734 *ppx = px; 4735 *ppy = py; 4736 } 4737 4738 /* Move to the previous place where a word begins. */ 4739 static void 4740 window_copy_cursor_previous_word(struct window_mode_entry *wme, 4741 const char *separators, int already) 4742 { 4743 struct window_copy_mode_data *data = wme->data; 4744 struct screen *back_s = data->backing; 4745 struct grid_reader gr; 4746 u_int px, py, oldy, hsize; 4747 4748 px = data->cx; 4749 hsize = screen_hsize(back_s); 4750 py = hsize + data->cy - data->oy; 4751 oldy = data->cy; 4752 4753 grid_reader_start(&gr, back_s->grid, px, py); 4754 grid_reader_cursor_previous_word(&gr, separators, already); 4755 grid_reader_get_cursor(&gr, &px, &py); 4756 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py); 4757 } 4758 4759 static void 4760 window_copy_scroll_up(struct window_mode_entry *wme, u_int ny) 4761 { 4762 struct window_pane *wp = wme->wp; 4763 struct window_copy_mode_data *data = wme->data; 4764 struct screen *s = &data->screen; 4765 struct screen_write_ctx ctx; 4766 4767 if (data->oy < ny) 4768 ny = data->oy; 4769 if (ny == 0) 4770 return; 4771 data->oy -= ny; 4772 4773 if (data->searchmark != NULL && !data->timeout) 4774 window_copy_search_marks(wme, NULL, data->searchregex, 1); 4775 window_copy_update_selection(wme, 0, 0); 4776 4777 screen_write_start_pane(&ctx, wp, NULL); 4778 screen_write_cursormove(&ctx, 0, 0, 0); 4779 screen_write_deleteline(&ctx, ny, 8); 4780 window_copy_write_lines(wme, &ctx, screen_size_y(s) - ny, ny); 4781 window_copy_write_line(wme, &ctx, 0); 4782 if (screen_size_y(s) > 1) 4783 window_copy_write_line(wme, &ctx, 1); 4784 if (screen_size_y(s) > 3) 4785 window_copy_write_line(wme, &ctx, screen_size_y(s) - 2); 4786 if (s->sel != NULL && screen_size_y(s) > ny) 4787 window_copy_write_line(wme, &ctx, screen_size_y(s) - ny - 1); 4788 screen_write_cursormove(&ctx, data->cx, data->cy, 0); 4789 screen_write_stop(&ctx); 4790 } 4791 4792 static void 4793 window_copy_scroll_down(struct window_mode_entry *wme, u_int ny) 4794 { 4795 struct window_pane *wp = wme->wp; 4796 struct window_copy_mode_data *data = wme->data; 4797 struct screen *s = &data->screen; 4798 struct screen_write_ctx ctx; 4799 4800 if (ny > screen_hsize(data->backing)) 4801 return; 4802 4803 if (data->oy > screen_hsize(data->backing) - ny) 4804 ny = screen_hsize(data->backing) - data->oy; 4805 if (ny == 0) 4806 return; 4807 data->oy += ny; 4808 4809 if (data->searchmark != NULL && !data->timeout) 4810 window_copy_search_marks(wme, NULL, data->searchregex, 1); 4811 window_copy_update_selection(wme, 0, 0); 4812 4813 screen_write_start_pane(&ctx, wp, NULL); 4814 screen_write_cursormove(&ctx, 0, 0, 0); 4815 screen_write_insertline(&ctx, ny, 8); 4816 window_copy_write_lines(wme, &ctx, 0, ny); 4817 if (s->sel != NULL && screen_size_y(s) > ny) 4818 window_copy_write_line(wme, &ctx, ny); 4819 else if (ny == 1) /* nuke position */ 4820 window_copy_write_line(wme, &ctx, 1); 4821 screen_write_cursormove(&ctx, data->cx, data->cy, 0); 4822 screen_write_stop(&ctx); 4823 } 4824 4825 static void 4826 window_copy_rectangle_set(struct window_mode_entry *wme, int rectflag) 4827 { 4828 struct window_copy_mode_data *data = wme->data; 4829 u_int px, py; 4830 4831 data->rectflag = rectflag; 4832 4833 py = screen_hsize(data->backing) + data->cy - data->oy; 4834 px = window_copy_find_length(wme, py); 4835 if (data->cx > px) 4836 window_copy_update_cursor(wme, px, data->cy); 4837 4838 window_copy_update_selection(wme, 1, 0); 4839 window_copy_redraw_screen(wme); 4840 } 4841 4842 static void 4843 window_copy_move_mouse(struct mouse_event *m) 4844 { 4845 struct window_pane *wp; 4846 struct window_mode_entry *wme; 4847 u_int x, y; 4848 4849 wp = cmd_mouse_pane(m, NULL, NULL); 4850 if (wp == NULL) 4851 return; 4852 wme = TAILQ_FIRST(&wp->modes); 4853 if (wme == NULL) 4854 return; 4855 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode) 4856 return; 4857 4858 if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) 4859 return; 4860 4861 window_copy_update_cursor(wme, x, y); 4862 } 4863 4864 void 4865 window_copy_start_drag(struct client *c, struct mouse_event *m) 4866 { 4867 struct window_pane *wp; 4868 struct window_mode_entry *wme; 4869 struct window_copy_mode_data *data; 4870 u_int x, y, yg; 4871 4872 if (c == NULL) 4873 return; 4874 4875 wp = cmd_mouse_pane(m, NULL, NULL); 4876 if (wp == NULL) 4877 return; 4878 wme = TAILQ_FIRST(&wp->modes); 4879 if (wme == NULL) 4880 return; 4881 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode) 4882 return; 4883 4884 if (cmd_mouse_at(wp, m, &x, &y, 1) != 0) 4885 return; 4886 4887 c->tty.mouse_drag_update = window_copy_drag_update; 4888 c->tty.mouse_drag_release = window_copy_drag_release; 4889 4890 data = wme->data; 4891 yg = screen_hsize(data->backing) + y - data->oy; 4892 if (x < data->selrx || x > data->endselrx || yg != data->selry) 4893 data->selflag = SEL_CHAR; 4894 switch (data->selflag) { 4895 case SEL_WORD: 4896 if (data->ws != NULL) { 4897 window_copy_update_cursor(wme, x, y); 4898 window_copy_cursor_previous_word_pos(wme, data->ws, 0, 4899 &x, &y); 4900 y -= screen_hsize(data->backing) - data->oy; 4901 } 4902 window_copy_update_cursor(wme, x, y); 4903 break; 4904 case SEL_LINE: 4905 window_copy_update_cursor(wme, 0, y); 4906 break; 4907 case SEL_CHAR: 4908 window_copy_update_cursor(wme, x, y); 4909 window_copy_start_selection(wme); 4910 break; 4911 } 4912 4913 window_copy_redraw_screen(wme); 4914 window_copy_drag_update(c, m); 4915 } 4916 4917 static void 4918 window_copy_drag_update(struct client *c, struct mouse_event *m) 4919 { 4920 struct window_pane *wp; 4921 struct window_mode_entry *wme; 4922 struct window_copy_mode_data *data; 4923 u_int x, y, old_cx, old_cy; 4924 struct timeval tv = { 4925 .tv_usec = WINDOW_COPY_DRAG_REPEAT_TIME 4926 }; 4927 4928 if (c == NULL) 4929 return; 4930 4931 wp = cmd_mouse_pane(m, NULL, NULL); 4932 if (wp == NULL) 4933 return; 4934 wme = TAILQ_FIRST(&wp->modes); 4935 if (wme == NULL) 4936 return; 4937 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode) 4938 return; 4939 4940 data = wme->data; 4941 evtimer_del(&data->dragtimer); 4942 4943 if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) 4944 return; 4945 old_cx = data->cx; 4946 old_cy = data->cy; 4947 4948 window_copy_update_cursor(wme, x, y); 4949 if (window_copy_update_selection(wme, 1, 0)) 4950 window_copy_redraw_selection(wme, old_cy); 4951 if (old_cy != data->cy || old_cx == data->cx) { 4952 if (y == 0) { 4953 evtimer_add(&data->dragtimer, &tv); 4954 window_copy_cursor_up(wme, 1); 4955 } else if (y == screen_size_y(&data->screen) - 1) { 4956 evtimer_add(&data->dragtimer, &tv); 4957 window_copy_cursor_down(wme, 1); 4958 } 4959 } 4960 } 4961 4962 static void 4963 window_copy_drag_release(struct client *c, struct mouse_event *m) 4964 { 4965 struct window_pane *wp; 4966 struct window_mode_entry *wme; 4967 struct window_copy_mode_data *data; 4968 4969 if (c == NULL) 4970 return; 4971 4972 wp = cmd_mouse_pane(m, NULL, NULL); 4973 if (wp == NULL) 4974 return; 4975 wme = TAILQ_FIRST(&wp->modes); 4976 if (wme == NULL) 4977 return; 4978 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode) 4979 return; 4980 4981 data = wme->data; 4982 evtimer_del(&data->dragtimer); 4983 } 4984 4985 static void 4986 window_copy_jump_to_mark(struct window_mode_entry *wme) 4987 { 4988 struct window_copy_mode_data *data = wme->data; 4989 u_int tmx, tmy; 4990 4991 tmx = data->cx; 4992 tmy = screen_hsize(data->backing) + data->cy - data->oy; 4993 data->cx = data->mx; 4994 if (data->my < screen_hsize(data->backing)) { 4995 data->cy = 0; 4996 data->oy = screen_hsize(data->backing) - data->my; 4997 } else { 4998 data->cy = data->my - screen_hsize(data->backing); 4999 data->oy = 0; 5000 } 5001 data->mx = tmx; 5002 data->my = tmy; 5003 data->showmark = 1; 5004 window_copy_update_selection(wme, 0, 0); 5005 window_copy_redraw_screen(wme); 5006 } 5007 5008 /* Scroll up if the cursor went off the visible screen. */ 5009 static void 5010 window_copy_acquire_cursor_up(struct window_mode_entry *wme, u_int hsize, 5011 u_int oy, u_int oldy, u_int px, u_int py) 5012 { 5013 u_int cy, yy, ny, nd; 5014 5015 yy = hsize - oy; 5016 if (py < yy) { 5017 ny = yy - py; 5018 cy = 0; 5019 nd = 1; 5020 } else { 5021 ny = 0; 5022 cy = py - yy; 5023 nd = oldy - cy + 1; 5024 } 5025 while (ny > 0) { 5026 window_copy_cursor_up(wme, 1); 5027 ny--; 5028 } 5029 window_copy_update_cursor(wme, px, cy); 5030 if (window_copy_update_selection(wme, 1, 0)) 5031 window_copy_redraw_lines(wme, cy, nd); 5032 } 5033 5034 /* Scroll down if the cursor went off the visible screen. */ 5035 static void 5036 window_copy_acquire_cursor_down(struct window_mode_entry *wme, u_int hsize, 5037 u_int sy, u_int oy, u_int oldy, u_int px, u_int py, int no_reset) 5038 { 5039 u_int cy, yy, ny, nd; 5040 5041 cy = py - hsize + oy; 5042 yy = sy - 1; 5043 if (cy > yy) { 5044 ny = cy - yy; 5045 oldy = yy; 5046 nd = 1; 5047 } else { 5048 ny = 0; 5049 nd = cy - oldy + 1; 5050 } 5051 while (ny > 0) { 5052 window_copy_cursor_down(wme, 1); 5053 ny--; 5054 } 5055 if (cy > yy) 5056 window_copy_update_cursor(wme, px, yy); 5057 else 5058 window_copy_update_cursor(wme, px, cy); 5059 if (window_copy_update_selection(wme, 1, no_reset)) 5060 window_copy_redraw_lines(wme, oldy, nd); 5061 } 5062