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