1 /* $OpenBSD: window-copy.c,v 1.333 2022/02/03 07:26:43 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_toggle_position(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 1281 data->hide_position = !data->hide_position; 1282 return (WINDOW_COPY_CMD_REDRAW); 1283 } 1284 1285 static enum window_copy_cmd_action 1286 window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs) 1287 { 1288 struct window_mode_entry *wme = cs->wme; 1289 struct window_copy_mode_data *data = wme->data; 1290 struct screen *s = data->backing; 1291 u_int oy; 1292 1293 oy = screen_hsize(s) + data->cy - data->oy; 1294 if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely) 1295 window_copy_other_end(wme); 1296 1297 data->cy = screen_size_y(&data->screen) - 1; 1298 data->cx = window_copy_find_length(wme, screen_hsize(s) + data->cy); 1299 data->oy = 0; 1300 1301 if (data->searchmark != NULL && !data->timeout) 1302 window_copy_search_marks(wme, NULL, data->searchregex, 1); 1303 window_copy_update_selection(wme, 1, 0); 1304 return (WINDOW_COPY_CMD_REDRAW); 1305 } 1306 1307 static enum window_copy_cmd_action 1308 window_copy_cmd_history_top(struct window_copy_cmd_state *cs) 1309 { 1310 struct window_mode_entry *wme = cs->wme; 1311 struct window_copy_mode_data *data = wme->data; 1312 u_int oy; 1313 1314 oy = screen_hsize(data->backing) + data->cy - data->oy; 1315 if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely) 1316 window_copy_other_end(wme); 1317 1318 data->cy = 0; 1319 data->cx = 0; 1320 data->oy = screen_hsize(data->backing); 1321 1322 if (data->searchmark != NULL && !data->timeout) 1323 window_copy_search_marks(wme, NULL, data->searchregex, 1); 1324 window_copy_update_selection(wme, 1, 0); 1325 return (WINDOW_COPY_CMD_REDRAW); 1326 } 1327 1328 static enum window_copy_cmd_action 1329 window_copy_cmd_jump_again(struct window_copy_cmd_state *cs) 1330 { 1331 struct window_mode_entry *wme = cs->wme; 1332 struct window_copy_mode_data *data = wme->data; 1333 u_int np = wme->prefix; 1334 1335 switch (data->jumptype) { 1336 case WINDOW_COPY_JUMPFORWARD: 1337 for (; np != 0; np--) 1338 window_copy_cursor_jump(wme); 1339 break; 1340 case WINDOW_COPY_JUMPBACKWARD: 1341 for (; np != 0; np--) 1342 window_copy_cursor_jump_back(wme); 1343 break; 1344 case WINDOW_COPY_JUMPTOFORWARD: 1345 for (; np != 0; np--) 1346 window_copy_cursor_jump_to(wme); 1347 break; 1348 case WINDOW_COPY_JUMPTOBACKWARD: 1349 for (; np != 0; np--) 1350 window_copy_cursor_jump_to_back(wme); 1351 break; 1352 } 1353 return (WINDOW_COPY_CMD_NOTHING); 1354 } 1355 1356 static enum window_copy_cmd_action 1357 window_copy_cmd_jump_reverse(struct window_copy_cmd_state *cs) 1358 { 1359 struct window_mode_entry *wme = cs->wme; 1360 struct window_copy_mode_data *data = wme->data; 1361 u_int np = wme->prefix; 1362 1363 switch (data->jumptype) { 1364 case WINDOW_COPY_JUMPFORWARD: 1365 for (; np != 0; np--) 1366 window_copy_cursor_jump_back(wme); 1367 break; 1368 case WINDOW_COPY_JUMPBACKWARD: 1369 for (; np != 0; np--) 1370 window_copy_cursor_jump(wme); 1371 break; 1372 case WINDOW_COPY_JUMPTOFORWARD: 1373 for (; np != 0; np--) 1374 window_copy_cursor_jump_to_back(wme); 1375 break; 1376 case WINDOW_COPY_JUMPTOBACKWARD: 1377 for (; np != 0; np--) 1378 window_copy_cursor_jump_to(wme); 1379 break; 1380 } 1381 return (WINDOW_COPY_CMD_NOTHING); 1382 } 1383 1384 static enum window_copy_cmd_action 1385 window_copy_cmd_middle_line(struct window_copy_cmd_state *cs) 1386 { 1387 struct window_mode_entry *wme = cs->wme; 1388 struct window_copy_mode_data *data = wme->data; 1389 1390 data->cx = 0; 1391 data->cy = (screen_size_y(&data->screen) - 1) / 2; 1392 1393 window_copy_update_selection(wme, 1, 0); 1394 return (WINDOW_COPY_CMD_REDRAW); 1395 } 1396 1397 static enum window_copy_cmd_action 1398 window_copy_cmd_previous_matching_bracket(struct window_copy_cmd_state *cs) 1399 { 1400 struct window_mode_entry *wme = cs->wme; 1401 u_int np = wme->prefix; 1402 struct window_copy_mode_data *data = wme->data; 1403 struct screen *s = data->backing; 1404 char open[] = "{[(", close[] = "}])"; 1405 char tried, found, start, *cp; 1406 u_int px, py, xx, n; 1407 struct grid_cell gc; 1408 int failed; 1409 1410 for (; np != 0; np--) { 1411 /* Get cursor position and line length. */ 1412 px = data->cx; 1413 py = screen_hsize(s) + data->cy - data->oy; 1414 xx = window_copy_find_length(wme, py); 1415 if (xx == 0) 1416 break; 1417 1418 /* 1419 * Get the current character. If not on a bracket, try the 1420 * previous. If still not, then behave like previous-word. 1421 */ 1422 tried = 0; 1423 retry: 1424 grid_get_cell(s->grid, px, py, &gc); 1425 if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING)) 1426 cp = NULL; 1427 else { 1428 found = *gc.data.data; 1429 cp = strchr(close, found); 1430 } 1431 if (cp == NULL) { 1432 if (data->modekeys == MODEKEY_EMACS) { 1433 if (!tried && px > 0) { 1434 px--; 1435 tried = 1; 1436 goto retry; 1437 } 1438 window_copy_cursor_previous_word(wme, close, 1); 1439 } 1440 continue; 1441 } 1442 start = open[cp - close]; 1443 1444 /* Walk backward until the matching bracket is reached. */ 1445 n = 1; 1446 failed = 0; 1447 do { 1448 if (px == 0) { 1449 if (py == 0) { 1450 failed = 1; 1451 break; 1452 } 1453 do { 1454 py--; 1455 xx = window_copy_find_length(wme, py); 1456 } while (xx == 0 && py > 0); 1457 if (xx == 0 && py == 0) { 1458 failed = 1; 1459 break; 1460 } 1461 px = xx - 1; 1462 } else 1463 px--; 1464 1465 grid_get_cell(s->grid, px, py, &gc); 1466 if (gc.data.size == 1 && 1467 (~gc.flags & GRID_FLAG_PADDING)) { 1468 if (*gc.data.data == found) 1469 n++; 1470 else if (*gc.data.data == start) 1471 n--; 1472 } 1473 } while (n != 0); 1474 1475 /* Move the cursor to the found location if any. */ 1476 if (!failed) 1477 window_copy_scroll_to(wme, px, py, 0); 1478 } 1479 1480 return (WINDOW_COPY_CMD_NOTHING); 1481 } 1482 1483 static enum window_copy_cmd_action 1484 window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state *cs) 1485 { 1486 struct window_mode_entry *wme = cs->wme; 1487 u_int np = wme->prefix; 1488 struct window_copy_mode_data *data = wme->data; 1489 struct screen *s = data->backing; 1490 char open[] = "{[(", close[] = "}])"; 1491 char tried, found, end, *cp; 1492 u_int px, py, xx, yy, sx, sy, n; 1493 struct grid_cell gc; 1494 int failed; 1495 struct grid_line *gl; 1496 1497 for (; np != 0; np--) { 1498 /* Get cursor position and line length. */ 1499 px = data->cx; 1500 py = screen_hsize(s) + data->cy - data->oy; 1501 xx = window_copy_find_length(wme, py); 1502 yy = screen_hsize(s) + screen_size_y(s) - 1; 1503 if (xx == 0) 1504 break; 1505 1506 /* 1507 * Get the current character. If not on a bracket, try the 1508 * next. If still not, then behave like next-word. 1509 */ 1510 tried = 0; 1511 retry: 1512 grid_get_cell(s->grid, px, py, &gc); 1513 if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING)) 1514 cp = NULL; 1515 else { 1516 found = *gc.data.data; 1517 1518 /* 1519 * In vi mode, attempt to move to previous bracket if a 1520 * closing bracket is found first. If this fails, 1521 * return to the original cursor position. 1522 */ 1523 cp = strchr(close, found); 1524 if (cp != NULL && data->modekeys == MODEKEY_VI) { 1525 sx = data->cx; 1526 sy = screen_hsize(s) + data->cy - data->oy; 1527 1528 window_copy_scroll_to(wme, px, py, 0); 1529 window_copy_cmd_previous_matching_bracket(cs); 1530 1531 px = data->cx; 1532 py = screen_hsize(s) + data->cy - data->oy; 1533 grid_get_cell(s->grid, px, py, &gc); 1534 if (gc.data.size == 1 && 1535 (~gc.flags & GRID_FLAG_PADDING) && 1536 strchr(close, *gc.data.data) != NULL) 1537 window_copy_scroll_to(wme, sx, sy, 0); 1538 break; 1539 } 1540 1541 cp = strchr(open, found); 1542 } 1543 if (cp == NULL) { 1544 if (data->modekeys == MODEKEY_EMACS) { 1545 if (!tried && px <= xx) { 1546 px++; 1547 tried = 1; 1548 goto retry; 1549 } 1550 window_copy_cursor_next_word_end(wme, open, 0); 1551 continue; 1552 } 1553 /* For vi, continue searching for bracket until EOL. */ 1554 if (px > xx) { 1555 if (py == yy) 1556 continue; 1557 gl = grid_get_line(s->grid, py); 1558 if (~gl->flags & GRID_LINE_WRAPPED) 1559 continue; 1560 if (gl->cellsize > s->grid->sx) 1561 continue; 1562 px = 0; 1563 py++; 1564 xx = window_copy_find_length(wme, py); 1565 } else 1566 px++; 1567 goto retry; 1568 } 1569 end = close[cp - open]; 1570 1571 /* Walk forward until the matching bracket is reached. */ 1572 n = 1; 1573 failed = 0; 1574 do { 1575 if (px > xx) { 1576 if (py == yy) { 1577 failed = 1; 1578 break; 1579 } 1580 px = 0; 1581 py++; 1582 xx = window_copy_find_length(wme, py); 1583 } else 1584 px++; 1585 1586 grid_get_cell(s->grid, px, py, &gc); 1587 if (gc.data.size == 1 && 1588 (~gc.flags & GRID_FLAG_PADDING)) { 1589 if (*gc.data.data == found) 1590 n++; 1591 else if (*gc.data.data == end) 1592 n--; 1593 } 1594 } while (n != 0); 1595 1596 /* Move the cursor to the found location if any. */ 1597 if (!failed) 1598 window_copy_scroll_to(wme, px, py, 0); 1599 } 1600 1601 return (WINDOW_COPY_CMD_NOTHING); 1602 } 1603 1604 static enum window_copy_cmd_action 1605 window_copy_cmd_next_paragraph(struct window_copy_cmd_state *cs) 1606 { 1607 struct window_mode_entry *wme = cs->wme; 1608 u_int np = wme->prefix; 1609 1610 for (; np != 0; np--) 1611 window_copy_next_paragraph(wme); 1612 return (WINDOW_COPY_CMD_NOTHING); 1613 } 1614 1615 static enum window_copy_cmd_action 1616 window_copy_cmd_next_space(struct window_copy_cmd_state *cs) 1617 { 1618 struct window_mode_entry *wme = cs->wme; 1619 u_int np = wme->prefix; 1620 1621 for (; np != 0; np--) 1622 window_copy_cursor_next_word(wme, ""); 1623 return (WINDOW_COPY_CMD_NOTHING); 1624 } 1625 1626 static enum window_copy_cmd_action 1627 window_copy_cmd_next_space_end(struct window_copy_cmd_state *cs) 1628 { 1629 struct window_mode_entry *wme = cs->wme; 1630 u_int np = wme->prefix; 1631 1632 for (; np != 0; np--) 1633 window_copy_cursor_next_word_end(wme, "", 0); 1634 return (WINDOW_COPY_CMD_NOTHING); 1635 } 1636 1637 static enum window_copy_cmd_action 1638 window_copy_cmd_next_word(struct window_copy_cmd_state *cs) 1639 { 1640 struct window_mode_entry *wme = cs->wme; 1641 u_int np = wme->prefix; 1642 const char *separators; 1643 1644 separators = options_get_string(cs->s->options, "word-separators"); 1645 1646 for (; np != 0; np--) 1647 window_copy_cursor_next_word(wme, separators); 1648 return (WINDOW_COPY_CMD_NOTHING); 1649 } 1650 1651 static enum window_copy_cmd_action 1652 window_copy_cmd_next_word_end(struct window_copy_cmd_state *cs) 1653 { 1654 struct window_mode_entry *wme = cs->wme; 1655 u_int np = wme->prefix; 1656 const char *separators; 1657 1658 separators = options_get_string(cs->s->options, "word-separators"); 1659 1660 for (; np != 0; np--) 1661 window_copy_cursor_next_word_end(wme, separators, 0); 1662 return (WINDOW_COPY_CMD_NOTHING); 1663 } 1664 1665 static enum window_copy_cmd_action 1666 window_copy_cmd_other_end(struct window_copy_cmd_state *cs) 1667 { 1668 struct window_mode_entry *wme = cs->wme; 1669 u_int np = wme->prefix; 1670 struct window_copy_mode_data *data = wme->data; 1671 1672 data->selflag = SEL_CHAR; 1673 if ((np % 2) != 0) 1674 window_copy_other_end(wme); 1675 return (WINDOW_COPY_CMD_NOTHING); 1676 } 1677 1678 static enum window_copy_cmd_action 1679 window_copy_cmd_page_down(struct window_copy_cmd_state *cs) 1680 { 1681 struct window_mode_entry *wme = cs->wme; 1682 struct window_copy_mode_data *data = wme->data; 1683 u_int np = wme->prefix; 1684 1685 for (; np != 0; np--) { 1686 if (window_copy_pagedown(wme, 0, data->scroll_exit)) 1687 return (WINDOW_COPY_CMD_CANCEL); 1688 } 1689 return (WINDOW_COPY_CMD_NOTHING); 1690 } 1691 1692 static enum window_copy_cmd_action 1693 window_copy_cmd_page_down_and_cancel(struct window_copy_cmd_state *cs) 1694 { 1695 struct window_mode_entry *wme = cs->wme; 1696 u_int np = wme->prefix; 1697 1698 for (; np != 0; np--) { 1699 if (window_copy_pagedown(wme, 0, 1)) 1700 return (WINDOW_COPY_CMD_CANCEL); 1701 } 1702 return (WINDOW_COPY_CMD_NOTHING); 1703 } 1704 1705 static enum window_copy_cmd_action 1706 window_copy_cmd_page_up(struct window_copy_cmd_state *cs) 1707 { 1708 struct window_mode_entry *wme = cs->wme; 1709 u_int np = wme->prefix; 1710 1711 for (; np != 0; np--) 1712 window_copy_pageup1(wme, 0); 1713 return (WINDOW_COPY_CMD_NOTHING); 1714 } 1715 1716 static enum window_copy_cmd_action 1717 window_copy_cmd_previous_paragraph(struct window_copy_cmd_state *cs) 1718 { 1719 struct window_mode_entry *wme = cs->wme; 1720 u_int np = wme->prefix; 1721 1722 for (; np != 0; np--) 1723 window_copy_previous_paragraph(wme); 1724 return (WINDOW_COPY_CMD_NOTHING); 1725 } 1726 1727 static enum window_copy_cmd_action 1728 window_copy_cmd_previous_space(struct window_copy_cmd_state *cs) 1729 { 1730 struct window_mode_entry *wme = cs->wme; 1731 u_int np = wme->prefix; 1732 1733 for (; np != 0; np--) 1734 window_copy_cursor_previous_word(wme, "", 1); 1735 return (WINDOW_COPY_CMD_NOTHING); 1736 } 1737 1738 static enum window_copy_cmd_action 1739 window_copy_cmd_previous_word(struct window_copy_cmd_state *cs) 1740 { 1741 struct window_mode_entry *wme = cs->wme; 1742 u_int np = wme->prefix; 1743 const char *separators; 1744 1745 separators = options_get_string(cs->s->options, "word-separators"); 1746 1747 for (; np != 0; np--) 1748 window_copy_cursor_previous_word(wme, separators, 1); 1749 return (WINDOW_COPY_CMD_NOTHING); 1750 } 1751 1752 static enum window_copy_cmd_action 1753 window_copy_cmd_rectangle_on(struct window_copy_cmd_state *cs) 1754 { 1755 struct window_mode_entry *wme = cs->wme; 1756 struct window_copy_mode_data *data = wme->data; 1757 1758 data->lineflag = LINE_SEL_NONE; 1759 window_copy_rectangle_set(wme, 1); 1760 1761 return (WINDOW_COPY_CMD_NOTHING); 1762 } 1763 1764 static enum window_copy_cmd_action 1765 window_copy_cmd_rectangle_off(struct window_copy_cmd_state *cs) 1766 { 1767 struct window_mode_entry *wme = cs->wme; 1768 struct window_copy_mode_data *data = wme->data; 1769 1770 data->lineflag = LINE_SEL_NONE; 1771 window_copy_rectangle_set(wme, 0); 1772 1773 return (WINDOW_COPY_CMD_NOTHING); 1774 } 1775 1776 static enum window_copy_cmd_action 1777 window_copy_cmd_rectangle_toggle(struct window_copy_cmd_state *cs) 1778 { 1779 struct window_mode_entry *wme = cs->wme; 1780 struct window_copy_mode_data *data = wme->data; 1781 1782 data->lineflag = LINE_SEL_NONE; 1783 window_copy_rectangle_set(wme, !data->rectflag); 1784 1785 return (WINDOW_COPY_CMD_NOTHING); 1786 } 1787 1788 static enum window_copy_cmd_action 1789 window_copy_cmd_scroll_down(struct window_copy_cmd_state *cs) 1790 { 1791 struct window_mode_entry *wme = cs->wme; 1792 struct window_copy_mode_data *data = wme->data; 1793 u_int np = wme->prefix; 1794 1795 for (; np != 0; np--) 1796 window_copy_cursor_down(wme, 1); 1797 if (data->scroll_exit && data->oy == 0) 1798 return (WINDOW_COPY_CMD_CANCEL); 1799 return (WINDOW_COPY_CMD_NOTHING); 1800 } 1801 1802 static enum window_copy_cmd_action 1803 window_copy_cmd_scroll_down_and_cancel(struct window_copy_cmd_state *cs) 1804 { 1805 struct window_mode_entry *wme = cs->wme; 1806 struct window_copy_mode_data *data = wme->data; 1807 u_int np = wme->prefix; 1808 1809 for (; np != 0; np--) 1810 window_copy_cursor_down(wme, 1); 1811 if (data->oy == 0) 1812 return (WINDOW_COPY_CMD_CANCEL); 1813 return (WINDOW_COPY_CMD_NOTHING); 1814 } 1815 1816 static enum window_copy_cmd_action 1817 window_copy_cmd_scroll_up(struct window_copy_cmd_state *cs) 1818 { 1819 struct window_mode_entry *wme = cs->wme; 1820 u_int np = wme->prefix; 1821 1822 for (; np != 0; np--) 1823 window_copy_cursor_up(wme, 1); 1824 return (WINDOW_COPY_CMD_NOTHING); 1825 } 1826 1827 static enum window_copy_cmd_action 1828 window_copy_cmd_search_again(struct window_copy_cmd_state *cs) 1829 { 1830 struct window_mode_entry *wme = cs->wme; 1831 struct window_copy_mode_data *data = wme->data; 1832 u_int np = wme->prefix; 1833 1834 if (data->searchtype == WINDOW_COPY_SEARCHUP) { 1835 for (; np != 0; np--) 1836 window_copy_search_up(wme, data->searchregex); 1837 } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { 1838 for (; np != 0; np--) 1839 window_copy_search_down(wme, data->searchregex); 1840 } 1841 return (WINDOW_COPY_CMD_NOTHING); 1842 } 1843 1844 static enum window_copy_cmd_action 1845 window_copy_cmd_search_reverse(struct window_copy_cmd_state *cs) 1846 { 1847 struct window_mode_entry *wme = cs->wme; 1848 struct window_copy_mode_data *data = wme->data; 1849 u_int np = wme->prefix; 1850 1851 if (data->searchtype == WINDOW_COPY_SEARCHUP) { 1852 for (; np != 0; np--) 1853 window_copy_search_down(wme, data->searchregex); 1854 } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { 1855 for (; np != 0; np--) 1856 window_copy_search_up(wme, data->searchregex); 1857 } 1858 return (WINDOW_COPY_CMD_NOTHING); 1859 } 1860 1861 static enum window_copy_cmd_action 1862 window_copy_cmd_select_line(struct window_copy_cmd_state *cs) 1863 { 1864 struct window_mode_entry *wme = cs->wme; 1865 struct window_copy_mode_data *data = wme->data; 1866 u_int np = wme->prefix; 1867 1868 data->lineflag = LINE_SEL_LEFT_RIGHT; 1869 data->rectflag = 0; 1870 data->selflag = SEL_LINE; 1871 data->dx = data->cx; 1872 data->dy = screen_hsize(data->backing) + data->cy - data->oy; 1873 1874 window_copy_cursor_start_of_line(wme); 1875 data->selrx = data->cx; 1876 data->selry = screen_hsize(data->backing) + data->cy - data->oy; 1877 data->endselry = data->selry; 1878 window_copy_start_selection(wme); 1879 window_copy_cursor_end_of_line(wme); 1880 data->endselry = screen_hsize(data->backing) + data->cy - data->oy; 1881 data->endselrx = window_copy_find_length(wme, data->endselry); 1882 for (; np > 1; np--) { 1883 window_copy_cursor_down(wme, 0); 1884 window_copy_cursor_end_of_line(wme); 1885 } 1886 1887 return (WINDOW_COPY_CMD_REDRAW); 1888 } 1889 1890 static enum window_copy_cmd_action 1891 window_copy_cmd_select_word(struct window_copy_cmd_state *cs) 1892 { 1893 struct window_mode_entry *wme = cs->wme; 1894 struct options *session_options = cs->s->options; 1895 struct window_copy_mode_data *data = wme->data; 1896 u_int px, py, nextx, nexty; 1897 1898 data->lineflag = LINE_SEL_LEFT_RIGHT; 1899 data->rectflag = 0; 1900 data->selflag = SEL_WORD; 1901 data->dx = data->cx; 1902 data->dy = screen_hsize(data->backing) + data->cy - data->oy; 1903 1904 data->separators = options_get_string(session_options, 1905 "word-separators"); 1906 window_copy_cursor_previous_word(wme, data->separators, 0); 1907 px = data->cx; 1908 py = screen_hsize(data->backing) + data->cy - data->oy; 1909 data->selrx = px; 1910 data->selry = py; 1911 window_copy_start_selection(wme); 1912 1913 /* Handle single character words. */ 1914 nextx = px + 1; 1915 nexty = py; 1916 if (grid_get_line(data->backing->grid, nexty)->flags & 1917 GRID_LINE_WRAPPED && nextx > screen_size_x(data->backing) - 1) { 1918 nextx = 0; 1919 nexty++; 1920 } 1921 if (px >= window_copy_find_length(wme, py) || 1922 !window_copy_in_set(wme, nextx, nexty, WHITESPACE)) 1923 window_copy_cursor_next_word_end(wme, data->separators, 1); 1924 else { 1925 window_copy_update_cursor(wme, px, data->cy); 1926 if (window_copy_update_selection(wme, 1, 1)) 1927 window_copy_redraw_lines(wme, data->cy, 1); 1928 } 1929 data->endselrx = data->cx; 1930 data->endselry = screen_hsize(data->backing) + data->cy - data->oy; 1931 if (data->dy > data->endselry) { 1932 data->dy = data->endselry; 1933 data->dx = data->endselrx; 1934 } else if (data->dx > data->endselrx) 1935 data->dx = data->endselrx; 1936 1937 return (WINDOW_COPY_CMD_REDRAW); 1938 } 1939 1940 static enum window_copy_cmd_action 1941 window_copy_cmd_set_mark(struct window_copy_cmd_state *cs) 1942 { 1943 struct window_copy_mode_data *data = cs->wme->data; 1944 1945 data->mx = data->cx; 1946 data->my = screen_hsize(data->backing) + data->cy - data->oy; 1947 data->showmark = 1; 1948 return (WINDOW_COPY_CMD_REDRAW); 1949 } 1950 1951 static enum window_copy_cmd_action 1952 window_copy_cmd_start_of_line(struct window_copy_cmd_state *cs) 1953 { 1954 struct window_mode_entry *wme = cs->wme; 1955 1956 window_copy_cursor_start_of_line(wme); 1957 return (WINDOW_COPY_CMD_NOTHING); 1958 } 1959 1960 static enum window_copy_cmd_action 1961 window_copy_cmd_top_line(struct window_copy_cmd_state *cs) 1962 { 1963 struct window_mode_entry *wme = cs->wme; 1964 struct window_copy_mode_data *data = wme->data; 1965 1966 data->cx = 0; 1967 data->cy = 0; 1968 1969 window_copy_update_selection(wme, 1, 0); 1970 return (WINDOW_COPY_CMD_REDRAW); 1971 } 1972 1973 static enum window_copy_cmd_action 1974 window_copy_cmd_copy_pipe_no_clear(struct window_copy_cmd_state *cs) 1975 { 1976 struct window_mode_entry *wme = cs->wme; 1977 struct client *c = cs->c; 1978 struct session *s = cs->s; 1979 struct winlink *wl = cs->wl; 1980 struct window_pane *wp = wme->wp; 1981 char *command = NULL, *prefix = NULL; 1982 const char *arg1 = args_string(cs->args, 1); 1983 const char *arg2 = args_string(cs->args, 2); 1984 1985 if (arg2 != NULL) 1986 prefix = format_single(NULL, arg2, c, s, wl, wp); 1987 1988 if (s != NULL && arg1 != NULL && *arg1 != '\0') 1989 command = format_single(NULL, arg1, c, s, wl, wp); 1990 window_copy_copy_pipe(wme, s, prefix, command); 1991 free(command); 1992 1993 free(prefix); 1994 return (WINDOW_COPY_CMD_NOTHING); 1995 } 1996 1997 static enum window_copy_cmd_action 1998 window_copy_cmd_copy_pipe(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_REDRAW); 2005 } 2006 2007 static enum window_copy_cmd_action 2008 window_copy_cmd_copy_pipe_and_cancel(struct window_copy_cmd_state *cs) 2009 { 2010 struct window_mode_entry *wme = cs->wme; 2011 2012 window_copy_cmd_copy_pipe_no_clear(cs); 2013 window_copy_clear_selection(wme); 2014 return (WINDOW_COPY_CMD_CANCEL); 2015 } 2016 2017 static enum window_copy_cmd_action 2018 window_copy_cmd_pipe_no_clear(struct window_copy_cmd_state *cs) 2019 { 2020 struct window_mode_entry *wme = cs->wme; 2021 struct client *c = cs->c; 2022 struct session *s = cs->s; 2023 struct winlink *wl = cs->wl; 2024 struct window_pane *wp = wme->wp; 2025 char *command = NULL; 2026 const char *arg1 = args_string(cs->args, 1); 2027 2028 if (s != NULL && arg1 != NULL && *arg1 != '\0') 2029 command = format_single(NULL, arg1, c, s, wl, wp); 2030 window_copy_pipe(wme, s, command); 2031 free(command); 2032 2033 return (WINDOW_COPY_CMD_NOTHING); 2034 } 2035 2036 static enum window_copy_cmd_action 2037 window_copy_cmd_pipe(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_REDRAW); 2044 } 2045 2046 static enum window_copy_cmd_action 2047 window_copy_cmd_pipe_and_cancel(struct window_copy_cmd_state *cs) 2048 { 2049 struct window_mode_entry *wme = cs->wme; 2050 2051 window_copy_cmd_pipe_no_clear(cs); 2052 window_copy_clear_selection(wme); 2053 return (WINDOW_COPY_CMD_CANCEL); 2054 } 2055 2056 static enum window_copy_cmd_action 2057 window_copy_cmd_goto_line(struct window_copy_cmd_state *cs) 2058 { 2059 struct window_mode_entry *wme = cs->wme; 2060 const char *arg1 = args_string(cs->args, 1); 2061 2062 if (*arg1 != '\0') 2063 window_copy_goto_line(wme, arg1); 2064 return (WINDOW_COPY_CMD_NOTHING); 2065 } 2066 2067 static enum window_copy_cmd_action 2068 window_copy_cmd_jump_backward(struct window_copy_cmd_state *cs) 2069 { 2070 struct window_mode_entry *wme = cs->wme; 2071 struct window_copy_mode_data *data = wme->data; 2072 u_int np = wme->prefix; 2073 const char *arg1 = args_string(cs->args, 1); 2074 2075 if (*arg1 != '\0') { 2076 data->jumptype = WINDOW_COPY_JUMPBACKWARD; 2077 free(data->jumpchar); 2078 data->jumpchar = utf8_fromcstr(arg1); 2079 for (; np != 0; np--) 2080 window_copy_cursor_jump_back(wme); 2081 } 2082 return (WINDOW_COPY_CMD_NOTHING); 2083 } 2084 2085 static enum window_copy_cmd_action 2086 window_copy_cmd_jump_forward(struct window_copy_cmd_state *cs) 2087 { 2088 struct window_mode_entry *wme = cs->wme; 2089 struct window_copy_mode_data *data = wme->data; 2090 u_int np = wme->prefix; 2091 const char *arg1 = args_string(cs->args, 1); 2092 2093 if (*arg1 != '\0') { 2094 data->jumptype = WINDOW_COPY_JUMPFORWARD; 2095 free(data->jumpchar); 2096 data->jumpchar = utf8_fromcstr(arg1); 2097 for (; np != 0; np--) 2098 window_copy_cursor_jump(wme); 2099 } 2100 return (WINDOW_COPY_CMD_NOTHING); 2101 } 2102 2103 static enum window_copy_cmd_action 2104 window_copy_cmd_jump_to_backward(struct window_copy_cmd_state *cs) 2105 { 2106 struct window_mode_entry *wme = cs->wme; 2107 struct window_copy_mode_data *data = wme->data; 2108 u_int np = wme->prefix; 2109 const char *arg1 = args_string(cs->args, 1); 2110 2111 if (*arg1 != '\0') { 2112 data->jumptype = WINDOW_COPY_JUMPTOBACKWARD; 2113 free(data->jumpchar); 2114 data->jumpchar = utf8_fromcstr(arg1); 2115 for (; np != 0; np--) 2116 window_copy_cursor_jump_to_back(wme); 2117 } 2118 return (WINDOW_COPY_CMD_NOTHING); 2119 } 2120 2121 static enum window_copy_cmd_action 2122 window_copy_cmd_jump_to_forward(struct window_copy_cmd_state *cs) 2123 { 2124 struct window_mode_entry *wme = cs->wme; 2125 struct window_copy_mode_data *data = wme->data; 2126 u_int np = wme->prefix; 2127 const char *arg1 = args_string(cs->args, 1); 2128 2129 if (*arg1 != '\0') { 2130 data->jumptype = WINDOW_COPY_JUMPTOFORWARD; 2131 free(data->jumpchar); 2132 data->jumpchar = utf8_fromcstr(arg1); 2133 for (; np != 0; np--) 2134 window_copy_cursor_jump_to(wme); 2135 } 2136 return (WINDOW_COPY_CMD_NOTHING); 2137 } 2138 2139 static enum window_copy_cmd_action 2140 window_copy_cmd_jump_to_mark(struct window_copy_cmd_state *cs) 2141 { 2142 struct window_mode_entry *wme = cs->wme; 2143 2144 window_copy_jump_to_mark(wme); 2145 return (WINDOW_COPY_CMD_NOTHING); 2146 } 2147 2148 static enum window_copy_cmd_action 2149 window_copy_cmd_search_backward(struct window_copy_cmd_state *cs) 2150 { 2151 struct window_mode_entry *wme = cs->wme; 2152 struct window_copy_mode_data *data = wme->data; 2153 u_int np = wme->prefix; 2154 2155 if (!window_copy_expand_search_string(cs)) 2156 return (WINDOW_COPY_CMD_NOTHING); 2157 2158 if (data->searchstr != NULL) { 2159 data->searchtype = WINDOW_COPY_SEARCHUP; 2160 data->searchregex = 1; 2161 data->timeout = 0; 2162 for (; np != 0; np--) 2163 window_copy_search_up(wme, 1); 2164 } 2165 return (WINDOW_COPY_CMD_NOTHING); 2166 } 2167 2168 static enum window_copy_cmd_action 2169 window_copy_cmd_search_backward_text(struct window_copy_cmd_state *cs) 2170 { 2171 struct window_mode_entry *wme = cs->wme; 2172 struct window_copy_mode_data *data = wme->data; 2173 u_int np = wme->prefix; 2174 2175 if (!window_copy_expand_search_string(cs)) 2176 return (WINDOW_COPY_CMD_NOTHING); 2177 2178 if (data->searchstr != NULL) { 2179 data->searchtype = WINDOW_COPY_SEARCHUP; 2180 data->searchregex = 0; 2181 data->timeout = 0; 2182 for (; np != 0; np--) 2183 window_copy_search_up(wme, 0); 2184 } 2185 return (WINDOW_COPY_CMD_NOTHING); 2186 } 2187 2188 static enum window_copy_cmd_action 2189 window_copy_cmd_search_forward(struct window_copy_cmd_state *cs) 2190 { 2191 struct window_mode_entry *wme = cs->wme; 2192 struct window_copy_mode_data *data = wme->data; 2193 u_int np = wme->prefix; 2194 2195 if (!window_copy_expand_search_string(cs)) 2196 return (WINDOW_COPY_CMD_NOTHING); 2197 2198 if (data->searchstr != NULL) { 2199 data->searchtype = WINDOW_COPY_SEARCHDOWN; 2200 data->searchregex = 1; 2201 data->timeout = 0; 2202 for (; np != 0; np--) 2203 window_copy_search_down(wme, 1); 2204 } 2205 return (WINDOW_COPY_CMD_NOTHING); 2206 } 2207 2208 static enum window_copy_cmd_action 2209 window_copy_cmd_search_forward_text(struct window_copy_cmd_state *cs) 2210 { 2211 struct window_mode_entry *wme = cs->wme; 2212 struct window_copy_mode_data *data = wme->data; 2213 u_int np = wme->prefix; 2214 2215 if (!window_copy_expand_search_string(cs)) 2216 return (WINDOW_COPY_CMD_NOTHING); 2217 2218 if (data->searchstr != NULL) { 2219 data->searchtype = WINDOW_COPY_SEARCHDOWN; 2220 data->searchregex = 0; 2221 data->timeout = 0; 2222 for (; np != 0; np--) 2223 window_copy_search_down(wme, 0); 2224 } 2225 return (WINDOW_COPY_CMD_NOTHING); 2226 } 2227 2228 static enum window_copy_cmd_action 2229 window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) 2230 { 2231 struct window_mode_entry *wme = cs->wme; 2232 struct window_copy_mode_data *data = wme->data; 2233 const char *arg1 = args_string(cs->args, 1); 2234 const char *ss = data->searchstr; 2235 char prefix; 2236 enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING; 2237 2238 data->timeout = 0; 2239 2240 log_debug("%s: %s", __func__, arg1); 2241 2242 prefix = *arg1++; 2243 if (data->searchx == -1 || data->searchy == -1) { 2244 data->searchx = data->cx; 2245 data->searchy = data->cy; 2246 data->searcho = data->oy; 2247 } else if (ss != NULL && strcmp(arg1, ss) != 0) { 2248 data->cx = data->searchx; 2249 data->cy = data->searchy; 2250 data->oy = data->searcho; 2251 action = WINDOW_COPY_CMD_REDRAW; 2252 } 2253 if (*arg1 == '\0') { 2254 window_copy_clear_marks(wme); 2255 return (WINDOW_COPY_CMD_REDRAW); 2256 } 2257 switch (prefix) { 2258 case '=': 2259 case '-': 2260 data->searchtype = WINDOW_COPY_SEARCHUP; 2261 data->searchregex = 0; 2262 free(data->searchstr); 2263 data->searchstr = xstrdup(arg1); 2264 if (!window_copy_search_up(wme, 0)) { 2265 window_copy_clear_marks(wme); 2266 return (WINDOW_COPY_CMD_REDRAW); 2267 } 2268 break; 2269 case '+': 2270 data->searchtype = WINDOW_COPY_SEARCHDOWN; 2271 data->searchregex = 0; 2272 free(data->searchstr); 2273 data->searchstr = xstrdup(arg1); 2274 if (!window_copy_search_down(wme, 0)) { 2275 window_copy_clear_marks(wme); 2276 return (WINDOW_COPY_CMD_REDRAW); 2277 } 2278 break; 2279 } 2280 return (action); 2281 } 2282 2283 static enum window_copy_cmd_action 2284 window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) 2285 { 2286 struct window_mode_entry *wme = cs->wme; 2287 struct window_copy_mode_data *data = wme->data; 2288 const char *arg1 = args_string(cs->args, 1); 2289 const char *ss = data->searchstr; 2290 char prefix; 2291 enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING; 2292 2293 data->timeout = 0; 2294 2295 log_debug("%s: %s", __func__, arg1); 2296 2297 prefix = *arg1++; 2298 if (data->searchx == -1 || data->searchy == -1) { 2299 data->searchx = data->cx; 2300 data->searchy = data->cy; 2301 data->searcho = data->oy; 2302 } else if (ss != NULL && strcmp(arg1, ss) != 0) { 2303 data->cx = data->searchx; 2304 data->cy = data->searchy; 2305 data->oy = data->searcho; 2306 action = WINDOW_COPY_CMD_REDRAW; 2307 } 2308 if (*arg1 == '\0') { 2309 window_copy_clear_marks(wme); 2310 return (WINDOW_COPY_CMD_REDRAW); 2311 } 2312 switch (prefix) { 2313 case '=': 2314 case '+': 2315 data->searchtype = WINDOW_COPY_SEARCHDOWN; 2316 data->searchregex = 0; 2317 free(data->searchstr); 2318 data->searchstr = xstrdup(arg1); 2319 if (!window_copy_search_down(wme, 0)) { 2320 window_copy_clear_marks(wme); 2321 return (WINDOW_COPY_CMD_REDRAW); 2322 } 2323 break; 2324 case '-': 2325 data->searchtype = WINDOW_COPY_SEARCHUP; 2326 data->searchregex = 0; 2327 free(data->searchstr); 2328 data->searchstr = xstrdup(arg1); 2329 if (!window_copy_search_up(wme, 0)) { 2330 window_copy_clear_marks(wme); 2331 return (WINDOW_COPY_CMD_REDRAW); 2332 } 2333 } 2334 return (action); 2335 } 2336 2337 static enum window_copy_cmd_action 2338 window_copy_cmd_refresh_from_pane(struct window_copy_cmd_state *cs) 2339 { 2340 struct window_mode_entry *wme = cs->wme; 2341 struct window_pane *wp = wme->swp; 2342 struct window_copy_mode_data *data = wme->data; 2343 2344 if (data->viewmode) 2345 return (WINDOW_COPY_CMD_NOTHING); 2346 2347 screen_free(data->backing); 2348 free(data->backing); 2349 data->backing = window_copy_clone_screen(&wp->base, &data->screen, NULL, NULL, wme->swp != wme->wp); 2350 2351 window_copy_size_changed(wme); 2352 return (WINDOW_COPY_CMD_REDRAW); 2353 } 2354 2355 static const struct { 2356 const char *command; 2357 u_int minargs; 2358 u_int maxargs; 2359 enum window_copy_cmd_clear clear; 2360 enum window_copy_cmd_action (*f)(struct window_copy_cmd_state *); 2361 } window_copy_cmd_table[] = { 2362 { .command = "append-selection", 2363 .minargs = 0, 2364 .maxargs = 0, 2365 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2366 .f = window_copy_cmd_append_selection 2367 }, 2368 { .command = "append-selection-and-cancel", 2369 .minargs = 0, 2370 .maxargs = 0, 2371 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2372 .f = window_copy_cmd_append_selection_and_cancel 2373 }, 2374 { .command = "back-to-indentation", 2375 .minargs = 0, 2376 .maxargs = 0, 2377 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2378 .f = window_copy_cmd_back_to_indentation 2379 }, 2380 { .command = "begin-selection", 2381 .minargs = 0, 2382 .maxargs = 0, 2383 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2384 .f = window_copy_cmd_begin_selection 2385 }, 2386 { .command = "bottom-line", 2387 .minargs = 0, 2388 .maxargs = 0, 2389 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2390 .f = window_copy_cmd_bottom_line 2391 }, 2392 { .command = "cancel", 2393 .minargs = 0, 2394 .maxargs = 0, 2395 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2396 .f = window_copy_cmd_cancel 2397 }, 2398 { .command = "clear-selection", 2399 .minargs = 0, 2400 .maxargs = 0, 2401 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2402 .f = window_copy_cmd_clear_selection 2403 }, 2404 { .command = "copy-end-of-line", 2405 .minargs = 0, 2406 .maxargs = 1, 2407 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2408 .f = window_copy_cmd_copy_end_of_line 2409 }, 2410 { .command = "copy-end-of-line-and-cancel", 2411 .minargs = 0, 2412 .maxargs = 1, 2413 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2414 .f = window_copy_cmd_copy_end_of_line_and_cancel 2415 }, 2416 { .command = "copy-pipe-end-of-line", 2417 .minargs = 0, 2418 .maxargs = 2, 2419 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2420 .f = window_copy_cmd_copy_pipe_end_of_line 2421 }, 2422 { .command = "copy-pipe-end-of-line-and-cancel", 2423 .minargs = 0, 2424 .maxargs = 2, 2425 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2426 .f = window_copy_cmd_copy_pipe_end_of_line_and_cancel 2427 }, 2428 { .command = "copy-line", 2429 .minargs = 0, 2430 .maxargs = 1, 2431 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2432 .f = window_copy_cmd_copy_line 2433 }, 2434 { .command = "copy-line-and-cancel", 2435 .minargs = 0, 2436 .maxargs = 1, 2437 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2438 .f = window_copy_cmd_copy_line_and_cancel 2439 }, 2440 { .command = "copy-pipe-line", 2441 .minargs = 0, 2442 .maxargs = 2, 2443 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2444 .f = window_copy_cmd_copy_pipe_line 2445 }, 2446 { .command = "copy-pipe-line-and-cancel", 2447 .minargs = 0, 2448 .maxargs = 2, 2449 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2450 .f = window_copy_cmd_copy_pipe_line_and_cancel 2451 }, 2452 { .command = "copy-pipe-no-clear", 2453 .minargs = 0, 2454 .maxargs = 2, 2455 .clear = WINDOW_COPY_CMD_CLEAR_NEVER, 2456 .f = window_copy_cmd_copy_pipe_no_clear 2457 }, 2458 { .command = "copy-pipe", 2459 .minargs = 0, 2460 .maxargs = 2, 2461 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2462 .f = window_copy_cmd_copy_pipe 2463 }, 2464 { .command = "copy-pipe-and-cancel", 2465 .minargs = 0, 2466 .maxargs = 2, 2467 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2468 .f = window_copy_cmd_copy_pipe_and_cancel 2469 }, 2470 { .command = "copy-selection-no-clear", 2471 .minargs = 0, 2472 .maxargs = 1, 2473 .clear = WINDOW_COPY_CMD_CLEAR_NEVER, 2474 .f = window_copy_cmd_copy_selection_no_clear 2475 }, 2476 { .command = "copy-selection", 2477 .minargs = 0, 2478 .maxargs = 1, 2479 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2480 .f = window_copy_cmd_copy_selection 2481 }, 2482 { .command = "copy-selection-and-cancel", 2483 .minargs = 0, 2484 .maxargs = 1, 2485 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2486 .f = window_copy_cmd_copy_selection_and_cancel 2487 }, 2488 { .command = "cursor-down", 2489 .minargs = 0, 2490 .maxargs = 0, 2491 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2492 .f = window_copy_cmd_cursor_down 2493 }, 2494 { .command = "cursor-down-and-cancel", 2495 .minargs = 0, 2496 .maxargs = 0, 2497 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2498 .f = window_copy_cmd_cursor_down_and_cancel 2499 }, 2500 { .command = "cursor-left", 2501 .minargs = 0, 2502 .maxargs = 0, 2503 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2504 .f = window_copy_cmd_cursor_left 2505 }, 2506 { .command = "cursor-right", 2507 .minargs = 0, 2508 .maxargs = 0, 2509 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2510 .f = window_copy_cmd_cursor_right 2511 }, 2512 { .command = "cursor-up", 2513 .minargs = 0, 2514 .maxargs = 0, 2515 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2516 .f = window_copy_cmd_cursor_up 2517 }, 2518 { .command = "end-of-line", 2519 .minargs = 0, 2520 .maxargs = 0, 2521 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2522 .f = window_copy_cmd_end_of_line 2523 }, 2524 { .command = "goto-line", 2525 .minargs = 1, 2526 .maxargs = 1, 2527 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2528 .f = window_copy_cmd_goto_line 2529 }, 2530 { .command = "halfpage-down", 2531 .minargs = 0, 2532 .maxargs = 0, 2533 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2534 .f = window_copy_cmd_halfpage_down 2535 }, 2536 { .command = "halfpage-down-and-cancel", 2537 .minargs = 0, 2538 .maxargs = 0, 2539 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2540 .f = window_copy_cmd_halfpage_down_and_cancel 2541 }, 2542 { .command = "halfpage-up", 2543 .minargs = 0, 2544 .maxargs = 0, 2545 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2546 .f = window_copy_cmd_halfpage_up 2547 }, 2548 { .command = "history-bottom", 2549 .minargs = 0, 2550 .maxargs = 0, 2551 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2552 .f = window_copy_cmd_history_bottom 2553 }, 2554 { .command = "history-top", 2555 .minargs = 0, 2556 .maxargs = 0, 2557 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2558 .f = window_copy_cmd_history_top 2559 }, 2560 { .command = "jump-again", 2561 .minargs = 0, 2562 .maxargs = 0, 2563 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2564 .f = window_copy_cmd_jump_again 2565 }, 2566 { .command = "jump-backward", 2567 .minargs = 1, 2568 .maxargs = 1, 2569 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2570 .f = window_copy_cmd_jump_backward 2571 }, 2572 { .command = "jump-forward", 2573 .minargs = 1, 2574 .maxargs = 1, 2575 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2576 .f = window_copy_cmd_jump_forward 2577 }, 2578 { .command = "jump-reverse", 2579 .minargs = 0, 2580 .maxargs = 0, 2581 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2582 .f = window_copy_cmd_jump_reverse 2583 }, 2584 { .command = "jump-to-backward", 2585 .minargs = 1, 2586 .maxargs = 1, 2587 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2588 .f = window_copy_cmd_jump_to_backward 2589 }, 2590 { .command = "jump-to-forward", 2591 .minargs = 1, 2592 .maxargs = 1, 2593 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2594 .f = window_copy_cmd_jump_to_forward 2595 }, 2596 { .command = "jump-to-mark", 2597 .minargs = 0, 2598 .maxargs = 0, 2599 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2600 .f = window_copy_cmd_jump_to_mark 2601 }, 2602 { .command = "middle-line", 2603 .minargs = 0, 2604 .maxargs = 0, 2605 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2606 .f = window_copy_cmd_middle_line 2607 }, 2608 { .command = "next-matching-bracket", 2609 .minargs = 0, 2610 .maxargs = 0, 2611 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2612 .f = window_copy_cmd_next_matching_bracket 2613 }, 2614 { .command = "next-paragraph", 2615 .minargs = 0, 2616 .maxargs = 0, 2617 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2618 .f = window_copy_cmd_next_paragraph 2619 }, 2620 { .command = "next-space", 2621 .minargs = 0, 2622 .maxargs = 0, 2623 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2624 .f = window_copy_cmd_next_space 2625 }, 2626 { .command = "next-space-end", 2627 .minargs = 0, 2628 .maxargs = 0, 2629 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2630 .f = window_copy_cmd_next_space_end 2631 }, 2632 { .command = "next-word", 2633 .minargs = 0, 2634 .maxargs = 0, 2635 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2636 .f = window_copy_cmd_next_word 2637 }, 2638 { .command = "next-word-end", 2639 .minargs = 0, 2640 .maxargs = 0, 2641 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2642 .f = window_copy_cmd_next_word_end 2643 }, 2644 { .command = "other-end", 2645 .minargs = 0, 2646 .maxargs = 0, 2647 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2648 .f = window_copy_cmd_other_end 2649 }, 2650 { .command = "page-down", 2651 .minargs = 0, 2652 .maxargs = 0, 2653 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2654 .f = window_copy_cmd_page_down 2655 }, 2656 { .command = "page-down-and-cancel", 2657 .minargs = 0, 2658 .maxargs = 0, 2659 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2660 .f = window_copy_cmd_page_down_and_cancel 2661 }, 2662 { .command = "page-up", 2663 .minargs = 0, 2664 .maxargs = 0, 2665 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2666 .f = window_copy_cmd_page_up 2667 }, 2668 { .command = "pipe-no-clear", 2669 .minargs = 0, 2670 .maxargs = 1, 2671 .clear = WINDOW_COPY_CMD_CLEAR_NEVER, 2672 .f = window_copy_cmd_pipe_no_clear 2673 }, 2674 { .command = "pipe", 2675 .minargs = 0, 2676 .maxargs = 1, 2677 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2678 .f = window_copy_cmd_pipe 2679 }, 2680 { .command = "pipe-and-cancel", 2681 .minargs = 0, 2682 .maxargs = 1, 2683 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2684 .f = window_copy_cmd_pipe_and_cancel 2685 }, 2686 { .command = "previous-matching-bracket", 2687 .minargs = 0, 2688 .maxargs = 0, 2689 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2690 .f = window_copy_cmd_previous_matching_bracket 2691 }, 2692 { .command = "previous-paragraph", 2693 .minargs = 0, 2694 .maxargs = 0, 2695 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2696 .f = window_copy_cmd_previous_paragraph 2697 }, 2698 { .command = "previous-space", 2699 .minargs = 0, 2700 .maxargs = 0, 2701 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2702 .f = window_copy_cmd_previous_space 2703 }, 2704 { .command = "previous-word", 2705 .minargs = 0, 2706 .maxargs = 0, 2707 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2708 .f = window_copy_cmd_previous_word 2709 }, 2710 { .command = "rectangle-on", 2711 .minargs = 0, 2712 .maxargs = 0, 2713 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2714 .f = window_copy_cmd_rectangle_on 2715 }, 2716 { .command = "rectangle-off", 2717 .minargs = 0, 2718 .maxargs = 0, 2719 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2720 .f = window_copy_cmd_rectangle_off 2721 }, 2722 { .command = "rectangle-toggle", 2723 .minargs = 0, 2724 .maxargs = 0, 2725 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2726 .f = window_copy_cmd_rectangle_toggle 2727 }, 2728 { .command = "refresh-from-pane", 2729 .minargs = 0, 2730 .maxargs = 0, 2731 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2732 .f = window_copy_cmd_refresh_from_pane 2733 }, 2734 { .command = "scroll-down", 2735 .minargs = 0, 2736 .maxargs = 0, 2737 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2738 .f = window_copy_cmd_scroll_down 2739 }, 2740 { .command = "scroll-down-and-cancel", 2741 .minargs = 0, 2742 .maxargs = 0, 2743 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2744 .f = window_copy_cmd_scroll_down_and_cancel 2745 }, 2746 { .command = "scroll-up", 2747 .minargs = 0, 2748 .maxargs = 0, 2749 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2750 .f = window_copy_cmd_scroll_up 2751 }, 2752 { .command = "search-again", 2753 .minargs = 0, 2754 .maxargs = 0, 2755 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2756 .f = window_copy_cmd_search_again 2757 }, 2758 { .command = "search-backward", 2759 .minargs = 0, 2760 .maxargs = 1, 2761 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2762 .f = window_copy_cmd_search_backward 2763 }, 2764 { .command = "search-backward-text", 2765 .minargs = 0, 2766 .maxargs = 1, 2767 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2768 .f = window_copy_cmd_search_backward_text 2769 }, 2770 { .command = "search-backward-incremental", 2771 .minargs = 1, 2772 .maxargs = 1, 2773 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2774 .f = window_copy_cmd_search_backward_incremental 2775 }, 2776 { .command = "search-forward", 2777 .minargs = 0, 2778 .maxargs = 1, 2779 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2780 .f = window_copy_cmd_search_forward 2781 }, 2782 { .command = "search-forward-text", 2783 .minargs = 0, 2784 .maxargs = 1, 2785 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2786 .f = window_copy_cmd_search_forward_text 2787 }, 2788 { .command = "search-forward-incremental", 2789 .minargs = 1, 2790 .maxargs = 1, 2791 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2792 .f = window_copy_cmd_search_forward_incremental 2793 }, 2794 { .command = "search-reverse", 2795 .minargs = 0, 2796 .maxargs = 0, 2797 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2798 .f = window_copy_cmd_search_reverse 2799 }, 2800 { .command = "select-line", 2801 .minargs = 0, 2802 .maxargs = 0, 2803 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2804 .f = window_copy_cmd_select_line 2805 }, 2806 { .command = "select-word", 2807 .minargs = 0, 2808 .maxargs = 0, 2809 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2810 .f = window_copy_cmd_select_word 2811 }, 2812 { .command = "set-mark", 2813 .minargs = 0, 2814 .maxargs = 0, 2815 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2816 .f = window_copy_cmd_set_mark 2817 }, 2818 { .command = "start-of-line", 2819 .minargs = 0, 2820 .maxargs = 0, 2821 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2822 .f = window_copy_cmd_start_of_line 2823 }, 2824 { .command = "stop-selection", 2825 .minargs = 0, 2826 .maxargs = 0, 2827 .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, 2828 .f = window_copy_cmd_stop_selection 2829 }, 2830 { .command = "toggle-position", 2831 .minargs = 0, 2832 .maxargs = 0, 2833 .clear = WINDOW_COPY_CMD_CLEAR_NEVER, 2834 .f = window_copy_cmd_toggle_position 2835 }, 2836 { .command = "top-line", 2837 .minargs = 0, 2838 .maxargs = 0, 2839 .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, 2840 .f = window_copy_cmd_top_line 2841 } 2842 }; 2843 2844 static void 2845 window_copy_command(struct window_mode_entry *wme, struct client *c, 2846 struct session *s, struct winlink *wl, struct args *args, 2847 struct mouse_event *m) 2848 { 2849 struct window_copy_mode_data *data = wme->data; 2850 struct window_copy_cmd_state cs; 2851 enum window_copy_cmd_action action; 2852 enum window_copy_cmd_clear clear = WINDOW_COPY_CMD_CLEAR_NEVER; 2853 const char *command; 2854 u_int i, count = args_count(args); 2855 int keys; 2856 2857 if (count == 0) 2858 return; 2859 command = args_string(args, 0); 2860 2861 if (m != NULL && m->valid && !MOUSE_WHEEL(m->b)) 2862 window_copy_move_mouse(m); 2863 2864 cs.wme = wme; 2865 cs.args = args; 2866 cs.m = m; 2867 2868 cs.c = c; 2869 cs.s = s; 2870 cs.wl = wl; 2871 2872 action = WINDOW_COPY_CMD_NOTHING; 2873 for (i = 0; i < nitems(window_copy_cmd_table); i++) { 2874 if (strcmp(window_copy_cmd_table[i].command, command) == 0) { 2875 if (count - 1 < window_copy_cmd_table[i].minargs || 2876 count - 1 > window_copy_cmd_table[i].maxargs) 2877 break; 2878 clear = window_copy_cmd_table[i].clear; 2879 action = window_copy_cmd_table[i].f(&cs); 2880 break; 2881 } 2882 } 2883 2884 if (strncmp(command, "search-", 7) != 0 && data->searchmark != NULL) { 2885 keys = options_get_number(wme->wp->window->options, "mode-keys"); 2886 if (clear == WINDOW_COPY_CMD_CLEAR_EMACS_ONLY && 2887 keys == MODEKEY_VI) 2888 clear = WINDOW_COPY_CMD_CLEAR_NEVER; 2889 if (clear != WINDOW_COPY_CMD_CLEAR_NEVER) { 2890 window_copy_clear_marks(wme); 2891 data->searchx = data->searchy = -1; 2892 } 2893 if (action == WINDOW_COPY_CMD_NOTHING) 2894 action = WINDOW_COPY_CMD_REDRAW; 2895 } 2896 wme->prefix = 1; 2897 2898 if (action == WINDOW_COPY_CMD_CANCEL) 2899 window_pane_reset_mode(wme->wp); 2900 else if (action == WINDOW_COPY_CMD_REDRAW) 2901 window_copy_redraw_screen(wme); 2902 } 2903 2904 static void 2905 window_copy_scroll_to(struct window_mode_entry *wme, u_int px, u_int py, 2906 int no_redraw) 2907 { 2908 struct window_copy_mode_data *data = wme->data; 2909 struct grid *gd = data->backing->grid; 2910 u_int offset, gap; 2911 2912 data->cx = px; 2913 2914 if (py >= gd->hsize - data->oy && py < gd->hsize - data->oy + gd->sy) 2915 data->cy = py - (gd->hsize - data->oy); 2916 else { 2917 gap = gd->sy / 4; 2918 if (py < gd->sy) { 2919 offset = 0; 2920 data->cy = py; 2921 } else if (py > gd->hsize + gd->sy - gap) { 2922 offset = gd->hsize; 2923 data->cy = py - gd->hsize; 2924 } else { 2925 offset = py + gap - gd->sy; 2926 data->cy = py - offset; 2927 } 2928 data->oy = gd->hsize - offset; 2929 } 2930 2931 if (!no_redraw && data->searchmark != NULL && !data->timeout) 2932 window_copy_search_marks(wme, NULL, data->searchregex, 1); 2933 window_copy_update_selection(wme, 1, 0); 2934 if (!no_redraw) 2935 window_copy_redraw_screen(wme); 2936 } 2937 2938 static int 2939 window_copy_search_compare(struct grid *gd, u_int px, u_int py, 2940 struct grid *sgd, u_int spx, int cis) 2941 { 2942 struct grid_cell gc, sgc; 2943 const struct utf8_data *ud, *sud; 2944 2945 grid_get_cell(gd, px, py, &gc); 2946 ud = &gc.data; 2947 grid_get_cell(sgd, spx, 0, &sgc); 2948 sud = &sgc.data; 2949 2950 if (ud->size != sud->size || ud->width != sud->width) 2951 return (0); 2952 2953 if (cis && ud->size == 1) 2954 return (tolower(ud->data[0]) == sud->data[0]); 2955 2956 return (memcmp(ud->data, sud->data, ud->size) == 0); 2957 } 2958 2959 static int 2960 window_copy_search_lr(struct grid *gd, struct grid *sgd, u_int *ppx, u_int py, 2961 u_int first, u_int last, int cis) 2962 { 2963 u_int ax, bx, px, pywrap, endline; 2964 int matched; 2965 struct grid_line *gl; 2966 2967 endline = gd->hsize + gd->sy - 1; 2968 for (ax = first; ax < last; ax++) { 2969 for (bx = 0; bx < sgd->sx; bx++) { 2970 px = ax + bx; 2971 pywrap = py; 2972 /* Wrap line. */ 2973 while (px >= gd->sx && pywrap < endline) { 2974 gl = grid_get_line(gd, pywrap); 2975 if (~gl->flags & GRID_LINE_WRAPPED) 2976 break; 2977 px -= gd->sx; 2978 pywrap++; 2979 } 2980 /* We have run off the end of the grid. */ 2981 if (px >= gd->sx) 2982 break; 2983 matched = window_copy_search_compare(gd, px, pywrap, 2984 sgd, bx, cis); 2985 if (!matched) 2986 break; 2987 } 2988 if (bx == sgd->sx) { 2989 *ppx = ax; 2990 return (1); 2991 } 2992 } 2993 return (0); 2994 } 2995 2996 static int 2997 window_copy_search_rl(struct grid *gd, 2998 struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis) 2999 { 3000 u_int ax, bx, px, pywrap, endline; 3001 int matched; 3002 struct grid_line *gl; 3003 3004 endline = gd->hsize + gd->sy - 1; 3005 for (ax = last; ax > first; ax--) { 3006 for (bx = 0; bx < sgd->sx; bx++) { 3007 px = ax - 1 + bx; 3008 pywrap = py; 3009 /* Wrap line. */ 3010 while (px >= gd->sx && pywrap < endline) { 3011 gl = grid_get_line(gd, pywrap); 3012 if (~gl->flags & GRID_LINE_WRAPPED) 3013 break; 3014 px -= gd->sx; 3015 pywrap++; 3016 } 3017 /* We have run off the end of the grid. */ 3018 if (px >= gd->sx) 3019 break; 3020 matched = window_copy_search_compare(gd, px, pywrap, 3021 sgd, bx, cis); 3022 if (!matched) 3023 break; 3024 } 3025 if (bx == sgd->sx) { 3026 *ppx = ax - 1; 3027 return (1); 3028 } 3029 } 3030 return (0); 3031 } 3032 3033 static int 3034 window_copy_search_lr_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py, 3035 u_int first, u_int last, regex_t *reg) 3036 { 3037 int eflags = 0; 3038 u_int endline, foundx, foundy, len, pywrap, size = 1; 3039 char *buf; 3040 regmatch_t regmatch; 3041 struct grid_line *gl; 3042 3043 /* 3044 * This can happen during search if the last match was the last 3045 * character on a line. 3046 */ 3047 if (first >= last) 3048 return (0); 3049 3050 /* Set flags for regex search. */ 3051 if (first != 0) 3052 eflags |= REG_NOTBOL; 3053 3054 /* Need to look at the entire string. */ 3055 buf = xmalloc(size); 3056 buf[0] = '\0'; 3057 buf = window_copy_stringify(gd, py, first, gd->sx, buf, &size); 3058 len = gd->sx - first; 3059 endline = gd->hsize + gd->sy - 1; 3060 pywrap = py; 3061 while (buf != NULL && pywrap <= endline) { 3062 gl = grid_get_line(gd, pywrap); 3063 if (~gl->flags & GRID_LINE_WRAPPED) 3064 break; 3065 pywrap++; 3066 buf = window_copy_stringify(gd, pywrap, 0, gd->sx, buf, &size); 3067 len += gd->sx; 3068 } 3069 3070 if (regexec(reg, buf, 1, ®match, eflags) == 0 && 3071 regmatch.rm_so != regmatch.rm_eo) { 3072 foundx = first; 3073 foundy = py; 3074 window_copy_cstrtocellpos(gd, len, &foundx, &foundy, 3075 buf + regmatch.rm_so); 3076 if (foundy == py && foundx < last) { 3077 *ppx = foundx; 3078 len -= foundx - first; 3079 window_copy_cstrtocellpos(gd, len, &foundx, &foundy, 3080 buf + regmatch.rm_eo); 3081 *psx = foundx; 3082 while (foundy > py) { 3083 *psx += gd->sx; 3084 foundy--; 3085 } 3086 *psx -= *ppx; 3087 free(buf); 3088 return (1); 3089 } 3090 } 3091 3092 free(buf); 3093 *ppx = 0; 3094 *psx = 0; 3095 return (0); 3096 } 3097 3098 static int 3099 window_copy_search_rl_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py, 3100 u_int first, u_int last, regex_t *reg) 3101 { 3102 int eflags = 0; 3103 u_int endline, len, pywrap, size = 1; 3104 char *buf; 3105 struct grid_line *gl; 3106 3107 /* Set flags for regex search. */ 3108 if (first != 0) 3109 eflags |= REG_NOTBOL; 3110 3111 /* Need to look at the entire string. */ 3112 buf = xmalloc(size); 3113 buf[0] = '\0'; 3114 buf = window_copy_stringify(gd, py, first, gd->sx, buf, &size); 3115 len = gd->sx - first; 3116 endline = gd->hsize + gd->sy - 1; 3117 pywrap = py; 3118 while (buf != NULL && (pywrap <= endline)) { 3119 gl = grid_get_line(gd, pywrap); 3120 if (~gl->flags & GRID_LINE_WRAPPED) 3121 break; 3122 pywrap++; 3123 buf = window_copy_stringify(gd, pywrap, 0, gd->sx, buf, &size); 3124 len += gd->sx; 3125 } 3126 3127 if (window_copy_last_regex(gd, py, first, last, len, ppx, psx, buf, 3128 reg, eflags)) 3129 { 3130 free(buf); 3131 return (1); 3132 } 3133 3134 free(buf); 3135 *ppx = 0; 3136 *psx = 0; 3137 return (0); 3138 } 3139 3140 static const char * 3141 window_copy_cellstring(const struct grid_line *gl, u_int px, size_t *size, 3142 int *allocated) 3143 { 3144 static struct utf8_data ud; 3145 struct grid_cell_entry *gce; 3146 char *copy; 3147 3148 if (px >= gl->cellsize) { 3149 *size = 1; 3150 *allocated = 0; 3151 return (" "); 3152 } 3153 3154 gce = &gl->celldata[px]; 3155 if (gce->flags & GRID_FLAG_PADDING) { 3156 *size = 0; 3157 *allocated = 0; 3158 return (NULL); 3159 } 3160 if (~gce->flags & GRID_FLAG_EXTENDED) { 3161 *size = 1; 3162 *allocated = 0; 3163 return (&gce->data.data); 3164 } 3165 3166 utf8_to_data(gl->extddata[gce->offset].data, &ud); 3167 if (ud.size == 0) { 3168 *size = 0; 3169 *allocated = 0; 3170 return (NULL); 3171 } 3172 *size = ud.size; 3173 *allocated = 1; 3174 3175 copy = xmalloc(ud.size); 3176 memcpy(copy, ud.data, ud.size); 3177 return (copy); 3178 } 3179 3180 /* Find last match in given range. */ 3181 static int 3182 window_copy_last_regex(struct grid *gd, u_int py, u_int first, u_int last, 3183 u_int len, u_int *ppx, u_int *psx, const char *buf, const regex_t *preg, 3184 int eflags) 3185 { 3186 u_int foundx, foundy, oldx, px = 0, savepx, savesx = 0; 3187 regmatch_t regmatch; 3188 3189 foundx = first; 3190 foundy = py; 3191 oldx = first; 3192 while (regexec(preg, buf + px, 1, ®match, eflags) == 0) { 3193 if (regmatch.rm_so == regmatch.rm_eo) 3194 break; 3195 window_copy_cstrtocellpos(gd, len, &foundx, &foundy, 3196 buf + px + regmatch.rm_so); 3197 if (foundy > py || foundx >= last) 3198 break; 3199 len -= foundx - oldx; 3200 savepx = foundx; 3201 window_copy_cstrtocellpos(gd, len, &foundx, &foundy, 3202 buf + px + regmatch.rm_eo); 3203 if (foundy > py || foundx >= last) { 3204 *ppx = savepx; 3205 *psx = foundx; 3206 while (foundy > py) { 3207 *psx += gd->sx; 3208 foundy--; 3209 } 3210 *psx -= *ppx; 3211 return (1); 3212 } else { 3213 savesx = foundx - savepx; 3214 len -= savesx; 3215 oldx = foundx; 3216 } 3217 px += regmatch.rm_eo; 3218 } 3219 3220 if (savesx > 0) { 3221 *ppx = savepx; 3222 *psx = savesx; 3223 return (1); 3224 } else { 3225 *ppx = 0; 3226 *psx = 0; 3227 return (0); 3228 } 3229 } 3230 3231 /* Stringify line and append to input buffer. Caller frees. */ 3232 static char * 3233 window_copy_stringify(struct grid *gd, u_int py, u_int first, u_int last, 3234 char *buf, u_int *size) 3235 { 3236 u_int ax, bx, newsize = *size; 3237 const struct grid_line *gl; 3238 const char *d; 3239 size_t bufsize = 1024, dlen; 3240 int allocated; 3241 3242 while (bufsize < newsize) 3243 bufsize *= 2; 3244 buf = xrealloc(buf, bufsize); 3245 3246 gl = grid_peek_line(gd, py); 3247 bx = *size - 1; 3248 for (ax = first; ax < last; ax++) { 3249 d = window_copy_cellstring(gl, ax, &dlen, &allocated); 3250 newsize += dlen; 3251 while (bufsize < newsize) { 3252 bufsize *= 2; 3253 buf = xrealloc(buf, bufsize); 3254 } 3255 if (dlen == 1) 3256 buf[bx++] = *d; 3257 else { 3258 memcpy(buf + bx, d, dlen); 3259 bx += dlen; 3260 } 3261 if (allocated) 3262 free((void *)d); 3263 } 3264 buf[newsize - 1] = '\0'; 3265 3266 *size = newsize; 3267 return (buf); 3268 } 3269 3270 /* Map start of C string containing UTF-8 data to grid cell position. */ 3271 static void 3272 window_copy_cstrtocellpos(struct grid *gd, u_int ncells, u_int *ppx, u_int *ppy, 3273 const char *str) 3274 { 3275 u_int cell, ccell, px, pywrap, pos, len; 3276 int match; 3277 const struct grid_line *gl; 3278 const char *d; 3279 size_t dlen; 3280 struct { 3281 const char *d; 3282 size_t dlen; 3283 int allocated; 3284 } *cells; 3285 3286 /* Populate the array of cell data. */ 3287 cells = xreallocarray(NULL, ncells, sizeof cells[0]); 3288 cell = 0; 3289 px = *ppx; 3290 pywrap = *ppy; 3291 gl = grid_peek_line(gd, pywrap); 3292 while (cell < ncells) { 3293 cells[cell].d = window_copy_cellstring(gl, px, 3294 &cells[cell].dlen, &cells[cell].allocated); 3295 cell++; 3296 px++; 3297 if (px == gd->sx) { 3298 px = 0; 3299 pywrap++; 3300 gl = grid_peek_line(gd, pywrap); 3301 } 3302 } 3303 3304 /* Locate starting cell. */ 3305 cell = 0; 3306 len = strlen(str); 3307 while (cell < ncells) { 3308 ccell = cell; 3309 pos = 0; 3310 match = 1; 3311 while (ccell < ncells) { 3312 if (str[pos] == '\0') { 3313 match = 0; 3314 break; 3315 } 3316 d = cells[ccell].d; 3317 dlen = cells[ccell].dlen; 3318 if (dlen == 1) { 3319 if (str[pos] != *d) { 3320 match = 0; 3321 break; 3322 } 3323 pos++; 3324 } else { 3325 if (dlen > len - pos) 3326 dlen = len - pos; 3327 if (memcmp(str + pos, d, dlen) != 0) { 3328 match = 0; 3329 break; 3330 } 3331 pos += dlen; 3332 } 3333 ccell++; 3334 } 3335 if (match) 3336 break; 3337 cell++; 3338 } 3339 3340 /* If not found this will be one past the end. */ 3341 px = *ppx + cell; 3342 pywrap = *ppy; 3343 while (px >= gd->sx) { 3344 px -= gd->sx; 3345 pywrap++; 3346 } 3347 3348 *ppx = px; 3349 *ppy = pywrap; 3350 3351 /* Free cell data. */ 3352 for (cell = 0; cell < ncells; cell++) { 3353 if (cells[cell].allocated) 3354 free((void *)cells[cell].d); 3355 } 3356 free(cells); 3357 } 3358 3359 static void 3360 window_copy_move_left(struct screen *s, u_int *fx, u_int *fy, int wrapflag) 3361 { 3362 if (*fx == 0) { /* left */ 3363 if (*fy == 0) { /* top */ 3364 if (wrapflag) { 3365 *fx = screen_size_x(s) - 1; 3366 *fy = screen_hsize(s) + screen_size_y(s) - 1; 3367 } 3368 return; 3369 } 3370 *fx = screen_size_x(s) - 1; 3371 *fy = *fy - 1; 3372 } else 3373 *fx = *fx - 1; 3374 } 3375 3376 static void 3377 window_copy_move_right(struct screen *s, u_int *fx, u_int *fy, int wrapflag) 3378 { 3379 if (*fx == screen_size_x(s) - 1) { /* right */ 3380 if (*fy == screen_hsize(s) + screen_size_y(s) - 1) { /* bottom */ 3381 if (wrapflag) { 3382 *fx = 0; 3383 *fy = 0; 3384 } 3385 return; 3386 } 3387 *fx = 0; 3388 *fy = *fy + 1; 3389 } else 3390 *fx = *fx + 1; 3391 } 3392 3393 static int 3394 window_copy_is_lowercase(const char *ptr) 3395 { 3396 while (*ptr != '\0') { 3397 if (*ptr != tolower((u_char)*ptr)) 3398 return (0); 3399 ++ptr; 3400 } 3401 return (1); 3402 } 3403 3404 /* 3405 * Handle backward wrapped regex searches with overlapping matches. In this case 3406 * find the longest overlapping match from previous wrapped lines. 3407 */ 3408 static void 3409 window_copy_search_back_overlap(struct grid *gd, regex_t *preg, u_int *ppx, 3410 u_int *psx, u_int *ppy, u_int endline) 3411 { 3412 u_int endx, endy, oldendx, oldendy, px, py, sx; 3413 int found = 1; 3414 3415 oldendx = *ppx + *psx; 3416 oldendy = *ppy - 1; 3417 while (oldendx > gd->sx - 1) { 3418 oldendx -= gd->sx; 3419 oldendy++; 3420 } 3421 endx = oldendx; 3422 endy = oldendy; 3423 px = *ppx; 3424 py = *ppy; 3425 while (found && px == 0 && py - 1 > endline && 3426 grid_get_line(gd, py - 2)->flags & GRID_LINE_WRAPPED && 3427 endx == oldendx && endy == oldendy) { 3428 py--; 3429 found = window_copy_search_rl_regex(gd, &px, &sx, py - 1, 0, 3430 gd->sx, preg); 3431 if (found) { 3432 endx = px + sx; 3433 endy = py - 1; 3434 while (endx > gd->sx - 1) { 3435 endx -= gd->sx; 3436 endy++; 3437 } 3438 if (endx == oldendx && endy == oldendy) { 3439 *ppx = px; 3440 *ppy = py; 3441 } 3442 } 3443 } 3444 } 3445 3446 /* 3447 * Search for text stored in sgd starting from position fx,fy up to endline. If 3448 * found, jump to it. If cis then ignore case. The direction is 0 for searching 3449 * up, down otherwise. If wrap then go to begin/end of grid and try again if 3450 * not found. 3451 */ 3452 static int 3453 window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, 3454 struct grid *sgd, u_int fx, u_int fy, u_int endline, int cis, int wrap, 3455 int direction, int regex) 3456 { 3457 u_int i, px, sx, ssize = 1; 3458 int found = 0, cflags = REG_EXTENDED; 3459 char *sbuf; 3460 regex_t reg; 3461 3462 if (regex) { 3463 sbuf = xmalloc(ssize); 3464 sbuf[0] = '\0'; 3465 sbuf = window_copy_stringify(sgd, 0, 0, sgd->sx, sbuf, &ssize); 3466 if (cis) 3467 cflags |= REG_ICASE; 3468 if (regcomp(®, sbuf, cflags) != 0) { 3469 free(sbuf); 3470 return (0); 3471 } 3472 free(sbuf); 3473 } 3474 3475 if (direction) { 3476 for (i = fy; i <= endline; i++) { 3477 if (regex) { 3478 found = window_copy_search_lr_regex(gd, 3479 &px, &sx, i, fx, gd->sx, ®); 3480 } else { 3481 found = window_copy_search_lr(gd, sgd, 3482 &px, i, fx, gd->sx, cis); 3483 } 3484 if (found) 3485 break; 3486 fx = 0; 3487 } 3488 } else { 3489 for (i = fy + 1; endline < i; i--) { 3490 if (regex) { 3491 found = window_copy_search_rl_regex(gd, 3492 &px, &sx, i - 1, 0, fx + 1, ®); 3493 if (found) { 3494 window_copy_search_back_overlap(gd, 3495 ®, &px, &sx, &i, endline); 3496 } 3497 } else { 3498 found = window_copy_search_rl(gd, sgd, 3499 &px, i - 1, 0, fx + 1, cis); 3500 } 3501 if (found) { 3502 i--; 3503 break; 3504 } 3505 fx = gd->sx - 1; 3506 } 3507 } 3508 if (regex) 3509 regfree(®); 3510 3511 if (found) { 3512 window_copy_scroll_to(wme, px, i, 1); 3513 return (1); 3514 } 3515 if (wrap) { 3516 return (window_copy_search_jump(wme, gd, sgd, 3517 direction ? 0 : gd->sx - 1, 3518 direction ? 0 : gd->hsize + gd->sy - 1, fy, cis, 0, 3519 direction, regex)); 3520 } 3521 return (0); 3522 } 3523 3524 static void 3525 window_copy_move_after_search_mark(struct window_copy_mode_data *data, 3526 u_int *fx, u_int *fy, int wrapflag) 3527 { 3528 struct screen *s = data->backing; 3529 u_int at, start; 3530 3531 if (window_copy_search_mark_at(data, *fx, *fy, &start) == 0 && 3532 data->searchmark[start] != 0) { 3533 while (window_copy_search_mark_at(data, *fx, *fy, &at) == 0) { 3534 if (data->searchmark[at] != data->searchmark[start]) 3535 break; 3536 /* Stop if not wrapping and at the end of the grid. */ 3537 if (!wrapflag && 3538 *fx == screen_size_x(s) - 1 && 3539 *fy == screen_hsize(s) + screen_size_y(s) - 1) 3540 break; 3541 3542 window_copy_move_right(s, fx, fy, wrapflag); 3543 } 3544 } 3545 } 3546 3547 /* 3548 * Search in for text searchstr. If direction is 0 then search up, otherwise 3549 * down. 3550 */ 3551 static int 3552 window_copy_search(struct window_mode_entry *wme, int direction, int regex) 3553 { 3554 struct window_pane *wp = wme->wp; 3555 struct window_copy_mode_data *data = wme->data; 3556 struct screen *s = data->backing, ss; 3557 struct screen_write_ctx ctx; 3558 struct grid *gd = s->grid; 3559 const char *str = data->searchstr; 3560 u_int at, endline, fx, fy, start; 3561 int cis, found, keys, visible_only; 3562 int wrapflag; 3563 3564 if (regex && str[strcspn(str, "^$*+()?[].\\")] == '\0') 3565 regex = 0; 3566 3567 data->searchdirection = direction; 3568 3569 if (data->timeout) 3570 return (0); 3571 3572 if (data->searchall || wp->searchstr == NULL || 3573 wp->searchregex != regex) { 3574 visible_only = 0; 3575 data->searchall = 0; 3576 } else 3577 visible_only = (strcmp(wp->searchstr, str) == 0); 3578 free(wp->searchstr); 3579 wp->searchstr = xstrdup(str); 3580 wp->searchregex = regex; 3581 3582 fx = data->cx; 3583 fy = screen_hsize(data->backing) - data->oy + data->cy; 3584 3585 screen_init(&ss, screen_write_strlen("%s", str), 1, 0); 3586 screen_write_start(&ctx, &ss); 3587 screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", str); 3588 screen_write_stop(&ctx); 3589 3590 wrapflag = options_get_number(wp->window->options, "wrap-search"); 3591 cis = window_copy_is_lowercase(str); 3592 3593 keys = options_get_number(wp->window->options, "mode-keys"); 3594 3595 if (direction) { 3596 /* 3597 * Behave according to mode-keys. If it is emacs, search forward 3598 * leaves the cursor after the match. If it is vi, the cursor 3599 * remains at the beginning of the match, regardless of 3600 * direction, which means that we need to start the next search 3601 * after the term the cursor is currently on when searching 3602 * forward. 3603 */ 3604 if (keys == MODEKEY_VI) { 3605 if (data->searchmark != NULL) 3606 window_copy_move_after_search_mark(data, &fx, 3607 &fy, wrapflag); 3608 else { 3609 /* 3610 * When there are no search marks, start the 3611 * search after the current cursor position. 3612 */ 3613 window_copy_move_right(s, &fx, &fy, wrapflag); 3614 } 3615 } 3616 endline = gd->hsize + gd->sy - 1; 3617 } 3618 else { 3619 window_copy_move_left(s, &fx, &fy, wrapflag); 3620 endline = 0; 3621 } 3622 3623 found = window_copy_search_jump(wme, gd, ss.grid, fx, fy, endline, cis, 3624 wrapflag, direction, regex); 3625 if (found) { 3626 window_copy_search_marks(wme, &ss, regex, visible_only); 3627 fx = data->cx; 3628 fy = screen_hsize(data->backing) - data->oy + data->cy; 3629 3630 /* 3631 * When searching forward, if the cursor is not at the beginning 3632 * of the mark, search again. 3633 */ 3634 if (direction && 3635 window_copy_search_mark_at(data, fx, fy, &at) == 0 && 3636 at > 0 && 3637 data->searchmark[at] == data->searchmark[at - 1]) { 3638 window_copy_move_after_search_mark(data, &fx, &fy, 3639 wrapflag); 3640 window_copy_search_jump(wme, gd, ss.grid, fx, 3641 fy, endline, cis, wrapflag, direction, 3642 regex); 3643 fx = data->cx; 3644 fy = screen_hsize(data->backing) - data->oy + data->cy; 3645 } 3646 3647 if (direction) { 3648 /* 3649 * When in Emacs mode, position the cursor just after 3650 * the mark. 3651 */ 3652 if (keys == MODEKEY_EMACS) { 3653 window_copy_move_after_search_mark(data, &fx, 3654 &fy, wrapflag); 3655 data->cx = fx; 3656 data->cy = fy - screen_hsize(data->backing) + 3657 data-> oy; 3658 } 3659 } 3660 else { 3661 /* 3662 * When searching backward, position the cursor at the 3663 * beginning of the mark. 3664 */ 3665 if (window_copy_search_mark_at(data, fx, fy, 3666 &start) == 0) { 3667 while (window_copy_search_mark_at(data, fx, fy, 3668 &at) == 0 && 3669 data->searchmark[at] == 3670 data->searchmark[start]) { 3671 data->cx = fx; 3672 data->cy = fy - 3673 screen_hsize(data->backing) + 3674 data-> oy; 3675 if (at == 0) 3676 break; 3677 3678 window_copy_move_left(s, &fx, &fy, 0); 3679 } 3680 } 3681 } 3682 } 3683 window_copy_redraw_screen(wme); 3684 3685 screen_free(&ss); 3686 return (found); 3687 } 3688 3689 static void 3690 window_copy_visible_lines(struct window_copy_mode_data *data, u_int *start, 3691 u_int *end) 3692 { 3693 struct grid *gd = data->backing->grid; 3694 const struct grid_line *gl; 3695 3696 for (*start = gd->hsize - data->oy; *start > 0; (*start)--) { 3697 gl = grid_peek_line(gd, (*start) - 1); 3698 if (~gl->flags & GRID_LINE_WRAPPED) 3699 break; 3700 } 3701 *end = gd->hsize - data->oy + gd->sy; 3702 } 3703 3704 static int 3705 window_copy_search_mark_at(struct window_copy_mode_data *data, u_int px, 3706 u_int py, u_int *at) 3707 { 3708 struct screen *s = data->backing; 3709 struct grid *gd = s->grid; 3710 3711 if (py < gd->hsize - data->oy) 3712 return (-1); 3713 if (py > gd->hsize - data->oy + gd->sy - 1) 3714 return (-1); 3715 *at = ((py - (gd->hsize - data->oy)) * gd->sx) + px; 3716 return (0); 3717 } 3718 3719 static int 3720 window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, 3721 int regex, int visible_only) 3722 { 3723 struct window_copy_mode_data *data = wme->data; 3724 struct screen *s = data->backing, ss; 3725 struct screen_write_ctx ctx; 3726 struct grid *gd = s->grid; 3727 int found, cis, stopped = 0; 3728 int cflags = REG_EXTENDED; 3729 u_int px, py, i, b, nfound = 0, width; 3730 u_int ssize = 1, start, end; 3731 char *sbuf; 3732 regex_t reg; 3733 uint64_t stop = 0, tstart, t; 3734 3735 if (ssp == NULL) { 3736 width = screen_write_strlen("%s", data->searchstr); 3737 screen_init(&ss, width, 1, 0); 3738 screen_write_start(&ctx, &ss); 3739 screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", 3740 data->searchstr); 3741 screen_write_stop(&ctx); 3742 ssp = &ss; 3743 } else 3744 width = screen_size_x(ssp); 3745 3746 cis = window_copy_is_lowercase(data->searchstr); 3747 3748 if (regex) { 3749 sbuf = xmalloc(ssize); 3750 sbuf[0] = '\0'; 3751 sbuf = window_copy_stringify(ssp->grid, 0, 0, ssp->grid->sx, 3752 sbuf, &ssize); 3753 if (cis) 3754 cflags |= REG_ICASE; 3755 if (regcomp(®, sbuf, cflags) != 0) { 3756 free(sbuf); 3757 return (0); 3758 } 3759 free(sbuf); 3760 } 3761 tstart = get_timer(); 3762 3763 if (visible_only) 3764 window_copy_visible_lines(data, &start, &end); 3765 else { 3766 start = 0; 3767 end = gd->hsize + gd->sy; 3768 stop = get_timer() + WINDOW_COPY_SEARCH_ALL_TIMEOUT; 3769 } 3770 3771 again: 3772 free(data->searchmark); 3773 data->searchmark = xcalloc(gd->sx, gd->sy); 3774 data->searchgen = 1; 3775 3776 for (py = start; py < end; py++) { 3777 px = 0; 3778 for (;;) { 3779 if (regex) { 3780 found = window_copy_search_lr_regex(gd, 3781 &px, &width, py, px, gd->sx, ®); 3782 if (!found) 3783 break; 3784 } else { 3785 found = window_copy_search_lr(gd, ssp->grid, 3786 &px, py, px, gd->sx, cis); 3787 if (!found) 3788 break; 3789 } 3790 nfound++; 3791 3792 if (window_copy_search_mark_at(data, px, py, &b) == 0) { 3793 if (b + width > gd->sx * gd->sy) 3794 width = (gd->sx * gd->sy) - b; 3795 for (i = b; i < b + width; i++) { 3796 if (data->searchmark[i] != 0) 3797 continue; 3798 data->searchmark[i] = data->searchgen; 3799 } 3800 if (data->searchgen == UCHAR_MAX) 3801 data->searchgen = 1; 3802 else 3803 data->searchgen++; 3804 } 3805 px += width; 3806 } 3807 3808 t = get_timer(); 3809 if (t - tstart > WINDOW_COPY_SEARCH_TIMEOUT) { 3810 data->timeout = 1; 3811 break; 3812 } 3813 if (stop != 0 && t > stop) { 3814 stopped = 1; 3815 break; 3816 } 3817 } 3818 if (data->timeout) { 3819 window_copy_clear_marks(wme); 3820 goto out; 3821 } 3822 3823 if (stopped && stop != 0) { 3824 /* Try again but just the visible context. */ 3825 window_copy_visible_lines(data, &start, &end); 3826 stop = 0; 3827 goto again; 3828 } 3829 3830 if (!visible_only) { 3831 if (stopped) { 3832 if (nfound > 1000) 3833 data->searchcount = 1000; 3834 else if (nfound > 100) 3835 data->searchcount = 100; 3836 else if (nfound > 10) 3837 data->searchcount = 10; 3838 else 3839 data->searchcount = -1; 3840 data->searchmore = 1; 3841 } else { 3842 data->searchcount = nfound; 3843 data->searchmore = 0; 3844 } 3845 } 3846 3847 out: 3848 if (ssp == &ss) 3849 screen_free(&ss); 3850 if (regex) 3851 regfree(®); 3852 return (1); 3853 } 3854 3855 static void 3856 window_copy_clear_marks(struct window_mode_entry *wme) 3857 { 3858 struct window_copy_mode_data *data = wme->data; 3859 3860 free(data->searchmark); 3861 data->searchmark = NULL; 3862 } 3863 3864 static int 3865 window_copy_search_up(struct window_mode_entry *wme, int regex) 3866 { 3867 return (window_copy_search(wme, 0, regex)); 3868 } 3869 3870 static int 3871 window_copy_search_down(struct window_mode_entry *wme, int regex) 3872 { 3873 return (window_copy_search(wme, 1, regex)); 3874 } 3875 3876 static void 3877 window_copy_goto_line(struct window_mode_entry *wme, const char *linestr) 3878 { 3879 struct window_copy_mode_data *data = wme->data; 3880 const char *errstr; 3881 int lineno; 3882 3883 lineno = strtonum(linestr, -1, INT_MAX, &errstr); 3884 if (errstr != NULL) 3885 return; 3886 if (lineno < 0 || (u_int)lineno > screen_hsize(data->backing)) 3887 lineno = screen_hsize(data->backing); 3888 3889 data->oy = lineno; 3890 window_copy_update_selection(wme, 1, 0); 3891 window_copy_redraw_screen(wme); 3892 } 3893 3894 static void 3895 window_copy_match_start_end(struct window_copy_mode_data *data, u_int at, 3896 u_int *start, u_int *end) 3897 { 3898 struct grid *gd = data->backing->grid; 3899 u_int last = (gd->sy * gd->sx) - 1; 3900 u_char mark = data->searchmark[at]; 3901 3902 *start = *end = at; 3903 while (*start != 0 && data->searchmark[*start] == mark) 3904 (*start)--; 3905 if (data->searchmark[*start] != mark) 3906 (*start)++; 3907 while (*end != last && data->searchmark[*end] == mark) 3908 (*end)++; 3909 if (data->searchmark[*end] != mark) 3910 (*end)--; 3911 } 3912 3913 static char * 3914 window_copy_match_at_cursor(struct window_copy_mode_data *data) 3915 { 3916 struct grid *gd = data->backing->grid; 3917 struct grid_cell gc; 3918 u_int at, start, end, cy, px, py; 3919 u_int sx = screen_size_x(data->backing); 3920 char *buf = NULL; 3921 size_t len = 0; 3922 3923 if (data->searchmark == NULL) 3924 return (NULL); 3925 3926 cy = screen_hsize(data->backing) - data->oy + data->cy; 3927 if (window_copy_search_mark_at(data, data->cx, cy, &at) != 0) 3928 return (NULL); 3929 if (data->searchmark[at] == 0) { 3930 /* Allow one position after the match. */ 3931 if (at == 0 || data->searchmark[--at] == 0) 3932 return (NULL); 3933 } 3934 window_copy_match_start_end(data, at, &start, &end); 3935 3936 /* 3937 * Cells will not be set in the marked array unless they are valid text 3938 * and wrapping will be taken care of, so we can just copy. 3939 */ 3940 for (at = start; at <= end; at++) { 3941 py = at / sx; 3942 px = at - (py * sx); 3943 3944 grid_get_cell(gd, px, gd->hsize + py - data->oy, &gc); 3945 buf = xrealloc(buf, len + gc.data.size + 1); 3946 memcpy(buf + len, gc.data.data, gc.data.size); 3947 len += gc.data.size; 3948 } 3949 if (len != 0) 3950 buf[len] = '\0'; 3951 return (buf); 3952 } 3953 3954 static void 3955 window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy, 3956 struct grid_cell *gc, const struct grid_cell *mgc, 3957 const struct grid_cell *cgc, const struct grid_cell *mkgc) 3958 { 3959 struct window_pane *wp = wme->wp; 3960 struct window_copy_mode_data *data = wme->data; 3961 u_int mark, start, end, cy, cursor, current; 3962 int inv = 0, found = 0; 3963 int keys; 3964 3965 if (data->showmark && fy == data->my) { 3966 gc->attr = mkgc->attr; 3967 if (fx == data->mx) 3968 inv = 1; 3969 if (inv) { 3970 gc->fg = mkgc->bg; 3971 gc->bg = mkgc->fg; 3972 } 3973 else { 3974 gc->fg = mkgc->fg; 3975 gc->bg = mkgc->bg; 3976 } 3977 } 3978 3979 if (data->searchmark == NULL) 3980 return; 3981 3982 if (window_copy_search_mark_at(data, fx, fy, ¤t) != 0) 3983 return; 3984 mark = data->searchmark[current]; 3985 if (mark == 0) 3986 return; 3987 3988 cy = screen_hsize(data->backing) - data->oy + data->cy; 3989 if (window_copy_search_mark_at(data, data->cx, cy, &cursor) == 0) { 3990 keys = options_get_number(wp->window->options, "mode-keys"); 3991 if (cursor != 0 && 3992 keys == MODEKEY_EMACS && 3993 data->searchdirection) { 3994 if (data->searchmark[cursor - 1] == mark) { 3995 cursor--; 3996 found = 1; 3997 } 3998 } else if (data->searchmark[cursor] == mark) 3999 found = 1; 4000 if (found) { 4001 window_copy_match_start_end(data, cursor, &start, &end); 4002 if (current >= start && current <= end) { 4003 gc->attr = cgc->attr; 4004 if (inv) { 4005 gc->fg = cgc->bg; 4006 gc->bg = cgc->fg; 4007 } 4008 else { 4009 gc->fg = cgc->fg; 4010 gc->bg = cgc->bg; 4011 } 4012 return; 4013 } 4014 } 4015 } 4016 4017 gc->attr = mgc->attr; 4018 if (inv) { 4019 gc->fg = mgc->bg; 4020 gc->bg = mgc->fg; 4021 } 4022 else { 4023 gc->fg = mgc->fg; 4024 gc->bg = mgc->bg; 4025 } 4026 } 4027 4028 static void 4029 window_copy_write_one(struct window_mode_entry *wme, 4030 struct screen_write_ctx *ctx, u_int py, u_int fy, u_int nx, 4031 const struct grid_cell *mgc, const struct grid_cell *cgc, 4032 const struct grid_cell *mkgc) 4033 { 4034 struct window_copy_mode_data *data = wme->data; 4035 struct grid *gd = data->backing->grid; 4036 struct grid_cell gc; 4037 u_int fx; 4038 4039 screen_write_cursormove(ctx, 0, py, 0); 4040 for (fx = 0; fx < nx; fx++) { 4041 grid_get_cell(gd, fx, fy, &gc); 4042 if (fx + gc.data.width <= nx) { 4043 window_copy_update_style(wme, fx, fy, &gc, mgc, cgc, 4044 mkgc); 4045 screen_write_cell(ctx, &gc); 4046 } 4047 } 4048 } 4049 4050 static void 4051 window_copy_write_line(struct window_mode_entry *wme, 4052 struct screen_write_ctx *ctx, u_int py) 4053 { 4054 struct window_pane *wp = wme->wp; 4055 struct window_copy_mode_data *data = wme->data; 4056 struct screen *s = &data->screen; 4057 struct options *oo = wp->window->options; 4058 struct grid_cell gc, mgc, cgc, mkgc; 4059 char hdr[512]; 4060 size_t size = 0; 4061 u_int hsize = screen_hsize(data->backing); 4062 4063 style_apply(&gc, oo, "mode-style", NULL); 4064 gc.flags |= GRID_FLAG_NOPALETTE; 4065 style_apply(&mgc, oo, "copy-mode-match-style", NULL); 4066 mgc.flags |= GRID_FLAG_NOPALETTE; 4067 style_apply(&cgc, oo, "copy-mode-current-match-style", NULL); 4068 cgc.flags |= GRID_FLAG_NOPALETTE; 4069 style_apply(&mkgc, oo, "copy-mode-mark-style", NULL); 4070 mkgc.flags |= GRID_FLAG_NOPALETTE; 4071 4072 if (py == 0 && s->rupper < s->rlower && !data->hide_position) { 4073 if (data->searchmark == NULL) { 4074 if (data->timeout) { 4075 size = xsnprintf(hdr, sizeof hdr, 4076 "(timed out) [%u/%u]", data->oy, hsize); 4077 } else { 4078 size = xsnprintf(hdr, sizeof hdr, 4079 "[%u/%u]", data->oy, hsize); 4080 } 4081 } else { 4082 if (data->searchcount == -1) { 4083 size = xsnprintf(hdr, sizeof hdr, 4084 "[%u/%u]", data->oy, hsize); 4085 } else { 4086 size = xsnprintf(hdr, sizeof hdr, 4087 "(%d%s results) [%u/%u]", data->searchcount, 4088 data->searchmore ? "+" : "", data->oy, 4089 hsize); 4090 } 4091 } 4092 if (size > screen_size_x(s)) 4093 size = screen_size_x(s); 4094 screen_write_cursormove(ctx, screen_size_x(s) - size, 0, 0); 4095 screen_write_puts(ctx, &gc, "%s", hdr); 4096 } else 4097 size = 0; 4098 4099 if (size < screen_size_x(s)) { 4100 window_copy_write_one(wme, ctx, py, hsize - data->oy + py, 4101 screen_size_x(s) - size, &mgc, &cgc, &mkgc); 4102 } 4103 4104 if (py == data->cy && data->cx == screen_size_x(s)) { 4105 screen_write_cursormove(ctx, screen_size_x(s) - 1, py, 0); 4106 screen_write_putc(ctx, &grid_default_cell, '$'); 4107 } 4108 } 4109 4110 static void 4111 window_copy_write_lines(struct window_mode_entry *wme, 4112 struct screen_write_ctx *ctx, u_int py, u_int ny) 4113 { 4114 u_int yy; 4115 4116 for (yy = py; yy < py + ny; yy++) 4117 window_copy_write_line(wme, ctx, py); 4118 } 4119 4120 static void 4121 window_copy_redraw_selection(struct window_mode_entry *wme, u_int old_y) 4122 { 4123 struct window_copy_mode_data *data = wme->data; 4124 struct grid *gd = data->backing->grid; 4125 u_int new_y, start, end; 4126 4127 new_y = data->cy; 4128 if (old_y <= new_y) { 4129 start = old_y; 4130 end = new_y; 4131 } else { 4132 start = new_y; 4133 end = old_y; 4134 } 4135 4136 /* 4137 * In word selection mode the first word on the line below the cursor 4138 * might be selected, so add this line to the redraw area. 4139 */ 4140 if (data->selflag == SEL_WORD) { 4141 /* Last grid line in data coordinates. */ 4142 if (end < gd->sy + data->oy - 1) 4143 end++; 4144 } 4145 window_copy_redraw_lines(wme, start, end - start + 1); 4146 } 4147 4148 static void 4149 window_copy_redraw_lines(struct window_mode_entry *wme, u_int py, u_int ny) 4150 { 4151 struct window_pane *wp = wme->wp; 4152 struct window_copy_mode_data *data = wme->data; 4153 struct screen_write_ctx ctx; 4154 u_int i; 4155 4156 screen_write_start_pane(&ctx, wp, NULL); 4157 for (i = py; i < py + ny; i++) 4158 window_copy_write_line(wme, &ctx, i); 4159 screen_write_cursormove(&ctx, data->cx, data->cy, 0); 4160 screen_write_stop(&ctx); 4161 } 4162 4163 static void 4164 window_copy_redraw_screen(struct window_mode_entry *wme) 4165 { 4166 struct window_copy_mode_data *data = wme->data; 4167 4168 window_copy_redraw_lines(wme, 0, screen_size_y(&data->screen)); 4169 } 4170 4171 static void 4172 window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin, 4173 int no_reset) 4174 { 4175 struct window_copy_mode_data *data = wme->data; 4176 u_int xx, yy; 4177 4178 xx = data->cx; 4179 yy = screen_hsize(data->backing) + data->cy - data->oy; 4180 switch (data->selflag) { 4181 case SEL_WORD: 4182 if (no_reset) 4183 break; 4184 begin = 0; 4185 if (data->dy > yy || (data->dy == yy && data->dx > xx)) { 4186 /* Right to left selection. */ 4187 window_copy_cursor_previous_word_pos(wme, 4188 data->separators, &xx, &yy); 4189 begin = 1; 4190 4191 /* Reset the end. */ 4192 data->endselx = data->endselrx; 4193 data->endsely = data->endselry; 4194 } else { 4195 /* Left to right selection. */ 4196 if (xx >= window_copy_find_length(wme, yy) || 4197 !window_copy_in_set(wme, xx + 1, yy, WHITESPACE)) { 4198 window_copy_cursor_next_word_end_pos(wme, 4199 data->separators, &xx, &yy); 4200 } 4201 4202 /* Reset the start. */ 4203 data->selx = data->selrx; 4204 data->sely = data->selry; 4205 } 4206 break; 4207 case SEL_LINE: 4208 if (no_reset) 4209 break; 4210 begin = 0; 4211 if (data->dy > yy) { 4212 /* Right to left selection. */ 4213 xx = 0; 4214 begin = 1; 4215 4216 /* Reset the end. */ 4217 data->endselx = data->endselrx; 4218 data->endsely = data->endselry; 4219 } else { 4220 /* Left to right selection. */ 4221 if (yy < data->endselry) 4222 yy = data->endselry; 4223 xx = window_copy_find_length(wme, yy); 4224 4225 /* Reset the start. */ 4226 data->selx = data->selrx; 4227 data->sely = data->selry; 4228 } 4229 break; 4230 case SEL_CHAR: 4231 break; 4232 } 4233 if (begin) { 4234 data->selx = xx; 4235 data->sely = yy; 4236 } else { 4237 data->endselx = xx; 4238 data->endsely = yy; 4239 } 4240 } 4241 4242 static void 4243 window_copy_synchronize_cursor(struct window_mode_entry *wme, int no_reset) 4244 { 4245 struct window_copy_mode_data *data = wme->data; 4246 4247 switch (data->cursordrag) { 4248 case CURSORDRAG_ENDSEL: 4249 window_copy_synchronize_cursor_end(wme, 0, no_reset); 4250 break; 4251 case CURSORDRAG_SEL: 4252 window_copy_synchronize_cursor_end(wme, 1, no_reset); 4253 break; 4254 case CURSORDRAG_NONE: 4255 break; 4256 } 4257 } 4258 4259 static void 4260 window_copy_update_cursor(struct window_mode_entry *wme, u_int cx, u_int cy) 4261 { 4262 struct window_pane *wp = wme->wp; 4263 struct window_copy_mode_data *data = wme->data; 4264 struct screen *s = &data->screen; 4265 struct screen_write_ctx ctx; 4266 u_int old_cx, old_cy; 4267 4268 old_cx = data->cx; old_cy = data->cy; 4269 data->cx = cx; data->cy = cy; 4270 if (old_cx == screen_size_x(s)) 4271 window_copy_redraw_lines(wme, old_cy, 1); 4272 if (data->cx == screen_size_x(s)) 4273 window_copy_redraw_lines(wme, data->cy, 1); 4274 else { 4275 screen_write_start_pane(&ctx, wp, NULL); 4276 screen_write_cursormove(&ctx, data->cx, data->cy, 0); 4277 screen_write_stop(&ctx); 4278 } 4279 } 4280 4281 static void 4282 window_copy_start_selection(struct window_mode_entry *wme) 4283 { 4284 struct window_copy_mode_data *data = wme->data; 4285 4286 data->selx = data->cx; 4287 data->sely = screen_hsize(data->backing) + data->cy - data->oy; 4288 4289 data->endselx = data->selx; 4290 data->endsely = data->sely; 4291 4292 data->cursordrag = CURSORDRAG_ENDSEL; 4293 4294 window_copy_set_selection(wme, 1, 0); 4295 } 4296 4297 static int 4298 window_copy_adjust_selection(struct window_mode_entry *wme, u_int *selx, 4299 u_int *sely) 4300 { 4301 struct window_copy_mode_data *data = wme->data; 4302 struct screen *s = &data->screen; 4303 u_int sx, sy, ty; 4304 int relpos; 4305 4306 sx = *selx; 4307 sy = *sely; 4308 4309 ty = screen_hsize(data->backing) - data->oy; 4310 if (sy < ty) { 4311 relpos = WINDOW_COPY_REL_POS_ABOVE; 4312 if (!data->rectflag) 4313 sx = 0; 4314 sy = 0; 4315 } else if (sy > ty + screen_size_y(s) - 1) { 4316 relpos = WINDOW_COPY_REL_POS_BELOW; 4317 if (!data->rectflag) 4318 sx = screen_size_x(s) - 1; 4319 sy = screen_size_y(s) - 1; 4320 } else { 4321 relpos = WINDOW_COPY_REL_POS_ON_SCREEN; 4322 sy -= ty; 4323 } 4324 4325 *selx = sx; 4326 *sely = sy; 4327 return (relpos); 4328 } 4329 4330 static int 4331 window_copy_update_selection(struct window_mode_entry *wme, int may_redraw, 4332 int no_reset) 4333 { 4334 struct window_copy_mode_data *data = wme->data; 4335 struct screen *s = &data->screen; 4336 4337 if (s->sel == NULL && data->lineflag == LINE_SEL_NONE) 4338 return (0); 4339 return (window_copy_set_selection(wme, may_redraw, no_reset)); 4340 } 4341 4342 static int 4343 window_copy_set_selection(struct window_mode_entry *wme, int may_redraw, 4344 int no_reset) 4345 { 4346 struct window_pane *wp = wme->wp; 4347 struct window_copy_mode_data *data = wme->data; 4348 struct screen *s = &data->screen; 4349 struct options *oo = wp->window->options; 4350 struct grid_cell gc; 4351 u_int sx, sy, cy, endsx, endsy; 4352 int startrelpos, endrelpos; 4353 4354 window_copy_synchronize_cursor(wme, no_reset); 4355 4356 /* Adjust the selection. */ 4357 sx = data->selx; 4358 sy = data->sely; 4359 startrelpos = window_copy_adjust_selection(wme, &sx, &sy); 4360 4361 /* Adjust the end of selection. */ 4362 endsx = data->endselx; 4363 endsy = data->endsely; 4364 endrelpos = window_copy_adjust_selection(wme, &endsx, &endsy); 4365 4366 /* Selection is outside of the current screen */ 4367 if (startrelpos == endrelpos && 4368 startrelpos != WINDOW_COPY_REL_POS_ON_SCREEN) { 4369 screen_hide_selection(s); 4370 return (0); 4371 } 4372 4373 /* Set colours and selection. */ 4374 style_apply(&gc, oo, "mode-style", NULL); 4375 gc.flags |= GRID_FLAG_NOPALETTE; 4376 screen_set_selection(s, sx, sy, endsx, endsy, data->rectflag, 4377 data->modekeys, &gc); 4378 4379 if (data->rectflag && may_redraw) { 4380 /* 4381 * Can't rely on the caller to redraw the right lines for 4382 * rectangle selection - find the highest line and the number 4383 * of lines, and redraw just past that in both directions 4384 */ 4385 cy = data->cy; 4386 if (data->cursordrag == CURSORDRAG_ENDSEL) { 4387 if (sy < cy) 4388 window_copy_redraw_lines(wme, sy, cy - sy + 1); 4389 else 4390 window_copy_redraw_lines(wme, cy, sy - cy + 1); 4391 } else { 4392 if (endsy < cy) { 4393 window_copy_redraw_lines(wme, endsy, 4394 cy - endsy + 1); 4395 } else { 4396 window_copy_redraw_lines(wme, cy, 4397 endsy - cy + 1); 4398 } 4399 } 4400 } 4401 4402 return (1); 4403 } 4404 4405 static void * 4406 window_copy_get_selection(struct window_mode_entry *wme, size_t *len) 4407 { 4408 struct window_pane *wp = wme->wp; 4409 struct window_copy_mode_data *data = wme->data; 4410 struct screen *s = &data->screen; 4411 char *buf; 4412 size_t off; 4413 u_int i, xx, yy, sx, sy, ex, ey, ey_last; 4414 u_int firstsx, lastex, restex, restsx, selx; 4415 int keys; 4416 4417 if (data->screen.sel == NULL && data->lineflag == LINE_SEL_NONE) { 4418 buf = window_copy_match_at_cursor(data); 4419 if (buf != NULL) 4420 *len = strlen(buf); 4421 else 4422 *len = 0; 4423 return (buf); 4424 } 4425 4426 buf = xmalloc(1); 4427 off = 0; 4428 4429 *buf = '\0'; 4430 4431 /* 4432 * The selection extends from selx,sely to (adjusted) cx,cy on 4433 * the base screen. 4434 */ 4435 4436 /* Find start and end. */ 4437 xx = data->endselx; 4438 yy = data->endsely; 4439 if (yy < data->sely || (yy == data->sely && xx < data->selx)) { 4440 sx = xx; sy = yy; 4441 ex = data->selx; ey = data->sely; 4442 } else { 4443 sx = data->selx; sy = data->sely; 4444 ex = xx; ey = yy; 4445 } 4446 4447 /* Trim ex to end of line. */ 4448 ey_last = window_copy_find_length(wme, ey); 4449 if (ex > ey_last) 4450 ex = ey_last; 4451 4452 /* 4453 * Deal with rectangle-copy if necessary; four situations: start of 4454 * first line (firstsx), end of last line (lastex), start (restsx) and 4455 * end (restex) of all other lines. 4456 */ 4457 xx = screen_size_x(s); 4458 4459 /* 4460 * Behave according to mode-keys. If it is emacs, copy like emacs, 4461 * keeping the top-left-most character, and dropping the 4462 * bottom-right-most, regardless of copy direction. If it is vi, also 4463 * keep bottom-right-most character. 4464 */ 4465 keys = options_get_number(wp->window->options, "mode-keys"); 4466 if (data->rectflag) { 4467 /* 4468 * Need to ignore the column with the cursor in it, which for 4469 * rectangular copy means knowing which side the cursor is on. 4470 */ 4471 if (data->cursordrag == CURSORDRAG_ENDSEL) 4472 selx = data->selx; 4473 else 4474 selx = data->endselx; 4475 if (selx < data->cx) { 4476 /* Selection start is on the left. */ 4477 if (keys == MODEKEY_EMACS) { 4478 lastex = data->cx; 4479 restex = data->cx; 4480 } 4481 else { 4482 lastex = data->cx + 1; 4483 restex = data->cx + 1; 4484 } 4485 firstsx = selx; 4486 restsx = selx; 4487 } else { 4488 /* Cursor is on the left. */ 4489 lastex = selx + 1; 4490 restex = selx + 1; 4491 firstsx = data->cx; 4492 restsx = data->cx; 4493 } 4494 } else { 4495 if (keys == MODEKEY_EMACS) 4496 lastex = ex; 4497 else 4498 lastex = ex + 1; 4499 restex = xx; 4500 firstsx = sx; 4501 restsx = 0; 4502 } 4503 4504 /* Copy the lines. */ 4505 for (i = sy; i <= ey; i++) { 4506 window_copy_copy_line(wme, &buf, &off, i, 4507 (i == sy ? firstsx : restsx), 4508 (i == ey ? lastex : restex)); 4509 } 4510 4511 /* Don't bother if no data. */ 4512 if (off == 0) { 4513 free(buf); 4514 *len = 0; 4515 return (NULL); 4516 } 4517 /* Remove final \n (unless at end in vi mode). */ 4518 if (keys == MODEKEY_EMACS || lastex <= ey_last) { 4519 if (~grid_get_line(data->backing->grid, ey)->flags & 4520 GRID_LINE_WRAPPED || lastex != ey_last) 4521 off -= 1; 4522 } 4523 *len = off; 4524 return (buf); 4525 } 4526 4527 static void 4528 window_copy_copy_buffer(struct window_mode_entry *wme, const char *prefix, 4529 void *buf, size_t len) 4530 { 4531 struct window_pane *wp = wme->wp; 4532 struct screen_write_ctx ctx; 4533 4534 if (options_get_number(global_options, "set-clipboard") != 0) { 4535 screen_write_start_pane(&ctx, wp, NULL); 4536 screen_write_setselection(&ctx, buf, len); 4537 screen_write_stop(&ctx); 4538 notify_pane("pane-set-clipboard", wp); 4539 } 4540 4541 paste_add(prefix, buf, len); 4542 } 4543 4544 static void * 4545 window_copy_pipe_run(struct window_mode_entry *wme, struct session *s, 4546 const char *cmd, size_t *len) 4547 { 4548 void *buf; 4549 struct job *job; 4550 4551 buf = window_copy_get_selection(wme, len); 4552 if (cmd == NULL || *cmd == '\0') 4553 cmd = options_get_string(global_options, "copy-command"); 4554 if (cmd != NULL && *cmd != '\0') { 4555 job = job_run(cmd, 0, NULL, NULL, s, NULL, NULL, NULL, NULL, 4556 NULL, JOB_NOWAIT, -1, -1); 4557 bufferevent_write(job_get_event(job), buf, *len); 4558 } 4559 return (buf); 4560 } 4561 4562 static void 4563 window_copy_pipe(struct window_mode_entry *wme, struct session *s, 4564 const char *cmd) 4565 { 4566 size_t len; 4567 4568 window_copy_pipe_run(wme, s, cmd, &len); 4569 } 4570 4571 static void 4572 window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s, 4573 const char *prefix, const char *cmd) 4574 { 4575 void *buf; 4576 size_t len; 4577 4578 buf = window_copy_pipe_run(wme, s, cmd, &len); 4579 if (buf != NULL) 4580 window_copy_copy_buffer(wme, prefix, buf, len); 4581 } 4582 4583 static void 4584 window_copy_copy_selection(struct window_mode_entry *wme, const char *prefix) 4585 { 4586 char *buf; 4587 size_t len; 4588 4589 buf = window_copy_get_selection(wme, &len); 4590 if (buf != NULL) 4591 window_copy_copy_buffer(wme, prefix, buf, len); 4592 } 4593 4594 static void 4595 window_copy_append_selection(struct window_mode_entry *wme) 4596 { 4597 struct window_pane *wp = wme->wp; 4598 char *buf; 4599 struct paste_buffer *pb; 4600 const char *bufdata, *bufname = NULL; 4601 size_t len, bufsize; 4602 struct screen_write_ctx ctx; 4603 4604 buf = window_copy_get_selection(wme, &len); 4605 if (buf == NULL) 4606 return; 4607 4608 if (options_get_number(global_options, "set-clipboard") != 0) { 4609 screen_write_start_pane(&ctx, wp, NULL); 4610 screen_write_setselection(&ctx, buf, len); 4611 screen_write_stop(&ctx); 4612 notify_pane("pane-set-clipboard", wp); 4613 } 4614 4615 pb = paste_get_top(&bufname); 4616 if (pb != NULL) { 4617 bufdata = paste_buffer_data(pb, &bufsize); 4618 buf = xrealloc(buf, len + bufsize); 4619 memmove(buf + bufsize, buf, len); 4620 memcpy(buf, bufdata, bufsize); 4621 len += bufsize; 4622 } 4623 if (paste_set(buf, len, bufname, NULL) != 0) 4624 free(buf); 4625 } 4626 4627 static void 4628 window_copy_copy_line(struct window_mode_entry *wme, char **buf, size_t *off, 4629 u_int sy, u_int sx, u_int ex) 4630 { 4631 struct window_copy_mode_data *data = wme->data; 4632 struct grid *gd = data->backing->grid; 4633 struct grid_cell gc; 4634 struct grid_line *gl; 4635 struct utf8_data ud; 4636 u_int i, xx, wrapped = 0; 4637 const char *s; 4638 4639 if (sx > ex) 4640 return; 4641 4642 /* 4643 * Work out if the line was wrapped at the screen edge and all of it is 4644 * on screen. 4645 */ 4646 gl = grid_get_line(gd, sy); 4647 if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx) 4648 wrapped = 1; 4649 4650 /* If the line was wrapped, don't strip spaces (use the full length). */ 4651 if (wrapped) 4652 xx = gl->cellsize; 4653 else 4654 xx = window_copy_find_length(wme, sy); 4655 if (ex > xx) 4656 ex = xx; 4657 if (sx > xx) 4658 sx = xx; 4659 4660 if (sx < ex) { 4661 for (i = sx; i < ex; i++) { 4662 grid_get_cell(gd, i, sy, &gc); 4663 if (gc.flags & GRID_FLAG_PADDING) 4664 continue; 4665 utf8_copy(&ud, &gc.data); 4666 if (ud.size == 1 && (gc.attr & GRID_ATTR_CHARSET)) { 4667 s = tty_acs_get(NULL, ud.data[0]); 4668 if (s != NULL && strlen(s) <= sizeof ud.data) { 4669 ud.size = strlen(s); 4670 memcpy(ud.data, s, ud.size); 4671 } 4672 } 4673 4674 *buf = xrealloc(*buf, (*off) + ud.size); 4675 memcpy(*buf + *off, ud.data, ud.size); 4676 *off += ud.size; 4677 } 4678 } 4679 4680 /* Only add a newline if the line wasn't wrapped. */ 4681 if (!wrapped || ex != xx) { 4682 *buf = xrealloc(*buf, (*off) + 1); 4683 (*buf)[(*off)++] = '\n'; 4684 } 4685 } 4686 4687 static void 4688 window_copy_clear_selection(struct window_mode_entry *wme) 4689 { 4690 struct window_copy_mode_data *data = wme->data; 4691 u_int px, py; 4692 4693 screen_clear_selection(&data->screen); 4694 4695 data->cursordrag = CURSORDRAG_NONE; 4696 data->lineflag = LINE_SEL_NONE; 4697 data->selflag = SEL_CHAR; 4698 4699 py = screen_hsize(data->backing) + data->cy - data->oy; 4700 px = window_copy_find_length(wme, py); 4701 if (data->cx > px) 4702 window_copy_update_cursor(wme, px, data->cy); 4703 } 4704 4705 static int 4706 window_copy_in_set(struct window_mode_entry *wme, u_int px, u_int py, 4707 const char *set) 4708 { 4709 struct window_copy_mode_data *data = wme->data; 4710 struct grid_cell gc; 4711 4712 grid_get_cell(data->backing->grid, px, py, &gc); 4713 if (gc.flags & GRID_FLAG_PADDING) 4714 return (0); 4715 return (utf8_cstrhas(set, &gc.data)); 4716 } 4717 4718 static u_int 4719 window_copy_find_length(struct window_mode_entry *wme, u_int py) 4720 { 4721 struct window_copy_mode_data *data = wme->data; 4722 4723 return (grid_line_length(data->backing->grid, py)); 4724 } 4725 4726 static void 4727 window_copy_cursor_start_of_line(struct window_mode_entry *wme) 4728 { 4729 struct window_copy_mode_data *data = wme->data; 4730 struct screen *back_s = data->backing; 4731 struct grid_reader gr; 4732 u_int px, py, oldy, hsize; 4733 4734 px = data->cx; 4735 hsize = screen_hsize(back_s); 4736 py = hsize + data->cy - data->oy; 4737 oldy = data->cy; 4738 4739 grid_reader_start(&gr, back_s->grid, px, py); 4740 grid_reader_cursor_start_of_line(&gr, 1); 4741 grid_reader_get_cursor(&gr, &px, &py); 4742 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py); 4743 } 4744 4745 static void 4746 window_copy_cursor_back_to_indentation(struct window_mode_entry *wme) 4747 { 4748 struct window_copy_mode_data *data = wme->data; 4749 struct screen *back_s = data->backing; 4750 struct grid_reader gr; 4751 u_int px, py, oldy, hsize; 4752 4753 px = data->cx; 4754 hsize = screen_hsize(back_s); 4755 py = hsize + data->cy - data->oy; 4756 oldy = data->cy; 4757 4758 grid_reader_start(&gr, back_s->grid, px, py); 4759 grid_reader_cursor_back_to_indentation(&gr); 4760 grid_reader_get_cursor(&gr, &px, &py); 4761 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py); 4762 } 4763 4764 static void 4765 window_copy_cursor_end_of_line(struct window_mode_entry *wme) 4766 { 4767 struct window_copy_mode_data *data = wme->data; 4768 struct screen *back_s = data->backing; 4769 struct grid_reader gr; 4770 u_int px, py, oldy, hsize; 4771 4772 px = data->cx; 4773 hsize = screen_hsize(back_s); 4774 py = hsize + data->cy - data->oy; 4775 oldy = data->cy; 4776 4777 grid_reader_start(&gr, back_s->grid, px, py); 4778 if (data->screen.sel != NULL && data->rectflag) 4779 grid_reader_cursor_end_of_line(&gr, 1, 1); 4780 else 4781 grid_reader_cursor_end_of_line(&gr, 1, 0); 4782 grid_reader_get_cursor(&gr, &px, &py); 4783 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s), 4784 data->oy, oldy, px, py, 0); 4785 } 4786 4787 static void 4788 window_copy_other_end(struct window_mode_entry *wme) 4789 { 4790 struct window_copy_mode_data *data = wme->data; 4791 struct screen *s = &data->screen; 4792 u_int selx, sely, cy, yy, hsize; 4793 4794 if (s->sel == NULL && data->lineflag == LINE_SEL_NONE) 4795 return; 4796 4797 if (data->lineflag == LINE_SEL_LEFT_RIGHT) 4798 data->lineflag = LINE_SEL_RIGHT_LEFT; 4799 else if (data->lineflag == LINE_SEL_RIGHT_LEFT) 4800 data->lineflag = LINE_SEL_LEFT_RIGHT; 4801 4802 switch (data->cursordrag) { 4803 case CURSORDRAG_NONE: 4804 case CURSORDRAG_SEL: 4805 data->cursordrag = CURSORDRAG_ENDSEL; 4806 break; 4807 case CURSORDRAG_ENDSEL: 4808 data->cursordrag = CURSORDRAG_SEL; 4809 break; 4810 } 4811 4812 selx = data->endselx; 4813 sely = data->endsely; 4814 if (data->cursordrag == CURSORDRAG_SEL) { 4815 selx = data->selx; 4816 sely = data->sely; 4817 } 4818 4819 cy = data->cy; 4820 yy = screen_hsize(data->backing) + data->cy - data->oy; 4821 4822 data->cx = selx; 4823 4824 hsize = screen_hsize(data->backing); 4825 if (sely < hsize - data->oy) { /* above */ 4826 data->oy = hsize - sely; 4827 data->cy = 0; 4828 } else if (sely > hsize - data->oy + screen_size_y(s)) { /* below */ 4829 data->oy = hsize - sely + screen_size_y(s) - 1; 4830 data->cy = screen_size_y(s) - 1; 4831 } else 4832 data->cy = cy + sely - yy; 4833 4834 window_copy_update_selection(wme, 1, 1); 4835 window_copy_redraw_screen(wme); 4836 } 4837 4838 static void 4839 window_copy_cursor_left(struct window_mode_entry *wme) 4840 { 4841 struct window_copy_mode_data *data = wme->data; 4842 struct screen *back_s = data->backing; 4843 struct grid_reader gr; 4844 u_int px, py, oldy, hsize; 4845 4846 px = data->cx; 4847 hsize = screen_hsize(back_s); 4848 py = hsize + data->cy - data->oy; 4849 oldy = data->cy; 4850 4851 grid_reader_start(&gr, back_s->grid, px, py); 4852 grid_reader_cursor_left(&gr, 1); 4853 grid_reader_get_cursor(&gr, &px, &py); 4854 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py); 4855 } 4856 4857 static void 4858 window_copy_cursor_right(struct window_mode_entry *wme, int all) 4859 { 4860 struct window_copy_mode_data *data = wme->data; 4861 struct screen *back_s = data->backing; 4862 struct grid_reader gr; 4863 u_int px, py, oldy, hsize; 4864 4865 px = data->cx; 4866 hsize = screen_hsize(back_s); 4867 py = hsize + data->cy - data->oy; 4868 oldy = data->cy; 4869 4870 grid_reader_start(&gr, back_s->grid, px, py); 4871 grid_reader_cursor_right(&gr, 1, all); 4872 grid_reader_get_cursor(&gr, &px, &py); 4873 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s), 4874 data->oy, oldy, px, py, 0); 4875 } 4876 4877 static void 4878 window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only) 4879 { 4880 struct window_copy_mode_data *data = wme->data; 4881 struct screen *s = &data->screen; 4882 u_int ox, oy, px, py; 4883 int norectsel; 4884 4885 norectsel = data->screen.sel == NULL || !data->rectflag; 4886 oy = screen_hsize(data->backing) + data->cy - data->oy; 4887 ox = window_copy_find_length(wme, oy); 4888 if (norectsel && data->cx != ox) { 4889 data->lastcx = data->cx; 4890 data->lastsx = ox; 4891 } 4892 4893 if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely) 4894 window_copy_other_end(wme); 4895 4896 if (scroll_only || data->cy == 0) { 4897 if (norectsel) 4898 data->cx = data->lastcx; 4899 window_copy_scroll_down(wme, 1); 4900 if (scroll_only) { 4901 if (data->cy == screen_size_y(s) - 1) 4902 window_copy_redraw_lines(wme, data->cy, 1); 4903 else 4904 window_copy_redraw_lines(wme, data->cy, 2); 4905 } 4906 } else { 4907 if (norectsel) { 4908 window_copy_update_cursor(wme, data->lastcx, 4909 data->cy - 1); 4910 } else 4911 window_copy_update_cursor(wme, data->cx, data->cy - 1); 4912 if (window_copy_update_selection(wme, 1, 0)) { 4913 if (data->cy == screen_size_y(s) - 1) 4914 window_copy_redraw_lines(wme, data->cy, 1); 4915 else 4916 window_copy_redraw_lines(wme, data->cy, 2); 4917 } 4918 } 4919 4920 if (norectsel) { 4921 py = screen_hsize(data->backing) + data->cy - data->oy; 4922 px = window_copy_find_length(wme, py); 4923 if ((data->cx >= data->lastsx && data->cx != px) || 4924 data->cx > px) 4925 { 4926 window_copy_update_cursor(wme, px, data->cy); 4927 if (window_copy_update_selection(wme, 1, 0)) 4928 window_copy_redraw_lines(wme, data->cy, 1); 4929 } 4930 } 4931 4932 if (data->lineflag == LINE_SEL_LEFT_RIGHT) 4933 { 4934 py = screen_hsize(data->backing) + data->cy - data->oy; 4935 if (data->rectflag) 4936 px = screen_size_x(data->backing); 4937 else 4938 px = window_copy_find_length(wme, py); 4939 window_copy_update_cursor(wme, px, data->cy); 4940 if (window_copy_update_selection(wme, 1, 0)) 4941 window_copy_redraw_lines(wme, data->cy, 1); 4942 } 4943 else if (data->lineflag == LINE_SEL_RIGHT_LEFT) 4944 { 4945 window_copy_update_cursor(wme, 0, data->cy); 4946 if (window_copy_update_selection(wme, 1, 0)) 4947 window_copy_redraw_lines(wme, data->cy, 1); 4948 } 4949 } 4950 4951 static void 4952 window_copy_cursor_down(struct window_mode_entry *wme, int scroll_only) 4953 { 4954 struct window_copy_mode_data *data = wme->data; 4955 struct screen *s = &data->screen; 4956 u_int ox, oy, px, py; 4957 int norectsel; 4958 4959 norectsel = data->screen.sel == NULL || !data->rectflag; 4960 oy = screen_hsize(data->backing) + data->cy - data->oy; 4961 ox = window_copy_find_length(wme, oy); 4962 if (norectsel && data->cx != ox) { 4963 data->lastcx = data->cx; 4964 data->lastsx = ox; 4965 } 4966 4967 if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely) 4968 window_copy_other_end(wme); 4969 4970 if (scroll_only || data->cy == screen_size_y(s) - 1) { 4971 if (norectsel) 4972 data->cx = data->lastcx; 4973 window_copy_scroll_up(wme, 1); 4974 if (scroll_only && data->cy > 0) 4975 window_copy_redraw_lines(wme, data->cy - 1, 2); 4976 } else { 4977 if (norectsel) { 4978 window_copy_update_cursor(wme, data->lastcx, 4979 data->cy + 1); 4980 } else 4981 window_copy_update_cursor(wme, data->cx, data->cy + 1); 4982 if (window_copy_update_selection(wme, 1, 0)) 4983 window_copy_redraw_lines(wme, data->cy - 1, 2); 4984 } 4985 4986 if (norectsel) { 4987 py = screen_hsize(data->backing) + data->cy - data->oy; 4988 px = window_copy_find_length(wme, py); 4989 if ((data->cx >= data->lastsx && data->cx != px) || 4990 data->cx > px) 4991 { 4992 window_copy_update_cursor(wme, px, data->cy); 4993 if (window_copy_update_selection(wme, 1, 0)) 4994 window_copy_redraw_lines(wme, data->cy, 1); 4995 } 4996 } 4997 4998 if (data->lineflag == LINE_SEL_LEFT_RIGHT) 4999 { 5000 py = screen_hsize(data->backing) + data->cy - data->oy; 5001 if (data->rectflag) 5002 px = screen_size_x(data->backing); 5003 else 5004 px = window_copy_find_length(wme, py); 5005 window_copy_update_cursor(wme, px, data->cy); 5006 if (window_copy_update_selection(wme, 1, 0)) 5007 window_copy_redraw_lines(wme, data->cy, 1); 5008 } 5009 else if (data->lineflag == LINE_SEL_RIGHT_LEFT) 5010 { 5011 window_copy_update_cursor(wme, 0, data->cy); 5012 if (window_copy_update_selection(wme, 1, 0)) 5013 window_copy_redraw_lines(wme, data->cy, 1); 5014 } 5015 } 5016 5017 static void 5018 window_copy_cursor_jump(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 + 1; 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 if (grid_reader_cursor_jump(&gr, data->jumpchar)) { 5032 grid_reader_get_cursor(&gr, &px, &py); 5033 window_copy_acquire_cursor_down(wme, hsize, 5034 screen_size_y(back_s), data->oy, oldy, px, py, 0); 5035 } 5036 } 5037 5038 static void 5039 window_copy_cursor_jump_back(struct window_mode_entry *wme) 5040 { 5041 struct window_copy_mode_data *data = wme->data; 5042 struct screen *back_s = data->backing; 5043 struct grid_reader gr; 5044 u_int px, py, oldy, hsize; 5045 5046 px = data->cx; 5047 hsize = screen_hsize(back_s); 5048 py = hsize + data->cy - data->oy; 5049 oldy = data->cy; 5050 5051 grid_reader_start(&gr, back_s->grid, px, py); 5052 grid_reader_cursor_left(&gr, 0); 5053 if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) { 5054 grid_reader_get_cursor(&gr, &px, &py); 5055 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, 5056 py); 5057 } 5058 } 5059 5060 static void 5061 window_copy_cursor_jump_to(struct window_mode_entry *wme) 5062 { 5063 struct window_copy_mode_data *data = wme->data; 5064 struct screen *back_s = data->backing; 5065 struct grid_reader gr; 5066 u_int px, py, oldy, hsize; 5067 5068 px = data->cx + 2; 5069 hsize = screen_hsize(back_s); 5070 py = hsize + data->cy - data->oy; 5071 oldy = data->cy; 5072 5073 grid_reader_start(&gr, back_s->grid, px, py); 5074 if (grid_reader_cursor_jump(&gr, data->jumpchar)) { 5075 grid_reader_cursor_left(&gr, 1); 5076 grid_reader_get_cursor(&gr, &px, &py); 5077 window_copy_acquire_cursor_down(wme, hsize, 5078 screen_size_y(back_s), data->oy, oldy, px, py, 0); 5079 } 5080 } 5081 5082 static void 5083 window_copy_cursor_jump_to_back(struct window_mode_entry *wme) 5084 { 5085 struct window_copy_mode_data *data = wme->data; 5086 struct screen *back_s = data->backing; 5087 struct grid_reader gr; 5088 u_int px, py, oldy, hsize; 5089 5090 px = data->cx; 5091 hsize = screen_hsize(back_s); 5092 py = hsize + data->cy - data->oy; 5093 oldy = data->cy; 5094 5095 grid_reader_start(&gr, back_s->grid, px, py); 5096 grid_reader_cursor_left(&gr, 0); 5097 grid_reader_cursor_left(&gr, 0); 5098 if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) { 5099 grid_reader_cursor_right(&gr, 1, 0); 5100 grid_reader_get_cursor(&gr, &px, &py); 5101 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, 5102 py); 5103 } 5104 } 5105 5106 static void 5107 window_copy_cursor_next_word(struct window_mode_entry *wme, 5108 const char *separators) 5109 { 5110 struct window_copy_mode_data *data = wme->data; 5111 struct screen *back_s = data->backing; 5112 struct grid_reader gr; 5113 u_int px, py, oldy, hsize; 5114 5115 px = data->cx; 5116 hsize = screen_hsize(back_s); 5117 py = hsize + data->cy - data->oy; 5118 oldy = data->cy; 5119 5120 grid_reader_start(&gr, back_s->grid, px, py); 5121 grid_reader_cursor_next_word(&gr, separators); 5122 grid_reader_get_cursor(&gr, &px, &py); 5123 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s), 5124 data->oy, oldy, px, py, 0); 5125 } 5126 5127 /* Compute the next place where a word ends. */ 5128 static void 5129 window_copy_cursor_next_word_end_pos(struct window_mode_entry *wme, 5130 const char *separators, u_int *ppx, u_int *ppy) 5131 { 5132 struct window_pane *wp = wme->wp; 5133 struct window_copy_mode_data *data = wme->data; 5134 struct options *oo = wp->window->options; 5135 struct screen *back_s = data->backing; 5136 struct grid_reader gr; 5137 u_int px, py, hsize; 5138 5139 px = data->cx; 5140 hsize = screen_hsize(back_s); 5141 py = hsize + data->cy - data->oy; 5142 5143 grid_reader_start(&gr, back_s->grid, px, py); 5144 if (options_get_number(oo, "mode-keys") == MODEKEY_VI) { 5145 if (!grid_reader_in_set(&gr, WHITESPACE)) 5146 grid_reader_cursor_right(&gr, 0, 0); 5147 grid_reader_cursor_next_word_end(&gr, separators); 5148 grid_reader_cursor_left(&gr, 1); 5149 } else 5150 grid_reader_cursor_next_word_end(&gr, separators); 5151 grid_reader_get_cursor(&gr, &px, &py); 5152 *ppx = px; 5153 *ppy = py; 5154 } 5155 5156 /* Move to the next place where a word ends. */ 5157 static void 5158 window_copy_cursor_next_word_end(struct window_mode_entry *wme, 5159 const char *separators, int no_reset) 5160 { 5161 struct window_pane *wp = wme->wp; 5162 struct window_copy_mode_data *data = wme->data; 5163 struct options *oo = wp->window->options; 5164 struct screen *back_s = data->backing; 5165 struct grid_reader gr; 5166 u_int px, py, oldy, hsize; 5167 5168 px = data->cx; 5169 hsize = screen_hsize(back_s); 5170 py = hsize + data->cy - data->oy; 5171 oldy = data->cy; 5172 5173 grid_reader_start(&gr, back_s->grid, px, py); 5174 if (options_get_number(oo, "mode-keys") == MODEKEY_VI) { 5175 if (!grid_reader_in_set(&gr, WHITESPACE)) 5176 grid_reader_cursor_right(&gr, 0, 0); 5177 grid_reader_cursor_next_word_end(&gr, separators); 5178 grid_reader_cursor_left(&gr, 1); 5179 } else 5180 grid_reader_cursor_next_word_end(&gr, separators); 5181 grid_reader_get_cursor(&gr, &px, &py); 5182 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s), 5183 data->oy, oldy, px, py, no_reset); 5184 } 5185 5186 /* Compute the previous place where a word begins. */ 5187 static void 5188 window_copy_cursor_previous_word_pos(struct window_mode_entry *wme, 5189 const char *separators, u_int *ppx, u_int *ppy) 5190 { 5191 struct window_copy_mode_data *data = wme->data; 5192 struct screen *back_s = data->backing; 5193 struct grid_reader gr; 5194 u_int px, py, hsize; 5195 5196 px = data->cx; 5197 hsize = screen_hsize(back_s); 5198 py = hsize + data->cy - data->oy; 5199 5200 grid_reader_start(&gr, back_s->grid, px, py); 5201 grid_reader_cursor_previous_word(&gr, separators, /* already= */ 0, 5202 /* stop_at_eol= */ 1); 5203 grid_reader_get_cursor(&gr, &px, &py); 5204 *ppx = px; 5205 *ppy = py; 5206 } 5207 5208 /* Move to the previous place where a word begins. */ 5209 static void 5210 window_copy_cursor_previous_word(struct window_mode_entry *wme, 5211 const char *separators, int already) 5212 { 5213 struct window_copy_mode_data *data = wme->data; 5214 struct window *w = wme->wp->window; 5215 struct screen *back_s = data->backing; 5216 struct grid_reader gr; 5217 u_int px, py, oldy, hsize; 5218 int stop_at_eol; 5219 5220 if (options_get_number(w->options, "mode-keys") == MODEKEY_EMACS) 5221 stop_at_eol = 1; 5222 else 5223 stop_at_eol = 0; 5224 5225 px = data->cx; 5226 hsize = screen_hsize(back_s); 5227 py = hsize + data->cy - data->oy; 5228 oldy = data->cy; 5229 5230 grid_reader_start(&gr, back_s->grid, px, py); 5231 grid_reader_cursor_previous_word(&gr, separators, already, stop_at_eol); 5232 grid_reader_get_cursor(&gr, &px, &py); 5233 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py); 5234 } 5235 5236 static void 5237 window_copy_scroll_up(struct window_mode_entry *wme, u_int ny) 5238 { 5239 struct window_pane *wp = wme->wp; 5240 struct window_copy_mode_data *data = wme->data; 5241 struct screen *s = &data->screen; 5242 struct screen_write_ctx ctx; 5243 5244 if (data->oy < ny) 5245 ny = data->oy; 5246 if (ny == 0) 5247 return; 5248 data->oy -= ny; 5249 5250 if (data->searchmark != NULL && !data->timeout) 5251 window_copy_search_marks(wme, NULL, data->searchregex, 1); 5252 window_copy_update_selection(wme, 0, 0); 5253 5254 screen_write_start_pane(&ctx, wp, NULL); 5255 screen_write_cursormove(&ctx, 0, 0, 0); 5256 screen_write_deleteline(&ctx, ny, 8); 5257 window_copy_write_lines(wme, &ctx, screen_size_y(s) - ny, ny); 5258 window_copy_write_line(wme, &ctx, 0); 5259 if (screen_size_y(s) > 1) 5260 window_copy_write_line(wme, &ctx, 1); 5261 if (screen_size_y(s) > 3) 5262 window_copy_write_line(wme, &ctx, screen_size_y(s) - 2); 5263 if (s->sel != NULL && screen_size_y(s) > ny) 5264 window_copy_write_line(wme, &ctx, screen_size_y(s) - ny - 1); 5265 screen_write_cursormove(&ctx, data->cx, data->cy, 0); 5266 screen_write_stop(&ctx); 5267 } 5268 5269 static void 5270 window_copy_scroll_down(struct window_mode_entry *wme, u_int ny) 5271 { 5272 struct window_pane *wp = wme->wp; 5273 struct window_copy_mode_data *data = wme->data; 5274 struct screen *s = &data->screen; 5275 struct screen_write_ctx ctx; 5276 5277 if (ny > screen_hsize(data->backing)) 5278 return; 5279 5280 if (data->oy > screen_hsize(data->backing) - ny) 5281 ny = screen_hsize(data->backing) - data->oy; 5282 if (ny == 0) 5283 return; 5284 data->oy += ny; 5285 5286 if (data->searchmark != NULL && !data->timeout) 5287 window_copy_search_marks(wme, NULL, data->searchregex, 1); 5288 window_copy_update_selection(wme, 0, 0); 5289 5290 screen_write_start_pane(&ctx, wp, NULL); 5291 screen_write_cursormove(&ctx, 0, 0, 0); 5292 screen_write_insertline(&ctx, ny, 8); 5293 window_copy_write_lines(wme, &ctx, 0, ny); 5294 if (s->sel != NULL && screen_size_y(s) > ny) 5295 window_copy_write_line(wme, &ctx, ny); 5296 else if (ny == 1) /* nuke position */ 5297 window_copy_write_line(wme, &ctx, 1); 5298 screen_write_cursormove(&ctx, data->cx, data->cy, 0); 5299 screen_write_stop(&ctx); 5300 } 5301 5302 static void 5303 window_copy_rectangle_set(struct window_mode_entry *wme, int rectflag) 5304 { 5305 struct window_copy_mode_data *data = wme->data; 5306 u_int px, py; 5307 5308 data->rectflag = rectflag; 5309 5310 py = screen_hsize(data->backing) + data->cy - data->oy; 5311 px = window_copy_find_length(wme, py); 5312 if (data->cx > px) 5313 window_copy_update_cursor(wme, px, data->cy); 5314 5315 window_copy_update_selection(wme, 1, 0); 5316 window_copy_redraw_screen(wme); 5317 } 5318 5319 static void 5320 window_copy_move_mouse(struct mouse_event *m) 5321 { 5322 struct window_pane *wp; 5323 struct window_mode_entry *wme; 5324 u_int x, y; 5325 5326 wp = cmd_mouse_pane(m, NULL, NULL); 5327 if (wp == NULL) 5328 return; 5329 wme = TAILQ_FIRST(&wp->modes); 5330 if (wme == NULL) 5331 return; 5332 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode) 5333 return; 5334 5335 if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) 5336 return; 5337 5338 window_copy_update_cursor(wme, x, y); 5339 } 5340 5341 void 5342 window_copy_start_drag(struct client *c, struct mouse_event *m) 5343 { 5344 struct window_pane *wp; 5345 struct window_mode_entry *wme; 5346 struct window_copy_mode_data *data; 5347 u_int x, y, yg; 5348 5349 if (c == NULL) 5350 return; 5351 5352 wp = cmd_mouse_pane(m, NULL, NULL); 5353 if (wp == NULL) 5354 return; 5355 wme = TAILQ_FIRST(&wp->modes); 5356 if (wme == NULL) 5357 return; 5358 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode) 5359 return; 5360 5361 if (cmd_mouse_at(wp, m, &x, &y, 1) != 0) 5362 return; 5363 5364 c->tty.mouse_drag_update = window_copy_drag_update; 5365 c->tty.mouse_drag_release = window_copy_drag_release; 5366 5367 data = wme->data; 5368 yg = screen_hsize(data->backing) + y - data->oy; 5369 if (x < data->selrx || x > data->endselrx || yg != data->selry) 5370 data->selflag = SEL_CHAR; 5371 switch (data->selflag) { 5372 case SEL_WORD: 5373 if (data->separators != NULL) { 5374 window_copy_update_cursor(wme, x, y); 5375 window_copy_cursor_previous_word_pos(wme, 5376 data->separators, &x, &y); 5377 y -= screen_hsize(data->backing) - data->oy; 5378 } 5379 window_copy_update_cursor(wme, x, y); 5380 break; 5381 case SEL_LINE: 5382 window_copy_update_cursor(wme, 0, y); 5383 break; 5384 case SEL_CHAR: 5385 window_copy_update_cursor(wme, x, y); 5386 window_copy_start_selection(wme); 5387 break; 5388 } 5389 5390 window_copy_redraw_screen(wme); 5391 window_copy_drag_update(c, m); 5392 } 5393 5394 static void 5395 window_copy_drag_update(struct client *c, struct mouse_event *m) 5396 { 5397 struct window_pane *wp; 5398 struct window_mode_entry *wme; 5399 struct window_copy_mode_data *data; 5400 u_int x, y, old_cx, old_cy; 5401 struct timeval tv = { 5402 .tv_usec = WINDOW_COPY_DRAG_REPEAT_TIME 5403 }; 5404 5405 if (c == NULL) 5406 return; 5407 5408 wp = cmd_mouse_pane(m, NULL, NULL); 5409 if (wp == NULL) 5410 return; 5411 wme = TAILQ_FIRST(&wp->modes); 5412 if (wme == NULL) 5413 return; 5414 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode) 5415 return; 5416 5417 data = wme->data; 5418 evtimer_del(&data->dragtimer); 5419 5420 if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) 5421 return; 5422 old_cx = data->cx; 5423 old_cy = data->cy; 5424 5425 window_copy_update_cursor(wme, x, y); 5426 if (window_copy_update_selection(wme, 1, 0)) 5427 window_copy_redraw_selection(wme, old_cy); 5428 if (old_cy != data->cy || old_cx == data->cx) { 5429 if (y == 0) { 5430 evtimer_add(&data->dragtimer, &tv); 5431 window_copy_cursor_up(wme, 1); 5432 } else if (y == screen_size_y(&data->screen) - 1) { 5433 evtimer_add(&data->dragtimer, &tv); 5434 window_copy_cursor_down(wme, 1); 5435 } 5436 } 5437 } 5438 5439 static void 5440 window_copy_drag_release(struct client *c, struct mouse_event *m) 5441 { 5442 struct window_pane *wp; 5443 struct window_mode_entry *wme; 5444 struct window_copy_mode_data *data; 5445 5446 if (c == NULL) 5447 return; 5448 5449 wp = cmd_mouse_pane(m, NULL, NULL); 5450 if (wp == NULL) 5451 return; 5452 wme = TAILQ_FIRST(&wp->modes); 5453 if (wme == NULL) 5454 return; 5455 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode) 5456 return; 5457 5458 data = wme->data; 5459 evtimer_del(&data->dragtimer); 5460 } 5461 5462 static void 5463 window_copy_jump_to_mark(struct window_mode_entry *wme) 5464 { 5465 struct window_copy_mode_data *data = wme->data; 5466 u_int tmx, tmy; 5467 5468 tmx = data->cx; 5469 tmy = screen_hsize(data->backing) + data->cy - data->oy; 5470 data->cx = data->mx; 5471 if (data->my < screen_hsize(data->backing)) { 5472 data->cy = 0; 5473 data->oy = screen_hsize(data->backing) - data->my; 5474 } else { 5475 data->cy = data->my - screen_hsize(data->backing); 5476 data->oy = 0; 5477 } 5478 data->mx = tmx; 5479 data->my = tmy; 5480 data->showmark = 1; 5481 window_copy_update_selection(wme, 0, 0); 5482 window_copy_redraw_screen(wme); 5483 } 5484 5485 /* Scroll up if the cursor went off the visible screen. */ 5486 static void 5487 window_copy_acquire_cursor_up(struct window_mode_entry *wme, u_int hsize, 5488 u_int oy, u_int oldy, u_int px, u_int py) 5489 { 5490 u_int cy, yy, ny, nd; 5491 5492 yy = hsize - oy; 5493 if (py < yy) { 5494 ny = yy - py; 5495 cy = 0; 5496 nd = 1; 5497 } else { 5498 ny = 0; 5499 cy = py - yy; 5500 nd = oldy - cy + 1; 5501 } 5502 while (ny > 0) { 5503 window_copy_cursor_up(wme, 1); 5504 ny--; 5505 } 5506 window_copy_update_cursor(wme, px, cy); 5507 if (window_copy_update_selection(wme, 1, 0)) 5508 window_copy_redraw_lines(wme, cy, nd); 5509 } 5510 5511 /* Scroll down if the cursor went off the visible screen. */ 5512 static void 5513 window_copy_acquire_cursor_down(struct window_mode_entry *wme, u_int hsize, 5514 u_int sy, u_int oy, u_int oldy, u_int px, u_int py, int no_reset) 5515 { 5516 u_int cy, yy, ny, nd; 5517 5518 cy = py - hsize + oy; 5519 yy = sy - 1; 5520 if (cy > yy) { 5521 ny = cy - yy; 5522 oldy = yy; 5523 nd = 1; 5524 } else { 5525 ny = 0; 5526 nd = cy - oldy + 1; 5527 } 5528 while (ny > 0) { 5529 window_copy_cursor_down(wme, 1); 5530 ny--; 5531 } 5532 if (cy > yy) 5533 window_copy_update_cursor(wme, px, yy); 5534 else 5535 window_copy_update_cursor(wme, px, cy); 5536 if (window_copy_update_selection(wme, 1, no_reset)) 5537 window_copy_redraw_lines(wme, oldy, nd); 5538 } 5539