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