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