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