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