1 /* $OpenBSD: window-copy.c,v 1.301 2020/07/15 10:09:54 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 void window_copy_move_right(struct screen *, u_int *, u_int *, int); 76 static int window_copy_is_lowercase(const char *); 77 static int window_copy_search_jump(struct window_mode_entry *, 78 struct grid *, struct grid *, u_int, u_int, u_int, int, int, 79 int, int); 80 static int window_copy_search(struct window_mode_entry *, int, int); 81 static int window_copy_search_up(struct window_mode_entry *, int); 82 static int window_copy_search_down(struct window_mode_entry *, int); 83 static void window_copy_goto_line(struct window_mode_entry *, const char *); 84 static void window_copy_update_cursor(struct window_mode_entry *, u_int, 85 u_int); 86 static void window_copy_start_selection(struct window_mode_entry *); 87 static int window_copy_adjust_selection(struct window_mode_entry *, 88 u_int *, u_int *); 89 static int window_copy_set_selection(struct window_mode_entry *, int, int); 90 static int window_copy_update_selection(struct window_mode_entry *, int, 91 int); 92 static void window_copy_synchronize_cursor(struct window_mode_entry *, int); 93 static void *window_copy_get_selection(struct window_mode_entry *, size_t *); 94 static void window_copy_copy_buffer(struct window_mode_entry *, 95 const char *, void *, size_t); 96 static void window_copy_copy_pipe(struct window_mode_entry *, 97 struct session *, const char *, const char *); 98 static void window_copy_copy_selection(struct window_mode_entry *, 99 const char *); 100 static void window_copy_append_selection(struct window_mode_entry *); 101 static void window_copy_clear_selection(struct window_mode_entry *); 102 static void window_copy_copy_line(struct window_mode_entry *, char **, 103 size_t *, u_int, u_int, u_int); 104 static int window_copy_in_set(struct window_mode_entry *, u_int, u_int, 105 const char *); 106 static u_int window_copy_find_length(struct window_mode_entry *, u_int); 107 static void window_copy_cursor_start_of_line(struct window_mode_entry *); 108 static void window_copy_cursor_back_to_indentation( 109 struct window_mode_entry *); 110 static void window_copy_cursor_end_of_line(struct window_mode_entry *); 111 static void window_copy_other_end(struct window_mode_entry *); 112 static void window_copy_cursor_left(struct window_mode_entry *); 113 static void window_copy_cursor_right(struct window_mode_entry *); 114 static void window_copy_cursor_up(struct window_mode_entry *, int); 115 static void window_copy_cursor_down(struct window_mode_entry *, int); 116 static void window_copy_cursor_jump(struct window_mode_entry *); 117 static void window_copy_cursor_jump_back(struct window_mode_entry *); 118 static void window_copy_cursor_jump_to(struct window_mode_entry *); 119 static void window_copy_cursor_jump_to_back(struct window_mode_entry *); 120 static void window_copy_cursor_next_word(struct window_mode_entry *, 121 const char *); 122 static void window_copy_cursor_next_word_end_pos(struct window_mode_entry *, 123 const char *, u_int *, u_int *); 124 static void window_copy_cursor_next_word_end(struct window_mode_entry *, 125 const char *, int); 126 static void window_copy_cursor_previous_word_pos(struct window_mode_entry *, 127 const char *, int, u_int *, u_int *); 128 static void window_copy_cursor_previous_word(struct window_mode_entry *, 129 const char *, int); 130 static void window_copy_scroll_up(struct window_mode_entry *, u_int); 131 static void window_copy_scroll_down(struct window_mode_entry *, u_int); 132 static void window_copy_rectangle_toggle(struct window_mode_entry *); 133 static void window_copy_move_mouse(struct mouse_event *); 134 static void window_copy_drag_update(struct client *, struct mouse_event *); 135 static void window_copy_drag_release(struct client *, struct mouse_event *); 136 static void window_copy_jump_to_mark(struct window_mode_entry *); 137 138 const struct window_mode window_copy_mode = { 139 .name = "copy-mode", 140 141 .init = window_copy_init, 142 .free = window_copy_free, 143 .resize = window_copy_resize, 144 .key_table = window_copy_key_table, 145 .command = window_copy_command, 146 .formats = window_copy_formats, 147 }; 148 149 const struct window_mode window_view_mode = { 150 .name = "view-mode", 151 152 .init = window_copy_view_init, 153 .free = window_copy_free, 154 .resize = window_copy_resize, 155 .key_table = window_copy_key_table, 156 .command = window_copy_command, 157 .formats = window_copy_formats, 158 }; 159 160 enum { 161 WINDOW_COPY_OFF, 162 WINDOW_COPY_SEARCHUP, 163 WINDOW_COPY_SEARCHDOWN, 164 WINDOW_COPY_JUMPFORWARD, 165 WINDOW_COPY_JUMPBACKWARD, 166 WINDOW_COPY_JUMPTOFORWARD, 167 WINDOW_COPY_JUMPTOBACKWARD, 168 }; 169 170 enum { 171 WINDOW_COPY_REL_POS_ABOVE, 172 WINDOW_COPY_REL_POS_ON_SCREEN, 173 WINDOW_COPY_REL_POS_BELOW, 174 }; 175 176 enum window_copy_cmd_action { 177 WINDOW_COPY_CMD_NOTHING, 178 WINDOW_COPY_CMD_REDRAW, 179 WINDOW_COPY_CMD_CANCEL, 180 }; 181 182 enum window_copy_cmd_clear { 183 WINDOW_COPY_CMD_CLEAR_ALWAYS, 184 WINDOW_COPY_CMD_CLEAR_NEVER, 185 WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 186 }; 187 188 struct window_copy_cmd_state { 189 struct window_mode_entry *wme; 190 struct args *args; 191 struct mouse_event *m; 192 193 struct client *c; 194 struct session *s; 195 struct winlink *wl; 196 }; 197 198 /* 199 * Copy mode's visible screen (the "screen" field) is filled from one of two 200 * sources: the original contents of the pane (used when we actually enter via 201 * the "copy-mode" command, to copy the contents of the current pane), or else 202 * a series of lines containing the output from an output-writing tmux command 203 * (such as any of the "show-*" or "list-*" commands). 204 * 205 * In either case, the full content of the copy-mode grid is pointed at by the 206 * "backing" field, and is copied into "screen" as needed (that is, when 207 * scrolling occurs). When copy-mode is backed by a pane, backing points 208 * directly at that pane's screen structure (&wp->base); when backed by a list 209 * of output-lines from a command, it points at a newly-allocated screen 210 * structure (which is deallocated when the mode ends). 211 */ 212 struct window_copy_mode_data { 213 struct screen screen; 214 215 struct screen *backing; 216 int backing_written; /* backing display started */ 217 218 int viewmode; /* view mode entered */ 219 220 u_int oy; /* number of lines scrolled up */ 221 222 u_int selx; /* beginning of selection */ 223 u_int sely; 224 225 u_int endselx; /* end of selection */ 226 u_int endsely; 227 228 enum { 229 CURSORDRAG_NONE, /* selection is independent of cursor */ 230 CURSORDRAG_ENDSEL, /* end is synchronized with cursor */ 231 CURSORDRAG_SEL, /* start is synchronized with cursor */ 232 } cursordrag; 233 234 int modekeys; 235 enum { 236 LINE_SEL_NONE, 237 LINE_SEL_LEFT_RIGHT, 238 LINE_SEL_RIGHT_LEFT, 239 } lineflag; /* line selection mode */ 240 int rectflag; /* in rectangle copy mode? */ 241 int scroll_exit; /* exit on scroll to end? */ 242 int hide_position; /* hide position marker */ 243 244 enum { 245 SEL_CHAR, /* select one char at a time */ 246 SEL_WORD, /* select one word at a time */ 247 SEL_LINE, /* select one line at a time */ 248 } selflag; 249 250 const char *ws; /* word separators */ 251 252 u_int dx; /* drag start position */ 253 u_int dy; 254 255 u_int selrx; /* selection reset positions */ 256 u_int selry; 257 u_int endselrx; 258 u_int endselry; 259 260 u_int cx; 261 u_int cy; 262 263 u_int lastcx; /* position in last line w/ content */ 264 u_int lastsx; /* size of last line w/ content */ 265 266 u_int mx; /* mark position */ 267 u_int my; 268 int showmark; 269 270 int searchtype; 271 int searchregex; 272 char *searchstr; 273 u_char *searchmark; 274 int searchcount; 275 int searchmore; 276 int searchthis; 277 int searchx; 278 int searchy; 279 int searcho; 280 u_char searchgen; 281 282 int timeout; /* search has timed out */ 283 #define WINDOW_COPY_SEARCH_TIMEOUT 10000 284 #define WINDOW_COPY_SEARCH_ALL_TIMEOUT 200 285 286 int jumptype; 287 char jumpchar; 288 289 struct event dragtimer; 290 #define WINDOW_COPY_DRAG_REPEAT_TIME 50000 291 }; 292 293 static void 294 window_copy_scroll_timer(__unused int fd, __unused short events, void *arg) 295 { 296 struct window_mode_entry *wme = arg; 297 struct window_pane *wp = wme->wp; 298 struct window_copy_mode_data *data = wme->data; 299 struct timeval tv = { 300 .tv_usec = WINDOW_COPY_DRAG_REPEAT_TIME 301 }; 302 303 evtimer_del(&data->dragtimer); 304 305 if (TAILQ_FIRST(&wp->modes) != wme) 306 return; 307 308 if (data->cy == 0) { 309 evtimer_add(&data->dragtimer, &tv); 310 window_copy_cursor_up(wme, 1); 311 } else if (data->cy == screen_size_y(&data->screen) - 1) { 312 evtimer_add(&data->dragtimer, &tv); 313 window_copy_cursor_down(wme, 1); 314 } 315 } 316 317 static struct screen * 318 window_copy_clone_screen(struct screen *src, struct screen *hint, u_int *cx, 319 u_int *cy, int trim) 320 { 321 struct screen *dst; 322 const struct grid_line *gl; 323 u_int sy, wx, wy; 324 int reflow; 325 326 dst = xcalloc(1, sizeof *dst); 327 328 sy = screen_hsize(src) + screen_size_y(src); 329 if (trim) { 330 while (sy > screen_hsize(src)) { 331 gl = grid_peek_line(src->grid, sy - 1); 332 if (gl->cellused != 0) 333 break; 334 sy--; 335 } 336 } 337 log_debug("%s: target screen is %ux%u, source %ux%u", __func__, 338 screen_size_x(src), sy, screen_size_x(hint), 339 screen_hsize(src) + screen_size_y(src)); 340 screen_init(dst, screen_size_x(src), sy, screen_hlimit(src)); 341 342 /* 343 * Ensure history is on for the backing grid so lines are not deleted 344 * during resizing. 345 */ 346 dst->grid->flags |= GRID_HISTORY; 347 grid_duplicate_lines(dst->grid, 0, src->grid, 0, sy); 348 349 dst->grid->sy = sy - screen_hsize(src); 350 dst->grid->hsize = screen_hsize(src); 351 dst->grid->hscrolled = src->grid->hscrolled; 352 if (src->cy > dst->grid->sy - 1) { 353 dst->cx = 0; 354 dst->cy = dst->grid->sy - 1; 355 } else { 356 dst->cx = src->cx; 357 dst->cy = src->cy; 358 } 359 360 if (cx != NULL && cy != NULL) { 361 *cx = dst->cx; 362 *cy = screen_hsize(dst) + dst->cy; 363 reflow = (screen_size_x(hint) != screen_size_x(dst)); 364 } 365 else 366 reflow = 0; 367 if (reflow) 368 grid_wrap_position(dst->grid, *cx, *cy, &wx, &wy); 369 screen_resize_cursor(dst, screen_size_x(hint), screen_size_y(hint), 1, 370 0, 0); 371 if (reflow) 372 grid_unwrap_position(dst->grid, cx, cy, wx, wy); 373 374 return (dst); 375 } 376 377 static struct window_copy_mode_data * 378 window_copy_common_init(struct window_mode_entry *wme) 379 { 380 struct window_pane *wp = wme->wp; 381 struct window_copy_mode_data *data; 382 struct screen *base = &wp->base; 383 384 wme->data = data = xcalloc(1, sizeof *data); 385 386 data->cursordrag = CURSORDRAG_NONE; 387 data->lineflag = LINE_SEL_NONE; 388 data->selflag = SEL_CHAR; 389 390 if (wp->searchstr != NULL) { 391 data->searchtype = WINDOW_COPY_SEARCHUP; 392 data->searchregex = wp->searchregex; 393 data->searchstr = xstrdup(wp->searchstr); 394 } else { 395 data->searchtype = WINDOW_COPY_OFF; 396 data->searchregex = 0; 397 data->searchstr = NULL; 398 } 399 data->searchx = data->searchy = data->searcho = -1; 400 401 data->jumptype = WINDOW_COPY_OFF; 402 data->jumpchar = '\0'; 403 404 screen_init(&data->screen, screen_size_x(base), screen_size_y(base), 0); 405 data->modekeys = options_get_number(wp->window->options, "mode-keys"); 406 407 evtimer_set(&data->dragtimer, window_copy_scroll_timer, wme); 408 409 return (data); 410 } 411 412 static struct screen * 413 window_copy_init(struct window_mode_entry *wme, 414 __unused struct cmd_find_state *fs, struct args *args) 415 { 416 struct window_pane *wp = wme->swp; 417 struct window_copy_mode_data *data; 418 struct screen *base = &wp->base; 419 struct screen_write_ctx ctx; 420 u_int i, cx, cy; 421 422 data = window_copy_common_init(wme); 423 data->backing = window_copy_clone_screen(base, &data->screen, &cx, &cy, 424 wme->swp != wme->wp); 425 426 data->cx = cx; 427 if (cy < screen_hsize(data->backing)) { 428 data->cy = 0; 429 data->oy = screen_hsize(data->backing) - cy; 430 } else { 431 data->cy = cy - screen_hsize(data->backing); 432 data->oy = 0; 433 } 434 435 data->scroll_exit = args_has(args, 'e'); 436 data->hide_position = args_has(args, 'H'); 437 438 data->screen.cx = data->cx; 439 data->screen.cy = data->cy; 440 data->mx = data->cx; 441 data->my = screen_hsize(data->backing) + data->cy - data->oy; 442 data->showmark = 0; 443 444 screen_write_start(&ctx, &data->screen); 445 for (i = 0; i < screen_size_y(&data->screen); i++) 446 window_copy_write_line(wme, &ctx, i); 447 screen_write_cursormove(&ctx, data->cx, data->cy, 0); 448 screen_write_stop(&ctx); 449 450 return (&data->screen); 451 } 452 453 static struct screen * 454 window_copy_view_init(struct window_mode_entry *wme, 455 __unused struct cmd_find_state *fs, __unused struct args *args) 456 { 457 struct window_pane *wp = wme->wp; 458 struct window_copy_mode_data *data; 459 struct screen *base = &wp->base; 460 struct screen *s; 461 462 data = window_copy_common_init(wme); 463 data->viewmode = 1; 464 465 data->backing = s = xmalloc(sizeof *data->backing); 466 screen_init(s, screen_size_x(base), screen_size_y(base), UINT_MAX); 467 data->mx = data->cx; 468 data->my = screen_hsize(data->backing) + data->cy - data->oy; 469 data->showmark = 0; 470 471 return (&data->screen); 472 } 473 474 static void 475 window_copy_free(struct window_mode_entry *wme) 476 { 477 struct window_copy_mode_data *data = wme->data; 478 479 evtimer_del(&data->dragtimer); 480 481 free(data->searchmark); 482 free(data->searchstr); 483 484 screen_free(data->backing); 485 free(data->backing); 486 487 screen_free(&data->screen); 488 free(data); 489 } 490 491 void 492 window_copy_add(struct window_pane *wp, const char *fmt, ...) 493 { 494 va_list ap; 495 496 va_start(ap, fmt); 497 window_copy_vadd(wp, fmt, ap); 498 va_end(ap); 499 } 500 501 void 502 window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap) 503 { 504 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); 505 struct window_copy_mode_data *data = wme->data; 506 struct screen *backing = data->backing; 507 struct screen_write_ctx back_ctx, ctx; 508 struct grid_cell gc; 509 u_int old_hsize, old_cy; 510 511 memcpy(&gc, &grid_default_cell, sizeof gc); 512 513 old_hsize = screen_hsize(data->backing); 514 screen_write_start(&back_ctx, backing); 515 if (data->backing_written) { 516 /* 517 * On the second or later line, do a CRLF before writing 518 * (so it's on a new line). 519 */ 520 screen_write_carriagereturn(&back_ctx); 521 screen_write_linefeed(&back_ctx, 0, 8); 522 } else 523 data->backing_written = 1; 524 old_cy = backing->cy; 525 screen_write_vnputs(&back_ctx, 0, &gc, fmt, ap); 526 screen_write_stop(&back_ctx); 527 528 data->oy += screen_hsize(data->backing) - old_hsize; 529 530 screen_write_start_pane(&ctx, wp, &data->screen); 531 532 /* 533 * If the history has changed, draw the top line. 534 * (If there's any history at all, it has changed.) 535 */ 536 if (screen_hsize(data->backing)) 537 window_copy_redraw_lines(wme, 0, 1); 538 539 /* Write the new lines. */ 540 window_copy_redraw_lines(wme, old_cy, backing->cy - old_cy + 1); 541 542 screen_write_stop(&ctx); 543 } 544 545 void 546 window_copy_pageup(struct window_pane *wp, int half_page) 547 { 548 window_copy_pageup1(TAILQ_FIRST(&wp->modes), half_page); 549 } 550 551 static void 552 window_copy_pageup1(struct window_mode_entry *wme, int half_page) 553 { 554 struct window_copy_mode_data *data = wme->data; 555 struct screen *s = &data->screen; 556 u_int n, ox, oy, px, py; 557 558 oy = screen_hsize(data->backing) + data->cy - data->oy; 559 ox = window_copy_find_length(wme, oy); 560 561 if (data->cx != ox) { 562 data->lastcx = data->cx; 563 data->lastsx = ox; 564 } 565 data->cx = data->lastcx; 566 567 n = 1; 568 if (screen_size_y(s) > 2) { 569 if (half_page) 570 n = screen_size_y(s) / 2; 571 else 572 n = screen_size_y(s) - 2; 573 } 574 575 if (data->oy + n > screen_hsize(data->backing)) { 576 data->oy = screen_hsize(data->backing); 577 if (data->cy < n) 578 data->cy = 0; 579 else 580 data->cy -= n; 581 } else 582 data->oy += n; 583 584 if (data->screen.sel == NULL || !data->rectflag) { 585 py = screen_hsize(data->backing) + data->cy - data->oy; 586 px = window_copy_find_length(wme, py); 587 if ((data->cx >= data->lastsx && data->cx != px) || 588 data->cx > px) 589 window_copy_cursor_end_of_line(wme); 590 } 591 592 if (data->searchmark != NULL && !data->timeout) 593 window_copy_search_marks(wme, NULL, data->searchregex, 1); 594 window_copy_update_selection(wme, 1, 0); 595 window_copy_redraw_screen(wme); 596 } 597 598 static int 599 window_copy_pagedown(struct window_mode_entry *wme, int half_page, 600 int scroll_exit) 601 { 602 struct window_copy_mode_data *data = wme->data; 603 struct screen *s = &data->screen; 604 u_int n, ox, oy, px, py; 605 606 oy = screen_hsize(data->backing) + data->cy - data->oy; 607 ox = window_copy_find_length(wme, oy); 608 609 if (data->cx != ox) { 610 data->lastcx = data->cx; 611 data->lastsx = ox; 612 } 613 data->cx = data->lastcx; 614 615 n = 1; 616 if (screen_size_y(s) > 2) { 617 if (half_page) 618 n = screen_size_y(s) / 2; 619 else 620 n = screen_size_y(s) - 2; 621 } 622 623 if (data->oy < n) { 624 data->oy = 0; 625 if (data->cy + (n - data->oy) >= screen_size_y(data->backing)) 626 data->cy = screen_size_y(data->backing) - 1; 627 else 628 data->cy += n - data->oy; 629 } else 630 data->oy -= n; 631 632 if (data->screen.sel == NULL || !data->rectflag) { 633 py = screen_hsize(data->backing) + data->cy - data->oy; 634 px = window_copy_find_length(wme, py); 635 if ((data->cx >= data->lastsx && data->cx != px) || 636 data->cx > px) 637 window_copy_cursor_end_of_line(wme); 638 } 639 640 if (scroll_exit && data->oy == 0) 641 return (1); 642 if (data->searchmark != NULL && !data->timeout) 643 window_copy_search_marks(wme, NULL, data->searchregex, 1); 644 window_copy_update_selection(wme, 1, 0); 645 window_copy_redraw_screen(wme); 646 return (0); 647 } 648 649 static void 650 window_copy_previous_paragraph(struct window_mode_entry *wme) 651 { 652 struct window_copy_mode_data *data = wme->data; 653 u_int oy; 654 655 oy = screen_hsize(data->backing) + data->cy - data->oy; 656 657 while (oy > 0 && window_copy_find_length(wme, oy) == 0) 658 oy--; 659 660 while (oy > 0 && window_copy_find_length(wme, oy) > 0) 661 oy--; 662 663 window_copy_scroll_to(wme, 0, oy, 0); 664 } 665 666 static void 667 window_copy_next_paragraph(struct window_mode_entry *wme) 668 { 669 struct window_copy_mode_data *data = wme->data; 670 struct screen *s = &data->screen; 671 u_int maxy, ox, oy; 672 673 oy = screen_hsize(data->backing) + data->cy - data->oy; 674 maxy = screen_hsize(data->backing) + screen_size_y(s) - 1; 675 676 while (oy < maxy && window_copy_find_length(wme, oy) == 0) 677 oy++; 678 679 while (oy < maxy && window_copy_find_length(wme, oy) > 0) 680 oy++; 681 682 ox = window_copy_find_length(wme, oy); 683 window_copy_scroll_to(wme, ox, oy, 0); 684 } 685 686 char * 687 window_copy_get_word(struct window_pane *wp, u_int x, u_int y) 688 { 689 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); 690 struct window_copy_mode_data *data = wme->data; 691 struct grid *gd = data->screen.grid; 692 693 return (format_grid_word(gd, x, gd->hsize + y)); 694 } 695 696 char * 697 window_copy_get_line(struct window_pane *wp, u_int y) 698 { 699 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); 700 struct window_copy_mode_data *data = wme->data; 701 struct grid *gd = data->screen.grid; 702 703 return (format_grid_line(gd, gd->hsize + y)); 704 } 705 706 static char * 707 window_copy_cursor_word_cb(struct format_tree *ft) 708 { 709 struct window_pane *wp = format_get_pane(ft); 710 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); 711 struct window_copy_mode_data *data = wme->data; 712 713 return (window_copy_get_word(wp, data->cx, data->cy)); 714 } 715 716 static char * 717 window_copy_cursor_line_cb(struct format_tree *ft) 718 { 719 struct window_pane *wp = format_get_pane(ft); 720 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); 721 struct window_copy_mode_data *data = wme->data; 722 723 return (window_copy_get_line(wp, data->cy)); 724 } 725 726 static char * 727 window_copy_search_match_cb(struct format_tree *ft) 728 { 729 struct window_pane *wp = format_get_pane(ft); 730 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); 731 struct window_copy_mode_data *data = wme->data; 732 733 return (window_copy_match_at_cursor(data)); 734 } 735 736 static void 737 window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft) 738 { 739 struct window_copy_mode_data *data = wme->data; 740 741 format_add(ft, "scroll_position", "%d", data->oy); 742 format_add(ft, "rectangle_toggle", "%d", data->rectflag); 743 744 format_add(ft, "copy_cursor_x", "%d", data->cx); 745 format_add(ft, "copy_cursor_y", "%d", data->cy); 746 747 format_add(ft, "selection_present", "%d", data->screen.sel != NULL); 748 if (data->screen.sel != NULL) { 749 format_add(ft, "selection_start_x", "%d", data->selx); 750 format_add(ft, "selection_start_y", "%d", data->sely); 751 format_add(ft, "selection_end_x", "%d", data->endselx); 752 format_add(ft, "selection_end_y", "%d", data->endsely); 753 format_add(ft, "selection_active", "%d", 754 data->cursordrag != CURSORDRAG_NONE); 755 } else 756 format_add(ft, "selection_active", "%d", 0); 757 758 format_add(ft, "search_present", "%d", data->searchmark != NULL); 759 format_add_cb(ft, "search_match", window_copy_search_match_cb); 760 761 format_add_cb(ft, "copy_cursor_word", window_copy_cursor_word_cb); 762 format_add_cb(ft, "copy_cursor_line", window_copy_cursor_line_cb); 763 } 764 765 static void 766 window_copy_size_changed(struct window_mode_entry *wme) 767 { 768 struct window_copy_mode_data *data = wme->data; 769 struct screen *s = &data->screen; 770 struct screen_write_ctx ctx; 771 int search = (data->searchmark != NULL); 772 773 window_copy_clear_selection(wme); 774 window_copy_clear_marks(wme); 775 776 screen_write_start(&ctx, s); 777 window_copy_write_lines(wme, &ctx, 0, screen_size_y(s)); 778 screen_write_stop(&ctx); 779 780 if (search && !data->timeout) 781 window_copy_search_marks(wme, NULL, data->searchregex, 0); 782 data->searchx = data->cx; 783 data->searchy = data->cy; 784 data->searcho = data->oy; 785 } 786 787 static void 788 window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy) 789 { 790 struct window_copy_mode_data *data = wme->data; 791 struct screen *s = &data->screen; 792 struct grid *gd = data->backing->grid; 793 u_int cx, cy, wx, wy; 794 int reflow; 795 796 screen_resize(s, sx, sy, 0); 797 cx = data->cx; 798 cy = gd->hsize + data->cy - data->oy; 799 reflow = (gd->sx != sx); 800 if (reflow) 801 grid_wrap_position(gd, cx, cy, &wx, &wy); 802 screen_resize_cursor(data->backing, sx, sy, 1, 0, 0); 803 if (reflow) 804 grid_unwrap_position(gd, &cx, &cy, wx, wy); 805 806 data->cx = cx; 807 if (cy < gd->hsize) { 808 data->cy = 0; 809 data->oy = gd->hsize - cy; 810 } else { 811 data->cy = cy - gd->hsize; 812 data->oy = 0; 813 } 814 815 window_copy_size_changed(wme); 816 window_copy_redraw_screen(wme); 817 } 818 819 static const char * 820 window_copy_key_table(struct window_mode_entry *wme) 821 { 822 struct window_pane *wp = wme->wp; 823 824 if (options_get_number(wp->window->options, "mode-keys") == MODEKEY_VI) 825 return ("copy-mode-vi"); 826 return ("copy-mode"); 827 } 828 829 static int 830 window_copy_expand_search_string(struct window_copy_cmd_state *cs) 831 { 832 struct window_mode_entry *wme = cs->wme; 833 struct window_copy_mode_data *data = wme->data; 834 const char *argument; 835 char *expanded; 836 837 if (cs->args->argc == 2) { 838 argument = cs->args->argv[1]; 839 if (*argument != '\0') { 840 if (args_has(cs->args, 'F')) { 841 expanded = format_single(NULL, argument, NULL, 842 NULL, NULL, wme->wp); 843 if (*expanded == '\0') { 844 free(expanded); 845 return (0); 846 } 847 free(data->searchstr); 848 data->searchstr = expanded; 849 } else { 850 free(data->searchstr); 851 data->searchstr = xstrdup(argument); 852 } 853 } 854 } 855 return (1); 856 } 857 858 static enum window_copy_cmd_action 859 window_copy_cmd_append_selection(struct window_copy_cmd_state *cs) 860 { 861 struct window_mode_entry *wme = cs->wme; 862 struct session *s = cs->s; 863 864 if (s != NULL) 865 window_copy_append_selection(wme); 866 window_copy_clear_selection(wme); 867 return (WINDOW_COPY_CMD_REDRAW); 868 } 869 870 static enum window_copy_cmd_action 871 window_copy_cmd_append_selection_and_cancel(struct window_copy_cmd_state *cs) 872 { 873 struct window_mode_entry *wme = cs->wme; 874 struct session *s = cs->s; 875 876 if (s != NULL) 877 window_copy_append_selection(wme); 878 window_copy_clear_selection(wme); 879 return (WINDOW_COPY_CMD_CANCEL); 880 } 881 882 static enum window_copy_cmd_action 883 window_copy_cmd_back_to_indentation(struct window_copy_cmd_state *cs) 884 { 885 struct window_mode_entry *wme = cs->wme; 886 887 window_copy_cursor_back_to_indentation(wme); 888 return (WINDOW_COPY_CMD_NOTHING); 889 } 890 891 static enum window_copy_cmd_action 892 window_copy_cmd_begin_selection(struct window_copy_cmd_state *cs) 893 { 894 struct window_mode_entry *wme = cs->wme; 895 struct client *c = cs->c; 896 struct mouse_event *m = cs->m; 897 struct window_copy_mode_data *data = wme->data; 898 899 if (m != NULL) { 900 window_copy_start_drag(c, m); 901 return (WINDOW_COPY_CMD_NOTHING); 902 } 903 904 data->lineflag = LINE_SEL_NONE; 905 data->selflag = SEL_CHAR; 906 window_copy_start_selection(wme); 907 return (WINDOW_COPY_CMD_REDRAW); 908 } 909 910 static enum window_copy_cmd_action 911 window_copy_cmd_stop_selection(struct window_copy_cmd_state *cs) 912 { 913 struct window_mode_entry *wme = cs->wme; 914 struct window_copy_mode_data *data = wme->data; 915 916 data->cursordrag = CURSORDRAG_NONE; 917 data->lineflag = LINE_SEL_NONE; 918 data->selflag = SEL_CHAR; 919 return (WINDOW_COPY_CMD_NOTHING); 920 } 921 922 static enum window_copy_cmd_action 923 window_copy_cmd_bottom_line(struct window_copy_cmd_state *cs) 924 { 925 struct window_mode_entry *wme = cs->wme; 926 struct window_copy_mode_data *data = wme->data; 927 928 data->cx = 0; 929 data->cy = screen_size_y(&data->screen) - 1; 930 931 window_copy_update_selection(wme, 1, 0); 932 return (WINDOW_COPY_CMD_REDRAW); 933 } 934 935 static enum window_copy_cmd_action 936 window_copy_cmd_cancel(__unused struct window_copy_cmd_state *cs) 937 { 938 return (WINDOW_COPY_CMD_CANCEL); 939 } 940 941 static enum window_copy_cmd_action 942 window_copy_cmd_clear_selection(struct window_copy_cmd_state *cs) 943 { 944 struct window_mode_entry *wme = cs->wme; 945 946 window_copy_clear_selection(wme); 947 return (WINDOW_COPY_CMD_REDRAW); 948 } 949 950 static enum window_copy_cmd_action 951 window_copy_cmd_copy_end_of_line(struct window_copy_cmd_state *cs) 952 { 953 struct window_mode_entry *wme = cs->wme; 954 struct client *c = cs->c; 955 struct session *s = cs->s; 956 struct winlink *wl = cs->wl; 957 struct window_pane *wp = wme->wp; 958 u_int np = wme->prefix; 959 char *prefix = NULL; 960 961 if (cs->args->argc == 2) 962 prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp); 963 964 window_copy_start_selection(wme); 965 for (; np > 1; np--) 966 window_copy_cursor_down(wme, 0); 967 window_copy_cursor_end_of_line(wme); 968 969 if (s != NULL) { 970 window_copy_copy_selection(wme, prefix); 971 972 free(prefix); 973 return (WINDOW_COPY_CMD_CANCEL); 974 } 975 976 free(prefix); 977 return (WINDOW_COPY_CMD_REDRAW); 978 } 979 980 static enum window_copy_cmd_action 981 window_copy_cmd_copy_line(struct window_copy_cmd_state *cs) 982 { 983 struct window_mode_entry *wme = cs->wme; 984 struct client *c = cs->c; 985 struct session *s = cs->s; 986 struct winlink *wl = cs->wl; 987 struct window_pane *wp = wme->wp; 988 struct window_copy_mode_data *data = wme->data; 989 u_int np = wme->prefix; 990 char *prefix = NULL; 991 992 if (cs->args->argc == 2) 993 prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp); 994 995 data->selflag = SEL_CHAR; 996 window_copy_cursor_start_of_line(wme); 997 window_copy_start_selection(wme); 998 for (; np > 1; np--) 999 window_copy_cursor_down(wme, 0); 1000 window_copy_cursor_end_of_line(wme); 1001 1002 if (s != NULL) { 1003 window_copy_copy_selection(wme, prefix); 1004 1005 free(prefix); 1006 return (WINDOW_COPY_CMD_CANCEL); 1007 } 1008 1009 free(prefix); 1010 return (WINDOW_COPY_CMD_REDRAW); 1011 } 1012 1013 static enum window_copy_cmd_action 1014 window_copy_cmd_copy_selection_no_clear(struct window_copy_cmd_state *cs) 1015 { 1016 struct window_mode_entry *wme = cs->wme; 1017 struct client *c = cs->c; 1018 struct session *s = cs->s; 1019 struct winlink *wl = cs->wl; 1020 struct window_pane *wp = wme->wp; 1021 char *prefix = NULL; 1022 1023 if (cs->args->argc == 2) 1024 prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp); 1025 1026 if (s != NULL) 1027 window_copy_copy_selection(wme, prefix); 1028 1029 free(prefix); 1030 return (WINDOW_COPY_CMD_NOTHING); 1031 } 1032 1033 static enum window_copy_cmd_action 1034 window_copy_cmd_copy_selection(struct window_copy_cmd_state *cs) 1035 { 1036 struct window_mode_entry *wme = cs->wme; 1037 1038 window_copy_cmd_copy_selection_no_clear(cs); 1039 window_copy_clear_selection(wme); 1040 return (WINDOW_COPY_CMD_REDRAW); 1041 } 1042 1043 static enum window_copy_cmd_action 1044 window_copy_cmd_copy_selection_and_cancel(struct window_copy_cmd_state *cs) 1045 { 1046 struct window_mode_entry *wme = cs->wme; 1047 1048 window_copy_cmd_copy_selection_no_clear(cs); 1049 window_copy_clear_selection(wme); 1050 return (WINDOW_COPY_CMD_CANCEL); 1051 } 1052 1053 static enum window_copy_cmd_action 1054 window_copy_cmd_cursor_down(struct window_copy_cmd_state *cs) 1055 { 1056 struct window_mode_entry *wme = cs->wme; 1057 u_int np = wme->prefix; 1058 1059 for (; np != 0; np--) 1060 window_copy_cursor_down(wme, 0); 1061 return (WINDOW_COPY_CMD_NOTHING); 1062 } 1063 1064 static enum window_copy_cmd_action 1065 window_copy_cmd_cursor_down_and_cancel(struct window_copy_cmd_state *cs) 1066 { 1067 struct window_mode_entry *wme = cs->wme; 1068 struct window_copy_mode_data *data = wme->data; 1069 u_int np = wme->prefix, cy; 1070 1071 cy = data->cy; 1072 for (; np != 0; np--) 1073 window_copy_cursor_down(wme, 0); 1074 if (cy == data->cy && data->oy == 0) 1075 return (WINDOW_COPY_CMD_CANCEL); 1076 return (WINDOW_COPY_CMD_NOTHING); 1077 } 1078 1079 static enum window_copy_cmd_action 1080 window_copy_cmd_cursor_left(struct window_copy_cmd_state *cs) 1081 { 1082 struct window_mode_entry *wme = cs->wme; 1083 u_int np = wme->prefix; 1084 1085 for (; np != 0; np--) 1086 window_copy_cursor_left(wme); 1087 return (WINDOW_COPY_CMD_NOTHING); 1088 } 1089 1090 static enum window_copy_cmd_action 1091 window_copy_cmd_cursor_right(struct window_copy_cmd_state *cs) 1092 { 1093 struct window_mode_entry *wme = cs->wme; 1094 u_int np = wme->prefix; 1095 1096 for (; np != 0; np--) 1097 window_copy_cursor_right(wme); 1098 return (WINDOW_COPY_CMD_NOTHING); 1099 } 1100 1101 static enum window_copy_cmd_action 1102 window_copy_cmd_cursor_up(struct window_copy_cmd_state *cs) 1103 { 1104 struct window_mode_entry *wme = cs->wme; 1105 u_int np = wme->prefix; 1106 1107 for (; np != 0; np--) 1108 window_copy_cursor_up(wme, 0); 1109 return (WINDOW_COPY_CMD_NOTHING); 1110 } 1111 1112 static enum window_copy_cmd_action 1113 window_copy_cmd_end_of_line(struct window_copy_cmd_state *cs) 1114 { 1115 struct window_mode_entry *wme = cs->wme; 1116 1117 window_copy_cursor_end_of_line(wme); 1118 return (WINDOW_COPY_CMD_NOTHING); 1119 } 1120 1121 static enum window_copy_cmd_action 1122 window_copy_cmd_halfpage_down(struct window_copy_cmd_state *cs) 1123 { 1124 struct window_mode_entry *wme = cs->wme; 1125 struct window_copy_mode_data *data = wme->data; 1126 u_int np = wme->prefix; 1127 1128 for (; np != 0; np--) { 1129 if (window_copy_pagedown(wme, 1, data->scroll_exit)) 1130 return (WINDOW_COPY_CMD_CANCEL); 1131 } 1132 return (WINDOW_COPY_CMD_NOTHING); 1133 } 1134 1135 static enum window_copy_cmd_action 1136 window_copy_cmd_halfpage_down_and_cancel(struct window_copy_cmd_state *cs) 1137 { 1138 1139 struct window_mode_entry *wme = cs->wme; 1140 u_int np = wme->prefix; 1141 1142 for (; np != 0; np--) { 1143 if (window_copy_pagedown(wme, 1, 1)) 1144 return (WINDOW_COPY_CMD_CANCEL); 1145 } 1146 return (WINDOW_COPY_CMD_NOTHING); 1147 } 1148 1149 static enum window_copy_cmd_action 1150 window_copy_cmd_halfpage_up(struct window_copy_cmd_state *cs) 1151 { 1152 struct window_mode_entry *wme = cs->wme; 1153 u_int np = wme->prefix; 1154 1155 for (; np != 0; np--) 1156 window_copy_pageup1(wme, 1); 1157 return (WINDOW_COPY_CMD_NOTHING); 1158 } 1159 1160 static enum window_copy_cmd_action 1161 window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs) 1162 { 1163 struct window_mode_entry *wme = cs->wme; 1164 struct window_copy_mode_data *data = wme->data; 1165 struct screen *s = data->backing; 1166 u_int oy; 1167 1168 oy = screen_hsize(s) + data->cy - data->oy; 1169 if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely) 1170 window_copy_other_end(wme); 1171 1172 data->cy = screen_size_y(&data->screen) - 1; 1173 data->cx = window_copy_find_length(wme, screen_hsize(s) + data->cy); 1174 data->oy = 0; 1175 1176 if (data->searchmark != NULL && !data->timeout) 1177 window_copy_search_marks(wme, NULL, data->searchregex, 1); 1178 window_copy_update_selection(wme, 1, 0); 1179 return (WINDOW_COPY_CMD_REDRAW); 1180 } 1181 1182 static enum window_copy_cmd_action 1183 window_copy_cmd_history_top(struct window_copy_cmd_state *cs) 1184 { 1185 struct window_mode_entry *wme = cs->wme; 1186 struct window_copy_mode_data *data = wme->data; 1187 u_int oy; 1188 1189 oy = screen_hsize(data->backing) + data->cy - data->oy; 1190 if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely) 1191 window_copy_other_end(wme); 1192 1193 data->cy = 0; 1194 data->cx = 0; 1195 data->oy = screen_hsize(data->backing); 1196 1197 if (data->searchmark != NULL && !data->timeout) 1198 window_copy_search_marks(wme, NULL, data->searchregex, 1); 1199 window_copy_update_selection(wme, 1, 0); 1200 return (WINDOW_COPY_CMD_REDRAW); 1201 } 1202 1203 static enum window_copy_cmd_action 1204 window_copy_cmd_jump_again(struct window_copy_cmd_state *cs) 1205 { 1206 struct window_mode_entry *wme = cs->wme; 1207 struct window_copy_mode_data *data = wme->data; 1208 u_int np = wme->prefix; 1209 1210 switch (data->jumptype) { 1211 case WINDOW_COPY_JUMPFORWARD: 1212 for (; np != 0; np--) 1213 window_copy_cursor_jump(wme); 1214 break; 1215 case WINDOW_COPY_JUMPBACKWARD: 1216 for (; np != 0; np--) 1217 window_copy_cursor_jump_back(wme); 1218 break; 1219 case WINDOW_COPY_JUMPTOFORWARD: 1220 for (; np != 0; np--) 1221 window_copy_cursor_jump_to(wme); 1222 break; 1223 case WINDOW_COPY_JUMPTOBACKWARD: 1224 for (; np != 0; np--) 1225 window_copy_cursor_jump_to_back(wme); 1226 break; 1227 } 1228 return (WINDOW_COPY_CMD_NOTHING); 1229 } 1230 1231 static enum window_copy_cmd_action 1232 window_copy_cmd_jump_reverse(struct window_copy_cmd_state *cs) 1233 { 1234 struct window_mode_entry *wme = cs->wme; 1235 struct window_copy_mode_data *data = wme->data; 1236 u_int np = wme->prefix; 1237 1238 switch (data->jumptype) { 1239 case WINDOW_COPY_JUMPFORWARD: 1240 for (; np != 0; np--) 1241 window_copy_cursor_jump_back(wme); 1242 break; 1243 case WINDOW_COPY_JUMPBACKWARD: 1244 for (; np != 0; np--) 1245 window_copy_cursor_jump(wme); 1246 break; 1247 case WINDOW_COPY_JUMPTOFORWARD: 1248 for (; np != 0; np--) 1249 window_copy_cursor_jump_to_back(wme); 1250 break; 1251 case WINDOW_COPY_JUMPTOBACKWARD: 1252 for (; np != 0; np--) 1253 window_copy_cursor_jump_to(wme); 1254 break; 1255 } 1256 return (WINDOW_COPY_CMD_NOTHING); 1257 } 1258 1259 static enum window_copy_cmd_action 1260 window_copy_cmd_middle_line(struct window_copy_cmd_state *cs) 1261 { 1262 struct window_mode_entry *wme = cs->wme; 1263 struct window_copy_mode_data *data = wme->data; 1264 1265 data->cx = 0; 1266 data->cy = (screen_size_y(&data->screen) - 1) / 2; 1267 1268 window_copy_update_selection(wme, 1, 0); 1269 return (WINDOW_COPY_CMD_REDRAW); 1270 } 1271 1272 static enum window_copy_cmd_action 1273 window_copy_cmd_previous_matching_bracket(struct window_copy_cmd_state *cs) 1274 { 1275 struct window_mode_entry *wme = cs->wme; 1276 u_int np = wme->prefix; 1277 struct window_copy_mode_data *data = wme->data; 1278 struct screen *s = data->backing; 1279 char open[] = "{[(", close[] = "}])"; 1280 char tried, found, start, *cp; 1281 u_int px, py, xx, n; 1282 struct grid_cell gc; 1283 int failed; 1284 1285 for (; np != 0; np--) { 1286 /* Get cursor position and line length. */ 1287 px = data->cx; 1288 py = screen_hsize(s) + data->cy - data->oy; 1289 xx = window_copy_find_length(wme, py); 1290 if (xx == 0) 1291 break; 1292 1293 /* 1294 * Get the current character. If not on a bracket, try the 1295 * previous. If still not, then behave like previous-word. 1296 */ 1297 tried = 0; 1298 retry: 1299 grid_get_cell(s->grid, px, py, &gc); 1300 if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING)) 1301 cp = NULL; 1302 else { 1303 found = *gc.data.data; 1304 cp = strchr(close, found); 1305 } 1306 if (cp == NULL) { 1307 if (data->modekeys == MODEKEY_EMACS) { 1308 if (!tried && px > 0) { 1309 px--; 1310 tried = 1; 1311 goto retry; 1312 } 1313 window_copy_cursor_previous_word(wme, "}]) ", 1); 1314 } 1315 continue; 1316 } 1317 start = open[cp - close]; 1318 1319 /* Walk backward until the matching bracket is reached. */ 1320 n = 1; 1321 failed = 0; 1322 do { 1323 if (px == 0) { 1324 if (py == 0) { 1325 failed = 1; 1326 break; 1327 } 1328 do { 1329 py--; 1330 xx = window_copy_find_length(wme, py); 1331 } while (xx == 0 && py > 0); 1332 if (xx == 0 && py == 0) { 1333 failed = 1; 1334 break; 1335 } 1336 px = xx - 1; 1337 } else 1338 px--; 1339 1340 grid_get_cell(s->grid, px, py, &gc); 1341 if (gc.data.size == 1 && 1342 (~gc.flags & GRID_FLAG_PADDING)) { 1343 if (*gc.data.data == found) 1344 n++; 1345 else if (*gc.data.data == start) 1346 n--; 1347 } 1348 } while (n != 0); 1349 1350 /* Move the cursor to the found location if any. */ 1351 if (!failed) 1352 window_copy_scroll_to(wme, px, py, 0); 1353 } 1354 1355 return (WINDOW_COPY_CMD_NOTHING); 1356 } 1357 1358 static enum window_copy_cmd_action 1359 window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state *cs) 1360 { 1361 struct window_mode_entry *wme = cs->wme; 1362 u_int np = wme->prefix; 1363 struct window_copy_mode_data *data = wme->data; 1364 struct screen *s = data->backing; 1365 char open[] = "{[(", close[] = "}])"; 1366 char tried, found, end, *cp; 1367 u_int px, py, xx, yy, sx, sy, n; 1368 struct grid_cell gc; 1369 int failed; 1370 struct grid_line *gl; 1371 1372 for (; np != 0; np--) { 1373 /* Get cursor position and line length. */ 1374 px = data->cx; 1375 py = screen_hsize(s) + data->cy - data->oy; 1376 xx = window_copy_find_length(wme, py); 1377 yy = screen_hsize(s) + screen_size_y(s) - 1; 1378 if (xx == 0) 1379 break; 1380 1381 /* 1382 * Get the current character. If not on a bracket, try the 1383 * next. If still not, then behave like next-word. 1384 */ 1385 tried = 0; 1386 retry: 1387 grid_get_cell(s->grid, px, py, &gc); 1388 if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING)) 1389 cp = NULL; 1390 else { 1391 found = *gc.data.data; 1392 1393 /* 1394 * In vi mode, attempt to move to previous bracket if a 1395 * closing bracket is found first. If this fails, 1396 * return to the original cursor position. 1397 */ 1398 cp = strchr(close, found); 1399 if (cp != NULL && data->modekeys == MODEKEY_VI) { 1400 sx = data->cx; 1401 sy = screen_hsize(s) + data->cy - data->oy; 1402 1403 window_copy_scroll_to(wme, px, py, 0); 1404 window_copy_cmd_previous_matching_bracket(cs); 1405 1406 px = data->cx; 1407 py = screen_hsize(s) + data->cy - data->oy; 1408 grid_get_cell(s->grid, px, py, &gc); 1409 if (gc.data.size == 1 && 1410 (~gc.flags & GRID_FLAG_PADDING) && 1411 strchr(close, *gc.data.data) != NULL) 1412 window_copy_scroll_to(wme, sx, sy, 0); 1413 break; 1414 } 1415 1416 cp = strchr(open, found); 1417 } 1418 if (cp == NULL) { 1419 if (data->modekeys == MODEKEY_EMACS) { 1420 if (!tried && px <= xx) { 1421 px++; 1422 tried = 1; 1423 goto retry; 1424 } 1425 window_copy_cursor_next_word_end(wme, "{[( ", 1426 0); 1427 continue; 1428 } 1429 /* For vi, continue searching for bracket until EOL. */ 1430 if (px > xx) { 1431 if (py == yy) 1432 continue; 1433 gl = grid_get_line(s->grid, py); 1434 if (~gl->flags & GRID_LINE_WRAPPED) 1435 continue; 1436 if (gl->cellsize > s->grid->sx) 1437 continue; 1438 px = 0; 1439 py++; 1440 xx = window_copy_find_length(wme, py); 1441 } else 1442 px++; 1443 goto retry; 1444 } 1445 end = close[cp - open]; 1446 1447 /* Walk forward until the matching bracket is reached. */ 1448 n = 1; 1449 failed = 0; 1450 do { 1451 if (px > xx) { 1452 if (py == yy) { 1453 failed = 1; 1454 break; 1455 } 1456 px = 0; 1457 py++; 1458 xx = window_copy_find_length(wme, py); 1459 } else 1460 px++; 1461 1462 grid_get_cell(s->grid, px, py, &gc); 1463 if (gc.data.size == 1 && 1464 (~gc.flags & GRID_FLAG_PADDING)) { 1465 if (*gc.data.data == found) 1466 n++; 1467 else if (*gc.data.data == end) 1468 n--; 1469 } 1470 } while (n != 0); 1471 1472 /* Move the cursor to the found location if any. */ 1473 if (!failed) 1474 window_copy_scroll_to(wme, px, py, 0); 1475 } 1476 1477 return (WINDOW_COPY_CMD_NOTHING); 1478 } 1479 1480 static enum window_copy_cmd_action 1481 window_copy_cmd_next_paragraph(struct window_copy_cmd_state *cs) 1482 { 1483 struct window_mode_entry *wme = cs->wme; 1484 u_int np = wme->prefix; 1485 1486 for (; np != 0; np--) 1487 window_copy_next_paragraph(wme); 1488 return (WINDOW_COPY_CMD_NOTHING); 1489 } 1490 1491 static enum window_copy_cmd_action 1492 window_copy_cmd_next_space(struct window_copy_cmd_state *cs) 1493 { 1494 struct window_mode_entry *wme = cs->wme; 1495 u_int np = wme->prefix; 1496 1497 for (; np != 0; np--) 1498 window_copy_cursor_next_word(wme, " "); 1499 return (WINDOW_COPY_CMD_NOTHING); 1500 } 1501 1502 static enum window_copy_cmd_action 1503 window_copy_cmd_next_space_end(struct window_copy_cmd_state *cs) 1504 { 1505 struct window_mode_entry *wme = cs->wme; 1506 u_int np = wme->prefix; 1507 1508 for (; np != 0; np--) 1509 window_copy_cursor_next_word_end(wme, " ", 0); 1510 return (WINDOW_COPY_CMD_NOTHING); 1511 } 1512 1513 static enum window_copy_cmd_action 1514 window_copy_cmd_next_word(struct window_copy_cmd_state *cs) 1515 { 1516 struct window_mode_entry *wme = cs->wme; 1517 struct session *s = cs->s; 1518 u_int np = wme->prefix; 1519 const char *ws; 1520 1521 ws = options_get_string(s->options, "word-separators"); 1522 for (; np != 0; np--) 1523 window_copy_cursor_next_word(wme, ws); 1524 return (WINDOW_COPY_CMD_NOTHING); 1525 } 1526 1527 static enum window_copy_cmd_action 1528 window_copy_cmd_next_word_end(struct window_copy_cmd_state *cs) 1529 { 1530 struct window_mode_entry *wme = cs->wme; 1531 struct session *s = cs->s; 1532 u_int np = wme->prefix; 1533 const char *ws; 1534 1535 ws = options_get_string(s->options, "word-separators"); 1536 for (; np != 0; np--) 1537 window_copy_cursor_next_word_end(wme, ws, 0); 1538 return (WINDOW_COPY_CMD_NOTHING); 1539 } 1540 1541 static enum window_copy_cmd_action 1542 window_copy_cmd_other_end(struct window_copy_cmd_state *cs) 1543 { 1544 struct window_mode_entry *wme = cs->wme; 1545 u_int np = wme->prefix; 1546 struct window_copy_mode_data *data = wme->data; 1547 1548 data->selflag = SEL_CHAR; 1549 if ((np % 2) != 0) 1550 window_copy_other_end(wme); 1551 return (WINDOW_COPY_CMD_NOTHING); 1552 } 1553 1554 static enum window_copy_cmd_action 1555 window_copy_cmd_page_down(struct window_copy_cmd_state *cs) 1556 { 1557 struct window_mode_entry *wme = cs->wme; 1558 struct window_copy_mode_data *data = wme->data; 1559 u_int np = wme->prefix; 1560 1561 for (; np != 0; np--) { 1562 if (window_copy_pagedown(wme, 0, data->scroll_exit)) 1563 return (WINDOW_COPY_CMD_CANCEL); 1564 } 1565 return (WINDOW_COPY_CMD_NOTHING); 1566 } 1567 1568 static enum window_copy_cmd_action 1569 window_copy_cmd_page_down_and_cancel(struct window_copy_cmd_state *cs) 1570 { 1571 struct window_mode_entry *wme = cs->wme; 1572 u_int np = wme->prefix; 1573 1574 for (; np != 0; np--) { 1575 if (window_copy_pagedown(wme, 0, 1)) 1576 return (WINDOW_COPY_CMD_CANCEL); 1577 } 1578 return (WINDOW_COPY_CMD_NOTHING); 1579 } 1580 1581 static enum window_copy_cmd_action 1582 window_copy_cmd_page_up(struct window_copy_cmd_state *cs) 1583 { 1584 struct window_mode_entry *wme = cs->wme; 1585 u_int np = wme->prefix; 1586 1587 for (; np != 0; np--) 1588 window_copy_pageup1(wme, 0); 1589 return (WINDOW_COPY_CMD_NOTHING); 1590 } 1591 1592 static enum window_copy_cmd_action 1593 window_copy_cmd_previous_paragraph(struct window_copy_cmd_state *cs) 1594 { 1595 struct window_mode_entry *wme = cs->wme; 1596 u_int np = wme->prefix; 1597 1598 for (; np != 0; np--) 1599 window_copy_previous_paragraph(wme); 1600 return (WINDOW_COPY_CMD_NOTHING); 1601 } 1602 1603 static enum window_copy_cmd_action 1604 window_copy_cmd_previous_space(struct window_copy_cmd_state *cs) 1605 { 1606 struct window_mode_entry *wme = cs->wme; 1607 u_int np = wme->prefix; 1608 1609 for (; np != 0; np--) 1610 window_copy_cursor_previous_word(wme, " ", 1); 1611 return (WINDOW_COPY_CMD_NOTHING); 1612 } 1613 1614 static enum window_copy_cmd_action 1615 window_copy_cmd_previous_word(struct window_copy_cmd_state *cs) 1616 { 1617 struct window_mode_entry *wme = cs->wme; 1618 struct session *s = cs->s; 1619 u_int np = wme->prefix; 1620 const char *ws; 1621 1622 ws = options_get_string(s->options, "word-separators"); 1623 for (; np != 0; np--) 1624 window_copy_cursor_previous_word(wme, ws, 1); 1625 return (WINDOW_COPY_CMD_NOTHING); 1626 } 1627 1628 static enum window_copy_cmd_action 1629 window_copy_cmd_rectangle_toggle(struct window_copy_cmd_state *cs) 1630 { 1631 struct window_mode_entry *wme = cs->wme; 1632 struct window_copy_mode_data *data = wme->data; 1633 1634 data->lineflag = LINE_SEL_NONE; 1635 window_copy_rectangle_toggle(wme); 1636 1637 return (WINDOW_COPY_CMD_NOTHING); 1638 } 1639 1640 static enum window_copy_cmd_action 1641 window_copy_cmd_scroll_down(struct window_copy_cmd_state *cs) 1642 { 1643 struct window_mode_entry *wme = cs->wme; 1644 struct window_copy_mode_data *data = wme->data; 1645 u_int np = wme->prefix; 1646 1647 for (; np != 0; np--) 1648 window_copy_cursor_down(wme, 1); 1649 if (data->scroll_exit && data->oy == 0) 1650 return (WINDOW_COPY_CMD_CANCEL); 1651 return (WINDOW_COPY_CMD_NOTHING); 1652 } 1653 1654 static enum window_copy_cmd_action 1655 window_copy_cmd_scroll_down_and_cancel(struct window_copy_cmd_state *cs) 1656 { 1657 struct window_mode_entry *wme = cs->wme; 1658 struct window_copy_mode_data *data = wme->data; 1659 u_int np = wme->prefix; 1660 1661 for (; np != 0; np--) 1662 window_copy_cursor_down(wme, 1); 1663 if (data->oy == 0) 1664 return (WINDOW_COPY_CMD_CANCEL); 1665 return (WINDOW_COPY_CMD_NOTHING); 1666 } 1667 1668 static enum window_copy_cmd_action 1669 window_copy_cmd_scroll_up(struct window_copy_cmd_state *cs) 1670 { 1671 struct window_mode_entry *wme = cs->wme; 1672 u_int np = wme->prefix; 1673 1674 for (; np != 0; np--) 1675 window_copy_cursor_up(wme, 1); 1676 return (WINDOW_COPY_CMD_NOTHING); 1677 } 1678 1679 static enum window_copy_cmd_action 1680 window_copy_cmd_search_again(struct window_copy_cmd_state *cs) 1681 { 1682 struct window_mode_entry *wme = cs->wme; 1683 struct window_copy_mode_data *data = wme->data; 1684 u_int np = wme->prefix; 1685 1686 if (data->searchtype == WINDOW_COPY_SEARCHUP) { 1687 for (; np != 0; np--) 1688 window_copy_search_up(wme, data->searchregex); 1689 } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { 1690 for (; np != 0; np--) 1691 window_copy_search_down(wme, data->searchregex); 1692 } 1693 return (WINDOW_COPY_CMD_NOTHING); 1694 } 1695 1696 static enum window_copy_cmd_action 1697 window_copy_cmd_search_reverse(struct window_copy_cmd_state *cs) 1698 { 1699 struct window_mode_entry *wme = cs->wme; 1700 struct window_copy_mode_data *data = wme->data; 1701 u_int np = wme->prefix; 1702 1703 if (data->searchtype == WINDOW_COPY_SEARCHUP) { 1704 for (; np != 0; np--) 1705 window_copy_search_down(wme, data->searchregex); 1706 } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { 1707 for (; np != 0; np--) 1708 window_copy_search_up(wme, data->searchregex); 1709 } 1710 return (WINDOW_COPY_CMD_NOTHING); 1711 } 1712 1713 static enum window_copy_cmd_action 1714 window_copy_cmd_select_line(struct window_copy_cmd_state *cs) 1715 { 1716 struct window_mode_entry *wme = cs->wme; 1717 struct window_copy_mode_data *data = wme->data; 1718 u_int np = wme->prefix; 1719 1720 data->lineflag = LINE_SEL_LEFT_RIGHT; 1721 data->rectflag = 0; 1722 data->selflag = SEL_LINE; 1723 data->dx = data->cx; 1724 data->dy = screen_hsize(data->backing) + data->cy - data->oy; 1725 1726 window_copy_cursor_start_of_line(wme); 1727 data->selrx = data->cx; 1728 data->selry = screen_hsize(data->backing) + data->cy - data->oy; 1729 data->endselrx = window_copy_find_length(wme, data->selry); 1730 data->endselry = data->selry; 1731 window_copy_start_selection(wme); 1732 for (; np > 1; np--) 1733 window_copy_cursor_down(wme, 0); 1734 window_copy_cursor_end_of_line(wme); 1735 1736 return (WINDOW_COPY_CMD_REDRAW); 1737 } 1738 1739 static enum window_copy_cmd_action 1740 window_copy_cmd_select_word(struct window_copy_cmd_state *cs) 1741 { 1742 struct window_mode_entry *wme = cs->wme; 1743 struct session *s = cs->s; 1744 struct window_copy_mode_data *data = wme->data; 1745 u_int px, py; 1746 1747 data->lineflag = LINE_SEL_LEFT_RIGHT; 1748 data->rectflag = 0; 1749 data->selflag = SEL_WORD; 1750 data->dx = data->cx; 1751 data->dy = screen_hsize(data->backing) + data->cy - data->oy; 1752 1753 data->ws = options_get_string(s->options, "word-separators"); 1754 window_copy_cursor_previous_word(wme, data->ws, 0); 1755 px = data->cx; 1756 py = screen_hsize(data->backing) + data->cy - data->oy; 1757 data->selrx = px; 1758 data->selry = py; 1759 window_copy_start_selection(wme); 1760 1761 if (px >= window_copy_find_length(wme, py) || 1762 !window_copy_in_set(wme, px + 1, py, data->ws)) 1763 window_copy_cursor_next_word_end(wme, data->ws, 1); 1764 else { 1765 window_copy_update_cursor(wme, px, data->cy); 1766 if (window_copy_update_selection(wme, 1, 1)) 1767 window_copy_redraw_lines(wme, data->cy, 1); 1768 } 1769 data->endselrx = data->cx; 1770 data->endselry = screen_hsize(data->backing) + data->cy - data->oy; 1771 if (data->dx > data->endselrx) 1772 data->dx = data->endselrx; 1773 1774 return (WINDOW_COPY_CMD_REDRAW); 1775 } 1776 1777 static enum window_copy_cmd_action 1778 window_copy_cmd_set_mark(struct window_copy_cmd_state *cs) 1779 { 1780 struct window_copy_mode_data *data = cs->wme->data; 1781 1782 data->mx = data->cx; 1783 data->my = screen_hsize(data->backing) + data->cy - data->oy; 1784 data->showmark = 1; 1785 return (WINDOW_COPY_CMD_REDRAW); 1786 } 1787 1788 static enum window_copy_cmd_action 1789 window_copy_cmd_start_of_line(struct window_copy_cmd_state *cs) 1790 { 1791 struct window_mode_entry *wme = cs->wme; 1792 1793 window_copy_cursor_start_of_line(wme); 1794 return (WINDOW_COPY_CMD_NOTHING); 1795 } 1796 1797 static enum window_copy_cmd_action 1798 window_copy_cmd_top_line(struct window_copy_cmd_state *cs) 1799 { 1800 struct window_mode_entry *wme = cs->wme; 1801 struct window_copy_mode_data *data = wme->data; 1802 1803 data->cx = 0; 1804 data->cy = 0; 1805 1806 window_copy_update_selection(wme, 1, 0); 1807 return (WINDOW_COPY_CMD_REDRAW); 1808 } 1809 1810 static enum window_copy_cmd_action 1811 window_copy_cmd_copy_pipe_no_clear(struct window_copy_cmd_state *cs) 1812 { 1813 struct window_mode_entry *wme = cs->wme; 1814 struct client *c = cs->c; 1815 struct session *s = cs->s; 1816 struct winlink *wl = cs->wl; 1817 struct window_pane *wp = wme->wp; 1818 char *command = NULL; 1819 char *prefix = NULL; 1820 1821 if (cs->args->argc == 3) 1822 prefix = format_single(NULL, cs->args->argv[2], c, s, wl, wp); 1823 1824 if (s != NULL && cs->args->argc > 1 && *cs->args->argv[1] != '\0') 1825 command = format_single(NULL, cs->args->argv[1], c, s, wl, wp); 1826 window_copy_copy_pipe(wme, s, prefix, command); 1827 free(command); 1828 1829 free(prefix); 1830 return (WINDOW_COPY_CMD_NOTHING); 1831 } 1832 1833 static enum window_copy_cmd_action 1834 window_copy_cmd_copy_pipe(struct window_copy_cmd_state *cs) 1835 { 1836 struct window_mode_entry *wme = cs->wme; 1837 1838 window_copy_cmd_copy_pipe_no_clear(cs); 1839 window_copy_clear_selection(wme); 1840 return (WINDOW_COPY_CMD_REDRAW); 1841 } 1842 1843 static enum window_copy_cmd_action 1844 window_copy_cmd_copy_pipe_and_cancel(struct window_copy_cmd_state *cs) 1845 { 1846 struct window_mode_entry *wme = cs->wme; 1847 1848 window_copy_cmd_copy_pipe_no_clear(cs); 1849 window_copy_clear_selection(wme); 1850 return (WINDOW_COPY_CMD_CANCEL); 1851 } 1852 1853 static enum window_copy_cmd_action 1854 window_copy_cmd_goto_line(struct window_copy_cmd_state *cs) 1855 { 1856 struct window_mode_entry *wme = cs->wme; 1857 const char *argument = cs->args->argv[1]; 1858 1859 if (*argument != '\0') 1860 window_copy_goto_line(wme, argument); 1861 return (WINDOW_COPY_CMD_NOTHING); 1862 } 1863 1864 static enum window_copy_cmd_action 1865 window_copy_cmd_jump_backward(struct window_copy_cmd_state *cs) 1866 { 1867 struct window_mode_entry *wme = cs->wme; 1868 struct window_copy_mode_data *data = wme->data; 1869 u_int np = wme->prefix; 1870 const char *argument = cs->args->argv[1]; 1871 1872 if (*argument != '\0') { 1873 data->jumptype = WINDOW_COPY_JUMPBACKWARD; 1874 data->jumpchar = *argument; 1875 for (; np != 0; np--) 1876 window_copy_cursor_jump_back(wme); 1877 } 1878 return (WINDOW_COPY_CMD_NOTHING); 1879 } 1880 1881 static enum window_copy_cmd_action 1882 window_copy_cmd_jump_forward(struct window_copy_cmd_state *cs) 1883 { 1884 struct window_mode_entry *wme = cs->wme; 1885 struct window_copy_mode_data *data = wme->data; 1886 u_int np = wme->prefix; 1887 const char *argument = cs->args->argv[1]; 1888 1889 if (*argument != '\0') { 1890 data->jumptype = WINDOW_COPY_JUMPFORWARD; 1891 data->jumpchar = *argument; 1892 for (; np != 0; np--) 1893 window_copy_cursor_jump(wme); 1894 } 1895 return (WINDOW_COPY_CMD_NOTHING); 1896 } 1897 1898 static enum window_copy_cmd_action 1899 window_copy_cmd_jump_to_backward(struct window_copy_cmd_state *cs) 1900 { 1901 struct window_mode_entry *wme = cs->wme; 1902 struct window_copy_mode_data *data = wme->data; 1903 u_int np = wme->prefix; 1904 const char *argument = cs->args->argv[1]; 1905 1906 if (*argument != '\0') { 1907 data->jumptype = WINDOW_COPY_JUMPTOBACKWARD; 1908 data->jumpchar = *argument; 1909 for (; np != 0; np--) 1910 window_copy_cursor_jump_to_back(wme); 1911 } 1912 return (WINDOW_COPY_CMD_NOTHING); 1913 } 1914 1915 static enum window_copy_cmd_action 1916 window_copy_cmd_jump_to_forward(struct window_copy_cmd_state *cs) 1917 { 1918 struct window_mode_entry *wme = cs->wme; 1919 struct window_copy_mode_data *data = wme->data; 1920 u_int np = wme->prefix; 1921 const char *argument = cs->args->argv[1]; 1922 1923 if (*argument != '\0') { 1924 data->jumptype = WINDOW_COPY_JUMPTOFORWARD; 1925 data->jumpchar = *argument; 1926 for (; np != 0; np--) 1927 window_copy_cursor_jump_to(wme); 1928 } 1929 return (WINDOW_COPY_CMD_NOTHING); 1930 } 1931 1932 static enum window_copy_cmd_action 1933 window_copy_cmd_jump_to_mark(struct window_copy_cmd_state *cs) 1934 { 1935 struct window_mode_entry *wme = cs->wme; 1936 1937 window_copy_jump_to_mark(wme); 1938 return (WINDOW_COPY_CMD_NOTHING); 1939 } 1940 1941 static enum window_copy_cmd_action 1942 window_copy_cmd_search_backward(struct window_copy_cmd_state *cs) 1943 { 1944 struct window_mode_entry *wme = cs->wme; 1945 struct window_copy_mode_data *data = wme->data; 1946 u_int np = wme->prefix; 1947 1948 if (!window_copy_expand_search_string(cs)) 1949 return (WINDOW_COPY_CMD_NOTHING); 1950 1951 if (data->searchstr != NULL) { 1952 data->searchtype = WINDOW_COPY_SEARCHUP; 1953 data->searchregex = 1; 1954 data->timeout = 0; 1955 for (; np != 0; np--) 1956 window_copy_search_up(wme, 1); 1957 } 1958 return (WINDOW_COPY_CMD_NOTHING); 1959 } 1960 1961 static enum window_copy_cmd_action 1962 window_copy_cmd_search_backward_text(struct window_copy_cmd_state *cs) 1963 { 1964 struct window_mode_entry *wme = cs->wme; 1965 struct window_copy_mode_data *data = wme->data; 1966 u_int np = wme->prefix; 1967 1968 if (!window_copy_expand_search_string(cs)) 1969 return (WINDOW_COPY_CMD_NOTHING); 1970 1971 if (data->searchstr != NULL) { 1972 data->searchtype = WINDOW_COPY_SEARCHUP; 1973 data->searchregex = 0; 1974 data->timeout = 0; 1975 for (; np != 0; np--) 1976 window_copy_search_up(wme, 0); 1977 } 1978 return (WINDOW_COPY_CMD_NOTHING); 1979 } 1980 1981 static enum window_copy_cmd_action 1982 window_copy_cmd_search_forward(struct window_copy_cmd_state *cs) 1983 { 1984 struct window_mode_entry *wme = cs->wme; 1985 struct window_copy_mode_data *data = wme->data; 1986 u_int np = wme->prefix; 1987 1988 if (!window_copy_expand_search_string(cs)) 1989 return (WINDOW_COPY_CMD_NOTHING); 1990 1991 if (data->searchstr != NULL) { 1992 data->searchtype = WINDOW_COPY_SEARCHDOWN; 1993 data->searchregex = 1; 1994 data->timeout = 0; 1995 for (; np != 0; np--) 1996 window_copy_search_down(wme, 1); 1997 } 1998 return (WINDOW_COPY_CMD_NOTHING); 1999 } 2000 2001 static enum window_copy_cmd_action 2002 window_copy_cmd_search_forward_text(struct window_copy_cmd_state *cs) 2003 { 2004 struct window_mode_entry *wme = cs->wme; 2005 struct window_copy_mode_data *data = wme->data; 2006 u_int np = wme->prefix; 2007 2008 if (!window_copy_expand_search_string(cs)) 2009 return (WINDOW_COPY_CMD_NOTHING); 2010 2011 if (data->searchstr != NULL) { 2012 data->searchtype = WINDOW_COPY_SEARCHDOWN; 2013 data->searchregex = 0; 2014 data->timeout = 0; 2015 for (; np != 0; np--) 2016 window_copy_search_down(wme, 0); 2017 } 2018 return (WINDOW_COPY_CMD_NOTHING); 2019 } 2020 2021 static enum window_copy_cmd_action 2022 window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) 2023 { 2024 struct window_mode_entry *wme = cs->wme; 2025 struct window_copy_mode_data *data = wme->data; 2026 const char *argument = cs->args->argv[1]; 2027 const char *ss = data->searchstr; 2028 char prefix; 2029 enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING; 2030 2031 data->timeout = 0; 2032 2033 prefix = *argument++; 2034 if (data->searchx == -1 || data->searchy == -1) { 2035 data->searchx = data->cx; 2036 data->searchy = data->cy; 2037 data->searcho = data->oy; 2038 } else if (ss != NULL && strcmp(argument, ss) != 0) { 2039 data->cx = data->searchx; 2040 data->cy = data->searchy; 2041 data->oy = data->searcho; 2042 action = WINDOW_COPY_CMD_REDRAW; 2043 } 2044 if (*argument == '\0') { 2045 window_copy_clear_marks(wme); 2046 return (WINDOW_COPY_CMD_REDRAW); 2047 } 2048 switch (prefix) { 2049 case '=': 2050 case '-': 2051 data->searchtype = WINDOW_COPY_SEARCHUP; 2052 data->searchregex = 0; 2053 free(data->searchstr); 2054 data->searchstr = xstrdup(argument); 2055 if (!window_copy_search_up(wme, 0)) { 2056 window_copy_clear_marks(wme); 2057 return (WINDOW_COPY_CMD_REDRAW); 2058 } 2059 break; 2060 case '+': 2061 data->searchtype = WINDOW_COPY_SEARCHDOWN; 2062 data->searchregex = 0; 2063 free(data->searchstr); 2064 data->searchstr = xstrdup(argument); 2065 if (!window_copy_search_down(wme, 0)) { 2066 window_copy_clear_marks(wme); 2067 return (WINDOW_COPY_CMD_REDRAW); 2068 } 2069 break; 2070 } 2071 return (action); 2072 } 2073 2074 static enum window_copy_cmd_action 2075 window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) 2076 { 2077 struct window_mode_entry *wme = cs->wme; 2078 struct window_copy_mode_data *data = wme->data; 2079 const char *argument = cs->args->argv[1]; 2080 const char *ss = data->searchstr; 2081 char prefix; 2082 enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING; 2083 2084 data->timeout = 0; 2085 2086 prefix = *argument++; 2087 if (data->searchx == -1 || data->searchy == -1) { 2088 data->searchx = data->cx; 2089 data->searchy = data->cy; 2090 data->searcho = data->oy; 2091 } else if (ss != NULL && strcmp(argument, ss) != 0) { 2092 data->cx = data->searchx; 2093 data->cy = data->searchy; 2094 data->oy = data->searcho; 2095 action = WINDOW_COPY_CMD_REDRAW; 2096 } 2097 if (*argument == '\0') { 2098 window_copy_clear_marks(wme); 2099 return (WINDOW_COPY_CMD_REDRAW); 2100 } 2101 switch (prefix) { 2102 case '=': 2103 case '+': 2104 data->searchtype = WINDOW_COPY_SEARCHDOWN; 2105 data->searchregex = 0; 2106 free(data->searchstr); 2107 data->searchstr = xstrdup(argument); 2108 if (!window_copy_search_down(wme, 0)) { 2109 window_copy_clear_marks(wme); 2110 return (WINDOW_COPY_CMD_REDRAW); 2111 } 2112 break; 2113 case '-': 2114 data->searchtype = WINDOW_COPY_SEARCHUP; 2115 data->searchregex = 0; 2116 free(data->searchstr); 2117 data->searchstr = xstrdup(argument); 2118 if (!window_copy_search_up(wme, 0)) { 2119 window_copy_clear_marks(wme); 2120 return (WINDOW_COPY_CMD_REDRAW); 2121 } 2122 } 2123 return (action); 2124 } 2125 2126 static enum window_copy_cmd_action 2127 window_copy_cmd_refresh_from_pane(struct window_copy_cmd_state *cs) 2128 { 2129 struct window_mode_entry *wme = cs->wme; 2130 struct window_pane *wp = wme->swp; 2131 struct window_copy_mode_data *data = wme->data; 2132 2133 if (data->viewmode) 2134 return (WINDOW_COPY_CMD_NOTHING); 2135 2136 screen_free(data->backing); 2137 free(data->backing); 2138 data->backing = window_copy_clone_screen(&wp->base, &data->screen, NULL, 2139 NULL, wme->swp != wme->wp); 2140 2141 window_copy_size_changed(wme); 2142 return (WINDOW_COPY_CMD_REDRAW); 2143 } 2144 2145 static const struct { 2146 const char *command; 2147 int minargs; 2148 int maxargs; 2149 enum window_copy_cmd_clear clear; 2150 enum window_copy_cmd_action (*f)(struct window_copy_cmd_state *); 2151 } window_copy_cmd_table[] = { 2152 { "append-selection", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2153 window_copy_cmd_append_selection }, 2154 { "append-selection-and-cancel", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2155 window_copy_cmd_append_selection_and_cancel }, 2156 { "back-to-indentation", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2157 window_copy_cmd_back_to_indentation }, 2158 { "begin-selection", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2159 window_copy_cmd_begin_selection }, 2160 { "bottom-line", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2161 window_copy_cmd_bottom_line }, 2162 { "cancel", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2163 window_copy_cmd_cancel }, 2164 { "clear-selection", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2165 window_copy_cmd_clear_selection }, 2166 { "copy-end-of-line", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2167 window_copy_cmd_copy_end_of_line }, 2168 { "copy-line", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2169 window_copy_cmd_copy_line }, 2170 { "copy-pipe-no-clear", 0, 2, WINDOW_COPY_CMD_CLEAR_NEVER, 2171 window_copy_cmd_copy_pipe_no_clear }, 2172 { "copy-pipe", 0, 2, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2173 window_copy_cmd_copy_pipe }, 2174 { "copy-pipe-and-cancel", 0, 2, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2175 window_copy_cmd_copy_pipe_and_cancel }, 2176 { "copy-selection-no-clear", 0, 1, WINDOW_COPY_CMD_CLEAR_NEVER, 2177 window_copy_cmd_copy_selection_no_clear }, 2178 { "copy-selection", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2179 window_copy_cmd_copy_selection }, 2180 { "copy-selection-and-cancel", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2181 window_copy_cmd_copy_selection_and_cancel }, 2182 { "cursor-down", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2183 window_copy_cmd_cursor_down }, 2184 { "cursor-down-and-cancel", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2185 window_copy_cmd_cursor_down_and_cancel }, 2186 { "cursor-left", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2187 window_copy_cmd_cursor_left }, 2188 { "cursor-right", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2189 window_copy_cmd_cursor_right }, 2190 { "cursor-up", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2191 window_copy_cmd_cursor_up }, 2192 { "end-of-line", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2193 window_copy_cmd_end_of_line }, 2194 { "goto-line", 1, 1, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2195 window_copy_cmd_goto_line }, 2196 { "halfpage-down", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2197 window_copy_cmd_halfpage_down }, 2198 { "halfpage-down-and-cancel", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2199 window_copy_cmd_halfpage_down_and_cancel }, 2200 { "halfpage-up", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2201 window_copy_cmd_halfpage_up }, 2202 { "history-bottom", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2203 window_copy_cmd_history_bottom }, 2204 { "history-top", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2205 window_copy_cmd_history_top }, 2206 { "jump-again", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2207 window_copy_cmd_jump_again }, 2208 { "jump-backward", 1, 1, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2209 window_copy_cmd_jump_backward }, 2210 { "jump-forward", 1, 1, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2211 window_copy_cmd_jump_forward }, 2212 { "jump-reverse", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2213 window_copy_cmd_jump_reverse }, 2214 { "jump-to-backward", 1, 1, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2215 window_copy_cmd_jump_to_backward }, 2216 { "jump-to-forward", 1, 1, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2217 window_copy_cmd_jump_to_forward }, 2218 { "jump-to-mark", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2219 window_copy_cmd_jump_to_mark }, 2220 { "middle-line", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2221 window_copy_cmd_middle_line }, 2222 { "next-matching-bracket", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2223 window_copy_cmd_next_matching_bracket }, 2224 { "next-paragraph", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2225 window_copy_cmd_next_paragraph }, 2226 { "next-space", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2227 window_copy_cmd_next_space }, 2228 { "next-space-end", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2229 window_copy_cmd_next_space_end }, 2230 { "next-word", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2231 window_copy_cmd_next_word }, 2232 { "next-word-end", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2233 window_copy_cmd_next_word_end }, 2234 { "other-end", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2235 window_copy_cmd_other_end }, 2236 { "page-down", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2237 window_copy_cmd_page_down }, 2238 { "page-down-and-cancel", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2239 window_copy_cmd_page_down_and_cancel }, 2240 { "page-up", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2241 window_copy_cmd_page_up }, 2242 { "previous-matching-bracket", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2243 window_copy_cmd_previous_matching_bracket }, 2244 { "previous-paragraph", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2245 window_copy_cmd_previous_paragraph }, 2246 { "previous-space", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2247 window_copy_cmd_previous_space }, 2248 { "previous-word", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2249 window_copy_cmd_previous_word }, 2250 { "rectangle-toggle", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2251 window_copy_cmd_rectangle_toggle }, 2252 { "refresh-from-pane", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2253 window_copy_cmd_refresh_from_pane }, 2254 { "scroll-down", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2255 window_copy_cmd_scroll_down }, 2256 { "scroll-down-and-cancel", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2257 window_copy_cmd_scroll_down_and_cancel }, 2258 { "scroll-up", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2259 window_copy_cmd_scroll_up }, 2260 { "search-again", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2261 window_copy_cmd_search_again }, 2262 { "search-backward", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2263 window_copy_cmd_search_backward }, 2264 { "search-backward-text", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2265 window_copy_cmd_search_backward_text }, 2266 { "search-backward-incremental", 1, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2267 window_copy_cmd_search_backward_incremental }, 2268 { "search-forward", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2269 window_copy_cmd_search_forward }, 2270 { "search-forward-text", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2271 window_copy_cmd_search_forward_text }, 2272 { "search-forward-incremental", 1, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2273 window_copy_cmd_search_forward_incremental }, 2274 { "search-reverse", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2275 window_copy_cmd_search_reverse }, 2276 { "select-line", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2277 window_copy_cmd_select_line }, 2278 { "select-word", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2279 window_copy_cmd_select_word }, 2280 { "set-mark", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2281 window_copy_cmd_set_mark }, 2282 { "start-of-line", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2283 window_copy_cmd_start_of_line }, 2284 { "stop-selection", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS, 2285 window_copy_cmd_stop_selection }, 2286 { "top-line", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2287 window_copy_cmd_top_line }, 2288 }; 2289 2290 static void 2291 window_copy_command(struct window_mode_entry *wme, struct client *c, 2292 struct session *s, struct winlink *wl, struct args *args, 2293 struct mouse_event *m) 2294 { 2295 struct window_copy_mode_data *data = wme->data; 2296 struct window_copy_cmd_state cs; 2297 enum window_copy_cmd_action action; 2298 enum window_copy_cmd_clear clear = WINDOW_COPY_CMD_CLEAR_NEVER; 2299 const char *command; 2300 u_int i; 2301 int keys; 2302 2303 if (args->argc == 0) 2304 return; 2305 command = args->argv[0]; 2306 2307 if (m != NULL && m->valid && !MOUSE_WHEEL(m->b)) 2308 window_copy_move_mouse(m); 2309 2310 cs.wme = wme; 2311 cs.args = args; 2312 cs.m = m; 2313 2314 cs.c = c; 2315 cs.s = s; 2316 cs.wl = wl; 2317 2318 action = WINDOW_COPY_CMD_NOTHING; 2319 for (i = 0; i < nitems(window_copy_cmd_table); i++) { 2320 if (strcmp(window_copy_cmd_table[i].command, command) == 0) { 2321 if (args->argc - 1 < window_copy_cmd_table[i].minargs || 2322 args->argc - 1 > window_copy_cmd_table[i].maxargs) 2323 break; 2324 clear = window_copy_cmd_table[i].clear; 2325 action = window_copy_cmd_table[i].f (&cs); 2326 break; 2327 } 2328 } 2329 2330 if (strncmp(command, "search-", 7) != 0 && data->searchmark != NULL) { 2331 keys = options_get_number(wme->wp->window->options, "mode-keys"); 2332 if (clear == WINDOW_COPY_CMD_CLEAR_EMACS_ONLY && 2333 keys == MODEKEY_VI) 2334 clear = WINDOW_COPY_CMD_CLEAR_NEVER; 2335 if (clear != WINDOW_COPY_CMD_CLEAR_NEVER) { 2336 window_copy_clear_marks(wme); 2337 data->searchx = data->searchy = -1; 2338 } else if (data->searchthis != -1) { 2339 data->searchthis = -1; 2340 action = WINDOW_COPY_CMD_REDRAW; 2341 } 2342 if (action == WINDOW_COPY_CMD_NOTHING) 2343 action = WINDOW_COPY_CMD_REDRAW; 2344 } 2345 wme->prefix = 1; 2346 2347 if (action == WINDOW_COPY_CMD_CANCEL) 2348 window_pane_reset_mode(wme->wp); 2349 else if (action == WINDOW_COPY_CMD_REDRAW) 2350 window_copy_redraw_screen(wme); 2351 } 2352 2353 static void 2354 window_copy_scroll_to(struct window_mode_entry *wme, u_int px, u_int py, 2355 int no_redraw) 2356 { 2357 struct window_copy_mode_data *data = wme->data; 2358 struct grid *gd = data->backing->grid; 2359 u_int offset, gap; 2360 2361 data->cx = px; 2362 2363 if (py >= gd->hsize - data->oy && py < gd->hsize - data->oy + gd->sy) 2364 data->cy = py - (gd->hsize - data->oy); 2365 else { 2366 gap = gd->sy / 4; 2367 if (py < gd->sy) { 2368 offset = 0; 2369 data->cy = py; 2370 } else if (py > gd->hsize + gd->sy - gap) { 2371 offset = gd->hsize; 2372 data->cy = py - gd->hsize; 2373 } else { 2374 offset = py + gap - gd->sy; 2375 data->cy = py - offset; 2376 } 2377 data->oy = gd->hsize - offset; 2378 } 2379 2380 if (!no_redraw && data->searchmark != NULL && !data->timeout) 2381 window_copy_search_marks(wme, NULL, data->searchregex, 1); 2382 window_copy_update_selection(wme, 1, 0); 2383 if (!no_redraw) 2384 window_copy_redraw_screen(wme); 2385 } 2386 2387 static int 2388 window_copy_search_compare(struct grid *gd, u_int px, u_int py, 2389 struct grid *sgd, u_int spx, int cis) 2390 { 2391 struct grid_cell gc, sgc; 2392 const struct utf8_data *ud, *sud; 2393 2394 grid_get_cell(gd, px, py, &gc); 2395 ud = &gc.data; 2396 grid_get_cell(sgd, spx, 0, &sgc); 2397 sud = &sgc.data; 2398 2399 if (ud->size != sud->size || ud->width != sud->width) 2400 return (0); 2401 2402 if (cis && ud->size == 1) 2403 return (tolower(ud->data[0]) == sud->data[0]); 2404 2405 return (memcmp(ud->data, sud->data, ud->size) == 0); 2406 } 2407 2408 static int 2409 window_copy_search_lr(struct grid *gd, 2410 struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis) 2411 { 2412 u_int ax, bx, px, pywrap, endline; 2413 int matched; 2414 struct grid_line *gl; 2415 2416 endline = gd->hsize + gd->sy - 1; 2417 for (ax = first; ax < last; ax++) { 2418 for (bx = 0; bx < sgd->sx; bx++) { 2419 px = ax + bx; 2420 pywrap = py; 2421 /* Wrap line. */ 2422 while (px >= gd->sx && pywrap < endline) { 2423 gl = grid_get_line(gd, pywrap); 2424 if (~gl->flags & GRID_LINE_WRAPPED) 2425 break; 2426 px -= gd->sx; 2427 pywrap++; 2428 } 2429 /* We have run off the end of the grid. */ 2430 if (px >= gd->sx) 2431 break; 2432 matched = window_copy_search_compare(gd, px, pywrap, 2433 sgd, bx, cis); 2434 if (!matched) 2435 break; 2436 } 2437 if (bx == sgd->sx) { 2438 *ppx = ax; 2439 return (1); 2440 } 2441 } 2442 return (0); 2443 } 2444 2445 static int 2446 window_copy_search_rl(struct grid *gd, 2447 struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis) 2448 { 2449 u_int ax, bx, px, pywrap, endline; 2450 int matched; 2451 struct grid_line *gl; 2452 2453 endline = gd->hsize + gd->sy - 1; 2454 for (ax = last; ax > first; ax--) { 2455 for (bx = 0; bx < sgd->sx; bx++) { 2456 px = ax - 1 + bx; 2457 pywrap = py; 2458 /* Wrap line. */ 2459 while (px >= gd->sx && pywrap < endline) { 2460 gl = grid_get_line(gd, pywrap); 2461 if (~gl->flags & GRID_LINE_WRAPPED) 2462 break; 2463 px -= gd->sx; 2464 pywrap++; 2465 } 2466 /* We have run off the end of the grid. */ 2467 if (px >= gd->sx) 2468 break; 2469 matched = window_copy_search_compare(gd, px, pywrap, 2470 sgd, bx, cis); 2471 if (!matched) 2472 break; 2473 } 2474 if (bx == sgd->sx) { 2475 *ppx = ax - 1; 2476 return (1); 2477 } 2478 } 2479 return (0); 2480 } 2481 2482 static int 2483 window_copy_search_lr_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py, 2484 u_int first, u_int last, regex_t *reg) 2485 { 2486 int eflags = 0; 2487 u_int endline, foundx, foundy, len, pywrap, size = 1; 2488 char *buf; 2489 regmatch_t regmatch; 2490 struct grid_line *gl; 2491 2492 /* 2493 * This can happen during search if the last match was the last 2494 * character on a line. 2495 */ 2496 if (first >= last) 2497 return (0); 2498 2499 /* Set flags for regex search. */ 2500 if (first != 0) 2501 eflags |= REG_NOTBOL; 2502 2503 /* Need to look at the entire string. */ 2504 buf = xmalloc(size); 2505 buf[0] = '\0'; 2506 buf = window_copy_stringify(gd, py, first, gd->sx, buf, &size); 2507 len = gd->sx - first; 2508 endline = gd->hsize + gd->sy - 1; 2509 pywrap = py; 2510 while (buf != NULL && pywrap <= endline) { 2511 gl = grid_get_line(gd, pywrap); 2512 if (~gl->flags & GRID_LINE_WRAPPED) 2513 break; 2514 pywrap++; 2515 buf = window_copy_stringify(gd, pywrap, 0, gd->sx, buf, &size); 2516 len += gd->sx; 2517 } 2518 2519 if (regexec(reg, buf, 1, ®match, eflags) == 0 && 2520 regmatch.rm_so != regmatch.rm_eo) { 2521 foundx = first; 2522 foundy = py; 2523 window_copy_cstrtocellpos(gd, len, &foundx, &foundy, 2524 buf + regmatch.rm_so); 2525 if (foundy == py && foundx < last) { 2526 *ppx = foundx; 2527 len -= foundx - first; 2528 window_copy_cstrtocellpos(gd, len, &foundx, &foundy, 2529 buf + regmatch.rm_eo); 2530 *psx = foundx; 2531 while (foundy > py) { 2532 *psx += gd->sx; 2533 foundy--; 2534 } 2535 *psx -= *ppx; 2536 free(buf); 2537 return (1); 2538 } 2539 } 2540 2541 free(buf); 2542 *ppx = 0; 2543 *psx = 0; 2544 return (0); 2545 } 2546 2547 static int 2548 window_copy_search_rl_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py, 2549 u_int first, u_int last, regex_t *reg) 2550 { 2551 int eflags = 0; 2552 u_int endline, len, pywrap, size = 1; 2553 char *buf; 2554 struct grid_line *gl; 2555 2556 /* Set flags for regex search. */ 2557 if (first != 0) 2558 eflags |= REG_NOTBOL; 2559 2560 /* Need to look at the entire string. */ 2561 buf = xmalloc(size); 2562 buf[0] = '\0'; 2563 buf = window_copy_stringify(gd, py, first, gd->sx, buf, &size); 2564 len = gd->sx - first; 2565 endline = gd->hsize + gd->sy - 1; 2566 pywrap = py; 2567 while (buf != NULL && (pywrap <= endline)) { 2568 gl = grid_get_line(gd, pywrap); 2569 if (~gl->flags & GRID_LINE_WRAPPED) 2570 break; 2571 pywrap++; 2572 buf = window_copy_stringify(gd, pywrap, 0, gd->sx, buf, &size); 2573 len += gd->sx; 2574 } 2575 2576 if (window_copy_last_regex(gd, py, first, last, len, ppx, psx, buf, 2577 reg, eflags)) 2578 { 2579 free(buf); 2580 return (1); 2581 } 2582 2583 free(buf); 2584 *ppx = 0; 2585 *psx = 0; 2586 return (0); 2587 } 2588 2589 static const char * 2590 window_copy_cellstring(const struct grid_line *gl, u_int px, size_t *size, 2591 int *allocated) 2592 { 2593 static struct utf8_data ud; 2594 struct grid_cell_entry *gce; 2595 char *copy; 2596 2597 if (px >= gl->cellsize) { 2598 *size = 1; 2599 *allocated = 0; 2600 return (" "); 2601 } 2602 2603 gce = &gl->celldata[px]; 2604 if (gce->flags & GRID_FLAG_PADDING) { 2605 *size = 0; 2606 *allocated = 0; 2607 return (NULL); 2608 } 2609 if (~gce->flags & GRID_FLAG_EXTENDED) { 2610 *size = 1; 2611 *allocated = 0; 2612 return (&gce->data.data); 2613 } 2614 2615 utf8_to_data(gl->extddata[gce->offset].data, &ud); 2616 *size = ud.size; 2617 *allocated = 1; 2618 2619 copy = xmalloc(ud.size); 2620 memcpy(copy, ud.data, ud.size); 2621 return (copy); 2622 } 2623 2624 /* Find last match in given range. */ 2625 static int 2626 window_copy_last_regex(struct grid *gd, u_int py, u_int first, u_int last, 2627 u_int len, u_int *ppx, u_int *psx, const char *buf, const regex_t *preg, 2628 int eflags) 2629 { 2630 u_int foundx, foundy, oldx, px = 0, savepx, savesx = 0; 2631 regmatch_t regmatch; 2632 2633 foundx = first; 2634 foundy = py; 2635 oldx = first; 2636 while (regexec(preg, buf + px, 1, ®match, eflags) == 0) { 2637 if (regmatch.rm_so == regmatch.rm_eo) 2638 break; 2639 window_copy_cstrtocellpos(gd, len, &foundx, &foundy, 2640 buf + px + regmatch.rm_so); 2641 if (foundy > py || foundx >= last) 2642 break; 2643 len -= foundx - oldx; 2644 savepx = foundx; 2645 window_copy_cstrtocellpos(gd, len, &foundx, &foundy, 2646 buf + px + regmatch.rm_eo); 2647 if (foundy > py || foundx >= last) { 2648 *ppx = savepx; 2649 *psx = foundx; 2650 while (foundy > py) { 2651 *psx += gd->sx; 2652 foundy--; 2653 } 2654 *psx -= *ppx; 2655 return (1); 2656 } else { 2657 savesx = foundx - savepx; 2658 len -= savesx; 2659 oldx = foundx; 2660 } 2661 px += regmatch.rm_eo; 2662 } 2663 2664 if (savesx > 0) { 2665 *ppx = savepx; 2666 *psx = savesx; 2667 return (1); 2668 } else { 2669 *ppx = 0; 2670 *psx = 0; 2671 return (0); 2672 } 2673 } 2674 2675 /* Stringify line and append to input buffer. Caller frees. */ 2676 static char * 2677 window_copy_stringify(struct grid *gd, u_int py, u_int first, u_int last, 2678 char *buf, u_int *size) 2679 { 2680 u_int ax, bx, newsize = *size; 2681 const struct grid_line *gl; 2682 const char *d; 2683 size_t bufsize = 1024, dlen; 2684 int allocated; 2685 2686 while (bufsize < newsize) 2687 bufsize *= 2; 2688 buf = xrealloc(buf, bufsize); 2689 2690 gl = grid_peek_line(gd, py); 2691 bx = *size - 1; 2692 for (ax = first; ax < last; ax++) { 2693 d = window_copy_cellstring(gl, ax, &dlen, &allocated); 2694 newsize += dlen; 2695 while (bufsize < newsize) { 2696 bufsize *= 2; 2697 buf = xrealloc(buf, bufsize); 2698 } 2699 if (dlen == 1) 2700 buf[bx++] = *d; 2701 else { 2702 memcpy(buf + bx, d, dlen); 2703 bx += dlen; 2704 } 2705 if (allocated) 2706 free((void *)d); 2707 } 2708 buf[newsize - 1] = '\0'; 2709 2710 *size = newsize; 2711 return (buf); 2712 } 2713 2714 /* Map start of C string containing UTF-8 data to grid cell position. */ 2715 static void 2716 window_copy_cstrtocellpos(struct grid *gd, u_int ncells, u_int *ppx, u_int *ppy, 2717 const char *str) 2718 { 2719 u_int cell, ccell, px, pywrap, pos, len; 2720 int match; 2721 const struct grid_line *gl; 2722 const char *d; 2723 size_t dlen; 2724 struct { 2725 const char *d; 2726 size_t dlen; 2727 int allocated; 2728 } *cells; 2729 2730 /* Populate the array of cell data. */ 2731 cells = xreallocarray(NULL, ncells, sizeof cells[0]); 2732 cell = 0; 2733 px = *ppx; 2734 pywrap = *ppy; 2735 gl = grid_peek_line(gd, pywrap); 2736 while (cell < ncells) { 2737 cells[cell].d = window_copy_cellstring(gl, px, 2738 &cells[cell].dlen, &cells[cell].allocated); 2739 cell++; 2740 px++; 2741 if (px == gd->sx) { 2742 px = 0; 2743 pywrap++; 2744 gl = grid_peek_line(gd, pywrap); 2745 } 2746 } 2747 2748 /* Locate starting cell. */ 2749 cell = 0; 2750 len = strlen(str); 2751 while (cell < ncells) { 2752 ccell = cell; 2753 pos = 0; 2754 match = 1; 2755 while (ccell < ncells) { 2756 if (str[pos] == '\0') { 2757 match = 0; 2758 break; 2759 } 2760 d = cells[ccell].d; 2761 dlen = cells[ccell].dlen; 2762 if (dlen == 1) { 2763 if (str[pos] != *d) { 2764 match = 0; 2765 break; 2766 } 2767 pos++; 2768 } else { 2769 if (dlen > len - pos) 2770 dlen = len - pos; 2771 if (memcmp(str + pos, d, dlen) != 0) { 2772 match = 0; 2773 break; 2774 } 2775 pos += dlen; 2776 } 2777 ccell++; 2778 } 2779 if (match) 2780 break; 2781 cell++; 2782 } 2783 2784 /* If not found this will be one past the end. */ 2785 px = *ppx + cell; 2786 pywrap = *ppy; 2787 while (px >= gd->sx) { 2788 px -= gd->sx; 2789 pywrap++; 2790 } 2791 2792 *ppx = px; 2793 *ppy = pywrap; 2794 2795 /* Free cell data. */ 2796 for (cell = 0; cell < ncells; cell++) { 2797 if (cells[cell].allocated) 2798 free((void *)cells[cell].d); 2799 } 2800 free(cells); 2801 } 2802 2803 static void 2804 window_copy_move_left(struct screen *s, u_int *fx, u_int *fy, int wrapflag) 2805 { 2806 if (*fx == 0) { /* left */ 2807 if (*fy == 0) { /* top */ 2808 if (wrapflag) { 2809 *fx = screen_size_x(s) - 1; 2810 *fy = screen_hsize(s) + screen_size_y(s) - 1; 2811 } 2812 return; 2813 } 2814 *fx = screen_size_x(s) - 1; 2815 *fy = *fy - 1; 2816 } else 2817 *fx = *fx - 1; 2818 } 2819 2820 static void 2821 window_copy_move_right(struct screen *s, u_int *fx, u_int *fy, int wrapflag) 2822 { 2823 if (*fx == screen_size_x(s) - 1) { /* right */ 2824 if (*fy == screen_hsize(s) + screen_size_y(s) - 1) { /* bottom */ 2825 if (wrapflag) { 2826 *fx = 0; 2827 *fy = 0; 2828 } 2829 return; 2830 } 2831 *fx = 0; 2832 *fy = *fy + 1; 2833 } else 2834 *fx = *fx + 1; 2835 } 2836 2837 static int 2838 window_copy_is_lowercase(const char *ptr) 2839 { 2840 while (*ptr != '\0') { 2841 if (*ptr != tolower((u_char)*ptr)) 2842 return (0); 2843 ++ptr; 2844 } 2845 return (1); 2846 } 2847 2848 /* 2849 * Search for text stored in sgd starting from position fx,fy up to endline. If 2850 * found, jump to it. If cis then ignore case. The direction is 0 for searching 2851 * up, down otherwise. If wrap then go to begin/end of grid and try again if 2852 * not found. 2853 */ 2854 static int 2855 window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, 2856 struct grid *sgd, u_int fx, u_int fy, u_int endline, int cis, int wrap, 2857 int direction, int regex) 2858 { 2859 u_int i, px, sx, ssize = 1; 2860 int found = 0, cflags = REG_EXTENDED; 2861 char *sbuf; 2862 regex_t reg; 2863 2864 if (regex) { 2865 sbuf = xmalloc(ssize); 2866 sbuf[0] = '\0'; 2867 sbuf = window_copy_stringify(sgd, 0, 0, sgd->sx, sbuf, &ssize); 2868 if (cis) 2869 cflags |= REG_ICASE; 2870 if (regcomp(®, sbuf, cflags) != 0) { 2871 free(sbuf); 2872 return (0); 2873 } 2874 } 2875 2876 if (direction) { 2877 for (i = fy; i <= endline; i++) { 2878 if (regex) { 2879 found = window_copy_search_lr_regex(gd, 2880 &px, &sx, i, fx, gd->sx, ®); 2881 } else { 2882 found = window_copy_search_lr(gd, sgd, 2883 &px, i, fx, gd->sx, cis); 2884 } 2885 if (found) 2886 break; 2887 fx = 0; 2888 } 2889 } else { 2890 for (i = fy + 1; endline < i; i--) { 2891 if (regex) { 2892 found = window_copy_search_rl_regex(gd, 2893 &px, &sx, i - 1, 0, fx + 1, ®); 2894 } else { 2895 found = window_copy_search_rl(gd, sgd, 2896 &px, i - 1, 0, fx + 1, cis); 2897 } 2898 if (found) { 2899 i--; 2900 break; 2901 } 2902 fx = gd->sx - 1; 2903 } 2904 } 2905 if (regex) { 2906 free(sbuf); 2907 regfree(®); 2908 } 2909 2910 if (found) { 2911 window_copy_scroll_to(wme, px, i, 1); 2912 return (1); 2913 } 2914 if (wrap) { 2915 return (window_copy_search_jump(wme, gd, sgd, 2916 direction ? 0 : gd->sx - 1, 2917 direction ? 0 : gd->hsize + gd->sy - 1, fy, cis, 0, 2918 direction, regex)); 2919 } 2920 return (0); 2921 } 2922 2923 /* 2924 * Search in for text searchstr. If direction is 0 then search up, otherwise 2925 * down. 2926 */ 2927 static int 2928 window_copy_search(struct window_mode_entry *wme, int direction, int regex) 2929 { 2930 struct window_pane *wp = wme->wp; 2931 struct window_copy_mode_data *data = wme->data; 2932 struct screen *s = data->backing, ss; 2933 struct screen_write_ctx ctx; 2934 struct grid *gd = s->grid; 2935 const char *str = data->searchstr; 2936 u_int fx, fy, endline; 2937 int wrapflag, cis, found, visible_only; 2938 2939 if (regex && str[strcspn(str, "^$*+()?[].\\")] == '\0') 2940 regex = 0; 2941 2942 if (data->timeout) 2943 return (0); 2944 2945 if (wp->searchstr == NULL || wp->searchregex != regex) 2946 visible_only = 0; 2947 else 2948 visible_only = (strcmp(wp->searchstr, str) == 0); 2949 free(wp->searchstr); 2950 wp->searchstr = xstrdup(str); 2951 wp->searchregex = regex; 2952 2953 fx = data->cx; 2954 fy = screen_hsize(data->backing) - data->oy + data->cy; 2955 2956 screen_init(&ss, screen_write_strlen("%s", str), 1, 0); 2957 screen_write_start(&ctx, &ss); 2958 screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", str); 2959 screen_write_stop(&ctx); 2960 2961 wrapflag = options_get_number(wp->window->options, "wrap-search"); 2962 cis = window_copy_is_lowercase(str); 2963 2964 if (direction) { 2965 window_copy_move_right(s, &fx, &fy, wrapflag); 2966 endline = gd->hsize + gd->sy - 1; 2967 } else { 2968 window_copy_move_left(s, &fx, &fy, wrapflag); 2969 endline = 0; 2970 } 2971 2972 found = window_copy_search_jump(wme, gd, ss.grid, fx, fy, endline, cis, 2973 wrapflag, direction, regex); 2974 if (found) 2975 window_copy_search_marks(wme, &ss, regex, visible_only); 2976 window_copy_redraw_screen(wme); 2977 2978 screen_free(&ss); 2979 return (found); 2980 } 2981 2982 static void 2983 window_copy_visible_lines(struct window_copy_mode_data *data, u_int *start, 2984 u_int *end) 2985 { 2986 struct grid *gd = data->backing->grid; 2987 const struct grid_line *gl; 2988 2989 for (*start = gd->hsize - data->oy; *start > 0; (*start)--) { 2990 gl = grid_peek_line(gd, (*start) - 1); 2991 if (~gl->flags & GRID_LINE_WRAPPED) 2992 break; 2993 } 2994 *end = gd->hsize - data->oy + gd->sy; 2995 } 2996 2997 static int 2998 window_copy_search_mark_at(struct window_copy_mode_data *data, u_int px, u_int py, 2999 u_int *at) 3000 { 3001 struct screen *s = data->backing; 3002 struct grid *gd = s->grid; 3003 3004 if (py < gd->hsize - data->oy) 3005 return (-1); 3006 if (py > gd->hsize - data->oy + gd->sy - 1) 3007 return (-1); 3008 *at = ((py - (gd->hsize - data->oy)) * gd->sx) + px; 3009 return (0); 3010 } 3011 3012 static int 3013 window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, 3014 int regex, int visible_only) 3015 { 3016 struct window_copy_mode_data *data = wme->data; 3017 struct screen *s = data->backing, ss; 3018 struct screen_write_ctx ctx; 3019 struct grid *gd = s->grid; 3020 int found, cis, which = -1, stopped = 0; 3021 int cflags = REG_EXTENDED; 3022 u_int px, py, i, b, nfound = 0, width; 3023 u_int ssize = 1, start, end; 3024 char *sbuf; 3025 regex_t reg; 3026 uint64_t stop = 0, tstart, t; 3027 3028 if (ssp == NULL) { 3029 width = screen_write_strlen("%s", data->searchstr); 3030 screen_init(&ss, width, 1, 0); 3031 screen_write_start(&ctx, &ss); 3032 screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", 3033 data->searchstr); 3034 screen_write_stop(&ctx); 3035 ssp = &ss; 3036 } else 3037 width = screen_size_x(ssp); 3038 3039 cis = window_copy_is_lowercase(data->searchstr); 3040 3041 if (regex) { 3042 sbuf = xmalloc(ssize); 3043 sbuf[0] = '\0'; 3044 sbuf = window_copy_stringify(ssp->grid, 0, 0, ssp->grid->sx, 3045 sbuf, &ssize); 3046 if (cis) 3047 cflags |= REG_ICASE; 3048 if (regcomp(®, sbuf, cflags) != 0) { 3049 free(sbuf); 3050 return (0); 3051 } 3052 } 3053 tstart = get_timer(); 3054 3055 if (visible_only) 3056 window_copy_visible_lines(data, &start, &end); 3057 else { 3058 start = 0; 3059 end = gd->hsize + gd->sy; 3060 stop = get_timer() + WINDOW_COPY_SEARCH_ALL_TIMEOUT; 3061 } 3062 3063 again: 3064 free(data->searchmark); 3065 data->searchmark = xcalloc(gd->sx, gd->sy); 3066 data->searchgen = 1; 3067 3068 for (py = start; py < end; py++) { 3069 px = 0; 3070 for (;;) { 3071 if (regex) { 3072 found = window_copy_search_lr_regex(gd, 3073 &px, &width, py, px, gd->sx, ®); 3074 if (!found) 3075 break; 3076 } else { 3077 found = window_copy_search_lr(gd, ssp->grid, 3078 &px, py, px, gd->sx, cis); 3079 if (!found) 3080 break; 3081 } 3082 3083 nfound++; 3084 if (px == data->cx && 3085 py == gd->hsize + data->cy - data->oy) 3086 which = nfound; 3087 3088 if (window_copy_search_mark_at(data, px, py, &b) == 0) { 3089 if (b + width > gd->sx * gd->sy) 3090 width = (gd->sx * gd->sy) - b; 3091 for (i = b; i < b + width; i++) 3092 data->searchmark[i] = data->searchgen; 3093 if (data->searchgen == UCHAR_MAX) 3094 data->searchgen = 1; 3095 else 3096 data->searchgen++; 3097 } 3098 3099 px++; 3100 } 3101 3102 t = get_timer(); 3103 if (t - tstart > WINDOW_COPY_SEARCH_TIMEOUT) { 3104 data->timeout = 1; 3105 break; 3106 } 3107 if (stop != 0 && t > stop) { 3108 stopped = 1; 3109 break; 3110 } 3111 } 3112 if (data->timeout) { 3113 window_copy_clear_marks(wme); 3114 goto out; 3115 } 3116 3117 if (stopped && stop != 0) { 3118 /* Try again but just the visible context. */ 3119 window_copy_visible_lines(data, &start, &end); 3120 stop = 0; 3121 goto again; 3122 } 3123 3124 if (!visible_only) { 3125 if (stopped) { 3126 data->searchthis = -1; 3127 if (nfound > 1000) 3128 data->searchcount = 1000; 3129 else if (nfound > 100) 3130 data->searchcount = 100; 3131 else if (nfound > 10) 3132 data->searchcount = 10; 3133 else 3134 data->searchcount = -1; 3135 data->searchmore = 1; 3136 } else { 3137 if (which != -1) 3138 data->searchthis = 1 + nfound - which; 3139 else 3140 data->searchthis = -1; 3141 data->searchcount = nfound; 3142 data->searchmore = 0; 3143 } 3144 } 3145 3146 out: 3147 if (ssp == &ss) 3148 screen_free(&ss); 3149 if (regex) { 3150 free(sbuf); 3151 regfree(®); 3152 } 3153 return (1); 3154 } 3155 3156 static void 3157 window_copy_clear_marks(struct window_mode_entry *wme) 3158 { 3159 struct window_copy_mode_data *data = wme->data; 3160 3161 free(data->searchmark); 3162 data->searchmark = NULL; 3163 } 3164 3165 static int 3166 window_copy_search_up(struct window_mode_entry *wme, int regex) 3167 { 3168 return (window_copy_search(wme, 0, regex)); 3169 } 3170 3171 static int 3172 window_copy_search_down(struct window_mode_entry *wme, int regex) 3173 { 3174 return (window_copy_search(wme, 1, regex)); 3175 } 3176 3177 static void 3178 window_copy_goto_line(struct window_mode_entry *wme, const char *linestr) 3179 { 3180 struct window_copy_mode_data *data = wme->data; 3181 const char *errstr; 3182 int lineno; 3183 3184 lineno = strtonum(linestr, -1, INT_MAX, &errstr); 3185 if (errstr != NULL) 3186 return; 3187 if (lineno < 0 || (u_int)lineno > screen_hsize(data->backing)) 3188 lineno = screen_hsize(data->backing); 3189 3190 data->oy = lineno; 3191 window_copy_update_selection(wme, 1, 0); 3192 window_copy_redraw_screen(wme); 3193 } 3194 3195 static void 3196 window_copy_match_start_end(struct window_copy_mode_data *data, u_int at, 3197 u_int *start, u_int *end) 3198 { 3199 struct grid *gd = data->backing->grid; 3200 u_int last = (gd->sy * gd->sx) - 1; 3201 u_char mark = data->searchmark[at]; 3202 3203 *start = *end = at; 3204 while (*start != 0 && data->searchmark[*start] == mark) 3205 (*start)--; 3206 if (data->searchmark[*start] != mark) 3207 (*start)++; 3208 while (*end != last && data->searchmark[*end] == mark) 3209 (*end)++; 3210 if (data->searchmark[*end] != mark) 3211 (*end)--; 3212 } 3213 3214 static char * 3215 window_copy_match_at_cursor(struct window_copy_mode_data *data) 3216 { 3217 struct grid *gd = data->backing->grid; 3218 struct grid_cell gc; 3219 u_int at, start, end, cy, px, py; 3220 u_int sx = screen_size_x(data->backing); 3221 char *buf = NULL; 3222 size_t len = 0; 3223 3224 if (data->searchmark == NULL) 3225 return (NULL); 3226 3227 cy = screen_hsize(data->backing) - data->oy + data->cy; 3228 if (window_copy_search_mark_at(data, data->cx, cy, &at) != 0) 3229 return (NULL); 3230 if (data->searchmark[at] == 0) 3231 return (NULL); 3232 window_copy_match_start_end(data, at, &start, &end); 3233 3234 /* 3235 * Cells will not be set in the marked array unless they are valid text 3236 * and wrapping will be taken care of, so we can just copy. 3237 */ 3238 for (at = start; at <= end; at++) { 3239 py = at / sx; 3240 px = at - (py * sx); 3241 3242 grid_get_cell(gd, px, gd->hsize + py - data->oy, &gc); 3243 buf = xrealloc(buf, len + gc.data.size + 1); 3244 memcpy(buf + len, gc.data.data, gc.data.size); 3245 len += gc.data.size; 3246 } 3247 if (len != 0) 3248 buf[len] = '\0'; 3249 return (buf); 3250 } 3251 3252 static void 3253 window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy, 3254 struct grid_cell *gc, const struct grid_cell *mgc, 3255 const struct grid_cell *cgc, const struct grid_cell *mkgc) 3256 { 3257 struct window_copy_mode_data *data = wme->data; 3258 u_int mark, start, end, cy, cursor, current; 3259 int inv = 0; 3260 3261 if (data->showmark && fy == data->my) { 3262 gc->attr = mkgc->attr; 3263 if (fx == data->mx) 3264 inv = 1; 3265 if (inv) { 3266 gc->fg = mkgc->bg; 3267 gc->bg = mkgc->fg; 3268 } 3269 else { 3270 gc->fg = mkgc->fg; 3271 gc->bg = mkgc->bg; 3272 } 3273 } 3274 3275 if (data->searchmark == NULL) 3276 return; 3277 3278 if (window_copy_search_mark_at(data, fx, fy, ¤t) != 0) 3279 return; 3280 mark = data->searchmark[current]; 3281 if (mark == 0) 3282 return; 3283 3284 cy = screen_hsize(data->backing) - data->oy + data->cy; 3285 if (window_copy_search_mark_at(data, data->cx, cy, &cursor) == 0 && 3286 data->searchmark[cursor] == mark) { 3287 window_copy_match_start_end(data, cursor, &start, &end); 3288 if (current >= start && current <= end) { 3289 gc->attr = cgc->attr; 3290 if (inv) { 3291 gc->fg = cgc->bg; 3292 gc->bg = cgc->fg; 3293 } 3294 else { 3295 gc->fg = cgc->fg; 3296 gc->bg = cgc->bg; 3297 } 3298 return; 3299 } 3300 } 3301 3302 gc->attr = mgc->attr; 3303 if (inv) { 3304 gc->fg = mgc->bg; 3305 gc->bg = mgc->fg; 3306 } 3307 else { 3308 gc->fg = mgc->fg; 3309 gc->bg = mgc->bg; 3310 } 3311 } 3312 3313 static void 3314 window_copy_write_one(struct window_mode_entry *wme, 3315 struct screen_write_ctx *ctx, u_int py, u_int fy, u_int nx, 3316 const struct grid_cell *mgc, const struct grid_cell *cgc, 3317 const struct grid_cell *mkgc) 3318 { 3319 struct window_copy_mode_data *data = wme->data; 3320 struct grid *gd = data->backing->grid; 3321 struct grid_cell gc; 3322 u_int fx; 3323 3324 screen_write_cursormove(ctx, 0, py, 0); 3325 for (fx = 0; fx < nx; fx++) { 3326 grid_get_cell(gd, fx, fy, &gc); 3327 if (fx + gc.data.width <= nx) { 3328 window_copy_update_style(wme, fx, fy, &gc, mgc, cgc, 3329 mkgc); 3330 screen_write_cell(ctx, &gc); 3331 } 3332 } 3333 } 3334 3335 static void 3336 window_copy_write_line(struct window_mode_entry *wme, 3337 struct screen_write_ctx *ctx, u_int py) 3338 { 3339 struct window_pane *wp = wme->wp; 3340 struct window_copy_mode_data *data = wme->data; 3341 struct screen *s = &data->screen; 3342 struct options *oo = wp->window->options; 3343 struct grid_cell gc, mgc, cgc, mkgc; 3344 char hdr[512]; 3345 size_t size = 0; 3346 u_int hsize = screen_hsize(data->backing); 3347 3348 style_apply(&gc, oo, "mode-style", NULL); 3349 gc.flags |= GRID_FLAG_NOPALETTE; 3350 style_apply(&mgc, oo, "copy-mode-match-style", NULL); 3351 mgc.flags |= GRID_FLAG_NOPALETTE; 3352 style_apply(&cgc, oo, "copy-mode-current-match-style", NULL); 3353 cgc.flags |= GRID_FLAG_NOPALETTE; 3354 style_apply(&mkgc, oo, "copy-mode-mark-style", NULL); 3355 mkgc.flags |= GRID_FLAG_NOPALETTE; 3356 3357 if (py == 0 && s->rupper < s->rlower && !data->hide_position) { 3358 if (data->searchmark == NULL) { 3359 if (data->timeout) { 3360 size = xsnprintf(hdr, sizeof hdr, 3361 "(timed out) [%u/%u]", data->oy, hsize); 3362 } else { 3363 size = xsnprintf(hdr, sizeof hdr, 3364 "[%u/%u]", data->oy, hsize); 3365 } 3366 } else { 3367 if (data->searchcount == -1) { 3368 size = xsnprintf(hdr, sizeof hdr, 3369 "[%u/%u]", data->oy, hsize); 3370 } else if (data->searchthis == -1) { 3371 size = xsnprintf(hdr, sizeof hdr, 3372 "(%d%s results) [%u/%u]", data->searchcount, 3373 data->searchmore ? "+" : "", data->oy, 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) 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 (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