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