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