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