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