1 /* $OpenBSD: window-copy.c,v 1.184 2017/09/13 07:31:07 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 21 #include <ctype.h> 22 #include <stdlib.h> 23 #include <string.h> 24 25 #include "tmux.h" 26 27 static const char *window_copy_key_table(struct window_pane *); 28 static void window_copy_command(struct window_pane *, struct client *, 29 struct session *, struct args *, struct mouse_event *); 30 static struct screen *window_copy_init(struct window_pane *, 31 struct cmd_find_state *, struct args *); 32 static void window_copy_free(struct window_pane *); 33 static int window_copy_pagedown(struct window_pane *, int); 34 static void window_copy_next_paragraph(struct window_pane *); 35 static void window_copy_previous_paragraph(struct window_pane *); 36 static void window_copy_resize(struct window_pane *, u_int, u_int); 37 38 static void window_copy_redraw_selection(struct window_pane *, u_int); 39 static void window_copy_redraw_lines(struct window_pane *, u_int, u_int); 40 static void window_copy_redraw_screen(struct window_pane *); 41 static void window_copy_write_line(struct window_pane *, 42 struct screen_write_ctx *, u_int); 43 static void window_copy_write_lines(struct window_pane *, 44 struct screen_write_ctx *, u_int, u_int); 45 46 static void window_copy_scroll_to(struct window_pane *, u_int, u_int); 47 static int window_copy_search_compare(struct grid *, u_int, u_int, 48 struct grid *, u_int, int); 49 static int window_copy_search_lr(struct grid *, struct grid *, u_int *, 50 u_int, u_int, u_int, int); 51 static int window_copy_search_rl(struct grid *, struct grid *, u_int *, 52 u_int, u_int, u_int, int); 53 static int window_copy_search_marks(struct window_pane *, struct screen *); 54 static void window_copy_clear_marks(struct window_pane *); 55 static void window_copy_move_left(struct screen *, u_int *, u_int *); 56 static void window_copy_move_right(struct screen *, u_int *, u_int *); 57 static int window_copy_is_lowercase(const char *); 58 static int window_copy_search_jump(struct window_pane *, struct grid *, 59 struct grid *, u_int, u_int, u_int, int, int, int); 60 static int window_copy_search(struct window_pane *, int); 61 static int window_copy_search_up(struct window_pane *); 62 static int window_copy_search_down(struct window_pane *); 63 static void window_copy_goto_line(struct window_pane *, const char *); 64 static void window_copy_update_cursor(struct window_pane *, u_int, u_int); 65 static void window_copy_start_selection(struct window_pane *); 66 static int window_copy_adjust_selection(struct window_pane *, u_int *, 67 u_int *); 68 static int window_copy_update_selection(struct window_pane *, int); 69 static void window_copy_synchronize_cursor(struct window_pane *wp); 70 static void *window_copy_get_selection(struct window_pane *, size_t *); 71 static void window_copy_copy_buffer(struct window_pane *, const char *, 72 void *, size_t); 73 static void window_copy_copy_pipe(struct window_pane *, struct session *, 74 const char *, const char *); 75 static void window_copy_copy_selection(struct window_pane *, const char *); 76 static void window_copy_append_selection(struct window_pane *, 77 const char *); 78 static void window_copy_clear_selection(struct window_pane *); 79 static void window_copy_copy_line(struct window_pane *, char **, size_t *, 80 u_int, u_int, u_int); 81 static int window_copy_in_set(struct window_pane *, u_int, u_int, 82 const char *); 83 static u_int window_copy_find_length(struct window_pane *, u_int); 84 static void window_copy_cursor_start_of_line(struct window_pane *); 85 static void window_copy_cursor_back_to_indentation(struct window_pane *); 86 static void window_copy_cursor_end_of_line(struct window_pane *); 87 static void window_copy_other_end(struct window_pane *); 88 static void window_copy_cursor_left(struct window_pane *); 89 static void window_copy_cursor_right(struct window_pane *); 90 static void window_copy_cursor_up(struct window_pane *, int); 91 static void window_copy_cursor_down(struct window_pane *, int); 92 static void window_copy_cursor_jump(struct window_pane *); 93 static void window_copy_cursor_jump_back(struct window_pane *); 94 static void window_copy_cursor_jump_to(struct window_pane *); 95 static void window_copy_cursor_jump_to_back(struct window_pane *); 96 static void window_copy_cursor_next_word(struct window_pane *, 97 const char *); 98 static void window_copy_cursor_next_word_end(struct window_pane *, 99 const char *); 100 static void window_copy_cursor_previous_word(struct window_pane *, 101 const char *); 102 static void window_copy_scroll_up(struct window_pane *, u_int); 103 static void window_copy_scroll_down(struct window_pane *, u_int); 104 static void window_copy_rectangle_toggle(struct window_pane *); 105 static void window_copy_move_mouse(struct mouse_event *); 106 static void window_copy_drag_update(struct client *, struct mouse_event *); 107 108 const struct window_mode window_copy_mode = { 109 .name = "copy-mode", 110 111 .init = window_copy_init, 112 .free = window_copy_free, 113 .resize = window_copy_resize, 114 .key_table = window_copy_key_table, 115 .command = window_copy_command, 116 }; 117 118 enum { 119 WINDOW_COPY_OFF, 120 WINDOW_COPY_SEARCHUP, 121 WINDOW_COPY_SEARCHDOWN, 122 WINDOW_COPY_JUMPFORWARD, 123 WINDOW_COPY_JUMPBACKWARD, 124 WINDOW_COPY_JUMPTOFORWARD, 125 WINDOW_COPY_JUMPTOBACKWARD, 126 }; 127 128 enum { 129 WINDOW_COPY_REL_POS_ABOVE, 130 WINDOW_COPY_REL_POS_ON_SCREEN, 131 WINDOW_COPY_REL_POS_BELOW, 132 }; 133 134 /* 135 * Copy mode's visible screen (the "screen" field) is filled from one of two 136 * sources: the original contents of the pane (used when we actually enter via 137 * the "copy-mode" command, to copy the contents of the current pane), or else 138 * a series of lines containing the output from an output-writing tmux command 139 * (such as any of the "show-*" or "list-*" commands). 140 * 141 * In either case, the full content of the copy-mode grid is pointed at by the 142 * "backing" field, and is copied into "screen" as needed (that is, when 143 * scrolling occurs). When copy-mode is backed by a pane, backing points 144 * directly at that pane's screen structure (&wp->base); when backed by a list 145 * of output-lines from a command, it points at a newly-allocated screen 146 * structure (which is deallocated when the mode ends). 147 */ 148 struct window_copy_mode_data { 149 struct screen screen; 150 151 struct screen *backing; 152 int backing_written; /* backing display started */ 153 154 u_int oy; /* number of lines scrolled up */ 155 156 u_int selx; /* beginning of selection */ 157 u_int sely; 158 159 u_int endselx; /* end of selection */ 160 u_int endsely; 161 162 enum { 163 CURSORDRAG_NONE, /* selection is independent of cursor */ 164 CURSORDRAG_ENDSEL, /* end is synchronized with cursor */ 165 CURSORDRAG_SEL, /* start is synchronized with cursor */ 166 } cursordrag; 167 168 int rectflag; /* in rectangle copy mode? */ 169 int scroll_exit; /* exit on scroll to end? */ 170 171 u_int cx; 172 u_int cy; 173 174 u_int lastcx; /* position in last line w/ content */ 175 u_int lastsx; /* size of last line w/ content */ 176 177 int searchtype; 178 char *searchstr; 179 bitstr_t *searchmark; 180 u_int searchcount; 181 int searchthis; 182 int searchx; 183 int searchy; 184 int searcho; 185 186 int jumptype; 187 char jumpchar; 188 }; 189 190 static struct screen * 191 window_copy_init(struct window_pane *wp, __unused struct cmd_find_state *fs, 192 __unused struct args *args) 193 { 194 struct window_copy_mode_data *data; 195 struct screen *s; 196 197 wp->modedata = data = xmalloc(sizeof *data); 198 199 data->oy = 0; 200 data->cx = 0; 201 data->cy = 0; 202 203 data->cursordrag = CURSORDRAG_NONE; 204 205 data->lastcx = 0; 206 data->lastsx = 0; 207 208 data->backing_written = 0; 209 210 data->rectflag = 0; 211 data->scroll_exit = 0; 212 213 if (wp->searchstr != NULL) { 214 data->searchtype = WINDOW_COPY_SEARCHUP; 215 data->searchstr = xstrdup(wp->searchstr); 216 } else { 217 data->searchtype = WINDOW_COPY_OFF; 218 data->searchstr = NULL; 219 } 220 data->searchmark = NULL; 221 data->searchx = data->searchy = data->searcho = -1; 222 223 if (wp->fd != -1) 224 bufferevent_disable(wp->event, EV_READ|EV_WRITE); 225 226 data->jumptype = WINDOW_COPY_OFF; 227 data->jumpchar = '\0'; 228 229 s = &data->screen; 230 screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); 231 s->sel.modekeys = options_get_number(wp->window->options, "mode-keys"); 232 233 data->backing = NULL; 234 235 return (s); 236 } 237 238 void 239 window_copy_init_from_pane(struct window_pane *wp, int scroll_exit) 240 { 241 struct window_copy_mode_data *data = wp->modedata; 242 struct screen *s = &data->screen; 243 struct screen_write_ctx ctx; 244 u_int i; 245 246 if (wp->mode != &window_copy_mode) 247 fatalx("not in copy mode"); 248 249 data->backing = &wp->base; 250 data->cx = data->backing->cx; 251 data->cy = data->backing->cy; 252 data->scroll_exit = scroll_exit; 253 254 s->cx = data->cx; 255 s->cy = data->cy; 256 257 screen_write_start(&ctx, NULL, s); 258 for (i = 0; i < screen_size_y(s); i++) 259 window_copy_write_line(wp, &ctx, i); 260 screen_write_cursormove(&ctx, data->cx, data->cy); 261 screen_write_stop(&ctx); 262 } 263 264 void 265 window_copy_init_for_output(struct window_pane *wp) 266 { 267 struct window_copy_mode_data *data = wp->modedata; 268 269 data->backing = xmalloc(sizeof *data->backing); 270 screen_init(data->backing, screen_size_x(&wp->base), 271 screen_size_y(&wp->base), UINT_MAX); 272 } 273 274 static void 275 window_copy_free(struct window_pane *wp) 276 { 277 struct window_copy_mode_data *data = wp->modedata; 278 279 if (wp->fd != -1) 280 bufferevent_enable(wp->event, EV_READ|EV_WRITE); 281 282 free(data->searchmark); 283 free(data->searchstr); 284 285 if (data->backing != &wp->base) { 286 screen_free(data->backing); 287 free(data->backing); 288 } 289 screen_free(&data->screen); 290 291 free(data); 292 } 293 294 void 295 window_copy_add(struct window_pane *wp, const char *fmt, ...) 296 { 297 va_list ap; 298 299 va_start(ap, fmt); 300 window_copy_vadd(wp, fmt, ap); 301 va_end(ap); 302 } 303 304 void 305 window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap) 306 { 307 struct window_copy_mode_data *data = wp->modedata; 308 struct screen *backing = data->backing; 309 struct screen_write_ctx back_ctx, ctx; 310 struct grid_cell gc; 311 u_int old_hsize, old_cy; 312 313 if (backing == &wp->base) 314 return; 315 316 memcpy(&gc, &grid_default_cell, sizeof gc); 317 318 old_hsize = screen_hsize(data->backing); 319 screen_write_start(&back_ctx, NULL, backing); 320 if (data->backing_written) { 321 /* 322 * On the second or later line, do a CRLF before writing 323 * (so it's on a new line). 324 */ 325 screen_write_carriagereturn(&back_ctx); 326 screen_write_linefeed(&back_ctx, 0, 8); 327 } else 328 data->backing_written = 1; 329 old_cy = backing->cy; 330 screen_write_vnputs(&back_ctx, 0, &gc, fmt, ap); 331 screen_write_stop(&back_ctx); 332 333 data->oy += screen_hsize(data->backing) - old_hsize; 334 335 screen_write_start(&ctx, wp, &data->screen); 336 337 /* 338 * If the history has changed, draw the top line. 339 * (If there's any history at all, it has changed.) 340 */ 341 if (screen_hsize(data->backing)) 342 window_copy_redraw_lines(wp, 0, 1); 343 344 /* Write the new lines. */ 345 window_copy_redraw_lines(wp, old_cy, backing->cy - old_cy + 1); 346 347 screen_write_stop(&ctx); 348 } 349 350 void 351 window_copy_pageup(struct window_pane *wp, int half_page) 352 { 353 struct window_copy_mode_data *data = wp->modedata; 354 struct screen *s = &data->screen; 355 u_int n, ox, oy, px, py; 356 357 oy = screen_hsize(data->backing) + data->cy - data->oy; 358 ox = window_copy_find_length(wp, oy); 359 360 if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely) 361 window_copy_other_end(wp); 362 363 if (data->cx != ox) { 364 data->lastcx = data->cx; 365 data->lastsx = ox; 366 } 367 data->cx = data->lastcx; 368 369 n = 1; 370 if (screen_size_y(s) > 2) { 371 if (half_page) 372 n = screen_size_y(s) / 2; 373 else 374 n = screen_size_y(s) - 2; 375 } 376 377 if (data->oy + n > screen_hsize(data->backing)) 378 data->oy = screen_hsize(data->backing); 379 else 380 data->oy += n; 381 382 if (!data->screen.sel.flag || !data->rectflag) { 383 py = screen_hsize(data->backing) + data->cy - data->oy; 384 px = window_copy_find_length(wp, py); 385 if ((data->cx >= data->lastsx && data->cx != px) || 386 data->cx > px) 387 window_copy_cursor_end_of_line(wp); 388 } 389 390 window_copy_update_selection(wp, 1); 391 window_copy_redraw_screen(wp); 392 } 393 394 static int 395 window_copy_pagedown(struct window_pane *wp, int half_page) 396 { 397 struct window_copy_mode_data *data = wp->modedata; 398 struct screen *s = &data->screen; 399 u_int n, ox, oy, px, py; 400 401 oy = screen_hsize(data->backing) + data->cy - data->oy; 402 ox = window_copy_find_length(wp, oy); 403 404 if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT && oy == data->sely) 405 window_copy_other_end(wp); 406 407 if (data->cx != ox) { 408 data->lastcx = data->cx; 409 data->lastsx = ox; 410 } 411 data->cx = data->lastcx; 412 413 n = 1; 414 if (screen_size_y(s) > 2) { 415 if (half_page) 416 n = screen_size_y(s) / 2; 417 else 418 n = screen_size_y(s) - 2; 419 } 420 421 if (data->oy < n) 422 data->oy = 0; 423 else 424 data->oy -= n; 425 426 if (!data->screen.sel.flag || !data->rectflag) { 427 py = screen_hsize(data->backing) + data->cy - data->oy; 428 px = window_copy_find_length(wp, py); 429 if ((data->cx >= data->lastsx && data->cx != px) || 430 data->cx > px) 431 window_copy_cursor_end_of_line(wp); 432 } 433 434 if (data->scroll_exit && data->oy == 0) 435 return (1); 436 window_copy_update_selection(wp, 1); 437 window_copy_redraw_screen(wp); 438 return (0); 439 } 440 441 static void 442 window_copy_previous_paragraph(struct window_pane *wp) 443 { 444 struct window_copy_mode_data *data = wp->modedata; 445 u_int oy; 446 447 oy = screen_hsize(data->backing) + data->cy - data->oy; 448 449 while (oy > 0 && window_copy_find_length(wp, oy) == 0) 450 oy--; 451 452 while (oy > 0 && window_copy_find_length(wp, oy) > 0) 453 oy--; 454 455 window_copy_scroll_to(wp, 0, oy); 456 } 457 458 static void 459 window_copy_next_paragraph(struct window_pane *wp) 460 { 461 struct window_copy_mode_data *data = wp->modedata; 462 struct screen *s = &data->screen; 463 u_int maxy, ox, oy; 464 465 oy = screen_hsize(data->backing) + data->cy - data->oy; 466 maxy = screen_hsize(data->backing) + screen_size_y(s) - 1; 467 468 while (oy < maxy && window_copy_find_length(wp, oy) == 0) 469 oy++; 470 471 while (oy < maxy && window_copy_find_length(wp, oy) > 0) 472 oy++; 473 474 ox = window_copy_find_length(wp, oy); 475 window_copy_scroll_to(wp, ox, oy); 476 } 477 478 static void 479 window_copy_resize(struct window_pane *wp, u_int sx, u_int sy) 480 { 481 struct window_copy_mode_data *data = wp->modedata; 482 struct screen *s = &data->screen; 483 struct screen_write_ctx ctx; 484 485 screen_resize(s, sx, sy, 1); 486 if (data->backing != &wp->base) 487 screen_resize(data->backing, sx, sy, 1); 488 489 if (data->cy > sy - 1) 490 data->cy = sy - 1; 491 if (data->cx > sx) 492 data->cx = sx; 493 if (data->oy > screen_hsize(data->backing)) 494 data->oy = screen_hsize(data->backing); 495 496 window_copy_clear_selection(wp); 497 498 screen_write_start(&ctx, NULL, s); 499 window_copy_write_lines(wp, &ctx, 0, screen_size_y(s) - 1); 500 screen_write_stop(&ctx); 501 502 if (data->searchmark != NULL) 503 window_copy_search_marks(wp, NULL); 504 data->searchx = data->cx; 505 data->searchy = data->cy; 506 data->searcho = data->oy; 507 508 window_copy_redraw_screen(wp); 509 } 510 511 static const char * 512 window_copy_key_table(struct window_pane *wp) 513 { 514 if (options_get_number(wp->window->options, "mode-keys") == MODEKEY_VI) 515 return ("copy-mode-vi"); 516 return ("copy-mode"); 517 } 518 519 static void 520 window_copy_command(struct window_pane *wp, struct client *c, struct session *s, 521 struct args *args, struct mouse_event *m) 522 { 523 struct window_copy_mode_data *data = wp->modedata; 524 struct screen *sn = &data->screen; 525 const char *command, *argument, *ws; 526 u_int np = wp->modeprefix; 527 int cancel = 0, redraw = 0; 528 char prefix; 529 530 if (args->argc == 0) 531 return; 532 command = args->argv[0]; 533 534 if (m != NULL && m->valid) 535 window_copy_move_mouse(m); 536 537 if (args->argc == 1) { 538 if (strcmp(command, "append-selection") == 0) { 539 if (s != NULL) 540 window_copy_append_selection(wp, NULL); 541 window_copy_clear_selection(wp); 542 redraw = 1; 543 } 544 if (strcmp(command, "append-selection-and-cancel") == 0) { 545 if (s != NULL) 546 window_copy_append_selection(wp, NULL); 547 window_copy_clear_selection(wp); 548 redraw = 1; 549 cancel = 1; 550 } 551 if (strcmp(command, "back-to-indentation") == 0) 552 window_copy_cursor_back_to_indentation(wp); 553 if (strcmp(command, "begin-selection") == 0) { 554 if (m != NULL) 555 window_copy_start_drag(c, m); 556 else { 557 sn->sel.lineflag = LINE_SEL_NONE; 558 window_copy_start_selection(wp); 559 redraw = 1; 560 } 561 } 562 if (strcmp(command, "stop-selection") == 0) 563 data->cursordrag = CURSORDRAG_NONE; 564 if (strcmp(command, "bottom-line") == 0) { 565 data->cx = 0; 566 data->cy = screen_size_y(sn) - 1; 567 window_copy_update_selection(wp, 1); 568 redraw = 1; 569 } 570 if (strcmp(command, "cancel") == 0) 571 cancel = 1; 572 if (strcmp(command, "clear-selection") == 0) { 573 window_copy_clear_selection(wp); 574 redraw = 1; 575 } 576 if (strcmp(command, "copy-end-of-line") == 0) { 577 window_copy_start_selection(wp); 578 for (; np > 1; np--) 579 window_copy_cursor_down(wp, 0); 580 window_copy_cursor_end_of_line(wp); 581 redraw = 1; 582 583 if (s != NULL) { 584 window_copy_copy_selection(wp, NULL); 585 cancel = 1; 586 } 587 } 588 if (strcmp(command, "copy-line") == 0) { 589 window_copy_cursor_start_of_line(wp); 590 window_copy_start_selection(wp); 591 for (; np > 1; np--) 592 window_copy_cursor_down(wp, 0); 593 window_copy_cursor_end_of_line(wp); 594 redraw = 1; 595 596 if (s != NULL) { 597 window_copy_copy_selection(wp, NULL); 598 cancel = 1; 599 } 600 } 601 if (strcmp(command, "copy-selection") == 0) { 602 if (s != NULL) 603 window_copy_copy_selection(wp, NULL); 604 window_copy_clear_selection(wp); 605 redraw = 1; 606 } 607 if (strcmp(command, "copy-selection-and-cancel") == 0) { 608 if (s != NULL) 609 window_copy_copy_selection(wp, NULL); 610 window_copy_clear_selection(wp); 611 redraw = 1; 612 cancel = 1; 613 } 614 if (strcmp(command, "cursor-down") == 0) { 615 for (; np != 0; np--) 616 window_copy_cursor_down(wp, 0); 617 } 618 if (strcmp(command, "cursor-left") == 0) { 619 for (; np != 0; np--) 620 window_copy_cursor_left(wp); 621 } 622 if (strcmp(command, "cursor-right") == 0) { 623 for (; np != 0; np--) 624 window_copy_cursor_right(wp); 625 } 626 if (strcmp(command, "cursor-up") == 0) { 627 for (; np != 0; np--) 628 window_copy_cursor_up(wp, 0); 629 } 630 if (strcmp(command, "end-of-line") == 0) 631 window_copy_cursor_end_of_line(wp); 632 if (strcmp(command, "halfpage-down") == 0) { 633 for (; np != 0; np--) { 634 if (window_copy_pagedown(wp, 1)) { 635 cancel = 1; 636 break; 637 } 638 } 639 } 640 if (strcmp(command, "halfpage-up") == 0) { 641 for (; np != 0; np--) 642 window_copy_pageup(wp, 1); 643 } 644 if (strcmp(command, "history-bottom") == 0) { 645 data->cx = 0; 646 data->cy = screen_size_y(sn) - 1; 647 data->oy = 0; 648 window_copy_update_selection(wp, 1); 649 redraw = 1; 650 } 651 if (strcmp(command, "history-top") == 0) { 652 data->cx = 0; 653 data->cy = 0; 654 data->oy = screen_hsize(data->backing); 655 window_copy_update_selection(wp, 1); 656 redraw = 1; 657 } 658 if (strcmp(command, "jump-again") == 0) { 659 switch (data->jumptype) { 660 case WINDOW_COPY_JUMPFORWARD: 661 for (; np != 0; np--) 662 window_copy_cursor_jump(wp); 663 break; 664 case WINDOW_COPY_JUMPBACKWARD: 665 for (; np != 0; np--) 666 window_copy_cursor_jump_back(wp); 667 break; 668 case WINDOW_COPY_JUMPTOFORWARD: 669 for (; np != 0; np--) 670 window_copy_cursor_jump_to(wp); 671 break; 672 case WINDOW_COPY_JUMPTOBACKWARD: 673 for (; np != 0; np--) 674 window_copy_cursor_jump_to_back(wp); 675 break; 676 } 677 } 678 if (strcmp(command, "jump-reverse") == 0) { 679 switch (data->jumptype) { 680 case WINDOW_COPY_JUMPFORWARD: 681 for (; np != 0; np--) 682 window_copy_cursor_jump_back(wp); 683 break; 684 case WINDOW_COPY_JUMPBACKWARD: 685 for (; np != 0; np--) 686 window_copy_cursor_jump(wp); 687 break; 688 case WINDOW_COPY_JUMPTOFORWARD: 689 for (; np != 0; np--) 690 window_copy_cursor_jump_to_back(wp); 691 break; 692 case WINDOW_COPY_JUMPTOBACKWARD: 693 for (; np != 0; np--) 694 window_copy_cursor_jump_to(wp); 695 break; 696 } 697 } 698 if (strcmp(command, "middle-line") == 0) { 699 data->cx = 0; 700 data->cy = (screen_size_y(sn) - 1) / 2; 701 window_copy_update_selection(wp, 1); 702 redraw = 1; 703 } 704 if (strcmp(command, "next-paragraph") == 0) { 705 for (; np != 0; np--) 706 window_copy_next_paragraph(wp); 707 } 708 if (strcmp(command, "next-space") == 0) { 709 for (; np != 0; np--) 710 window_copy_cursor_next_word(wp, " "); 711 } 712 if (strcmp(command, "next-space-end") == 0) { 713 for (; np != 0; np--) 714 window_copy_cursor_next_word_end(wp, " "); 715 } 716 if (strcmp(command, "next-word") == 0) { 717 ws = options_get_string(s->options, "word-separators"); 718 for (; np != 0; np--) 719 window_copy_cursor_next_word(wp, ws); 720 } 721 if (strcmp(command, "next-word-end") == 0) { 722 ws = options_get_string(s->options, "word-separators"); 723 for (; np != 0; np--) 724 window_copy_cursor_next_word_end(wp, ws); 725 } 726 if (strcmp(command, "other-end") == 0) { 727 if ((np % 2) != 0) 728 window_copy_other_end(wp); 729 } 730 if (strcmp(command, "page-down") == 0) { 731 for (; np != 0; np--) { 732 if (window_copy_pagedown(wp, 0)) { 733 cancel = 1; 734 break; 735 } 736 } 737 } 738 if (strcmp(command, "page-up") == 0) { 739 for (; np != 0; np--) 740 window_copy_pageup(wp, 0); 741 } 742 if (strcmp(command, "previous-paragraph") == 0) { 743 for (; np != 0; np--) 744 window_copy_previous_paragraph(wp); 745 } 746 if (strcmp(command, "previous-space") == 0) { 747 for (; np != 0; np--) 748 window_copy_cursor_previous_word(wp, " "); 749 } 750 if (strcmp(command, "previous-word") == 0) { 751 ws = options_get_string(s->options, "word-separators"); 752 for (; np != 0; np--) 753 window_copy_cursor_previous_word(wp, ws); 754 } 755 if (strcmp(command, "rectangle-toggle") == 0) { 756 sn->sel.lineflag = LINE_SEL_NONE; 757 window_copy_rectangle_toggle(wp); 758 } 759 if (strcmp(command, "scroll-down") == 0) { 760 for (; np != 0; np--) 761 window_copy_cursor_down(wp, 1); 762 if (data->scroll_exit && data->oy == 0) 763 cancel = 1; 764 } 765 if (strcmp(command, "scroll-up") == 0) { 766 for (; np != 0; np--) 767 window_copy_cursor_up(wp, 1); 768 } 769 if (strcmp(command, "search-again") == 0) { 770 if (data->searchtype == WINDOW_COPY_SEARCHUP) { 771 for (; np != 0; np--) 772 window_copy_search_up(wp); 773 } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { 774 for (; np != 0; np--) 775 window_copy_search_down(wp); 776 } 777 } 778 if (strcmp(command, "search-reverse") == 0) { 779 if (data->searchtype == WINDOW_COPY_SEARCHUP) { 780 for (; np != 0; np--) 781 window_copy_search_down(wp); 782 } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { 783 for (; np != 0; np--) 784 window_copy_search_up(wp); 785 } 786 } 787 if (strcmp(command, "select-line") == 0) { 788 sn->sel.lineflag = LINE_SEL_LEFT_RIGHT; 789 data->rectflag = 0; 790 window_copy_cursor_start_of_line(wp); 791 window_copy_start_selection(wp); 792 for (; np > 1; np--) 793 window_copy_cursor_down(wp, 0); 794 window_copy_cursor_end_of_line(wp); 795 redraw = 1; 796 } 797 if (strcmp(command, "select-word") == 0) { 798 sn->sel.lineflag = LINE_SEL_LEFT_RIGHT; 799 data->rectflag = 0; 800 ws = options_get_string(s->options, "word-separators"); 801 window_copy_cursor_previous_word(wp, ws); 802 window_copy_start_selection(wp); 803 window_copy_cursor_next_word_end(wp, ws); 804 redraw = 1; 805 } 806 if (strcmp(command, "start-of-line") == 0) 807 window_copy_cursor_start_of_line(wp); 808 if (strcmp(command, "top-line") == 0) { 809 data->cx = 0; 810 data->cy = 0; 811 window_copy_update_selection(wp, 1); 812 redraw = 1; 813 } 814 } else if (args->argc == 2 && *args->argv[1] != '\0') { 815 argument = args->argv[1]; 816 if (strcmp(command, "copy-pipe") == 0) { 817 if (s != NULL) 818 window_copy_copy_pipe(wp, s, NULL, argument); 819 } 820 if (strcmp(command, "copy-pipe-and-cancel") == 0) { 821 if (s != NULL) { 822 window_copy_copy_pipe(wp, s, NULL, argument); 823 cancel = 1; 824 } 825 } 826 if (strcmp(command, "goto-line") == 0) 827 window_copy_goto_line(wp, argument); 828 if (strcmp(command, "jump-backward") == 0) { 829 data->jumptype = WINDOW_COPY_JUMPBACKWARD; 830 data->jumpchar = *argument; 831 for (; np != 0; np--) 832 window_copy_cursor_jump_back(wp); 833 } 834 if (strcmp(command, "jump-forward") == 0) { 835 data->jumptype = WINDOW_COPY_JUMPFORWARD; 836 data->jumpchar = *argument; 837 for (; np != 0; np--) 838 window_copy_cursor_jump(wp); 839 } 840 if (strcmp(command, "jump-to-backward") == 0) { 841 data->jumptype = WINDOW_COPY_JUMPTOBACKWARD; 842 data->jumpchar = *argument; 843 for (; np != 0; np--) 844 window_copy_cursor_jump_to_back(wp); 845 } 846 if (strcmp(command, "jump-to-forward") == 0) { 847 data->jumptype = WINDOW_COPY_JUMPTOFORWARD; 848 data->jumpchar = *argument; 849 for (; np != 0; np--) 850 window_copy_cursor_jump_to(wp); 851 } 852 if (strcmp(command, "search-backward") == 0) { 853 data->searchtype = WINDOW_COPY_SEARCHUP; 854 free(data->searchstr); 855 data->searchstr = xstrdup(argument); 856 for (; np != 0; np--) 857 window_copy_search_up(wp); 858 } 859 if (strcmp(command, "search-forward") == 0) { 860 data->searchtype = WINDOW_COPY_SEARCHDOWN; 861 free(data->searchstr); 862 data->searchstr = xstrdup(argument); 863 for (; np != 0; np--) 864 window_copy_search_down(wp); 865 } 866 if (strcmp(command, "search-backward-incremental") == 0) { 867 prefix = *argument++; 868 if (data->searchx == -1 || data->searchy == -1) { 869 data->searchx = data->cx; 870 data->searchy = data->cy; 871 data->searcho = data->oy; 872 } else if (data->searchstr != NULL && 873 strcmp(argument, data->searchstr) != 0) { 874 data->cx = data->searchx; 875 data->cy = data->searchy; 876 data->oy = data->searcho; 877 redraw = 1; 878 } 879 if (*argument == '\0') { 880 window_copy_clear_marks(wp); 881 redraw = 1; 882 } else if (prefix == '=' || prefix == '-') { 883 data->searchtype = WINDOW_COPY_SEARCHUP; 884 free(data->searchstr); 885 data->searchstr = xstrdup(argument); 886 if (!window_copy_search_up(wp)) { 887 window_copy_clear_marks(wp); 888 redraw = 1; 889 } 890 } else if (prefix == '+') { 891 data->searchtype = WINDOW_COPY_SEARCHDOWN; 892 free(data->searchstr); 893 data->searchstr = xstrdup(argument); 894 if (!window_copy_search_down(wp)) { 895 window_copy_clear_marks(wp); 896 redraw = 1; 897 } 898 } 899 } 900 if (strcmp(command, "search-forward-incremental") == 0) { 901 prefix = *argument++; 902 if (data->searchx == -1 || data->searchy == -1) { 903 data->searchx = data->cx; 904 data->searchy = data->cy; 905 data->searcho = data->oy; 906 } else if (data->searchstr != NULL && 907 strcmp(argument, data->searchstr) != 0) { 908 data->cx = data->searchx; 909 data->cy = data->searchy; 910 data->oy = data->searcho; 911 redraw = 1; 912 } 913 if (*argument == '\0') { 914 window_copy_clear_marks(wp); 915 redraw = 1; 916 } else if (prefix == '=' || prefix == '+') { 917 data->searchtype = WINDOW_COPY_SEARCHDOWN; 918 free(data->searchstr); 919 data->searchstr = xstrdup(argument); 920 if (!window_copy_search_down(wp)) { 921 window_copy_clear_marks(wp); 922 redraw = 1; 923 } 924 } else if (prefix == '-') { 925 data->searchtype = WINDOW_COPY_SEARCHUP; 926 free(data->searchstr); 927 data->searchstr = xstrdup(argument); 928 if (!window_copy_search_up(wp)) { 929 window_copy_clear_marks(wp); 930 redraw = 1; 931 } 932 } 933 } 934 } 935 936 if (strncmp(command, "search-", 7) != 0 && data->searchmark != NULL) { 937 window_copy_clear_marks(wp); 938 redraw = 1; 939 data->searchx = data->searchy = -1; 940 } 941 942 if (cancel) 943 window_pane_reset_mode(wp); 944 else if (redraw) 945 window_copy_redraw_screen(wp); 946 wp->modeprefix = 1; 947 } 948 949 static void 950 window_copy_scroll_to(struct window_pane *wp, u_int px, u_int py) 951 { 952 struct window_copy_mode_data *data = wp->modedata; 953 struct grid *gd = data->backing->grid; 954 u_int offset, gap; 955 956 data->cx = px; 957 958 gap = gd->sy / 4; 959 if (py < gd->sy) { 960 offset = 0; 961 data->cy = py; 962 } else if (py > gd->hsize + gd->sy - gap) { 963 offset = gd->hsize; 964 data->cy = py - gd->hsize; 965 } else { 966 offset = py + gap - gd->sy; 967 data->cy = py - offset; 968 } 969 data->oy = gd->hsize - offset; 970 971 window_copy_update_selection(wp, 1); 972 window_copy_redraw_screen(wp); 973 } 974 975 static int 976 window_copy_search_compare(struct grid *gd, u_int px, u_int py, 977 struct grid *sgd, u_int spx, int cis) 978 { 979 struct grid_cell gc, sgc; 980 const struct utf8_data *ud, *sud; 981 982 grid_get_cell(gd, px, py, &gc); 983 ud = &gc.data; 984 grid_get_cell(sgd, spx, 0, &sgc); 985 sud = &sgc.data; 986 987 if (ud->size != sud->size || ud->width != sud->width) 988 return (0); 989 990 if (cis && ud->size == 1) 991 return (tolower(ud->data[0]) == sud->data[0]); 992 993 return (memcmp(ud->data, sud->data, ud->size) == 0); 994 } 995 996 static int 997 window_copy_search_lr(struct grid *gd, 998 struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis) 999 { 1000 u_int ax, bx, px; 1001 int matched; 1002 1003 for (ax = first; ax < last; ax++) { 1004 if (ax + sgd->sx > gd->sx) 1005 break; 1006 for (bx = 0; bx < sgd->sx; bx++) { 1007 px = ax + bx; 1008 matched = window_copy_search_compare(gd, px, py, sgd, 1009 bx, cis); 1010 if (!matched) 1011 break; 1012 } 1013 if (bx == sgd->sx) { 1014 *ppx = ax; 1015 return (1); 1016 } 1017 } 1018 return (0); 1019 } 1020 1021 static int 1022 window_copy_search_rl(struct grid *gd, 1023 struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis) 1024 { 1025 u_int ax, bx, px; 1026 int matched; 1027 1028 for (ax = last + 1; ax > first; ax--) { 1029 if (gd->sx - (ax - 1) < sgd->sx) 1030 continue; 1031 for (bx = 0; bx < sgd->sx; bx++) { 1032 px = ax - 1 + bx; 1033 matched = window_copy_search_compare(gd, px, py, sgd, 1034 bx, cis); 1035 if (!matched) 1036 break; 1037 } 1038 if (bx == sgd->sx) { 1039 *ppx = ax - 1; 1040 return (1); 1041 } 1042 } 1043 return (0); 1044 } 1045 1046 static void 1047 window_copy_move_left(struct screen *s, u_int *fx, u_int *fy) 1048 { 1049 if (*fx == 0) { /* left */ 1050 if (*fy == 0) /* top */ 1051 return; 1052 *fx = screen_size_x(s) - 1; 1053 *fy = *fy - 1; 1054 } else 1055 *fx = *fx - 1; 1056 } 1057 1058 static void 1059 window_copy_move_right(struct screen *s, u_int *fx, u_int *fy) 1060 { 1061 if (*fx == screen_size_x(s) - 1) { /* right */ 1062 if (*fy == screen_hsize(s) + screen_size_y(s)) /* bottom */ 1063 return; 1064 *fx = 0; 1065 *fy = *fy + 1; 1066 } else 1067 *fx = *fx + 1; 1068 } 1069 1070 static int 1071 window_copy_is_lowercase(const char *ptr) 1072 { 1073 while (*ptr != '\0') { 1074 if (*ptr != tolower((u_char)*ptr)) 1075 return (0); 1076 ++ptr; 1077 } 1078 return (1); 1079 } 1080 1081 /* 1082 * Search for text stored in sgd starting from position fx,fy up to endline. If 1083 * found, jump to it. If cis then ignore case. The direction is 0 for searching 1084 * up, down otherwise. If wrap then go to begin/end of grid and try again if 1085 * not found. 1086 */ 1087 static int 1088 window_copy_search_jump(struct window_pane *wp, struct grid *gd, 1089 struct grid *sgd, u_int fx, u_int fy, u_int endline, int cis, int wrap, 1090 int direction) 1091 { 1092 u_int i, px; 1093 int found; 1094 1095 found = 0; 1096 if (direction) { 1097 for (i = fy; i <= endline; i++) { 1098 found = window_copy_search_lr(gd, sgd, &px, i, fx, 1099 gd->sx, cis); 1100 if (found) 1101 break; 1102 fx = 0; 1103 } 1104 } else { 1105 for (i = fy + 1; endline < i; i--) { 1106 found = window_copy_search_rl(gd, sgd, &px, i - 1, 0, 1107 fx, cis); 1108 if (found) { 1109 i--; 1110 break; 1111 } 1112 fx = gd->sx; 1113 } 1114 } 1115 1116 if (found) { 1117 window_copy_scroll_to(wp, px, i); 1118 return (1); 1119 } 1120 if (wrap) { 1121 return (window_copy_search_jump(wp, gd, sgd, 1122 direction ? 0 : gd->sx - 1, 1123 direction ? 0 : gd->hsize + gd->sy - 1, fy, cis, 0, 1124 direction)); 1125 } 1126 return (0); 1127 } 1128 1129 /* 1130 * Search in for text searchstr. If direction is 0 then search up, otherwise 1131 * down. 1132 */ 1133 static int 1134 window_copy_search(struct window_pane *wp, int direction) 1135 { 1136 struct window_copy_mode_data *data = wp->modedata; 1137 struct screen *s = data->backing, ss; 1138 struct screen_write_ctx ctx; 1139 struct grid *gd = s->grid; 1140 u_int fx, fy, endline; 1141 int wrapflag, cis, found; 1142 1143 free(wp->searchstr); 1144 wp->searchstr = xstrdup(data->searchstr); 1145 1146 fx = data->cx; 1147 fy = screen_hsize(data->backing) - data->oy + data->cy; 1148 1149 screen_init(&ss, screen_write_strlen("%s", data->searchstr), 1, 0); 1150 screen_write_start(&ctx, NULL, &ss); 1151 screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", data->searchstr); 1152 screen_write_stop(&ctx); 1153 1154 if (direction) 1155 window_copy_move_right(s, &fx, &fy); 1156 else 1157 window_copy_move_left(s, &fx, &fy); 1158 window_copy_clear_selection(wp); 1159 1160 wrapflag = options_get_number(wp->window->options, "wrap-search"); 1161 cis = window_copy_is_lowercase(data->searchstr); 1162 1163 if (direction) 1164 endline = gd->hsize + gd->sy - 1; 1165 else 1166 endline = 0; 1167 found = window_copy_search_jump(wp, gd, ss.grid, fx, fy, endline, cis, 1168 wrapflag, direction); 1169 1170 if (window_copy_search_marks(wp, &ss)) 1171 window_copy_redraw_screen(wp); 1172 1173 screen_free(&ss); 1174 return (found); 1175 } 1176 1177 static int 1178 window_copy_search_marks(struct window_pane *wp, struct screen *ssp) 1179 { 1180 struct window_copy_mode_data *data = wp->modedata; 1181 struct screen *s = data->backing, ss; 1182 struct screen_write_ctx ctx; 1183 struct grid *gd = s->grid; 1184 int found, cis, which = -1; 1185 u_int px, py, b, nfound = 0, width; 1186 1187 if (ssp == NULL) { 1188 width = screen_write_strlen("%s", data->searchstr); 1189 screen_init(&ss, width, 1, 0); 1190 screen_write_start(&ctx, NULL, &ss); 1191 screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", 1192 data->searchstr); 1193 screen_write_stop(&ctx); 1194 ssp = &ss; 1195 } else 1196 width = screen_size_x(ssp); 1197 1198 cis = window_copy_is_lowercase(data->searchstr); 1199 1200 free(data->searchmark); 1201 data->searchmark = bit_alloc((gd->hsize + gd->sy) * gd->sx); 1202 1203 for (py = 0; py < gd->hsize + gd->sy; py++) { 1204 px = 0; 1205 for (;;) { 1206 found = window_copy_search_lr(gd, ssp->grid, &px, py, 1207 px, gd->sx, cis); 1208 if (!found) 1209 break; 1210 1211 nfound++; 1212 if (px == data->cx && py == gd->hsize + data->cy - data->oy) 1213 which = nfound; 1214 1215 b = (py * gd->sx) + px; 1216 bit_nset(data->searchmark, b, b + width - 1); 1217 1218 px++; 1219 } 1220 } 1221 1222 if (which != -1) 1223 data->searchthis = 1 + nfound - which; 1224 else 1225 data->searchthis = -1; 1226 data->searchcount = nfound; 1227 1228 if (ssp == &ss) 1229 screen_free(&ss); 1230 return (nfound); 1231 } 1232 1233 static void 1234 window_copy_clear_marks(struct window_pane *wp) 1235 { 1236 struct window_copy_mode_data *data = wp->modedata; 1237 1238 free(data->searchmark); 1239 data->searchmark = NULL; 1240 } 1241 1242 static int 1243 window_copy_search_up(struct window_pane *wp) 1244 { 1245 return (window_copy_search(wp, 0)); 1246 } 1247 1248 static int 1249 window_copy_search_down(struct window_pane *wp) 1250 { 1251 return (window_copy_search(wp, 1)); 1252 } 1253 1254 static void 1255 window_copy_goto_line(struct window_pane *wp, const char *linestr) 1256 { 1257 struct window_copy_mode_data *data = wp->modedata; 1258 const char *errstr; 1259 u_int lineno; 1260 1261 lineno = strtonum(linestr, 0, screen_hsize(data->backing), &errstr); 1262 if (errstr != NULL) 1263 return; 1264 1265 data->oy = lineno; 1266 window_copy_update_selection(wp, 1); 1267 window_copy_redraw_screen(wp); 1268 } 1269 1270 static void 1271 window_copy_write_line(struct window_pane *wp, struct screen_write_ctx *ctx, 1272 u_int py) 1273 { 1274 struct window_copy_mode_data *data = wp->modedata; 1275 struct screen *s = &data->screen; 1276 struct options *oo = wp->window->options; 1277 struct grid_cell gc; 1278 char hdr[512]; 1279 size_t size = 0; 1280 1281 style_apply(&gc, oo, "mode-style"); 1282 gc.flags |= GRID_FLAG_NOPALETTE; 1283 1284 if (py == 0) { 1285 if (data->searchmark == NULL) { 1286 size = xsnprintf(hdr, sizeof hdr, 1287 "[%u/%u]", data->oy, screen_hsize(data->backing)); 1288 } else { 1289 if (data->searchthis == -1) { 1290 size = xsnprintf(hdr, sizeof hdr, 1291 "(%u results) [%d/%u]", data->searchcount, 1292 data->oy, screen_hsize(data->backing)); 1293 } else { 1294 size = xsnprintf(hdr, sizeof hdr, 1295 "(%u/%u results) [%d/%u]", data->searchthis, 1296 data->searchcount, data->oy, 1297 screen_hsize(data->backing)); 1298 } 1299 } 1300 if (size > screen_size_x(s)) 1301 size = screen_size_x(s); 1302 screen_write_cursormove(ctx, screen_size_x(s) - size, 0); 1303 screen_write_puts(ctx, &gc, "%s", hdr); 1304 } else 1305 size = 0; 1306 1307 if (size < screen_size_x(s)) { 1308 screen_write_cursormove(ctx, 0, py); 1309 screen_write_copy(ctx, data->backing, 0, 1310 (screen_hsize(data->backing) - data->oy) + py, 1311 screen_size_x(s) - size, 1, data->searchmark, &gc); 1312 } 1313 1314 if (py == data->cy && data->cx == screen_size_x(s)) { 1315 memcpy(&gc, &grid_default_cell, sizeof gc); 1316 screen_write_cursormove(ctx, screen_size_x(s) - 1, py); 1317 screen_write_putc(ctx, &gc, '$'); 1318 } 1319 } 1320 1321 static void 1322 window_copy_write_lines(struct window_pane *wp, struct screen_write_ctx *ctx, 1323 u_int py, u_int ny) 1324 { 1325 u_int yy; 1326 1327 for (yy = py; yy < py + ny; yy++) 1328 window_copy_write_line(wp, ctx, py); 1329 } 1330 1331 static void 1332 window_copy_redraw_selection(struct window_pane *wp, u_int old_y) 1333 { 1334 struct window_copy_mode_data *data = wp->modedata; 1335 u_int new_y, start, end; 1336 1337 new_y = data->cy; 1338 if (old_y <= new_y) { 1339 start = old_y; 1340 end = new_y; 1341 } else { 1342 start = new_y; 1343 end = old_y; 1344 } 1345 window_copy_redraw_lines(wp, start, end - start + 1); 1346 } 1347 1348 static void 1349 window_copy_redraw_lines(struct window_pane *wp, u_int py, u_int ny) 1350 { 1351 struct window_copy_mode_data *data = wp->modedata; 1352 struct screen_write_ctx ctx; 1353 u_int i; 1354 1355 screen_write_start(&ctx, wp, NULL); 1356 for (i = py; i < py + ny; i++) 1357 window_copy_write_line(wp, &ctx, i); 1358 screen_write_cursormove(&ctx, data->cx, data->cy); 1359 screen_write_stop(&ctx); 1360 } 1361 1362 static void 1363 window_copy_redraw_screen(struct window_pane *wp) 1364 { 1365 struct window_copy_mode_data *data = wp->modedata; 1366 1367 window_copy_redraw_lines(wp, 0, screen_size_y(&data->screen)); 1368 } 1369 1370 static void 1371 window_copy_synchronize_cursor(struct window_pane *wp) 1372 { 1373 struct window_copy_mode_data *data = wp->modedata; 1374 u_int xx, yy; 1375 1376 xx = data->cx; 1377 yy = screen_hsize(data->backing) + data->cy - data->oy; 1378 1379 switch (data->cursordrag) { 1380 case CURSORDRAG_ENDSEL: 1381 data->endselx = xx; 1382 data->endsely = yy; 1383 break; 1384 case CURSORDRAG_SEL: 1385 data->selx = xx; 1386 data->sely = yy; 1387 break; 1388 case CURSORDRAG_NONE: 1389 break; 1390 } 1391 } 1392 1393 static void 1394 window_copy_update_cursor(struct window_pane *wp, u_int cx, u_int cy) 1395 { 1396 struct window_copy_mode_data *data = wp->modedata; 1397 struct screen *s = &data->screen; 1398 struct screen_write_ctx ctx; 1399 u_int old_cx, old_cy; 1400 1401 old_cx = data->cx; old_cy = data->cy; 1402 data->cx = cx; data->cy = cy; 1403 if (old_cx == screen_size_x(s)) 1404 window_copy_redraw_lines(wp, old_cy, 1); 1405 if (data->cx == screen_size_x(s)) 1406 window_copy_redraw_lines(wp, data->cy, 1); 1407 else { 1408 screen_write_start(&ctx, wp, NULL); 1409 screen_write_cursormove(&ctx, data->cx, data->cy); 1410 screen_write_stop(&ctx); 1411 } 1412 } 1413 1414 static void 1415 window_copy_start_selection(struct window_pane *wp) 1416 { 1417 struct window_copy_mode_data *data = wp->modedata; 1418 struct screen *s = &data->screen; 1419 1420 data->selx = data->cx; 1421 data->sely = screen_hsize(data->backing) + data->cy - data->oy; 1422 1423 data->endselx = data->selx; 1424 data->endsely = data->sely; 1425 1426 data->cursordrag = CURSORDRAG_ENDSEL; 1427 1428 s->sel.flag = 1; 1429 window_copy_update_selection(wp, 1); 1430 } 1431 1432 static int 1433 window_copy_adjust_selection(struct window_pane *wp, u_int *selx, u_int *sely) 1434 { 1435 struct window_copy_mode_data *data = wp->modedata; 1436 struct screen *s = &data->screen; 1437 u_int sx, sy, ty; 1438 int relpos; 1439 1440 sx = *selx; 1441 sy = *sely; 1442 1443 ty = screen_hsize(data->backing) - data->oy; 1444 if (sy < ty) { 1445 relpos = WINDOW_COPY_REL_POS_ABOVE; 1446 if (!data->rectflag) 1447 sx = 0; 1448 sy = 0; 1449 } else if (sy > ty + screen_size_y(s) - 1) { 1450 relpos = WINDOW_COPY_REL_POS_BELOW; 1451 if (!data->rectflag) 1452 sx = screen_size_x(s) - 1; 1453 sy = screen_size_y(s) - 1; 1454 } else { 1455 relpos = WINDOW_COPY_REL_POS_ON_SCREEN; 1456 sy -= ty; 1457 } 1458 1459 *selx = sx; 1460 *sely = sy; 1461 return (relpos); 1462 } 1463 1464 static int 1465 window_copy_update_selection(struct window_pane *wp, int may_redraw) 1466 { 1467 struct window_copy_mode_data *data = wp->modedata; 1468 struct screen *s = &data->screen; 1469 struct options *oo = wp->window->options; 1470 struct grid_cell gc; 1471 u_int sx, sy, cy, endsx, endsy; 1472 int startrelpos, endrelpos; 1473 1474 if (!s->sel.flag && s->sel.lineflag == LINE_SEL_NONE) 1475 return (0); 1476 1477 window_copy_synchronize_cursor(wp); 1478 1479 /* Adjust the selection. */ 1480 sx = data->selx; 1481 sy = data->sely; 1482 startrelpos = window_copy_adjust_selection(wp, &sx, &sy); 1483 1484 /* Adjust the end of selection. */ 1485 endsx = data->endselx; 1486 endsy = data->endsely; 1487 endrelpos = window_copy_adjust_selection(wp, &endsx, &endsy); 1488 1489 /* Selection is outside of the current screen */ 1490 if (startrelpos == endrelpos && 1491 startrelpos != WINDOW_COPY_REL_POS_ON_SCREEN) { 1492 screen_hide_selection(s); 1493 return (0); 1494 } 1495 1496 /* Set colours and selection. */ 1497 style_apply(&gc, oo, "mode-style"); 1498 gc.flags |= GRID_FLAG_NOPALETTE; 1499 screen_set_selection(s, sx, sy, endsx, endsy, data->rectflag, &gc); 1500 1501 if (data->rectflag && may_redraw) { 1502 /* 1503 * Can't rely on the caller to redraw the right lines for 1504 * rectangle selection - find the highest line and the number 1505 * of lines, and redraw just past that in both directions 1506 */ 1507 cy = data->cy; 1508 if (data->cursordrag == CURSORDRAG_ENDSEL) { 1509 if (sy < cy) 1510 window_copy_redraw_lines(wp, sy, cy - sy + 1); 1511 else 1512 window_copy_redraw_lines(wp, cy, sy - cy + 1); 1513 } else { 1514 if (endsy < cy) 1515 window_copy_redraw_lines(wp, endsy, cy - endsy + 1); 1516 else 1517 window_copy_redraw_lines(wp, cy, endsy - cy + 1); 1518 } 1519 } 1520 1521 return (1); 1522 } 1523 1524 static void * 1525 window_copy_get_selection(struct window_pane *wp, size_t *len) 1526 { 1527 struct window_copy_mode_data *data = wp->modedata; 1528 struct screen *s = &data->screen; 1529 char *buf; 1530 size_t off; 1531 u_int i, xx, yy, sx, sy, ex, ey, ey_last; 1532 u_int firstsx, lastex, restex, restsx; 1533 int keys; 1534 1535 if (!s->sel.flag && s->sel.lineflag == LINE_SEL_NONE) 1536 return (NULL); 1537 1538 buf = xmalloc(1); 1539 off = 0; 1540 1541 *buf = '\0'; 1542 1543 /* 1544 * The selection extends from selx,sely to (adjusted) cx,cy on 1545 * the base screen. 1546 */ 1547 1548 /* Find start and end. */ 1549 xx = data->endselx; 1550 yy = data->endsely; 1551 if (yy < data->sely || (yy == data->sely && xx < data->selx)) { 1552 sx = xx; sy = yy; 1553 ex = data->selx; ey = data->sely; 1554 } else { 1555 sx = data->selx; sy = data->sely; 1556 ex = xx; ey = yy; 1557 } 1558 1559 /* Trim ex to end of line. */ 1560 ey_last = window_copy_find_length(wp, ey); 1561 if (ex > ey_last) 1562 ex = ey_last; 1563 1564 /* 1565 * Deal with rectangle-copy if necessary; four situations: start of 1566 * first line (firstsx), end of last line (lastex), start (restsx) and 1567 * end (restex) of all other lines. 1568 */ 1569 xx = screen_size_x(s); 1570 1571 /* 1572 * Behave according to mode-keys. If it is emacs, copy like emacs, 1573 * keeping the top-left-most character, and dropping the 1574 * bottom-right-most, regardless of copy direction. If it is vi, also 1575 * keep bottom-right-most character. 1576 */ 1577 keys = options_get_number(wp->window->options, "mode-keys"); 1578 if (data->rectflag) { 1579 /* 1580 * Need to ignore the column with the cursor in it, which for 1581 * rectangular copy means knowing which side the cursor is on. 1582 */ 1583 if (data->selx < data->cx) { 1584 /* Selection start is on the left. */ 1585 if (keys == MODEKEY_EMACS) { 1586 lastex = data->cx; 1587 restex = data->cx; 1588 } 1589 else { 1590 lastex = data->cx + 1; 1591 restex = data->cx + 1; 1592 } 1593 firstsx = data->selx; 1594 restsx = data->selx; 1595 } else { 1596 /* Cursor is on the left. */ 1597 lastex = data->selx + 1; 1598 restex = data->selx + 1; 1599 firstsx = data->cx; 1600 restsx = data->cx; 1601 } 1602 } else { 1603 if (keys == MODEKEY_EMACS) 1604 lastex = ex; 1605 else 1606 lastex = ex + 1; 1607 restex = xx; 1608 firstsx = sx; 1609 restsx = 0; 1610 } 1611 1612 /* Copy the lines. */ 1613 for (i = sy; i <= ey; i++) { 1614 window_copy_copy_line(wp, &buf, &off, i, 1615 (i == sy ? firstsx : restsx), 1616 (i == ey ? lastex : restex)); 1617 } 1618 1619 /* Don't bother if no data. */ 1620 if (off == 0) { 1621 free(buf); 1622 return (NULL); 1623 } 1624 if (keys == MODEKEY_EMACS || lastex <= ey_last) 1625 off -= 1; /* remove final \n (unless at end in vi mode) */ 1626 *len = off; 1627 return (buf); 1628 } 1629 1630 static void 1631 window_copy_copy_buffer(struct window_pane *wp, const char *bufname, void *buf, 1632 size_t len) 1633 { 1634 struct screen_write_ctx ctx; 1635 1636 if (options_get_number(global_options, "set-clipboard") != 0) { 1637 screen_write_start(&ctx, wp, NULL); 1638 screen_write_setselection(&ctx, buf, len); 1639 screen_write_stop(&ctx); 1640 notify_pane("pane-set-clipboard", wp); 1641 } 1642 1643 if (paste_set(buf, len, bufname, NULL) != 0) 1644 free(buf); 1645 } 1646 1647 static void 1648 window_copy_copy_pipe(struct window_pane *wp, struct session *s, 1649 const char *bufname, const char *arg) 1650 { 1651 void *buf; 1652 size_t len; 1653 struct job *job; 1654 char *expanded; 1655 1656 buf = window_copy_get_selection(wp, &len); 1657 if (buf == NULL) 1658 return; 1659 expanded = format_single(NULL, arg, NULL, s, NULL, wp); 1660 1661 job = job_run(expanded, s, NULL, NULL, NULL, NULL, NULL); 1662 bufferevent_write(job->event, buf, len); 1663 1664 free(expanded); 1665 window_copy_copy_buffer(wp, bufname, buf, len); 1666 } 1667 1668 static void 1669 window_copy_copy_selection(struct window_pane *wp, const char *bufname) 1670 { 1671 void *buf; 1672 size_t len; 1673 1674 buf = window_copy_get_selection(wp, &len); 1675 if (buf == NULL) 1676 return; 1677 1678 window_copy_copy_buffer(wp, bufname, buf, len); 1679 } 1680 1681 static void 1682 window_copy_append_selection(struct window_pane *wp, const char *bufname) 1683 { 1684 char *buf; 1685 struct paste_buffer *pb; 1686 const char *bufdata; 1687 size_t len, bufsize; 1688 struct screen_write_ctx ctx; 1689 1690 buf = window_copy_get_selection(wp, &len); 1691 if (buf == NULL) 1692 return; 1693 1694 if (options_get_number(global_options, "set-clipboard") != 0) { 1695 screen_write_start(&ctx, wp, NULL); 1696 screen_write_setselection(&ctx, buf, len); 1697 screen_write_stop(&ctx); 1698 notify_pane("pane-set-clipboard", wp); 1699 } 1700 1701 if (bufname == NULL || *bufname == '\0') 1702 pb = paste_get_top(&bufname); 1703 else 1704 pb = paste_get_name(bufname); 1705 if (pb != NULL) { 1706 bufdata = paste_buffer_data(pb, &bufsize); 1707 buf = xrealloc(buf, len + bufsize); 1708 memmove(buf + bufsize, buf, len); 1709 memcpy(buf, bufdata, bufsize); 1710 len += bufsize; 1711 } 1712 if (paste_set(buf, len, bufname, NULL) != 0) 1713 free(buf); 1714 } 1715 1716 static void 1717 window_copy_copy_line(struct window_pane *wp, char **buf, size_t *off, u_int sy, 1718 u_int sx, u_int ex) 1719 { 1720 struct window_copy_mode_data *data = wp->modedata; 1721 struct grid *gd = data->backing->grid; 1722 struct grid_cell gc; 1723 struct grid_line *gl; 1724 struct utf8_data ud; 1725 u_int i, xx, wrapped = 0; 1726 const char *s; 1727 1728 if (sx > ex) 1729 return; 1730 1731 /* 1732 * Work out if the line was wrapped at the screen edge and all of it is 1733 * on screen. 1734 */ 1735 gl = &gd->linedata[sy]; 1736 if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx) 1737 wrapped = 1; 1738 1739 /* If the line was wrapped, don't strip spaces (use the full length). */ 1740 if (wrapped) 1741 xx = gl->cellsize; 1742 else 1743 xx = window_copy_find_length(wp, sy); 1744 if (ex > xx) 1745 ex = xx; 1746 if (sx > xx) 1747 sx = xx; 1748 1749 if (sx < ex) { 1750 for (i = sx; i < ex; i++) { 1751 grid_get_cell(gd, i, sy, &gc); 1752 if (gc.flags & GRID_FLAG_PADDING) 1753 continue; 1754 utf8_copy(&ud, &gc.data); 1755 if (ud.size == 1 && (gc.attr & GRID_ATTR_CHARSET)) { 1756 s = tty_acs_get(NULL, ud.data[0]); 1757 if (s != NULL && strlen(s) <= sizeof ud.data) { 1758 ud.size = strlen(s); 1759 memcpy(ud.data, s, ud.size); 1760 } 1761 } 1762 1763 *buf = xrealloc(*buf, (*off) + ud.size); 1764 memcpy(*buf + *off, ud.data, ud.size); 1765 *off += ud.size; 1766 } 1767 } 1768 1769 /* Only add a newline if the line wasn't wrapped. */ 1770 if (!wrapped || ex != xx) { 1771 *buf = xrealloc(*buf, (*off) + 1); 1772 (*buf)[(*off)++] = '\n'; 1773 } 1774 } 1775 1776 static void 1777 window_copy_clear_selection(struct window_pane *wp) 1778 { 1779 struct window_copy_mode_data *data = wp->modedata; 1780 u_int px, py; 1781 1782 screen_clear_selection(&data->screen); 1783 1784 data->cursordrag = CURSORDRAG_NONE; 1785 1786 py = screen_hsize(data->backing) + data->cy - data->oy; 1787 px = window_copy_find_length(wp, py); 1788 if (data->cx > px) 1789 window_copy_update_cursor(wp, px, data->cy); 1790 } 1791 1792 static int 1793 window_copy_in_set(struct window_pane *wp, u_int px, u_int py, const char *set) 1794 { 1795 struct window_copy_mode_data *data = wp->modedata; 1796 struct grid_cell gc; 1797 const struct utf8_data *ud; 1798 1799 grid_get_cell(data->backing->grid, px, py, &gc); 1800 1801 ud = &gc.data; 1802 if (ud->size != 1 || (gc.flags & GRID_FLAG_PADDING)) 1803 return (0); 1804 if (*ud->data == 0x00 || *ud->data == 0x7f) 1805 return (0); 1806 return (strchr(set, *ud->data) != NULL); 1807 } 1808 1809 static u_int 1810 window_copy_find_length(struct window_pane *wp, u_int py) 1811 { 1812 struct window_copy_mode_data *data = wp->modedata; 1813 struct screen *s = data->backing; 1814 struct grid_cell gc; 1815 u_int px; 1816 1817 /* 1818 * If the pane has been resized, its grid can contain old overlong 1819 * lines. grid_peek_cell does not allow accessing cells beyond the 1820 * width of the grid, and screen_write_copy treats them as spaces, so 1821 * ignore them here too. 1822 */ 1823 px = s->grid->linedata[py].cellsize; 1824 if (px > screen_size_x(s)) 1825 px = screen_size_x(s); 1826 while (px > 0) { 1827 grid_get_cell(s->grid, px - 1, py, &gc); 1828 if (gc.data.size != 1 || *gc.data.data != ' ') 1829 break; 1830 px--; 1831 } 1832 return (px); 1833 } 1834 1835 static void 1836 window_copy_cursor_start_of_line(struct window_pane *wp) 1837 { 1838 struct window_copy_mode_data *data = wp->modedata; 1839 struct screen *back_s = data->backing; 1840 struct screen *s = &data->screen; 1841 struct grid *gd = back_s->grid; 1842 u_int py; 1843 1844 if (data->cx == 0 && s->sel.lineflag == LINE_SEL_NONE) { 1845 py = screen_hsize(back_s) + data->cy - data->oy; 1846 while (py > 0 && 1847 gd->linedata[py-1].flags & GRID_LINE_WRAPPED) { 1848 window_copy_cursor_up(wp, 0); 1849 py = screen_hsize(back_s) + data->cy - data->oy; 1850 } 1851 } 1852 window_copy_update_cursor(wp, 0, data->cy); 1853 if (window_copy_update_selection(wp, 1)) 1854 window_copy_redraw_lines(wp, data->cy, 1); 1855 } 1856 1857 static void 1858 window_copy_cursor_back_to_indentation(struct window_pane *wp) 1859 { 1860 struct window_copy_mode_data *data = wp->modedata; 1861 u_int px, py, xx; 1862 struct grid_cell gc; 1863 1864 px = 0; 1865 py = screen_hsize(data->backing) + data->cy - data->oy; 1866 xx = window_copy_find_length(wp, py); 1867 1868 while (px < xx) { 1869 grid_get_cell(data->backing->grid, px, py, &gc); 1870 if (gc.data.size != 1 || *gc.data.data != ' ') 1871 break; 1872 px++; 1873 } 1874 1875 window_copy_update_cursor(wp, px, data->cy); 1876 if (window_copy_update_selection(wp, 1)) 1877 window_copy_redraw_lines(wp, data->cy, 1); 1878 } 1879 1880 static void 1881 window_copy_cursor_end_of_line(struct window_pane *wp) 1882 { 1883 struct window_copy_mode_data *data = wp->modedata; 1884 struct screen *back_s = data->backing; 1885 struct screen *s = &data->screen; 1886 struct grid *gd = back_s->grid; 1887 u_int px, py; 1888 1889 py = screen_hsize(back_s) + data->cy - data->oy; 1890 px = window_copy_find_length(wp, py); 1891 1892 if (data->cx == px && s->sel.lineflag == LINE_SEL_NONE) { 1893 if (data->screen.sel.flag && data->rectflag) 1894 px = screen_size_x(back_s); 1895 if (gd->linedata[py].flags & GRID_LINE_WRAPPED) { 1896 while (py < gd->sy + gd->hsize && 1897 gd->linedata[py].flags & GRID_LINE_WRAPPED) { 1898 window_copy_cursor_down(wp, 0); 1899 py = screen_hsize(back_s) 1900 + data->cy - data->oy; 1901 } 1902 px = window_copy_find_length(wp, py); 1903 } 1904 } 1905 window_copy_update_cursor(wp, px, data->cy); 1906 1907 if (window_copy_update_selection(wp, 1)) 1908 window_copy_redraw_lines(wp, data->cy, 1); 1909 } 1910 1911 static void 1912 window_copy_other_end(struct window_pane *wp) 1913 { 1914 struct window_copy_mode_data *data = wp->modedata; 1915 struct screen *s = &data->screen; 1916 u_int selx, sely, cy, yy, hsize; 1917 1918 if (!s->sel.flag && s->sel.lineflag == LINE_SEL_NONE) 1919 return; 1920 1921 if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT) 1922 s->sel.lineflag = LINE_SEL_RIGHT_LEFT; 1923 else if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT) 1924 s->sel.lineflag = LINE_SEL_LEFT_RIGHT; 1925 1926 switch (data->cursordrag) { 1927 case CURSORDRAG_NONE: 1928 case CURSORDRAG_SEL: 1929 data->cursordrag = CURSORDRAG_ENDSEL; 1930 break; 1931 case CURSORDRAG_ENDSEL: 1932 data->cursordrag = CURSORDRAG_SEL; 1933 break; 1934 } 1935 1936 selx = data->endselx; 1937 sely = data->endsely; 1938 if (data->cursordrag == CURSORDRAG_SEL) { 1939 selx = data->selx; 1940 sely = data->sely; 1941 } 1942 1943 cy = data->cy; 1944 yy = screen_hsize(data->backing) + data->cy - data->oy; 1945 1946 data->cx = selx; 1947 1948 hsize = screen_hsize(data->backing); 1949 if (sely < hsize - data->oy) { /* above */ 1950 data->oy = hsize - sely; 1951 data->cy = 0; 1952 } else if (sely > hsize - data->oy + screen_size_y(s)) { /* below */ 1953 data->oy = hsize - sely + screen_size_y(s) - 1; 1954 data->cy = screen_size_y(s) - 1; 1955 } else 1956 data->cy = cy + sely - yy; 1957 1958 window_copy_update_selection(wp, 1); 1959 window_copy_redraw_screen(wp); 1960 } 1961 1962 static void 1963 window_copy_cursor_left(struct window_pane *wp) 1964 { 1965 struct window_copy_mode_data *data = wp->modedata; 1966 u_int py, cx; 1967 struct grid_cell gc; 1968 1969 py = screen_hsize(data->backing) + data->cy - data->oy; 1970 cx = data->cx; 1971 while (cx > 0) { 1972 grid_get_cell(data->backing->grid, cx, py, &gc); 1973 if (~gc.flags & GRID_FLAG_PADDING) 1974 break; 1975 cx--; 1976 } 1977 if (cx == 0 && py > 0) { 1978 window_copy_cursor_up(wp, 0); 1979 window_copy_cursor_end_of_line(wp); 1980 } else if (cx > 0) { 1981 window_copy_update_cursor(wp, cx - 1, data->cy); 1982 if (window_copy_update_selection(wp, 1)) 1983 window_copy_redraw_lines(wp, data->cy, 1); 1984 } 1985 } 1986 1987 static void 1988 window_copy_cursor_right(struct window_pane *wp) 1989 { 1990 struct window_copy_mode_data *data = wp->modedata; 1991 u_int px, py, yy, cx, cy; 1992 struct grid_cell gc; 1993 1994 py = screen_hsize(data->backing) + data->cy - data->oy; 1995 yy = screen_hsize(data->backing) + screen_size_y(data->backing) - 1; 1996 if (data->screen.sel.flag && data->rectflag) 1997 px = screen_size_x(&data->screen); 1998 else 1999 px = window_copy_find_length(wp, py); 2000 2001 if (data->cx >= px && py < yy) { 2002 window_copy_cursor_start_of_line(wp); 2003 window_copy_cursor_down(wp, 0); 2004 } else if (data->cx < px) { 2005 cx = data->cx + 1; 2006 cy = screen_hsize(data->backing) + data->cy - data->oy; 2007 while (cx < px) { 2008 grid_get_cell(data->backing->grid, cx, cy, &gc); 2009 if (~gc.flags & GRID_FLAG_PADDING) 2010 break; 2011 cx++; 2012 } 2013 window_copy_update_cursor(wp, cx, data->cy); 2014 if (window_copy_update_selection(wp, 1)) 2015 window_copy_redraw_lines(wp, data->cy, 1); 2016 } 2017 } 2018 2019 static void 2020 window_copy_cursor_up(struct window_pane *wp, int scroll_only) 2021 { 2022 struct window_copy_mode_data *data = wp->modedata; 2023 struct screen *s = &data->screen; 2024 u_int ox, oy, px, py; 2025 2026 oy = screen_hsize(data->backing) + data->cy - data->oy; 2027 ox = window_copy_find_length(wp, oy); 2028 if (data->cx != ox) { 2029 data->lastcx = data->cx; 2030 data->lastsx = ox; 2031 } 2032 2033 if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely) 2034 window_copy_other_end(wp); 2035 2036 data->cx = data->lastcx; 2037 if (scroll_only || data->cy == 0) { 2038 window_copy_scroll_down(wp, 1); 2039 if (scroll_only) { 2040 if (data->cy == screen_size_y(s) - 1) 2041 window_copy_redraw_lines(wp, data->cy, 1); 2042 else 2043 window_copy_redraw_lines(wp, data->cy, 2); 2044 } 2045 } else { 2046 window_copy_update_cursor(wp, data->cx, data->cy - 1); 2047 if (window_copy_update_selection(wp, 1)) { 2048 if (data->cy == screen_size_y(s) - 1) 2049 window_copy_redraw_lines(wp, data->cy, 1); 2050 else 2051 window_copy_redraw_lines(wp, data->cy, 2); 2052 } 2053 } 2054 2055 if (!data->screen.sel.flag || !data->rectflag) { 2056 py = screen_hsize(data->backing) + data->cy - data->oy; 2057 px = window_copy_find_length(wp, py); 2058 if ((data->cx >= data->lastsx && data->cx != px) || 2059 data->cx > px) 2060 window_copy_cursor_end_of_line(wp); 2061 } 2062 2063 if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT) 2064 window_copy_cursor_end_of_line(wp); 2065 else if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT) 2066 window_copy_cursor_start_of_line(wp); 2067 } 2068 2069 static void 2070 window_copy_cursor_down(struct window_pane *wp, int scroll_only) 2071 { 2072 struct window_copy_mode_data *data = wp->modedata; 2073 struct screen *s = &data->screen; 2074 u_int ox, oy, px, py; 2075 2076 oy = screen_hsize(data->backing) + data->cy - data->oy; 2077 ox = window_copy_find_length(wp, oy); 2078 if (data->cx != ox) { 2079 data->lastcx = data->cx; 2080 data->lastsx = ox; 2081 } 2082 2083 if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely) 2084 window_copy_other_end(wp); 2085 2086 data->cx = data->lastcx; 2087 if (scroll_only || data->cy == screen_size_y(s) - 1) { 2088 window_copy_scroll_up(wp, 1); 2089 if (scroll_only && data->cy > 0) 2090 window_copy_redraw_lines(wp, data->cy - 1, 2); 2091 } else { 2092 window_copy_update_cursor(wp, data->cx, data->cy + 1); 2093 if (window_copy_update_selection(wp, 1)) 2094 window_copy_redraw_lines(wp, data->cy - 1, 2); 2095 } 2096 2097 if (!data->screen.sel.flag || !data->rectflag) { 2098 py = screen_hsize(data->backing) + data->cy - data->oy; 2099 px = window_copy_find_length(wp, py); 2100 if ((data->cx >= data->lastsx && data->cx != px) || 2101 data->cx > px) 2102 window_copy_cursor_end_of_line(wp); 2103 } 2104 2105 if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT) 2106 window_copy_cursor_end_of_line(wp); 2107 else if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT) 2108 window_copy_cursor_start_of_line(wp); 2109 } 2110 2111 static void 2112 window_copy_cursor_jump(struct window_pane *wp) 2113 { 2114 struct window_copy_mode_data *data = wp->modedata; 2115 struct screen *back_s = data->backing; 2116 struct grid_cell gc; 2117 u_int px, py, xx; 2118 2119 px = data->cx + 1; 2120 py = screen_hsize(back_s) + data->cy - data->oy; 2121 xx = window_copy_find_length(wp, py); 2122 2123 while (px < xx) { 2124 grid_get_cell(back_s->grid, px, py, &gc); 2125 if (!(gc.flags & GRID_FLAG_PADDING) && 2126 gc.data.size == 1 && *gc.data.data == data->jumpchar) { 2127 window_copy_update_cursor(wp, px, data->cy); 2128 if (window_copy_update_selection(wp, 1)) 2129 window_copy_redraw_lines(wp, data->cy, 1); 2130 return; 2131 } 2132 px++; 2133 } 2134 } 2135 2136 static void 2137 window_copy_cursor_jump_back(struct window_pane *wp) 2138 { 2139 struct window_copy_mode_data *data = wp->modedata; 2140 struct screen *back_s = data->backing; 2141 struct grid_cell gc; 2142 u_int px, py; 2143 2144 px = data->cx; 2145 py = screen_hsize(back_s) + data->cy - data->oy; 2146 2147 if (px > 0) 2148 px--; 2149 2150 for (;;) { 2151 grid_get_cell(back_s->grid, px, py, &gc); 2152 if (!(gc.flags & GRID_FLAG_PADDING) && 2153 gc.data.size == 1 && *gc.data.data == data->jumpchar) { 2154 window_copy_update_cursor(wp, px, data->cy); 2155 if (window_copy_update_selection(wp, 1)) 2156 window_copy_redraw_lines(wp, data->cy, 1); 2157 return; 2158 } 2159 if (px == 0) 2160 break; 2161 px--; 2162 } 2163 } 2164 2165 static void 2166 window_copy_cursor_jump_to(struct window_pane *wp) 2167 { 2168 struct window_copy_mode_data *data = wp->modedata; 2169 struct screen *back_s = data->backing; 2170 struct grid_cell gc; 2171 u_int px, py, xx; 2172 2173 px = data->cx + 2; 2174 py = screen_hsize(back_s) + data->cy - data->oy; 2175 xx = window_copy_find_length(wp, py); 2176 2177 while (px < xx) { 2178 grid_get_cell(back_s->grid, px, py, &gc); 2179 if (!(gc.flags & GRID_FLAG_PADDING) && 2180 gc.data.size == 1 && *gc.data.data == data->jumpchar) { 2181 window_copy_update_cursor(wp, px - 1, data->cy); 2182 if (window_copy_update_selection(wp, 1)) 2183 window_copy_redraw_lines(wp, data->cy, 1); 2184 return; 2185 } 2186 px++; 2187 } 2188 } 2189 2190 static void 2191 window_copy_cursor_jump_to_back(struct window_pane *wp) 2192 { 2193 struct window_copy_mode_data *data = wp->modedata; 2194 struct screen *back_s = data->backing; 2195 struct grid_cell gc; 2196 u_int px, py; 2197 2198 px = data->cx; 2199 py = screen_hsize(back_s) + data->cy - data->oy; 2200 2201 if (px > 0) 2202 px--; 2203 2204 if (px > 0) 2205 px--; 2206 2207 for (;;) { 2208 grid_get_cell(back_s->grid, px, py, &gc); 2209 if (!(gc.flags & GRID_FLAG_PADDING) && 2210 gc.data.size == 1 && *gc.data.data == data->jumpchar) { 2211 window_copy_update_cursor(wp, px + 1, data->cy); 2212 if (window_copy_update_selection(wp, 1)) 2213 window_copy_redraw_lines(wp, data->cy, 1); 2214 return; 2215 } 2216 if (px == 0) 2217 break; 2218 px--; 2219 } 2220 } 2221 2222 static void 2223 window_copy_cursor_next_word(struct window_pane *wp, const char *separators) 2224 { 2225 struct window_copy_mode_data *data = wp->modedata; 2226 struct screen *back_s = data->backing; 2227 u_int px, py, xx, yy; 2228 int expected = 0; 2229 2230 px = data->cx; 2231 py = screen_hsize(back_s) + data->cy - data->oy; 2232 xx = window_copy_find_length(wp, py); 2233 yy = screen_hsize(back_s) + screen_size_y(back_s) - 1; 2234 2235 /* 2236 * First skip past any nonword characters and then any word characters. 2237 * 2238 * expected is initially set to 0 for the former and then 1 for the 2239 * latter. 2240 */ 2241 do { 2242 while (px > xx || 2243 window_copy_in_set(wp, px, py, separators) == expected) { 2244 /* Move down if we're past the end of the line. */ 2245 if (px > xx) { 2246 if (py == yy) 2247 return; 2248 window_copy_cursor_down(wp, 0); 2249 px = 0; 2250 2251 py = screen_hsize(back_s) + data->cy - data->oy; 2252 xx = window_copy_find_length(wp, py); 2253 } else 2254 px++; 2255 } 2256 expected = !expected; 2257 } while (expected == 1); 2258 2259 window_copy_update_cursor(wp, px, data->cy); 2260 if (window_copy_update_selection(wp, 1)) 2261 window_copy_redraw_lines(wp, data->cy, 1); 2262 } 2263 2264 static void 2265 window_copy_cursor_next_word_end(struct window_pane *wp, 2266 const char *separators) 2267 { 2268 struct window_copy_mode_data *data = wp->modedata; 2269 struct options *oo = wp->window->options; 2270 struct screen *back_s = data->backing; 2271 u_int px, py, xx, yy; 2272 int keys, expected = 1; 2273 2274 px = data->cx; 2275 py = screen_hsize(back_s) + data->cy - data->oy; 2276 xx = window_copy_find_length(wp, py); 2277 yy = screen_hsize(back_s) + screen_size_y(back_s) - 1; 2278 2279 keys = options_get_number(oo, "mode-keys"); 2280 if (keys == MODEKEY_VI && !window_copy_in_set(wp, px, py, separators)) 2281 px++; 2282 2283 /* 2284 * First skip past any word characters, then any nonword characters. 2285 * 2286 * expected is initially set to 1 for the former and then 0 for the 2287 * latter. 2288 */ 2289 do { 2290 while (px > xx || 2291 window_copy_in_set(wp, px, py, separators) == expected) { 2292 /* Move down if we're past the end of the line. */ 2293 if (px > xx) { 2294 if (py == yy) 2295 return; 2296 window_copy_cursor_down(wp, 0); 2297 px = 0; 2298 2299 py = screen_hsize(back_s) + data->cy - data->oy; 2300 xx = window_copy_find_length(wp, py); 2301 } else 2302 px++; 2303 } 2304 expected = !expected; 2305 } while (expected == 0); 2306 2307 if (keys == MODEKEY_VI && px != 0) 2308 px--; 2309 2310 window_copy_update_cursor(wp, px, data->cy); 2311 if (window_copy_update_selection(wp, 1)) 2312 window_copy_redraw_lines(wp, data->cy, 1); 2313 } 2314 2315 /* Move to the previous place where a word begins. */ 2316 static void 2317 window_copy_cursor_previous_word(struct window_pane *wp, 2318 const char *separators) 2319 { 2320 struct window_copy_mode_data *data = wp->modedata; 2321 u_int px, py; 2322 2323 px = data->cx; 2324 py = screen_hsize(data->backing) + data->cy - data->oy; 2325 2326 /* Move back to the previous word character. */ 2327 for (;;) { 2328 if (px > 0) { 2329 px--; 2330 if (!window_copy_in_set(wp, px, py, separators)) 2331 break; 2332 } else { 2333 if (data->cy == 0 && 2334 (screen_hsize(data->backing) == 0 || 2335 data->oy >= screen_hsize(data->backing) - 1)) 2336 goto out; 2337 window_copy_cursor_up(wp, 0); 2338 2339 py = screen_hsize(data->backing) + data->cy - data->oy; 2340 px = window_copy_find_length(wp, py); 2341 } 2342 } 2343 2344 /* Move back to the beginning of this word. */ 2345 while (px > 0 && !window_copy_in_set(wp, px - 1, py, separators)) 2346 px--; 2347 2348 out: 2349 window_copy_update_cursor(wp, px, data->cy); 2350 if (window_copy_update_selection(wp, 1)) 2351 window_copy_redraw_lines(wp, data->cy, 1); 2352 } 2353 2354 static void 2355 window_copy_scroll_up(struct window_pane *wp, u_int ny) 2356 { 2357 struct window_copy_mode_data *data = wp->modedata; 2358 struct screen *s = &data->screen; 2359 struct screen_write_ctx ctx; 2360 2361 if (data->oy < ny) 2362 ny = data->oy; 2363 if (ny == 0) 2364 return; 2365 data->oy -= ny; 2366 2367 window_copy_update_selection(wp, 0); 2368 2369 screen_write_start(&ctx, wp, NULL); 2370 screen_write_cursormove(&ctx, 0, 0); 2371 screen_write_deleteline(&ctx, ny, 8); 2372 window_copy_write_lines(wp, &ctx, screen_size_y(s) - ny, ny); 2373 window_copy_write_line(wp, &ctx, 0); 2374 if (screen_size_y(s) > 1) 2375 window_copy_write_line(wp, &ctx, 1); 2376 if (screen_size_y(s) > 3) 2377 window_copy_write_line(wp, &ctx, screen_size_y(s) - 2); 2378 if (s->sel.flag && screen_size_y(s) > ny) 2379 window_copy_write_line(wp, &ctx, screen_size_y(s) - ny - 1); 2380 screen_write_cursormove(&ctx, data->cx, data->cy); 2381 screen_write_stop(&ctx); 2382 } 2383 2384 static void 2385 window_copy_scroll_down(struct window_pane *wp, u_int ny) 2386 { 2387 struct window_copy_mode_data *data = wp->modedata; 2388 struct screen *s = &data->screen; 2389 struct screen_write_ctx ctx; 2390 2391 if (ny > screen_hsize(data->backing)) 2392 return; 2393 2394 if (data->oy > screen_hsize(data->backing) - ny) 2395 ny = screen_hsize(data->backing) - data->oy; 2396 if (ny == 0) 2397 return; 2398 data->oy += ny; 2399 2400 window_copy_update_selection(wp, 0); 2401 2402 screen_write_start(&ctx, wp, NULL); 2403 screen_write_cursormove(&ctx, 0, 0); 2404 screen_write_insertline(&ctx, ny, 8); 2405 window_copy_write_lines(wp, &ctx, 0, ny); 2406 if (s->sel.flag && screen_size_y(s) > ny) 2407 window_copy_write_line(wp, &ctx, ny); 2408 else if (ny == 1) /* nuke position */ 2409 window_copy_write_line(wp, &ctx, 1); 2410 screen_write_cursormove(&ctx, data->cx, data->cy); 2411 screen_write_stop(&ctx); 2412 } 2413 2414 void 2415 window_copy_add_formats(struct window_pane *wp, struct format_tree *ft) 2416 { 2417 struct window_copy_mode_data *data = wp->modedata; 2418 struct screen *s = &data->screen; 2419 2420 if (wp->mode != &window_copy_mode) 2421 return; 2422 2423 format_add(ft, "selection_present", "%d", s->sel.flag); 2424 format_add(ft, "scroll_position", "%d", data->oy); 2425 } 2426 2427 static void 2428 window_copy_rectangle_toggle(struct window_pane *wp) 2429 { 2430 struct window_copy_mode_data *data = wp->modedata; 2431 u_int px, py; 2432 2433 data->rectflag = !data->rectflag; 2434 2435 py = screen_hsize(data->backing) + data->cy - data->oy; 2436 px = window_copy_find_length(wp, py); 2437 if (data->cx > px) 2438 window_copy_update_cursor(wp, px, data->cy); 2439 2440 window_copy_update_selection(wp, 1); 2441 window_copy_redraw_screen(wp); 2442 } 2443 2444 static void 2445 window_copy_move_mouse(struct mouse_event *m) 2446 { 2447 struct window_pane *wp; 2448 u_int x, y; 2449 2450 wp = cmd_mouse_pane(m, NULL, NULL); 2451 if (wp == NULL || wp->mode != &window_copy_mode) 2452 return; 2453 2454 if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) 2455 return; 2456 2457 window_copy_update_cursor(wp, x, y); 2458 } 2459 2460 void 2461 window_copy_start_drag(struct client *c, struct mouse_event *m) 2462 { 2463 struct window_pane *wp; 2464 u_int x, y; 2465 2466 if (c == NULL) 2467 return; 2468 2469 wp = cmd_mouse_pane(m, NULL, NULL); 2470 if (wp == NULL || wp->mode != &window_copy_mode) 2471 return; 2472 2473 if (cmd_mouse_at(wp, m, &x, &y, 1) != 0) 2474 return; 2475 2476 c->tty.mouse_drag_update = window_copy_drag_update; 2477 c->tty.mouse_drag_release = NULL; /* will fire MouseDragEnd key */ 2478 2479 window_copy_update_cursor(wp, x, y); 2480 window_copy_start_selection(wp); 2481 window_copy_redraw_screen(wp); 2482 } 2483 2484 static void 2485 window_copy_drag_update(__unused struct client *c, struct mouse_event *m) 2486 { 2487 struct window_pane *wp; 2488 struct window_copy_mode_data *data; 2489 u_int x, y, old_cy; 2490 2491 wp = cmd_mouse_pane(m, NULL, NULL); 2492 if (wp == NULL || wp->mode != &window_copy_mode) 2493 return; 2494 data = wp->modedata; 2495 2496 if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) 2497 return; 2498 old_cy = data->cy; 2499 2500 window_copy_update_cursor(wp, x, y); 2501 if (window_copy_update_selection(wp, 1)) 2502 window_copy_redraw_selection(wp, old_cy); 2503 } 2504