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