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