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