1 /* $OpenBSD: window-copy.c,v 1.348 2024/04/23 13:34:51 jsg 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 struct grid_line *gl; 3622 3623 if (regex) { 3624 sbuf = xmalloc(ssize); 3625 sbuf[0] = '\0'; 3626 sbuf = window_copy_stringify(sgd, 0, 0, sgd->sx, sbuf, &ssize); 3627 if (cis) 3628 cflags |= REG_ICASE; 3629 if (regcomp(®, sbuf, cflags) != 0) { 3630 free(sbuf); 3631 return (0); 3632 } 3633 free(sbuf); 3634 } 3635 3636 if (direction) { 3637 for (i = fy; i <= endline; i++) { 3638 gl = grid_get_line(gd, i); 3639 if (i != endline && gl->flags & GRID_LINE_WRAPPED) 3640 continue; 3641 if (regex) { 3642 found = window_copy_search_lr_regex(gd, 3643 &px, &sx, i, fx, gd->sx, ®); 3644 } else { 3645 found = window_copy_search_lr(gd, sgd, 3646 &px, i, fx, gd->sx, cis); 3647 } 3648 if (found) 3649 break; 3650 fx = 0; 3651 } 3652 } else { 3653 for (i = fy + 1; endline < i; i--) { 3654 gl = grid_get_line(gd, i - 1); 3655 if (i != endline && gl->flags & GRID_LINE_WRAPPED) 3656 continue; 3657 if (regex) { 3658 found = window_copy_search_rl_regex(gd, 3659 &px, &sx, i - 1, 0, fx + 1, ®); 3660 if (found) { 3661 window_copy_search_back_overlap(gd, 3662 ®, &px, &sx, &i, endline); 3663 } 3664 } else { 3665 found = window_copy_search_rl(gd, sgd, 3666 &px, i - 1, 0, fx + 1, cis); 3667 } 3668 if (found) { 3669 i--; 3670 break; 3671 } 3672 fx = gd->sx - 1; 3673 } 3674 } 3675 if (regex) 3676 regfree(®); 3677 3678 if (found) { 3679 window_copy_scroll_to(wme, px, i, 1); 3680 return (1); 3681 } 3682 if (wrap) { 3683 return (window_copy_search_jump(wme, gd, sgd, 3684 direction ? 0 : gd->sx - 1, 3685 direction ? 0 : gd->hsize + gd->sy - 1, fy, cis, 0, 3686 direction, regex)); 3687 } 3688 return (0); 3689 } 3690 3691 static void 3692 window_copy_move_after_search_mark(struct window_copy_mode_data *data, 3693 u_int *fx, u_int *fy, int wrapflag) 3694 { 3695 struct screen *s = data->backing; 3696 u_int at, start; 3697 3698 if (window_copy_search_mark_at(data, *fx, *fy, &start) == 0 && 3699 data->searchmark[start] != 0) { 3700 while (window_copy_search_mark_at(data, *fx, *fy, &at) == 0) { 3701 if (data->searchmark[at] != data->searchmark[start]) 3702 break; 3703 /* Stop if not wrapping and at the end of the grid. */ 3704 if (!wrapflag && 3705 *fx == screen_size_x(s) - 1 && 3706 *fy == screen_hsize(s) + screen_size_y(s) - 1) 3707 break; 3708 3709 window_copy_move_right(s, fx, fy, wrapflag); 3710 } 3711 } 3712 } 3713 3714 /* 3715 * Search in for text searchstr. If direction is 0 then search up, otherwise 3716 * down. 3717 */ 3718 static int 3719 window_copy_search(struct window_mode_entry *wme, int direction, int regex) 3720 { 3721 struct window_pane *wp = wme->wp; 3722 struct window_copy_mode_data *data = wme->data; 3723 struct screen *s = data->backing, ss; 3724 struct screen_write_ctx ctx; 3725 struct grid *gd = s->grid; 3726 const char *str = data->searchstr; 3727 u_int at, endline, fx, fy, start; 3728 int cis, found, keys, visible_only; 3729 int wrapflag; 3730 3731 if (regex && str[strcspn(str, "^$*+()?[].\\")] == '\0') 3732 regex = 0; 3733 3734 data->searchdirection = direction; 3735 3736 if (data->timeout) 3737 return (0); 3738 3739 if (data->searchall || wp->searchstr == NULL || 3740 wp->searchregex != regex) { 3741 visible_only = 0; 3742 data->searchall = 0; 3743 } else 3744 visible_only = (strcmp(wp->searchstr, str) == 0); 3745 if (visible_only == 0 && data->searchmark != NULL) 3746 window_copy_clear_marks(wme); 3747 free(wp->searchstr); 3748 wp->searchstr = xstrdup(str); 3749 wp->searchregex = regex; 3750 3751 fx = data->cx; 3752 fy = screen_hsize(data->backing) - data->oy + data->cy; 3753 3754 screen_init(&ss, screen_write_strlen("%s", str), 1, 0); 3755 screen_write_start(&ctx, &ss); 3756 screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", str); 3757 screen_write_stop(&ctx); 3758 3759 wrapflag = options_get_number(wp->window->options, "wrap-search"); 3760 cis = window_copy_is_lowercase(str); 3761 3762 keys = options_get_number(wp->window->options, "mode-keys"); 3763 3764 if (direction) { 3765 /* 3766 * Behave according to mode-keys. If it is emacs, search forward 3767 * leaves the cursor after the match. If it is vi, the cursor 3768 * remains at the beginning of the match, regardless of 3769 * direction, which means that we need to start the next search 3770 * after the term the cursor is currently on when searching 3771 * forward. 3772 */ 3773 if (keys == MODEKEY_VI) { 3774 if (data->searchmark != NULL) 3775 window_copy_move_after_search_mark(data, &fx, 3776 &fy, wrapflag); 3777 else { 3778 /* 3779 * When there are no search marks, start the 3780 * search after the current cursor position. 3781 */ 3782 window_copy_move_right(s, &fx, &fy, wrapflag); 3783 } 3784 } 3785 endline = gd->hsize + gd->sy - 1; 3786 } else { 3787 window_copy_move_left(s, &fx, &fy, wrapflag); 3788 endline = 0; 3789 } 3790 3791 found = window_copy_search_jump(wme, gd, ss.grid, fx, fy, endline, cis, 3792 wrapflag, direction, regex); 3793 if (found) { 3794 window_copy_search_marks(wme, &ss, regex, visible_only); 3795 fx = data->cx; 3796 fy = screen_hsize(data->backing) - data->oy + data->cy; 3797 3798 /* 3799 * When searching forward, if the cursor is not at the beginning 3800 * of the mark, search again. 3801 */ 3802 if (direction && 3803 window_copy_search_mark_at(data, fx, fy, &at) == 0 && 3804 at > 0 && 3805 data->searchmark != NULL && 3806 data->searchmark[at] == data->searchmark[at - 1]) { 3807 window_copy_move_after_search_mark(data, &fx, &fy, 3808 wrapflag); 3809 window_copy_search_jump(wme, gd, ss.grid, fx, 3810 fy, endline, cis, wrapflag, direction, 3811 regex); 3812 fx = data->cx; 3813 fy = screen_hsize(data->backing) - data->oy + data->cy; 3814 } 3815 3816 if (direction) { 3817 /* 3818 * When in Emacs mode, position the cursor just after 3819 * the mark. 3820 */ 3821 if (keys == MODEKEY_EMACS) { 3822 window_copy_move_after_search_mark(data, &fx, 3823 &fy, wrapflag); 3824 data->cx = fx; 3825 data->cy = fy - screen_hsize(data->backing) + 3826 data-> oy; 3827 } 3828 } else { 3829 /* 3830 * When searching backward, position the cursor at the 3831 * beginning of the mark. 3832 */ 3833 if (window_copy_search_mark_at(data, fx, fy, 3834 &start) == 0) { 3835 while (window_copy_search_mark_at(data, fx, fy, 3836 &at) == 0 && 3837 data->searchmark != NULL && 3838 data->searchmark[at] == 3839 data->searchmark[start]) { 3840 data->cx = fx; 3841 data->cy = fy - 3842 screen_hsize(data->backing) + 3843 data-> oy; 3844 if (at == 0) 3845 break; 3846 3847 window_copy_move_left(s, &fx, &fy, 0); 3848 } 3849 } 3850 } 3851 } 3852 window_copy_redraw_screen(wme); 3853 3854 screen_free(&ss); 3855 return (found); 3856 } 3857 3858 static void 3859 window_copy_visible_lines(struct window_copy_mode_data *data, u_int *start, 3860 u_int *end) 3861 { 3862 struct grid *gd = data->backing->grid; 3863 const struct grid_line *gl; 3864 3865 for (*start = gd->hsize - data->oy; *start > 0; (*start)--) { 3866 gl = grid_peek_line(gd, (*start) - 1); 3867 if (~gl->flags & GRID_LINE_WRAPPED) 3868 break; 3869 } 3870 *end = gd->hsize - data->oy + gd->sy; 3871 } 3872 3873 static int 3874 window_copy_search_mark_at(struct window_copy_mode_data *data, u_int px, 3875 u_int py, u_int *at) 3876 { 3877 struct screen *s = data->backing; 3878 struct grid *gd = s->grid; 3879 3880 if (py < gd->hsize - data->oy) 3881 return (-1); 3882 if (py > gd->hsize - data->oy + gd->sy - 1) 3883 return (-1); 3884 *at = ((py - (gd->hsize - data->oy)) * gd->sx) + px; 3885 return (0); 3886 } 3887 3888 static int 3889 window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, 3890 int regex, int visible_only) 3891 { 3892 struct window_copy_mode_data *data = wme->data; 3893 struct screen *s = data->backing, ss; 3894 struct screen_write_ctx ctx; 3895 struct grid *gd = s->grid; 3896 int found, cis, stopped = 0; 3897 int cflags = REG_EXTENDED; 3898 u_int px, py, i, b, nfound = 0, width; 3899 u_int ssize = 1, start, end; 3900 char *sbuf; 3901 regex_t reg; 3902 uint64_t stop = 0, tstart, t; 3903 3904 if (ssp == NULL) { 3905 width = screen_write_strlen("%s", data->searchstr); 3906 screen_init(&ss, width, 1, 0); 3907 screen_write_start(&ctx, &ss); 3908 screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", 3909 data->searchstr); 3910 screen_write_stop(&ctx); 3911 ssp = &ss; 3912 } else 3913 width = screen_size_x(ssp); 3914 3915 cis = window_copy_is_lowercase(data->searchstr); 3916 3917 if (regex) { 3918 sbuf = xmalloc(ssize); 3919 sbuf[0] = '\0'; 3920 sbuf = window_copy_stringify(ssp->grid, 0, 0, ssp->grid->sx, 3921 sbuf, &ssize); 3922 if (cis) 3923 cflags |= REG_ICASE; 3924 if (regcomp(®, sbuf, cflags) != 0) { 3925 free(sbuf); 3926 return (0); 3927 } 3928 free(sbuf); 3929 } 3930 tstart = get_timer(); 3931 3932 if (visible_only) 3933 window_copy_visible_lines(data, &start, &end); 3934 else { 3935 start = 0; 3936 end = gd->hsize + gd->sy; 3937 stop = get_timer() + WINDOW_COPY_SEARCH_ALL_TIMEOUT; 3938 } 3939 3940 again: 3941 free(data->searchmark); 3942 data->searchmark = xcalloc(gd->sx, gd->sy); 3943 data->searchgen = 1; 3944 3945 for (py = start; py < end; py++) { 3946 px = 0; 3947 for (;;) { 3948 if (regex) { 3949 found = window_copy_search_lr_regex(gd, 3950 &px, &width, py, px, gd->sx, ®); 3951 if (!found) 3952 break; 3953 } else { 3954 found = window_copy_search_lr(gd, ssp->grid, 3955 &px, py, px, gd->sx, cis); 3956 if (!found) 3957 break; 3958 } 3959 nfound++; 3960 3961 if (window_copy_search_mark_at(data, px, py, &b) == 0) { 3962 if (b + width > gd->sx * gd->sy) 3963 width = (gd->sx * gd->sy) - b; 3964 for (i = b; i < b + width; i++) { 3965 if (data->searchmark[i] != 0) 3966 continue; 3967 data->searchmark[i] = data->searchgen; 3968 } 3969 if (data->searchgen == UCHAR_MAX) 3970 data->searchgen = 1; 3971 else 3972 data->searchgen++; 3973 } 3974 px += width; 3975 } 3976 3977 t = get_timer(); 3978 if (t - tstart > WINDOW_COPY_SEARCH_TIMEOUT) { 3979 data->timeout = 1; 3980 break; 3981 } 3982 if (stop != 0 && t > stop) { 3983 stopped = 1; 3984 break; 3985 } 3986 } 3987 if (data->timeout) { 3988 window_copy_clear_marks(wme); 3989 goto out; 3990 } 3991 3992 if (stopped && stop != 0) { 3993 /* Try again but just the visible context. */ 3994 window_copy_visible_lines(data, &start, &end); 3995 stop = 0; 3996 goto again; 3997 } 3998 3999 if (!visible_only) { 4000 if (stopped) { 4001 if (nfound > 1000) 4002 data->searchcount = 1000; 4003 else if (nfound > 100) 4004 data->searchcount = 100; 4005 else if (nfound > 10) 4006 data->searchcount = 10; 4007 else 4008 data->searchcount = -1; 4009 data->searchmore = 1; 4010 } else { 4011 data->searchcount = nfound; 4012 data->searchmore = 0; 4013 } 4014 } 4015 4016 out: 4017 if (ssp == &ss) 4018 screen_free(&ss); 4019 if (regex) 4020 regfree(®); 4021 return (1); 4022 } 4023 4024 static void 4025 window_copy_clear_marks(struct window_mode_entry *wme) 4026 { 4027 struct window_copy_mode_data *data = wme->data; 4028 4029 free(data->searchmark); 4030 data->searchmark = NULL; 4031 } 4032 4033 static int 4034 window_copy_search_up(struct window_mode_entry *wme, int regex) 4035 { 4036 return (window_copy_search(wme, 0, regex)); 4037 } 4038 4039 static int 4040 window_copy_search_down(struct window_mode_entry *wme, int regex) 4041 { 4042 return (window_copy_search(wme, 1, regex)); 4043 } 4044 4045 static void 4046 window_copy_goto_line(struct window_mode_entry *wme, const char *linestr) 4047 { 4048 struct window_copy_mode_data *data = wme->data; 4049 const char *errstr; 4050 int lineno; 4051 4052 lineno = strtonum(linestr, -1, INT_MAX, &errstr); 4053 if (errstr != NULL) 4054 return; 4055 if (lineno < 0 || (u_int)lineno > screen_hsize(data->backing)) 4056 lineno = screen_hsize(data->backing); 4057 4058 data->oy = lineno; 4059 window_copy_update_selection(wme, 1, 0); 4060 window_copy_redraw_screen(wme); 4061 } 4062 4063 static void 4064 window_copy_match_start_end(struct window_copy_mode_data *data, u_int at, 4065 u_int *start, u_int *end) 4066 { 4067 struct grid *gd = data->backing->grid; 4068 u_int last = (gd->sy * gd->sx) - 1; 4069 u_char mark = data->searchmark[at]; 4070 4071 *start = *end = at; 4072 while (*start != 0 && data->searchmark[*start] == mark) 4073 (*start)--; 4074 if (data->searchmark[*start] != mark) 4075 (*start)++; 4076 while (*end != last && data->searchmark[*end] == mark) 4077 (*end)++; 4078 if (data->searchmark[*end] != mark) 4079 (*end)--; 4080 } 4081 4082 static char * 4083 window_copy_match_at_cursor(struct window_copy_mode_data *data) 4084 { 4085 struct grid *gd = data->backing->grid; 4086 struct grid_cell gc; 4087 u_int at, start, end, cy, px, py; 4088 u_int sx = screen_size_x(data->backing); 4089 char *buf = NULL; 4090 size_t len = 0; 4091 4092 if (data->searchmark == NULL) 4093 return (NULL); 4094 4095 cy = screen_hsize(data->backing) - data->oy + data->cy; 4096 if (window_copy_search_mark_at(data, data->cx, cy, &at) != 0) 4097 return (NULL); 4098 if (data->searchmark[at] == 0) { 4099 /* Allow one position after the match. */ 4100 if (at == 0 || data->searchmark[--at] == 0) 4101 return (NULL); 4102 } 4103 window_copy_match_start_end(data, at, &start, &end); 4104 4105 /* 4106 * Cells will not be set in the marked array unless they are valid text 4107 * and wrapping will be taken care of, so we can just copy. 4108 */ 4109 for (at = start; at <= end; at++) { 4110 py = at / sx; 4111 px = at - (py * sx); 4112 4113 grid_get_cell(gd, px, gd->hsize + py - data->oy, &gc); 4114 buf = xrealloc(buf, len + gc.data.size + 1); 4115 memcpy(buf + len, gc.data.data, gc.data.size); 4116 len += gc.data.size; 4117 } 4118 if (len != 0) 4119 buf[len] = '\0'; 4120 return (buf); 4121 } 4122 4123 static void 4124 window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy, 4125 struct grid_cell *gc, const struct grid_cell *mgc, 4126 const struct grid_cell *cgc, const struct grid_cell *mkgc) 4127 { 4128 struct window_pane *wp = wme->wp; 4129 struct window_copy_mode_data *data = wme->data; 4130 u_int mark, start, end, cy, cursor, current; 4131 int inv = 0, found = 0; 4132 int keys; 4133 4134 if (data->showmark && fy == data->my) { 4135 gc->attr = mkgc->attr; 4136 if (fx == data->mx) 4137 inv = 1; 4138 if (inv) { 4139 gc->fg = mkgc->bg; 4140 gc->bg = mkgc->fg; 4141 } 4142 else { 4143 gc->fg = mkgc->fg; 4144 gc->bg = mkgc->bg; 4145 } 4146 } 4147 4148 if (data->searchmark == NULL) 4149 return; 4150 4151 if (window_copy_search_mark_at(data, fx, fy, ¤t) != 0) 4152 return; 4153 mark = data->searchmark[current]; 4154 if (mark == 0) 4155 return; 4156 4157 cy = screen_hsize(data->backing) - data->oy + data->cy; 4158 if (window_copy_search_mark_at(data, data->cx, cy, &cursor) == 0) { 4159 keys = options_get_number(wp->window->options, "mode-keys"); 4160 if (cursor != 0 && 4161 keys == MODEKEY_EMACS && 4162 data->searchdirection) { 4163 if (data->searchmark[cursor - 1] == mark) { 4164 cursor--; 4165 found = 1; 4166 } 4167 } else if (data->searchmark[cursor] == mark) 4168 found = 1; 4169 if (found) { 4170 window_copy_match_start_end(data, cursor, &start, &end); 4171 if (current >= start && current <= end) { 4172 gc->attr = cgc->attr; 4173 if (inv) { 4174 gc->fg = cgc->bg; 4175 gc->bg = cgc->fg; 4176 } 4177 else { 4178 gc->fg = cgc->fg; 4179 gc->bg = cgc->bg; 4180 } 4181 return; 4182 } 4183 } 4184 } 4185 4186 gc->attr = mgc->attr; 4187 if (inv) { 4188 gc->fg = mgc->bg; 4189 gc->bg = mgc->fg; 4190 } 4191 else { 4192 gc->fg = mgc->fg; 4193 gc->bg = mgc->bg; 4194 } 4195 } 4196 4197 static void 4198 window_copy_write_one(struct window_mode_entry *wme, 4199 struct screen_write_ctx *ctx, u_int py, u_int fy, u_int nx, 4200 const struct grid_cell *mgc, const struct grid_cell *cgc, 4201 const struct grid_cell *mkgc) 4202 { 4203 struct window_copy_mode_data *data = wme->data; 4204 struct grid *gd = data->backing->grid; 4205 struct grid_cell gc; 4206 u_int fx; 4207 4208 screen_write_cursormove(ctx, 0, py, 0); 4209 for (fx = 0; fx < nx; fx++) { 4210 grid_get_cell(gd, fx, fy, &gc); 4211 if (fx + gc.data.width <= nx) { 4212 window_copy_update_style(wme, fx, fy, &gc, mgc, cgc, 4213 mkgc); 4214 screen_write_cell(ctx, &gc); 4215 } 4216 } 4217 } 4218 4219 static void 4220 window_copy_write_line(struct window_mode_entry *wme, 4221 struct screen_write_ctx *ctx, u_int py) 4222 { 4223 struct window_pane *wp = wme->wp; 4224 struct window_copy_mode_data *data = wme->data; 4225 struct screen *s = &data->screen; 4226 struct options *oo = wp->window->options; 4227 struct grid_line *gl; 4228 struct grid_cell gc, mgc, cgc, mkgc; 4229 char hdr[512], tmp[256], *t; 4230 size_t size = 0; 4231 u_int hsize = screen_hsize(data->backing); 4232 4233 style_apply(&gc, oo, "mode-style", NULL); 4234 gc.flags |= GRID_FLAG_NOPALETTE; 4235 style_apply(&mgc, oo, "copy-mode-match-style", NULL); 4236 mgc.flags |= GRID_FLAG_NOPALETTE; 4237 style_apply(&cgc, oo, "copy-mode-current-match-style", NULL); 4238 cgc.flags |= GRID_FLAG_NOPALETTE; 4239 style_apply(&mkgc, oo, "copy-mode-mark-style", NULL); 4240 mkgc.flags |= GRID_FLAG_NOPALETTE; 4241 4242 if (py == 0 && s->rupper < s->rlower && !data->hide_position) { 4243 gl = grid_get_line(data->backing->grid, hsize - data->oy); 4244 if (gl->time == 0) 4245 xsnprintf(tmp, sizeof tmp, "[%u/%u]", data->oy, hsize); 4246 else { 4247 t = format_pretty_time(gl->time, 1); 4248 xsnprintf(tmp, sizeof tmp, "%s [%u/%u]", t, data->oy, 4249 hsize); 4250 free(t); 4251 } 4252 4253 if (data->searchmark == NULL) { 4254 if (data->timeout) { 4255 size = xsnprintf(hdr, sizeof hdr, 4256 "(timed out) %s", tmp); 4257 } else 4258 size = xsnprintf(hdr, sizeof hdr, "%s", tmp); 4259 } else { 4260 if (data->searchcount == -1) 4261 size = xsnprintf(hdr, sizeof hdr, "%s", tmp); 4262 else { 4263 size = xsnprintf(hdr, sizeof hdr, 4264 "(%d%s results) %s", data->searchcount, 4265 data->searchmore ? "+" : "", tmp); 4266 } 4267 } 4268 if (size > screen_size_x(s)) 4269 size = screen_size_x(s); 4270 screen_write_cursormove(ctx, screen_size_x(s) - size, 0, 0); 4271 screen_write_puts(ctx, &gc, "%s", hdr); 4272 } else 4273 size = 0; 4274 4275 if (size < screen_size_x(s)) { 4276 window_copy_write_one(wme, ctx, py, hsize - data->oy + py, 4277 screen_size_x(s) - size, &mgc, &cgc, &mkgc); 4278 } 4279 4280 if (py == data->cy && data->cx == screen_size_x(s)) { 4281 screen_write_cursormove(ctx, screen_size_x(s) - 1, py, 0); 4282 screen_write_putc(ctx, &grid_default_cell, '$'); 4283 } 4284 } 4285 4286 static void 4287 window_copy_write_lines(struct window_mode_entry *wme, 4288 struct screen_write_ctx *ctx, u_int py, u_int ny) 4289 { 4290 u_int yy; 4291 4292 for (yy = py; yy < py + ny; yy++) 4293 window_copy_write_line(wme, ctx, py); 4294 } 4295 4296 static void 4297 window_copy_redraw_selection(struct window_mode_entry *wme, u_int old_y) 4298 { 4299 struct window_copy_mode_data *data = wme->data; 4300 struct grid *gd = data->backing->grid; 4301 u_int new_y, start, end; 4302 4303 new_y = data->cy; 4304 if (old_y <= new_y) { 4305 start = old_y; 4306 end = new_y; 4307 } else { 4308 start = new_y; 4309 end = old_y; 4310 } 4311 4312 /* 4313 * In word selection mode the first word on the line below the cursor 4314 * might be selected, so add this line to the redraw area. 4315 */ 4316 if (data->selflag == SEL_WORD) { 4317 /* Last grid line in data coordinates. */ 4318 if (end < gd->sy + data->oy - 1) 4319 end++; 4320 } 4321 window_copy_redraw_lines(wme, start, end - start + 1); 4322 } 4323 4324 static void 4325 window_copy_redraw_lines(struct window_mode_entry *wme, u_int py, u_int ny) 4326 { 4327 struct window_pane *wp = wme->wp; 4328 struct window_copy_mode_data *data = wme->data; 4329 struct screen_write_ctx ctx; 4330 u_int i; 4331 4332 screen_write_start_pane(&ctx, wp, NULL); 4333 for (i = py; i < py + ny; i++) 4334 window_copy_write_line(wme, &ctx, i); 4335 screen_write_cursormove(&ctx, data->cx, data->cy, 0); 4336 screen_write_stop(&ctx); 4337 } 4338 4339 static void 4340 window_copy_redraw_screen(struct window_mode_entry *wme) 4341 { 4342 struct window_copy_mode_data *data = wme->data; 4343 4344 window_copy_redraw_lines(wme, 0, screen_size_y(&data->screen)); 4345 } 4346 4347 static void 4348 window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin, 4349 int no_reset) 4350 { 4351 struct window_copy_mode_data *data = wme->data; 4352 u_int xx, yy; 4353 4354 xx = data->cx; 4355 yy = screen_hsize(data->backing) + data->cy - data->oy; 4356 switch (data->selflag) { 4357 case SEL_WORD: 4358 if (no_reset) 4359 break; 4360 begin = 0; 4361 if (data->dy > yy || (data->dy == yy && data->dx > xx)) { 4362 /* Right to left selection. */ 4363 window_copy_cursor_previous_word_pos(wme, 4364 data->separators, &xx, &yy); 4365 begin = 1; 4366 4367 /* Reset the end. */ 4368 data->endselx = data->endselrx; 4369 data->endsely = data->endselry; 4370 } else { 4371 /* Left to right selection. */ 4372 if (xx >= window_copy_find_length(wme, yy) || 4373 !window_copy_in_set(wme, xx + 1, yy, WHITESPACE)) { 4374 window_copy_cursor_next_word_end_pos(wme, 4375 data->separators, &xx, &yy); 4376 } 4377 4378 /* Reset the start. */ 4379 data->selx = data->selrx; 4380 data->sely = data->selry; 4381 } 4382 break; 4383 case SEL_LINE: 4384 if (no_reset) 4385 break; 4386 begin = 0; 4387 if (data->dy > yy) { 4388 /* Right to left selection. */ 4389 xx = 0; 4390 begin = 1; 4391 4392 /* Reset the end. */ 4393 data->endselx = data->endselrx; 4394 data->endsely = data->endselry; 4395 } else { 4396 /* Left to right selection. */ 4397 if (yy < data->endselry) 4398 yy = data->endselry; 4399 xx = window_copy_find_length(wme, yy); 4400 4401 /* Reset the start. */ 4402 data->selx = data->selrx; 4403 data->sely = data->selry; 4404 } 4405 break; 4406 case SEL_CHAR: 4407 break; 4408 } 4409 if (begin) { 4410 data->selx = xx; 4411 data->sely = yy; 4412 } else { 4413 data->endselx = xx; 4414 data->endsely = yy; 4415 } 4416 } 4417 4418 static void 4419 window_copy_synchronize_cursor(struct window_mode_entry *wme, int no_reset) 4420 { 4421 struct window_copy_mode_data *data = wme->data; 4422 4423 switch (data->cursordrag) { 4424 case CURSORDRAG_ENDSEL: 4425 window_copy_synchronize_cursor_end(wme, 0, no_reset); 4426 break; 4427 case CURSORDRAG_SEL: 4428 window_copy_synchronize_cursor_end(wme, 1, no_reset); 4429 break; 4430 case CURSORDRAG_NONE: 4431 break; 4432 } 4433 } 4434 4435 static void 4436 window_copy_update_cursor(struct window_mode_entry *wme, u_int cx, u_int cy) 4437 { 4438 struct window_pane *wp = wme->wp; 4439 struct window_copy_mode_data *data = wme->data; 4440 struct screen *s = &data->screen; 4441 struct screen_write_ctx ctx; 4442 u_int old_cx, old_cy; 4443 4444 old_cx = data->cx; old_cy = data->cy; 4445 data->cx = cx; data->cy = cy; 4446 if (old_cx == screen_size_x(s)) 4447 window_copy_redraw_lines(wme, old_cy, 1); 4448 if (data->cx == screen_size_x(s)) 4449 window_copy_redraw_lines(wme, data->cy, 1); 4450 else { 4451 screen_write_start_pane(&ctx, wp, NULL); 4452 screen_write_cursormove(&ctx, data->cx, data->cy, 0); 4453 screen_write_stop(&ctx); 4454 } 4455 } 4456 4457 static void 4458 window_copy_start_selection(struct window_mode_entry *wme) 4459 { 4460 struct window_copy_mode_data *data = wme->data; 4461 4462 data->selx = data->cx; 4463 data->sely = screen_hsize(data->backing) + data->cy - data->oy; 4464 4465 data->endselx = data->selx; 4466 data->endsely = data->sely; 4467 4468 data->cursordrag = CURSORDRAG_ENDSEL; 4469 4470 window_copy_set_selection(wme, 1, 0); 4471 } 4472 4473 static int 4474 window_copy_adjust_selection(struct window_mode_entry *wme, u_int *selx, 4475 u_int *sely) 4476 { 4477 struct window_copy_mode_data *data = wme->data; 4478 struct screen *s = &data->screen; 4479 u_int sx, sy, ty; 4480 int relpos; 4481 4482 sx = *selx; 4483 sy = *sely; 4484 4485 ty = screen_hsize(data->backing) - data->oy; 4486 if (sy < ty) { 4487 relpos = WINDOW_COPY_REL_POS_ABOVE; 4488 if (!data->rectflag) 4489 sx = 0; 4490 sy = 0; 4491 } else if (sy > ty + screen_size_y(s) - 1) { 4492 relpos = WINDOW_COPY_REL_POS_BELOW; 4493 if (!data->rectflag) 4494 sx = screen_size_x(s) - 1; 4495 sy = screen_size_y(s) - 1; 4496 } else { 4497 relpos = WINDOW_COPY_REL_POS_ON_SCREEN; 4498 sy -= ty; 4499 } 4500 4501 *selx = sx; 4502 *sely = sy; 4503 return (relpos); 4504 } 4505 4506 static int 4507 window_copy_update_selection(struct window_mode_entry *wme, int may_redraw, 4508 int no_reset) 4509 { 4510 struct window_copy_mode_data *data = wme->data; 4511 struct screen *s = &data->screen; 4512 4513 if (s->sel == NULL && data->lineflag == LINE_SEL_NONE) 4514 return (0); 4515 return (window_copy_set_selection(wme, may_redraw, no_reset)); 4516 } 4517 4518 static int 4519 window_copy_set_selection(struct window_mode_entry *wme, int may_redraw, 4520 int no_reset) 4521 { 4522 struct window_pane *wp = wme->wp; 4523 struct window_copy_mode_data *data = wme->data; 4524 struct screen *s = &data->screen; 4525 struct options *oo = wp->window->options; 4526 struct grid_cell gc; 4527 u_int sx, sy, cy, endsx, endsy; 4528 int startrelpos, endrelpos; 4529 4530 window_copy_synchronize_cursor(wme, no_reset); 4531 4532 /* Adjust the selection. */ 4533 sx = data->selx; 4534 sy = data->sely; 4535 startrelpos = window_copy_adjust_selection(wme, &sx, &sy); 4536 4537 /* Adjust the end of selection. */ 4538 endsx = data->endselx; 4539 endsy = data->endsely; 4540 endrelpos = window_copy_adjust_selection(wme, &endsx, &endsy); 4541 4542 /* Selection is outside of the current screen */ 4543 if (startrelpos == endrelpos && 4544 startrelpos != WINDOW_COPY_REL_POS_ON_SCREEN) { 4545 screen_hide_selection(s); 4546 return (0); 4547 } 4548 4549 /* Set colours and selection. */ 4550 style_apply(&gc, oo, "mode-style", NULL); 4551 gc.flags |= GRID_FLAG_NOPALETTE; 4552 screen_set_selection(s, sx, sy, endsx, endsy, data->rectflag, 4553 data->modekeys, &gc); 4554 4555 if (data->rectflag && may_redraw) { 4556 /* 4557 * Can't rely on the caller to redraw the right lines for 4558 * rectangle selection - find the highest line and the number 4559 * of lines, and redraw just past that in both directions 4560 */ 4561 cy = data->cy; 4562 if (data->cursordrag == CURSORDRAG_ENDSEL) { 4563 if (sy < cy) 4564 window_copy_redraw_lines(wme, sy, cy - sy + 1); 4565 else 4566 window_copy_redraw_lines(wme, cy, sy - cy + 1); 4567 } else { 4568 if (endsy < cy) { 4569 window_copy_redraw_lines(wme, endsy, 4570 cy - endsy + 1); 4571 } else { 4572 window_copy_redraw_lines(wme, cy, 4573 endsy - cy + 1); 4574 } 4575 } 4576 } 4577 4578 return (1); 4579 } 4580 4581 static void * 4582 window_copy_get_selection(struct window_mode_entry *wme, size_t *len) 4583 { 4584 struct window_pane *wp = wme->wp; 4585 struct window_copy_mode_data *data = wme->data; 4586 struct screen *s = &data->screen; 4587 char *buf; 4588 size_t off; 4589 u_int i, xx, yy, sx, sy, ex, ey, ey_last; 4590 u_int firstsx, lastex, restex, restsx, selx; 4591 int keys; 4592 4593 if (data->screen.sel == NULL && data->lineflag == LINE_SEL_NONE) { 4594 buf = window_copy_match_at_cursor(data); 4595 if (buf != NULL) 4596 *len = strlen(buf); 4597 else 4598 *len = 0; 4599 return (buf); 4600 } 4601 4602 buf = xmalloc(1); 4603 off = 0; 4604 4605 *buf = '\0'; 4606 4607 /* 4608 * The selection extends from selx,sely to (adjusted) cx,cy on 4609 * the base screen. 4610 */ 4611 4612 /* Find start and end. */ 4613 xx = data->endselx; 4614 yy = data->endsely; 4615 if (yy < data->sely || (yy == data->sely && xx < data->selx)) { 4616 sx = xx; sy = yy; 4617 ex = data->selx; ey = data->sely; 4618 } else { 4619 sx = data->selx; sy = data->sely; 4620 ex = xx; ey = yy; 4621 } 4622 4623 /* Trim ex to end of line. */ 4624 ey_last = window_copy_find_length(wme, ey); 4625 if (ex > ey_last) 4626 ex = ey_last; 4627 4628 /* 4629 * Deal with rectangle-copy if necessary; four situations: start of 4630 * first line (firstsx), end of last line (lastex), start (restsx) and 4631 * end (restex) of all other lines. 4632 */ 4633 xx = screen_size_x(s); 4634 4635 /* 4636 * Behave according to mode-keys. If it is emacs, copy like emacs, 4637 * keeping the top-left-most character, and dropping the 4638 * bottom-right-most, regardless of copy direction. If it is vi, also 4639 * keep bottom-right-most character. 4640 */ 4641 keys = options_get_number(wp->window->options, "mode-keys"); 4642 if (data->rectflag) { 4643 /* 4644 * Need to ignore the column with the cursor in it, which for 4645 * rectangular copy means knowing which side the cursor is on. 4646 */ 4647 if (data->cursordrag == CURSORDRAG_ENDSEL) 4648 selx = data->selx; 4649 else 4650 selx = data->endselx; 4651 if (selx < data->cx) { 4652 /* Selection start is on the left. */ 4653 if (keys == MODEKEY_EMACS) { 4654 lastex = data->cx; 4655 restex = data->cx; 4656 } 4657 else { 4658 lastex = data->cx + 1; 4659 restex = data->cx + 1; 4660 } 4661 firstsx = selx; 4662 restsx = selx; 4663 } else { 4664 /* Cursor is on the left. */ 4665 lastex = selx + 1; 4666 restex = selx + 1; 4667 firstsx = data->cx; 4668 restsx = data->cx; 4669 } 4670 } else { 4671 if (keys == MODEKEY_EMACS) 4672 lastex = ex; 4673 else 4674 lastex = ex + 1; 4675 restex = xx; 4676 firstsx = sx; 4677 restsx = 0; 4678 } 4679 4680 /* Copy the lines. */ 4681 for (i = sy; i <= ey; i++) { 4682 window_copy_copy_line(wme, &buf, &off, i, 4683 (i == sy ? firstsx : restsx), 4684 (i == ey ? lastex : restex)); 4685 } 4686 4687 /* Don't bother if no data. */ 4688 if (off == 0) { 4689 free(buf); 4690 *len = 0; 4691 return (NULL); 4692 } 4693 /* Remove final \n (unless at end in vi mode). */ 4694 if (keys == MODEKEY_EMACS || lastex <= ey_last) { 4695 if (~grid_get_line(data->backing->grid, ey)->flags & 4696 GRID_LINE_WRAPPED || lastex != ey_last) 4697 off -= 1; 4698 } 4699 *len = off; 4700 return (buf); 4701 } 4702 4703 static void 4704 window_copy_copy_buffer(struct window_mode_entry *wme, const char *prefix, 4705 void *buf, size_t len) 4706 { 4707 struct window_pane *wp = wme->wp; 4708 struct screen_write_ctx ctx; 4709 4710 if (options_get_number(global_options, "set-clipboard") != 0) { 4711 screen_write_start_pane(&ctx, wp, NULL); 4712 screen_write_setselection(&ctx, "", buf, len); 4713 screen_write_stop(&ctx); 4714 notify_pane("pane-set-clipboard", wp); 4715 } 4716 4717 paste_add(prefix, buf, len); 4718 } 4719 4720 static void * 4721 window_copy_pipe_run(struct window_mode_entry *wme, struct session *s, 4722 const char *cmd, size_t *len) 4723 { 4724 void *buf; 4725 struct job *job; 4726 4727 buf = window_copy_get_selection(wme, len); 4728 if (cmd == NULL || *cmd == '\0') 4729 cmd = options_get_string(global_options, "copy-command"); 4730 if (cmd != NULL && *cmd != '\0') { 4731 job = job_run(cmd, 0, NULL, NULL, s, NULL, NULL, NULL, NULL, 4732 NULL, JOB_NOWAIT, -1, -1); 4733 bufferevent_write(job_get_event(job), buf, *len); 4734 } 4735 return (buf); 4736 } 4737 4738 static void 4739 window_copy_pipe(struct window_mode_entry *wme, struct session *s, 4740 const char *cmd) 4741 { 4742 size_t len; 4743 4744 window_copy_pipe_run(wme, s, cmd, &len); 4745 } 4746 4747 static void 4748 window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s, 4749 const char *prefix, const char *cmd) 4750 { 4751 void *buf; 4752 size_t len; 4753 4754 buf = window_copy_pipe_run(wme, s, cmd, &len); 4755 if (buf != NULL) 4756 window_copy_copy_buffer(wme, prefix, buf, len); 4757 } 4758 4759 static void 4760 window_copy_copy_selection(struct window_mode_entry *wme, const char *prefix) 4761 { 4762 char *buf; 4763 size_t len; 4764 4765 buf = window_copy_get_selection(wme, &len); 4766 if (buf != NULL) 4767 window_copy_copy_buffer(wme, prefix, buf, len); 4768 } 4769 4770 static void 4771 window_copy_append_selection(struct window_mode_entry *wme) 4772 { 4773 struct window_pane *wp = wme->wp; 4774 char *buf; 4775 struct paste_buffer *pb; 4776 const char *bufdata, *bufname = NULL; 4777 size_t len, bufsize; 4778 struct screen_write_ctx ctx; 4779 4780 buf = window_copy_get_selection(wme, &len); 4781 if (buf == NULL) 4782 return; 4783 4784 if (options_get_number(global_options, "set-clipboard") != 0) { 4785 screen_write_start_pane(&ctx, wp, NULL); 4786 screen_write_setselection(&ctx, "", buf, len); 4787 screen_write_stop(&ctx); 4788 notify_pane("pane-set-clipboard", wp); 4789 } 4790 4791 pb = paste_get_top(&bufname); 4792 if (pb != NULL) { 4793 bufdata = paste_buffer_data(pb, &bufsize); 4794 buf = xrealloc(buf, len + bufsize); 4795 memmove(buf + bufsize, buf, len); 4796 memcpy(buf, bufdata, bufsize); 4797 len += bufsize; 4798 } 4799 if (paste_set(buf, len, bufname, NULL) != 0) 4800 free(buf); 4801 } 4802 4803 static void 4804 window_copy_copy_line(struct window_mode_entry *wme, char **buf, size_t *off, 4805 u_int sy, u_int sx, u_int ex) 4806 { 4807 struct window_copy_mode_data *data = wme->data; 4808 struct grid *gd = data->backing->grid; 4809 struct grid_cell gc; 4810 struct grid_line *gl; 4811 struct utf8_data ud; 4812 u_int i, xx, wrapped = 0; 4813 const char *s; 4814 4815 if (sx > ex) 4816 return; 4817 4818 /* 4819 * Work out if the line was wrapped at the screen edge and all of it is 4820 * on screen. 4821 */ 4822 gl = grid_get_line(gd, sy); 4823 if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx) 4824 wrapped = 1; 4825 4826 /* If the line was wrapped, don't strip spaces (use the full length). */ 4827 if (wrapped) 4828 xx = gl->cellsize; 4829 else 4830 xx = window_copy_find_length(wme, sy); 4831 if (ex > xx) 4832 ex = xx; 4833 if (sx > xx) 4834 sx = xx; 4835 4836 if (sx < ex) { 4837 for (i = sx; i < ex; i++) { 4838 grid_get_cell(gd, i, sy, &gc); 4839 if (gc.flags & GRID_FLAG_PADDING) 4840 continue; 4841 utf8_copy(&ud, &gc.data); 4842 if (ud.size == 1 && (gc.attr & GRID_ATTR_CHARSET)) { 4843 s = tty_acs_get(NULL, ud.data[0]); 4844 if (s != NULL && strlen(s) <= sizeof ud.data) { 4845 ud.size = strlen(s); 4846 memcpy(ud.data, s, ud.size); 4847 } 4848 } 4849 4850 *buf = xrealloc(*buf, (*off) + ud.size); 4851 memcpy(*buf + *off, ud.data, ud.size); 4852 *off += ud.size; 4853 } 4854 } 4855 4856 /* Only add a newline if the line wasn't wrapped. */ 4857 if (!wrapped || ex != xx) { 4858 *buf = xrealloc(*buf, (*off) + 1); 4859 (*buf)[(*off)++] = '\n'; 4860 } 4861 } 4862 4863 static void 4864 window_copy_clear_selection(struct window_mode_entry *wme) 4865 { 4866 struct window_copy_mode_data *data = wme->data; 4867 u_int px, py; 4868 4869 screen_clear_selection(&data->screen); 4870 4871 data->cursordrag = CURSORDRAG_NONE; 4872 data->lineflag = LINE_SEL_NONE; 4873 data->selflag = SEL_CHAR; 4874 4875 py = screen_hsize(data->backing) + data->cy - data->oy; 4876 px = window_copy_find_length(wme, py); 4877 if (data->cx > px) 4878 window_copy_update_cursor(wme, px, data->cy); 4879 } 4880 4881 static int 4882 window_copy_in_set(struct window_mode_entry *wme, u_int px, u_int py, 4883 const char *set) 4884 { 4885 struct window_copy_mode_data *data = wme->data; 4886 struct grid_cell gc; 4887 4888 grid_get_cell(data->backing->grid, px, py, &gc); 4889 if (gc.flags & GRID_FLAG_PADDING) 4890 return (0); 4891 return (utf8_cstrhas(set, &gc.data)); 4892 } 4893 4894 static u_int 4895 window_copy_find_length(struct window_mode_entry *wme, u_int py) 4896 { 4897 struct window_copy_mode_data *data = wme->data; 4898 4899 return (grid_line_length(data->backing->grid, py)); 4900 } 4901 4902 static void 4903 window_copy_cursor_start_of_line(struct window_mode_entry *wme) 4904 { 4905 struct window_copy_mode_data *data = wme->data; 4906 struct screen *back_s = data->backing; 4907 struct grid_reader gr; 4908 u_int px, py, oldy, hsize; 4909 4910 px = data->cx; 4911 hsize = screen_hsize(back_s); 4912 py = hsize + data->cy - data->oy; 4913 oldy = data->cy; 4914 4915 grid_reader_start(&gr, back_s->grid, px, py); 4916 grid_reader_cursor_start_of_line(&gr, 1); 4917 grid_reader_get_cursor(&gr, &px, &py); 4918 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py); 4919 } 4920 4921 static void 4922 window_copy_cursor_back_to_indentation(struct window_mode_entry *wme) 4923 { 4924 struct window_copy_mode_data *data = wme->data; 4925 struct screen *back_s = data->backing; 4926 struct grid_reader gr; 4927 u_int px, py, oldy, hsize; 4928 4929 px = data->cx; 4930 hsize = screen_hsize(back_s); 4931 py = hsize + data->cy - data->oy; 4932 oldy = data->cy; 4933 4934 grid_reader_start(&gr, back_s->grid, px, py); 4935 grid_reader_cursor_back_to_indentation(&gr); 4936 grid_reader_get_cursor(&gr, &px, &py); 4937 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py); 4938 } 4939 4940 static void 4941 window_copy_cursor_end_of_line(struct window_mode_entry *wme) 4942 { 4943 struct window_copy_mode_data *data = wme->data; 4944 struct screen *back_s = data->backing; 4945 struct grid_reader gr; 4946 u_int px, py, oldy, hsize; 4947 4948 px = data->cx; 4949 hsize = screen_hsize(back_s); 4950 py = hsize + data->cy - data->oy; 4951 oldy = data->cy; 4952 4953 grid_reader_start(&gr, back_s->grid, px, py); 4954 if (data->screen.sel != NULL && data->rectflag) 4955 grid_reader_cursor_end_of_line(&gr, 1, 1); 4956 else 4957 grid_reader_cursor_end_of_line(&gr, 1, 0); 4958 grid_reader_get_cursor(&gr, &px, &py); 4959 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s), 4960 data->oy, oldy, px, py, 0); 4961 } 4962 4963 static void 4964 window_copy_other_end(struct window_mode_entry *wme) 4965 { 4966 struct window_copy_mode_data *data = wme->data; 4967 struct screen *s = &data->screen; 4968 u_int selx, sely, cy, yy, hsize; 4969 4970 if (s->sel == NULL && data->lineflag == LINE_SEL_NONE) 4971 return; 4972 4973 if (data->lineflag == LINE_SEL_LEFT_RIGHT) 4974 data->lineflag = LINE_SEL_RIGHT_LEFT; 4975 else if (data->lineflag == LINE_SEL_RIGHT_LEFT) 4976 data->lineflag = LINE_SEL_LEFT_RIGHT; 4977 4978 switch (data->cursordrag) { 4979 case CURSORDRAG_NONE: 4980 case CURSORDRAG_SEL: 4981 data->cursordrag = CURSORDRAG_ENDSEL; 4982 break; 4983 case CURSORDRAG_ENDSEL: 4984 data->cursordrag = CURSORDRAG_SEL; 4985 break; 4986 } 4987 4988 selx = data->endselx; 4989 sely = data->endsely; 4990 if (data->cursordrag == CURSORDRAG_SEL) { 4991 selx = data->selx; 4992 sely = data->sely; 4993 } 4994 4995 cy = data->cy; 4996 yy = screen_hsize(data->backing) + data->cy - data->oy; 4997 4998 data->cx = selx; 4999 5000 hsize = screen_hsize(data->backing); 5001 if (sely < hsize - data->oy) { /* above */ 5002 data->oy = hsize - sely; 5003 data->cy = 0; 5004 } else if (sely > hsize - data->oy + screen_size_y(s)) { /* below */ 5005 data->oy = hsize - sely + screen_size_y(s) - 1; 5006 data->cy = screen_size_y(s) - 1; 5007 } else 5008 data->cy = cy + sely - yy; 5009 5010 window_copy_update_selection(wme, 1, 1); 5011 window_copy_redraw_screen(wme); 5012 } 5013 5014 static void 5015 window_copy_cursor_left(struct window_mode_entry *wme) 5016 { 5017 struct window_copy_mode_data *data = wme->data; 5018 struct screen *back_s = data->backing; 5019 struct grid_reader gr; 5020 u_int px, py, oldy, hsize; 5021 5022 px = data->cx; 5023 hsize = screen_hsize(back_s); 5024 py = hsize + data->cy - data->oy; 5025 oldy = data->cy; 5026 5027 grid_reader_start(&gr, back_s->grid, px, py); 5028 grid_reader_cursor_left(&gr, 1); 5029 grid_reader_get_cursor(&gr, &px, &py); 5030 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py); 5031 } 5032 5033 static void 5034 window_copy_cursor_right(struct window_mode_entry *wme, int all) 5035 { 5036 struct window_copy_mode_data *data = wme->data; 5037 struct screen *back_s = data->backing; 5038 struct grid_reader gr; 5039 u_int px, py, oldy, hsize; 5040 5041 px = data->cx; 5042 hsize = screen_hsize(back_s); 5043 py = hsize + data->cy - data->oy; 5044 oldy = data->cy; 5045 5046 grid_reader_start(&gr, back_s->grid, px, py); 5047 grid_reader_cursor_right(&gr, 1, all); 5048 grid_reader_get_cursor(&gr, &px, &py); 5049 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s), 5050 data->oy, oldy, px, py, 0); 5051 } 5052 5053 static void 5054 window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only) 5055 { 5056 struct window_copy_mode_data *data = wme->data; 5057 struct screen *s = &data->screen; 5058 u_int ox, oy, px, py; 5059 int norectsel; 5060 5061 norectsel = data->screen.sel == NULL || !data->rectflag; 5062 oy = screen_hsize(data->backing) + data->cy - data->oy; 5063 ox = window_copy_find_length(wme, oy); 5064 if (norectsel && data->cx != ox) { 5065 data->lastcx = data->cx; 5066 data->lastsx = ox; 5067 } 5068 5069 if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely) 5070 window_copy_other_end(wme); 5071 5072 if (scroll_only || data->cy == 0) { 5073 if (norectsel) 5074 data->cx = data->lastcx; 5075 window_copy_scroll_down(wme, 1); 5076 if (scroll_only) { 5077 if (data->cy == screen_size_y(s) - 1) 5078 window_copy_redraw_lines(wme, data->cy, 1); 5079 else 5080 window_copy_redraw_lines(wme, data->cy, 2); 5081 } 5082 } else { 5083 if (norectsel) { 5084 window_copy_update_cursor(wme, data->lastcx, 5085 data->cy - 1); 5086 } else 5087 window_copy_update_cursor(wme, data->cx, data->cy - 1); 5088 if (window_copy_update_selection(wme, 1, 0)) { 5089 if (data->cy == screen_size_y(s) - 1) 5090 window_copy_redraw_lines(wme, data->cy, 1); 5091 else 5092 window_copy_redraw_lines(wme, data->cy, 2); 5093 } 5094 } 5095 5096 if (norectsel) { 5097 py = screen_hsize(data->backing) + data->cy - data->oy; 5098 px = window_copy_find_length(wme, py); 5099 if ((data->cx >= data->lastsx && data->cx != px) || 5100 data->cx > px) 5101 { 5102 window_copy_update_cursor(wme, px, data->cy); 5103 if (window_copy_update_selection(wme, 1, 0)) 5104 window_copy_redraw_lines(wme, data->cy, 1); 5105 } 5106 } 5107 5108 if (data->lineflag == LINE_SEL_LEFT_RIGHT) 5109 { 5110 py = screen_hsize(data->backing) + data->cy - data->oy; 5111 if (data->rectflag) 5112 px = screen_size_x(data->backing); 5113 else 5114 px = window_copy_find_length(wme, py); 5115 window_copy_update_cursor(wme, px, data->cy); 5116 if (window_copy_update_selection(wme, 1, 0)) 5117 window_copy_redraw_lines(wme, data->cy, 1); 5118 } 5119 else if (data->lineflag == LINE_SEL_RIGHT_LEFT) 5120 { 5121 window_copy_update_cursor(wme, 0, data->cy); 5122 if (window_copy_update_selection(wme, 1, 0)) 5123 window_copy_redraw_lines(wme, data->cy, 1); 5124 } 5125 } 5126 5127 static void 5128 window_copy_cursor_down(struct window_mode_entry *wme, int scroll_only) 5129 { 5130 struct window_copy_mode_data *data = wme->data; 5131 struct screen *s = &data->screen; 5132 u_int ox, oy, px, py; 5133 int norectsel; 5134 5135 norectsel = data->screen.sel == NULL || !data->rectflag; 5136 oy = screen_hsize(data->backing) + data->cy - data->oy; 5137 ox = window_copy_find_length(wme, oy); 5138 if (norectsel && data->cx != ox) { 5139 data->lastcx = data->cx; 5140 data->lastsx = ox; 5141 } 5142 5143 if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely) 5144 window_copy_other_end(wme); 5145 5146 if (scroll_only || data->cy == screen_size_y(s) - 1) { 5147 if (norectsel) 5148 data->cx = data->lastcx; 5149 window_copy_scroll_up(wme, 1); 5150 if (scroll_only && data->cy > 0) 5151 window_copy_redraw_lines(wme, data->cy - 1, 2); 5152 } else { 5153 if (norectsel) { 5154 window_copy_update_cursor(wme, data->lastcx, 5155 data->cy + 1); 5156 } else 5157 window_copy_update_cursor(wme, data->cx, data->cy + 1); 5158 if (window_copy_update_selection(wme, 1, 0)) 5159 window_copy_redraw_lines(wme, data->cy - 1, 2); 5160 } 5161 5162 if (norectsel) { 5163 py = screen_hsize(data->backing) + data->cy - data->oy; 5164 px = window_copy_find_length(wme, py); 5165 if ((data->cx >= data->lastsx && data->cx != px) || 5166 data->cx > px) 5167 { 5168 window_copy_update_cursor(wme, px, data->cy); 5169 if (window_copy_update_selection(wme, 1, 0)) 5170 window_copy_redraw_lines(wme, data->cy, 1); 5171 } 5172 } 5173 5174 if (data->lineflag == LINE_SEL_LEFT_RIGHT) 5175 { 5176 py = screen_hsize(data->backing) + data->cy - data->oy; 5177 if (data->rectflag) 5178 px = screen_size_x(data->backing); 5179 else 5180 px = window_copy_find_length(wme, py); 5181 window_copy_update_cursor(wme, px, data->cy); 5182 if (window_copy_update_selection(wme, 1, 0)) 5183 window_copy_redraw_lines(wme, data->cy, 1); 5184 } 5185 else if (data->lineflag == LINE_SEL_RIGHT_LEFT) 5186 { 5187 window_copy_update_cursor(wme, 0, data->cy); 5188 if (window_copy_update_selection(wme, 1, 0)) 5189 window_copy_redraw_lines(wme, data->cy, 1); 5190 } 5191 } 5192 5193 static void 5194 window_copy_cursor_jump(struct window_mode_entry *wme) 5195 { 5196 struct window_copy_mode_data *data = wme->data; 5197 struct screen *back_s = data->backing; 5198 struct grid_reader gr; 5199 u_int px, py, oldy, hsize; 5200 5201 px = data->cx + 1; 5202 hsize = screen_hsize(back_s); 5203 py = hsize + data->cy - data->oy; 5204 oldy = data->cy; 5205 5206 grid_reader_start(&gr, back_s->grid, px, py); 5207 if (grid_reader_cursor_jump(&gr, data->jumpchar)) { 5208 grid_reader_get_cursor(&gr, &px, &py); 5209 window_copy_acquire_cursor_down(wme, hsize, 5210 screen_size_y(back_s), data->oy, oldy, px, py, 0); 5211 } 5212 } 5213 5214 static void 5215 window_copy_cursor_jump_back(struct window_mode_entry *wme) 5216 { 5217 struct window_copy_mode_data *data = wme->data; 5218 struct screen *back_s = data->backing; 5219 struct grid_reader gr; 5220 u_int px, py, oldy, hsize; 5221 5222 px = data->cx; 5223 hsize = screen_hsize(back_s); 5224 py = hsize + data->cy - data->oy; 5225 oldy = data->cy; 5226 5227 grid_reader_start(&gr, back_s->grid, px, py); 5228 grid_reader_cursor_left(&gr, 0); 5229 if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) { 5230 grid_reader_get_cursor(&gr, &px, &py); 5231 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, 5232 py); 5233 } 5234 } 5235 5236 static void 5237 window_copy_cursor_jump_to(struct window_mode_entry *wme) 5238 { 5239 struct window_copy_mode_data *data = wme->data; 5240 struct screen *back_s = data->backing; 5241 struct grid_reader gr; 5242 u_int px, py, oldy, hsize; 5243 5244 px = data->cx + 2; 5245 hsize = screen_hsize(back_s); 5246 py = hsize + data->cy - data->oy; 5247 oldy = data->cy; 5248 5249 grid_reader_start(&gr, back_s->grid, px, py); 5250 if (grid_reader_cursor_jump(&gr, data->jumpchar)) { 5251 grid_reader_cursor_left(&gr, 1); 5252 grid_reader_get_cursor(&gr, &px, &py); 5253 window_copy_acquire_cursor_down(wme, hsize, 5254 screen_size_y(back_s), data->oy, oldy, px, py, 0); 5255 } 5256 } 5257 5258 static void 5259 window_copy_cursor_jump_to_back(struct window_mode_entry *wme) 5260 { 5261 struct window_copy_mode_data *data = wme->data; 5262 struct screen *back_s = data->backing; 5263 struct grid_reader gr; 5264 u_int px, py, oldy, hsize; 5265 5266 px = data->cx; 5267 hsize = screen_hsize(back_s); 5268 py = hsize + data->cy - data->oy; 5269 oldy = data->cy; 5270 5271 grid_reader_start(&gr, back_s->grid, px, py); 5272 grid_reader_cursor_left(&gr, 0); 5273 grid_reader_cursor_left(&gr, 0); 5274 if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) { 5275 grid_reader_cursor_right(&gr, 1, 0); 5276 grid_reader_get_cursor(&gr, &px, &py); 5277 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, 5278 py); 5279 } 5280 } 5281 5282 static void 5283 window_copy_cursor_next_word(struct window_mode_entry *wme, 5284 const char *separators) 5285 { 5286 struct window_copy_mode_data *data = wme->data; 5287 struct screen *back_s = data->backing; 5288 struct grid_reader gr; 5289 u_int px, py, oldy, hsize; 5290 5291 px = data->cx; 5292 hsize = screen_hsize(back_s); 5293 py = hsize + data->cy - data->oy; 5294 oldy = data->cy; 5295 5296 grid_reader_start(&gr, back_s->grid, px, py); 5297 grid_reader_cursor_next_word(&gr, separators); 5298 grid_reader_get_cursor(&gr, &px, &py); 5299 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s), 5300 data->oy, oldy, px, py, 0); 5301 } 5302 5303 /* Compute the next place where a word ends. */ 5304 static void 5305 window_copy_cursor_next_word_end_pos(struct window_mode_entry *wme, 5306 const char *separators, u_int *ppx, u_int *ppy) 5307 { 5308 struct window_pane *wp = wme->wp; 5309 struct window_copy_mode_data *data = wme->data; 5310 struct options *oo = wp->window->options; 5311 struct screen *back_s = data->backing; 5312 struct grid_reader gr; 5313 u_int px, py, hsize; 5314 5315 px = data->cx; 5316 hsize = screen_hsize(back_s); 5317 py = hsize + data->cy - data->oy; 5318 5319 grid_reader_start(&gr, back_s->grid, px, py); 5320 if (options_get_number(oo, "mode-keys") == MODEKEY_VI) { 5321 if (!grid_reader_in_set(&gr, WHITESPACE)) 5322 grid_reader_cursor_right(&gr, 0, 0); 5323 grid_reader_cursor_next_word_end(&gr, separators); 5324 grid_reader_cursor_left(&gr, 1); 5325 } else 5326 grid_reader_cursor_next_word_end(&gr, separators); 5327 grid_reader_get_cursor(&gr, &px, &py); 5328 *ppx = px; 5329 *ppy = py; 5330 } 5331 5332 /* Move to the next place where a word ends. */ 5333 static void 5334 window_copy_cursor_next_word_end(struct window_mode_entry *wme, 5335 const char *separators, int no_reset) 5336 { 5337 struct window_pane *wp = wme->wp; 5338 struct window_copy_mode_data *data = wme->data; 5339 struct options *oo = wp->window->options; 5340 struct screen *back_s = data->backing; 5341 struct grid_reader gr; 5342 u_int px, py, oldy, hsize; 5343 5344 px = data->cx; 5345 hsize = screen_hsize(back_s); 5346 py = hsize + data->cy - data->oy; 5347 oldy = data->cy; 5348 5349 grid_reader_start(&gr, back_s->grid, px, py); 5350 if (options_get_number(oo, "mode-keys") == MODEKEY_VI) { 5351 if (!grid_reader_in_set(&gr, WHITESPACE)) 5352 grid_reader_cursor_right(&gr, 0, 0); 5353 grid_reader_cursor_next_word_end(&gr, separators); 5354 grid_reader_cursor_left(&gr, 1); 5355 } else 5356 grid_reader_cursor_next_word_end(&gr, separators); 5357 grid_reader_get_cursor(&gr, &px, &py); 5358 window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s), 5359 data->oy, oldy, px, py, no_reset); 5360 } 5361 5362 /* Compute the previous place where a word begins. */ 5363 static void 5364 window_copy_cursor_previous_word_pos(struct window_mode_entry *wme, 5365 const char *separators, u_int *ppx, u_int *ppy) 5366 { 5367 struct window_copy_mode_data *data = wme->data; 5368 struct screen *back_s = data->backing; 5369 struct grid_reader gr; 5370 u_int px, py, hsize; 5371 5372 px = data->cx; 5373 hsize = screen_hsize(back_s); 5374 py = hsize + data->cy - data->oy; 5375 5376 grid_reader_start(&gr, back_s->grid, px, py); 5377 grid_reader_cursor_previous_word(&gr, separators, /* already= */ 0, 5378 /* stop_at_eol= */ 1); 5379 grid_reader_get_cursor(&gr, &px, &py); 5380 *ppx = px; 5381 *ppy = py; 5382 } 5383 5384 /* Move to the previous place where a word begins. */ 5385 static void 5386 window_copy_cursor_previous_word(struct window_mode_entry *wme, 5387 const char *separators, int already) 5388 { 5389 struct window_copy_mode_data *data = wme->data; 5390 struct window *w = wme->wp->window; 5391 struct screen *back_s = data->backing; 5392 struct grid_reader gr; 5393 u_int px, py, oldy, hsize; 5394 int stop_at_eol; 5395 5396 if (options_get_number(w->options, "mode-keys") == MODEKEY_EMACS) 5397 stop_at_eol = 1; 5398 else 5399 stop_at_eol = 0; 5400 5401 px = data->cx; 5402 hsize = screen_hsize(back_s); 5403 py = hsize + data->cy - data->oy; 5404 oldy = data->cy; 5405 5406 grid_reader_start(&gr, back_s->grid, px, py); 5407 grid_reader_cursor_previous_word(&gr, separators, already, stop_at_eol); 5408 grid_reader_get_cursor(&gr, &px, &py); 5409 window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py); 5410 } 5411 5412 static void 5413 window_copy_cursor_prompt(struct window_mode_entry *wme, int direction, 5414 const char *args) 5415 { 5416 struct window_copy_mode_data *data = wme->data; 5417 struct screen *s = data->backing; 5418 struct grid *gd = s->grid; 5419 u_int end_line; 5420 u_int line = gd->hsize - data->oy + data->cy; 5421 int add, line_flag; 5422 5423 if (args != NULL && strcmp(args, "-o") == 0) 5424 line_flag = GRID_LINE_START_OUTPUT; 5425 else 5426 line_flag = GRID_LINE_START_PROMPT; 5427 5428 if (direction == 0) { /* up */ 5429 add = -1; 5430 end_line = 0; 5431 } else { /* down */ 5432 add = 1; 5433 end_line = gd->hsize + gd->sy - 1; 5434 } 5435 5436 if (line == end_line) 5437 return; 5438 for (;;) { 5439 if (line == end_line) 5440 return; 5441 line += add; 5442 5443 if (grid_get_line(gd, line)->flags & line_flag) 5444 break; 5445 } 5446 5447 data->cx = 0; 5448 if (line > gd->hsize) { 5449 data->cy = line - gd->hsize; 5450 data->oy = 0; 5451 } else { 5452 data->cy = 0; 5453 data->oy = gd->hsize - line; 5454 } 5455 5456 window_copy_update_selection(wme, 1, 0); 5457 window_copy_redraw_screen(wme); 5458 } 5459 5460 static void 5461 window_copy_scroll_up(struct window_mode_entry *wme, u_int ny) 5462 { 5463 struct window_pane *wp = wme->wp; 5464 struct window_copy_mode_data *data = wme->data; 5465 struct screen *s = &data->screen; 5466 struct screen_write_ctx ctx; 5467 5468 if (data->oy < ny) 5469 ny = data->oy; 5470 if (ny == 0) 5471 return; 5472 data->oy -= ny; 5473 5474 if (data->searchmark != NULL && !data->timeout) 5475 window_copy_search_marks(wme, NULL, data->searchregex, 1); 5476 window_copy_update_selection(wme, 0, 0); 5477 5478 screen_write_start_pane(&ctx, wp, NULL); 5479 screen_write_cursormove(&ctx, 0, 0, 0); 5480 screen_write_deleteline(&ctx, ny, 8); 5481 window_copy_write_lines(wme, &ctx, screen_size_y(s) - ny, ny); 5482 window_copy_write_line(wme, &ctx, 0); 5483 if (screen_size_y(s) > 1) 5484 window_copy_write_line(wme, &ctx, 1); 5485 if (screen_size_y(s) > 3) 5486 window_copy_write_line(wme, &ctx, screen_size_y(s) - 2); 5487 if (s->sel != NULL && screen_size_y(s) > ny) 5488 window_copy_write_line(wme, &ctx, screen_size_y(s) - ny - 1); 5489 screen_write_cursormove(&ctx, data->cx, data->cy, 0); 5490 screen_write_stop(&ctx); 5491 } 5492 5493 static void 5494 window_copy_scroll_down(struct window_mode_entry *wme, u_int ny) 5495 { 5496 struct window_pane *wp = wme->wp; 5497 struct window_copy_mode_data *data = wme->data; 5498 struct screen *s = &data->screen; 5499 struct screen_write_ctx ctx; 5500 5501 if (ny > screen_hsize(data->backing)) 5502 return; 5503 5504 if (data->oy > screen_hsize(data->backing) - ny) 5505 ny = screen_hsize(data->backing) - data->oy; 5506 if (ny == 0) 5507 return; 5508 data->oy += ny; 5509 5510 if (data->searchmark != NULL && !data->timeout) 5511 window_copy_search_marks(wme, NULL, data->searchregex, 1); 5512 window_copy_update_selection(wme, 0, 0); 5513 5514 screen_write_start_pane(&ctx, wp, NULL); 5515 screen_write_cursormove(&ctx, 0, 0, 0); 5516 screen_write_insertline(&ctx, ny, 8); 5517 window_copy_write_lines(wme, &ctx, 0, ny); 5518 if (s->sel != NULL && screen_size_y(s) > ny) 5519 window_copy_write_line(wme, &ctx, ny); 5520 else if (ny == 1) /* nuke position */ 5521 window_copy_write_line(wme, &ctx, 1); 5522 screen_write_cursormove(&ctx, data->cx, data->cy, 0); 5523 screen_write_stop(&ctx); 5524 } 5525 5526 static void 5527 window_copy_rectangle_set(struct window_mode_entry *wme, int rectflag) 5528 { 5529 struct window_copy_mode_data *data = wme->data; 5530 u_int px, py; 5531 5532 data->rectflag = rectflag; 5533 5534 py = screen_hsize(data->backing) + data->cy - data->oy; 5535 px = window_copy_find_length(wme, py); 5536 if (data->cx > px) 5537 window_copy_update_cursor(wme, px, data->cy); 5538 5539 window_copy_update_selection(wme, 1, 0); 5540 window_copy_redraw_screen(wme); 5541 } 5542 5543 static void 5544 window_copy_move_mouse(struct mouse_event *m) 5545 { 5546 struct window_pane *wp; 5547 struct window_mode_entry *wme; 5548 u_int x, y; 5549 5550 wp = cmd_mouse_pane(m, NULL, NULL); 5551 if (wp == NULL) 5552 return; 5553 wme = TAILQ_FIRST(&wp->modes); 5554 if (wme == NULL) 5555 return; 5556 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode) 5557 return; 5558 5559 if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) 5560 return; 5561 5562 window_copy_update_cursor(wme, x, y); 5563 } 5564 5565 void 5566 window_copy_start_drag(struct client *c, struct mouse_event *m) 5567 { 5568 struct window_pane *wp; 5569 struct window_mode_entry *wme; 5570 struct window_copy_mode_data *data; 5571 u_int x, y, yg; 5572 5573 if (c == NULL) 5574 return; 5575 5576 wp = cmd_mouse_pane(m, NULL, NULL); 5577 if (wp == NULL) 5578 return; 5579 wme = TAILQ_FIRST(&wp->modes); 5580 if (wme == NULL) 5581 return; 5582 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode) 5583 return; 5584 5585 if (cmd_mouse_at(wp, m, &x, &y, 1) != 0) 5586 return; 5587 5588 c->tty.mouse_drag_update = window_copy_drag_update; 5589 c->tty.mouse_drag_release = window_copy_drag_release; 5590 5591 data = wme->data; 5592 yg = screen_hsize(data->backing) + y - data->oy; 5593 if (x < data->selrx || x > data->endselrx || yg != data->selry) 5594 data->selflag = SEL_CHAR; 5595 switch (data->selflag) { 5596 case SEL_WORD: 5597 if (data->separators != NULL) { 5598 window_copy_update_cursor(wme, x, y); 5599 window_copy_cursor_previous_word_pos(wme, 5600 data->separators, &x, &y); 5601 y -= screen_hsize(data->backing) - data->oy; 5602 } 5603 window_copy_update_cursor(wme, x, y); 5604 break; 5605 case SEL_LINE: 5606 window_copy_update_cursor(wme, 0, y); 5607 break; 5608 case SEL_CHAR: 5609 window_copy_update_cursor(wme, x, y); 5610 window_copy_start_selection(wme); 5611 break; 5612 } 5613 5614 window_copy_redraw_screen(wme); 5615 window_copy_drag_update(c, m); 5616 } 5617 5618 static void 5619 window_copy_drag_update(struct client *c, struct mouse_event *m) 5620 { 5621 struct window_pane *wp; 5622 struct window_mode_entry *wme; 5623 struct window_copy_mode_data *data; 5624 u_int x, y, old_cx, old_cy; 5625 struct timeval tv = { 5626 .tv_usec = WINDOW_COPY_DRAG_REPEAT_TIME 5627 }; 5628 5629 if (c == NULL) 5630 return; 5631 5632 wp = cmd_mouse_pane(m, NULL, NULL); 5633 if (wp == NULL) 5634 return; 5635 wme = TAILQ_FIRST(&wp->modes); 5636 if (wme == NULL) 5637 return; 5638 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode) 5639 return; 5640 5641 data = wme->data; 5642 evtimer_del(&data->dragtimer); 5643 5644 if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) 5645 return; 5646 old_cx = data->cx; 5647 old_cy = data->cy; 5648 5649 window_copy_update_cursor(wme, x, y); 5650 if (window_copy_update_selection(wme, 1, 0)) 5651 window_copy_redraw_selection(wme, old_cy); 5652 if (old_cy != data->cy || old_cx == data->cx) { 5653 if (y == 0) { 5654 evtimer_add(&data->dragtimer, &tv); 5655 window_copy_cursor_up(wme, 1); 5656 } else if (y == screen_size_y(&data->screen) - 1) { 5657 evtimer_add(&data->dragtimer, &tv); 5658 window_copy_cursor_down(wme, 1); 5659 } 5660 } 5661 } 5662 5663 static void 5664 window_copy_drag_release(struct client *c, struct mouse_event *m) 5665 { 5666 struct window_pane *wp; 5667 struct window_mode_entry *wme; 5668 struct window_copy_mode_data *data; 5669 5670 if (c == NULL) 5671 return; 5672 5673 wp = cmd_mouse_pane(m, NULL, NULL); 5674 if (wp == NULL) 5675 return; 5676 wme = TAILQ_FIRST(&wp->modes); 5677 if (wme == NULL) 5678 return; 5679 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode) 5680 return; 5681 5682 data = wme->data; 5683 evtimer_del(&data->dragtimer); 5684 } 5685 5686 static void 5687 window_copy_jump_to_mark(struct window_mode_entry *wme) 5688 { 5689 struct window_copy_mode_data *data = wme->data; 5690 u_int tmx, tmy; 5691 5692 tmx = data->cx; 5693 tmy = screen_hsize(data->backing) + data->cy - data->oy; 5694 data->cx = data->mx; 5695 if (data->my < screen_hsize(data->backing)) { 5696 data->cy = 0; 5697 data->oy = screen_hsize(data->backing) - data->my; 5698 } else { 5699 data->cy = data->my - screen_hsize(data->backing); 5700 data->oy = 0; 5701 } 5702 data->mx = tmx; 5703 data->my = tmy; 5704 data->showmark = 1; 5705 window_copy_update_selection(wme, 0, 0); 5706 window_copy_redraw_screen(wme); 5707 } 5708 5709 /* Scroll up if the cursor went off the visible screen. */ 5710 static void 5711 window_copy_acquire_cursor_up(struct window_mode_entry *wme, u_int hsize, 5712 u_int oy, u_int oldy, u_int px, u_int py) 5713 { 5714 u_int cy, yy, ny, nd; 5715 5716 yy = hsize - oy; 5717 if (py < yy) { 5718 ny = yy - py; 5719 cy = 0; 5720 nd = 1; 5721 } else { 5722 ny = 0; 5723 cy = py - yy; 5724 nd = oldy - cy + 1; 5725 } 5726 while (ny > 0) { 5727 window_copy_cursor_up(wme, 1); 5728 ny--; 5729 } 5730 window_copy_update_cursor(wme, px, cy); 5731 if (window_copy_update_selection(wme, 1, 0)) 5732 window_copy_redraw_lines(wme, cy, nd); 5733 } 5734 5735 /* Scroll down if the cursor went off the visible screen. */ 5736 static void 5737 window_copy_acquire_cursor_down(struct window_mode_entry *wme, u_int hsize, 5738 u_int sy, u_int oy, u_int oldy, u_int px, u_int py, int no_reset) 5739 { 5740 u_int cy, yy, ny, nd; 5741 5742 cy = py - hsize + oy; 5743 yy = sy - 1; 5744 if (cy > yy) { 5745 ny = cy - yy; 5746 oldy = yy; 5747 nd = 1; 5748 } else { 5749 ny = 0; 5750 nd = cy - oldy + 1; 5751 } 5752 while (ny > 0) { 5753 window_copy_cursor_down(wme, 1); 5754 ny--; 5755 } 5756 if (cy > yy) 5757 window_copy_update_cursor(wme, px, yy); 5758 else 5759 window_copy_update_cursor(wme, px, cy); 5760 if (window_copy_update_selection(wme, 1, no_reset)) 5761 window_copy_redraw_lines(wme, oldy, nd); 5762 } 5763