1 /* $OpenBSD: window-copy.c,v 1.181 2017/08/23 09:18:22 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, int); 61 static int window_copy_search_up(struct window_pane *, int); 62 static int window_copy_search_down(struct window_pane *, int); 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 *, int); 95 static void window_copy_cursor_jump_to_back(struct window_pane *, int); 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, 1); 671 break; 672 case WINDOW_COPY_JUMPTOBACKWARD: 673 for (; np != 0; np--) 674 window_copy_cursor_jump_to_back(wp, 1); 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, 1); 691 break; 692 case WINDOW_COPY_JUMPTOBACKWARD: 693 for (; np != 0; np--) 694 window_copy_cursor_jump_to(wp, 1); 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, 1); 773 } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { 774 for (; np != 0; np--) 775 window_copy_search_down(wp, 1); 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, 1); 782 } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { 783 for (; np != 0; np--) 784 window_copy_search_up(wp, 1); 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, 1); 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, 1); 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, 1); 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, 1); 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, 1)) { 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, 1)) { 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, 1)) { 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, 1)) { 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. If moveflag is 0 then look for string at the current cursor position 1132 * as well. 1133 */ 1134 static int 1135 window_copy_search(struct window_pane *wp, int direction, int moveflag) 1136 { 1137 struct window_copy_mode_data *data = wp->modedata; 1138 struct screen *s = data->backing, ss; 1139 struct screen_write_ctx ctx; 1140 struct grid *gd = s->grid; 1141 u_int fx, fy, endline; 1142 int wrapflag, cis, found; 1143 1144 free(wp->searchstr); 1145 wp->searchstr = xstrdup(data->searchstr); 1146 1147 fx = data->cx; 1148 fy = screen_hsize(data->backing) - data->oy + data->cy; 1149 1150 screen_init(&ss, screen_write_strlen("%s", data->searchstr), 1, 0); 1151 screen_write_start(&ctx, NULL, &ss); 1152 screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", data->searchstr); 1153 screen_write_stop(&ctx); 1154 1155 if (moveflag) { 1156 if (direction) 1157 window_copy_move_right(s, &fx, &fy); 1158 else 1159 window_copy_move_left(s, &fx, &fy); 1160 } 1161 window_copy_clear_selection(wp); 1162 1163 wrapflag = options_get_number(wp->window->options, "wrap-search"); 1164 cis = window_copy_is_lowercase(data->searchstr); 1165 1166 if (direction) 1167 endline = gd->hsize + gd->sy - 1; 1168 else 1169 endline = 0; 1170 found = window_copy_search_jump(wp, gd, ss.grid, fx, fy, endline, cis, 1171 wrapflag, direction); 1172 1173 if (window_copy_search_marks(wp, &ss)) 1174 window_copy_redraw_screen(wp); 1175 1176 screen_free(&ss); 1177 return (found); 1178 } 1179 1180 static int 1181 window_copy_search_marks(struct window_pane *wp, struct screen *ssp) 1182 { 1183 struct window_copy_mode_data *data = wp->modedata; 1184 struct screen *s = data->backing, ss; 1185 struct screen_write_ctx ctx; 1186 struct grid *gd = s->grid; 1187 int found, cis, which = -1; 1188 u_int px, py, b, nfound = 0, width; 1189 1190 if (ssp == NULL) { 1191 width = screen_write_strlen("%s", data->searchstr); 1192 screen_init(&ss, width, 1, 0); 1193 screen_write_start(&ctx, NULL, &ss); 1194 screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", 1195 data->searchstr); 1196 screen_write_stop(&ctx); 1197 ssp = &ss; 1198 } else 1199 width = screen_size_x(ssp); 1200 1201 cis = window_copy_is_lowercase(data->searchstr); 1202 1203 free(data->searchmark); 1204 data->searchmark = bit_alloc((gd->hsize + gd->sy) * gd->sx); 1205 1206 for (py = 0; py < gd->hsize + gd->sy; py++) { 1207 px = 0; 1208 for (;;) { 1209 found = window_copy_search_lr(gd, ssp->grid, &px, py, 1210 px, gd->sx, cis); 1211 if (!found) 1212 break; 1213 1214 nfound++; 1215 if (px == data->cx && py == gd->hsize + data->cy - data->oy) 1216 which = nfound; 1217 1218 b = (py * gd->sx) + px; 1219 bit_nset(data->searchmark, b, b + width - 1); 1220 1221 px++; 1222 } 1223 } 1224 1225 if (which != -1) 1226 data->searchthis = 1 + nfound - which; 1227 else 1228 data->searchthis = -1; 1229 data->searchcount = nfound; 1230 1231 if (ssp == &ss) 1232 screen_free(&ss); 1233 return (nfound); 1234 } 1235 1236 static void 1237 window_copy_clear_marks(struct window_pane *wp) 1238 { 1239 struct window_copy_mode_data *data = wp->modedata; 1240 1241 free(data->searchmark); 1242 data->searchmark = NULL; 1243 } 1244 1245 static int 1246 window_copy_search_up(struct window_pane *wp, int moveflag) 1247 { 1248 return (window_copy_search(wp, 0, moveflag)); 1249 } 1250 1251 static int 1252 window_copy_search_down(struct window_pane *wp, int moveflag) 1253 { 1254 return (window_copy_search(wp, 1, moveflag)); 1255 } 1256 1257 static void 1258 window_copy_goto_line(struct window_pane *wp, const char *linestr) 1259 { 1260 struct window_copy_mode_data *data = wp->modedata; 1261 const char *errstr; 1262 u_int lineno; 1263 1264 lineno = strtonum(linestr, 0, screen_hsize(data->backing), &errstr); 1265 if (errstr != NULL) 1266 return; 1267 1268 data->oy = lineno; 1269 window_copy_update_selection(wp, 1); 1270 window_copy_redraw_screen(wp); 1271 } 1272 1273 static void 1274 window_copy_write_line(struct window_pane *wp, struct screen_write_ctx *ctx, 1275 u_int py) 1276 { 1277 struct window_copy_mode_data *data = wp->modedata; 1278 struct screen *s = &data->screen; 1279 struct options *oo = wp->window->options; 1280 struct grid_cell gc; 1281 char hdr[512]; 1282 size_t size = 0; 1283 1284 style_apply(&gc, oo, "mode-style"); 1285 gc.flags |= GRID_FLAG_NOPALETTE; 1286 1287 if (py == 0) { 1288 if (data->searchmark == NULL) { 1289 size = xsnprintf(hdr, sizeof hdr, 1290 "[%u/%u]", data->oy, screen_hsize(data->backing)); 1291 } else { 1292 if (data->searchthis == -1) { 1293 size = xsnprintf(hdr, sizeof hdr, 1294 "(%u results) [%d/%u]", data->searchcount, 1295 data->oy, screen_hsize(data->backing)); 1296 } else { 1297 size = xsnprintf(hdr, sizeof hdr, 1298 "(%u/%u results) [%d/%u]", data->searchthis, 1299 data->searchcount, data->oy, 1300 screen_hsize(data->backing)); 1301 } 1302 } 1303 if (size > screen_size_x(s)) 1304 size = screen_size_x(s); 1305 screen_write_cursormove(ctx, screen_size_x(s) - size, 0); 1306 screen_write_puts(ctx, &gc, "%s", hdr); 1307 } else 1308 size = 0; 1309 1310 if (size < screen_size_x(s)) { 1311 screen_write_cursormove(ctx, 0, py); 1312 screen_write_copy(ctx, data->backing, 0, 1313 (screen_hsize(data->backing) - data->oy) + py, 1314 screen_size_x(s) - size, 1, data->searchmark, &gc); 1315 } 1316 1317 if (py == data->cy && data->cx == screen_size_x(s)) { 1318 memcpy(&gc, &grid_default_cell, sizeof gc); 1319 screen_write_cursormove(ctx, screen_size_x(s) - 1, py); 1320 screen_write_putc(ctx, &gc, '$'); 1321 } 1322 } 1323 1324 static void 1325 window_copy_write_lines(struct window_pane *wp, struct screen_write_ctx *ctx, 1326 u_int py, u_int ny) 1327 { 1328 u_int yy; 1329 1330 for (yy = py; yy < py + ny; yy++) 1331 window_copy_write_line(wp, ctx, py); 1332 } 1333 1334 static void 1335 window_copy_redraw_selection(struct window_pane *wp, u_int old_y) 1336 { 1337 struct window_copy_mode_data *data = wp->modedata; 1338 u_int new_y, start, end; 1339 1340 new_y = data->cy; 1341 if (old_y <= new_y) { 1342 start = old_y; 1343 end = new_y; 1344 } else { 1345 start = new_y; 1346 end = old_y; 1347 } 1348 window_copy_redraw_lines(wp, start, end - start + 1); 1349 } 1350 1351 static void 1352 window_copy_redraw_lines(struct window_pane *wp, u_int py, u_int ny) 1353 { 1354 struct window_copy_mode_data *data = wp->modedata; 1355 struct screen_write_ctx ctx; 1356 u_int i; 1357 1358 screen_write_start(&ctx, wp, NULL); 1359 for (i = py; i < py + ny; i++) 1360 window_copy_write_line(wp, &ctx, i); 1361 screen_write_cursormove(&ctx, data->cx, data->cy); 1362 screen_write_stop(&ctx); 1363 } 1364 1365 static void 1366 window_copy_redraw_screen(struct window_pane *wp) 1367 { 1368 struct window_copy_mode_data *data = wp->modedata; 1369 1370 window_copy_redraw_lines(wp, 0, screen_size_y(&data->screen)); 1371 } 1372 1373 static void 1374 window_copy_synchronize_cursor(struct window_pane *wp) 1375 { 1376 struct window_copy_mode_data *data = wp->modedata; 1377 u_int xx, yy; 1378 1379 xx = data->cx; 1380 yy = screen_hsize(data->backing) + data->cy - data->oy; 1381 1382 switch (data->cursordrag) { 1383 case CURSORDRAG_ENDSEL: 1384 data->endselx = xx; 1385 data->endsely = yy; 1386 break; 1387 case CURSORDRAG_SEL: 1388 data->selx = xx; 1389 data->sely = yy; 1390 break; 1391 case CURSORDRAG_NONE: 1392 break; 1393 } 1394 } 1395 1396 static void 1397 window_copy_update_cursor(struct window_pane *wp, u_int cx, u_int cy) 1398 { 1399 struct window_copy_mode_data *data = wp->modedata; 1400 struct screen *s = &data->screen; 1401 struct screen_write_ctx ctx; 1402 u_int old_cx, old_cy; 1403 1404 old_cx = data->cx; old_cy = data->cy; 1405 data->cx = cx; data->cy = cy; 1406 if (old_cx == screen_size_x(s)) 1407 window_copy_redraw_lines(wp, old_cy, 1); 1408 if (data->cx == screen_size_x(s)) 1409 window_copy_redraw_lines(wp, data->cy, 1); 1410 else { 1411 screen_write_start(&ctx, wp, NULL); 1412 screen_write_cursormove(&ctx, data->cx, data->cy); 1413 screen_write_stop(&ctx); 1414 } 1415 } 1416 1417 static void 1418 window_copy_start_selection(struct window_pane *wp) 1419 { 1420 struct window_copy_mode_data *data = wp->modedata; 1421 struct screen *s = &data->screen; 1422 1423 data->selx = data->cx; 1424 data->sely = screen_hsize(data->backing) + data->cy - data->oy; 1425 1426 data->endselx = data->selx; 1427 data->endsely = data->sely; 1428 1429 data->cursordrag = CURSORDRAG_ENDSEL; 1430 1431 s->sel.flag = 1; 1432 window_copy_update_selection(wp, 1); 1433 } 1434 1435 static int 1436 window_copy_adjust_selection(struct window_pane *wp, u_int *selx, u_int *sely) 1437 { 1438 struct window_copy_mode_data *data = wp->modedata; 1439 struct screen *s = &data->screen; 1440 u_int sx, sy, ty; 1441 int relpos; 1442 1443 sx = *selx; 1444 sy = *sely; 1445 1446 ty = screen_hsize(data->backing) - data->oy; 1447 if (sy < ty) { 1448 relpos = WINDOW_COPY_REL_POS_ABOVE; 1449 if (!data->rectflag) 1450 sx = 0; 1451 sy = 0; 1452 } else if (sy > ty + screen_size_y(s) - 1) { 1453 relpos = WINDOW_COPY_REL_POS_BELOW; 1454 if (!data->rectflag) 1455 sx = screen_size_x(s) - 1; 1456 sy = screen_size_y(s) - 1; 1457 } else { 1458 relpos = WINDOW_COPY_REL_POS_ON_SCREEN; 1459 sy -= ty; 1460 } 1461 1462 *selx = sx; 1463 *sely = sy; 1464 return (relpos); 1465 } 1466 1467 static int 1468 window_copy_update_selection(struct window_pane *wp, int may_redraw) 1469 { 1470 struct window_copy_mode_data *data = wp->modedata; 1471 struct screen *s = &data->screen; 1472 struct options *oo = wp->window->options; 1473 struct grid_cell gc; 1474 u_int sx, sy, cy, endsx, endsy; 1475 int startrelpos, endrelpos; 1476 1477 if (!s->sel.flag && s->sel.lineflag == LINE_SEL_NONE) 1478 return (0); 1479 1480 window_copy_synchronize_cursor(wp); 1481 1482 /* Adjust the selection. */ 1483 sx = data->selx; 1484 sy = data->sely; 1485 startrelpos = window_copy_adjust_selection(wp, &sx, &sy); 1486 1487 /* Adjust the end of selection. */ 1488 endsx = data->endselx; 1489 endsy = data->endsely; 1490 endrelpos = window_copy_adjust_selection(wp, &endsx, &endsy); 1491 1492 /* Selection is outside of the current screen */ 1493 if (startrelpos == endrelpos && 1494 startrelpos != WINDOW_COPY_REL_POS_ON_SCREEN) { 1495 screen_hide_selection(s); 1496 return (0); 1497 } 1498 1499 /* Set colours and selection. */ 1500 style_apply(&gc, oo, "mode-style"); 1501 gc.flags |= GRID_FLAG_NOPALETTE; 1502 screen_set_selection(s, sx, sy, endsx, endsy, data->rectflag, &gc); 1503 1504 if (data->rectflag && may_redraw) { 1505 /* 1506 * Can't rely on the caller to redraw the right lines for 1507 * rectangle selection - find the highest line and the number 1508 * of lines, and redraw just past that in both directions 1509 */ 1510 cy = data->cy; 1511 if (sy < cy) 1512 window_copy_redraw_lines(wp, sy, cy - sy + 1); 1513 else 1514 window_copy_redraw_lines(wp, cy, sy - cy + 1); 1515 } 1516 1517 return (1); 1518 } 1519 1520 static void * 1521 window_copy_get_selection(struct window_pane *wp, size_t *len) 1522 { 1523 struct window_copy_mode_data *data = wp->modedata; 1524 struct screen *s = &data->screen; 1525 char *buf; 1526 size_t off; 1527 u_int i, xx, yy, sx, sy, ex, ey, ey_last; 1528 u_int firstsx, lastex, restex, restsx; 1529 int keys; 1530 1531 if (!s->sel.flag && s->sel.lineflag == LINE_SEL_NONE) 1532 return (NULL); 1533 1534 buf = xmalloc(1); 1535 off = 0; 1536 1537 *buf = '\0'; 1538 1539 /* 1540 * The selection extends from selx,sely to (adjusted) cx,cy on 1541 * the base screen. 1542 */ 1543 1544 /* Find start and end. */ 1545 xx = data->endselx; 1546 yy = data->endsely; 1547 if (yy < data->sely || (yy == data->sely && xx < data->selx)) { 1548 sx = xx; sy = yy; 1549 ex = data->selx; ey = data->sely; 1550 } else { 1551 sx = data->selx; sy = data->sely; 1552 ex = xx; ey = yy; 1553 } 1554 1555 /* Trim ex to end of line. */ 1556 ey_last = window_copy_find_length(wp, ey); 1557 if (ex > ey_last) 1558 ex = ey_last; 1559 1560 /* 1561 * Deal with rectangle-copy if necessary; four situations: start of 1562 * first line (firstsx), end of last line (lastex), start (restsx) and 1563 * end (restex) of all other lines. 1564 */ 1565 xx = screen_size_x(s); 1566 1567 /* 1568 * Behave according to mode-keys. If it is emacs, copy like emacs, 1569 * keeping the top-left-most character, and dropping the 1570 * bottom-right-most, regardless of copy direction. If it is vi, also 1571 * keep bottom-right-most character. 1572 */ 1573 keys = options_get_number(wp->window->options, "mode-keys"); 1574 if (data->rectflag) { 1575 /* 1576 * Need to ignore the column with the cursor in it, which for 1577 * rectangular copy means knowing which side the cursor is on. 1578 */ 1579 if (data->selx < data->cx) { 1580 /* Selection start is on the left. */ 1581 if (keys == MODEKEY_EMACS) { 1582 lastex = data->cx; 1583 restex = data->cx; 1584 } 1585 else { 1586 lastex = data->cx + 1; 1587 restex = data->cx + 1; 1588 } 1589 firstsx = data->selx; 1590 restsx = data->selx; 1591 } else { 1592 /* Cursor is on the left. */ 1593 lastex = data->selx + 1; 1594 restex = data->selx + 1; 1595 firstsx = data->cx; 1596 restsx = data->cx; 1597 } 1598 } else { 1599 if (keys == MODEKEY_EMACS) 1600 lastex = ex; 1601 else 1602 lastex = ex + 1; 1603 restex = xx; 1604 firstsx = sx; 1605 restsx = 0; 1606 } 1607 1608 /* Copy the lines. */ 1609 for (i = sy; i <= ey; i++) { 1610 window_copy_copy_line(wp, &buf, &off, i, 1611 (i == sy ? firstsx : restsx), 1612 (i == ey ? lastex : restex)); 1613 } 1614 1615 /* Don't bother if no data. */ 1616 if (off == 0) { 1617 free(buf); 1618 return (NULL); 1619 } 1620 if (keys == MODEKEY_EMACS || lastex <= ey_last) 1621 off -= 1; /* remove final \n (unless at end in vi mode) */ 1622 *len = off; 1623 return (buf); 1624 } 1625 1626 static void 1627 window_copy_copy_buffer(struct window_pane *wp, const char *bufname, void *buf, 1628 size_t len) 1629 { 1630 struct screen_write_ctx ctx; 1631 1632 if (options_get_number(global_options, "set-clipboard") != 0) { 1633 screen_write_start(&ctx, wp, NULL); 1634 screen_write_setselection(&ctx, buf, len); 1635 screen_write_stop(&ctx); 1636 notify_pane("pane-set-clipboard", wp); 1637 } 1638 1639 if (paste_set(buf, len, bufname, NULL) != 0) 1640 free(buf); 1641 } 1642 1643 static void 1644 window_copy_copy_pipe(struct window_pane *wp, struct session *s, 1645 const char *bufname, const char *arg) 1646 { 1647 void *buf; 1648 size_t len; 1649 struct job *job; 1650 char *expanded; 1651 1652 buf = window_copy_get_selection(wp, &len); 1653 if (buf == NULL) 1654 return; 1655 expanded = format_single(NULL, arg, NULL, s, NULL, wp); 1656 1657 job = job_run(expanded, s, NULL, NULL, NULL, NULL, NULL); 1658 bufferevent_write(job->event, buf, len); 1659 1660 free(expanded); 1661 window_copy_copy_buffer(wp, bufname, buf, len); 1662 } 1663 1664 static void 1665 window_copy_copy_selection(struct window_pane *wp, const char *bufname) 1666 { 1667 void *buf; 1668 size_t len; 1669 1670 buf = window_copy_get_selection(wp, &len); 1671 if (buf == NULL) 1672 return; 1673 1674 window_copy_copy_buffer(wp, bufname, buf, len); 1675 } 1676 1677 static void 1678 window_copy_append_selection(struct window_pane *wp, const char *bufname) 1679 { 1680 char *buf; 1681 struct paste_buffer *pb; 1682 const char *bufdata; 1683 size_t len, bufsize; 1684 struct screen_write_ctx ctx; 1685 1686 buf = window_copy_get_selection(wp, &len); 1687 if (buf == NULL) 1688 return; 1689 1690 if (options_get_number(global_options, "set-clipboard") != 0) { 1691 screen_write_start(&ctx, wp, NULL); 1692 screen_write_setselection(&ctx, buf, len); 1693 screen_write_stop(&ctx); 1694 notify_pane("pane-set-clipboard", wp); 1695 } 1696 1697 if (bufname == NULL || *bufname == '\0') 1698 pb = paste_get_top(&bufname); 1699 else 1700 pb = paste_get_name(bufname); 1701 if (pb != NULL) { 1702 bufdata = paste_buffer_data(pb, &bufsize); 1703 buf = xrealloc(buf, len + bufsize); 1704 memmove(buf + bufsize, buf, len); 1705 memcpy(buf, bufdata, bufsize); 1706 len += bufsize; 1707 } 1708 if (paste_set(buf, len, bufname, NULL) != 0) 1709 free(buf); 1710 } 1711 1712 static void 1713 window_copy_copy_line(struct window_pane *wp, char **buf, size_t *off, u_int sy, 1714 u_int sx, u_int ex) 1715 { 1716 struct window_copy_mode_data *data = wp->modedata; 1717 struct grid *gd = data->backing->grid; 1718 struct grid_cell gc; 1719 struct grid_line *gl; 1720 struct utf8_data ud; 1721 u_int i, xx, wrapped = 0; 1722 const char *s; 1723 1724 if (sx > ex) 1725 return; 1726 1727 /* 1728 * Work out if the line was wrapped at the screen edge and all of it is 1729 * on screen. 1730 */ 1731 gl = &gd->linedata[sy]; 1732 if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx) 1733 wrapped = 1; 1734 1735 /* If the line was wrapped, don't strip spaces (use the full length). */ 1736 if (wrapped) 1737 xx = gl->cellsize; 1738 else 1739 xx = window_copy_find_length(wp, sy); 1740 if (ex > xx) 1741 ex = xx; 1742 if (sx > xx) 1743 sx = xx; 1744 1745 if (sx < ex) { 1746 for (i = sx; i < ex; i++) { 1747 grid_get_cell(gd, i, sy, &gc); 1748 if (gc.flags & GRID_FLAG_PADDING) 1749 continue; 1750 utf8_copy(&ud, &gc.data); 1751 if (ud.size == 1 && (gc.attr & GRID_ATTR_CHARSET)) { 1752 s = tty_acs_get(NULL, ud.data[0]); 1753 if (s != NULL && strlen(s) <= sizeof ud.data) { 1754 ud.size = strlen(s); 1755 memcpy(ud.data, s, ud.size); 1756 } 1757 } 1758 1759 *buf = xrealloc(*buf, (*off) + ud.size); 1760 memcpy(*buf + *off, ud.data, ud.size); 1761 *off += ud.size; 1762 } 1763 } 1764 1765 /* Only add a newline if the line wasn't wrapped. */ 1766 if (!wrapped || ex != xx) { 1767 *buf = xrealloc(*buf, (*off) + 1); 1768 (*buf)[(*off)++] = '\n'; 1769 } 1770 } 1771 1772 static void 1773 window_copy_clear_selection(struct window_pane *wp) 1774 { 1775 struct window_copy_mode_data *data = wp->modedata; 1776 u_int px, py; 1777 1778 screen_clear_selection(&data->screen); 1779 1780 data->cursordrag = CURSORDRAG_NONE; 1781 1782 py = screen_hsize(data->backing) + data->cy - data->oy; 1783 px = window_copy_find_length(wp, py); 1784 if (data->cx > px) 1785 window_copy_update_cursor(wp, px, data->cy); 1786 } 1787 1788 static int 1789 window_copy_in_set(struct window_pane *wp, u_int px, u_int py, const char *set) 1790 { 1791 struct window_copy_mode_data *data = wp->modedata; 1792 struct grid_cell gc; 1793 const struct utf8_data *ud; 1794 1795 grid_get_cell(data->backing->grid, px, py, &gc); 1796 1797 ud = &gc.data; 1798 if (ud->size != 1 || (gc.flags & GRID_FLAG_PADDING)) 1799 return (0); 1800 if (*ud->data == 0x00 || *ud->data == 0x7f) 1801 return (0); 1802 return (strchr(set, *ud->data) != NULL); 1803 } 1804 1805 static u_int 1806 window_copy_find_length(struct window_pane *wp, u_int py) 1807 { 1808 struct window_copy_mode_data *data = wp->modedata; 1809 struct screen *s = data->backing; 1810 struct grid_cell gc; 1811 u_int px; 1812 1813 /* 1814 * If the pane has been resized, its grid can contain old overlong 1815 * lines. grid_peek_cell does not allow accessing cells beyond the 1816 * width of the grid, and screen_write_copy treats them as spaces, so 1817 * ignore them here too. 1818 */ 1819 px = s->grid->linedata[py].cellsize; 1820 if (px > screen_size_x(s)) 1821 px = screen_size_x(s); 1822 while (px > 0) { 1823 grid_get_cell(s->grid, px - 1, py, &gc); 1824 if (gc.data.size != 1 || *gc.data.data != ' ') 1825 break; 1826 px--; 1827 } 1828 return (px); 1829 } 1830 1831 static void 1832 window_copy_cursor_start_of_line(struct window_pane *wp) 1833 { 1834 struct window_copy_mode_data *data = wp->modedata; 1835 struct screen *back_s = data->backing; 1836 struct screen *s = &data->screen; 1837 struct grid *gd = back_s->grid; 1838 u_int py; 1839 1840 if (data->cx == 0 && s->sel.lineflag == LINE_SEL_NONE) { 1841 py = screen_hsize(back_s) + data->cy - data->oy; 1842 while (py > 0 && 1843 gd->linedata[py-1].flags & GRID_LINE_WRAPPED) { 1844 window_copy_cursor_up(wp, 0); 1845 py = screen_hsize(back_s) + data->cy - data->oy; 1846 } 1847 } 1848 window_copy_update_cursor(wp, 0, data->cy); 1849 if (window_copy_update_selection(wp, 1)) 1850 window_copy_redraw_lines(wp, data->cy, 1); 1851 } 1852 1853 static void 1854 window_copy_cursor_back_to_indentation(struct window_pane *wp) 1855 { 1856 struct window_copy_mode_data *data = wp->modedata; 1857 u_int px, py, xx; 1858 struct grid_cell gc; 1859 1860 px = 0; 1861 py = screen_hsize(data->backing) + data->cy - data->oy; 1862 xx = window_copy_find_length(wp, py); 1863 1864 while (px < xx) { 1865 grid_get_cell(data->backing->grid, px, py, &gc); 1866 if (gc.data.size != 1 || *gc.data.data != ' ') 1867 break; 1868 px++; 1869 } 1870 1871 window_copy_update_cursor(wp, px, data->cy); 1872 if (window_copy_update_selection(wp, 1)) 1873 window_copy_redraw_lines(wp, data->cy, 1); 1874 } 1875 1876 static void 1877 window_copy_cursor_end_of_line(struct window_pane *wp) 1878 { 1879 struct window_copy_mode_data *data = wp->modedata; 1880 struct screen *back_s = data->backing; 1881 struct screen *s = &data->screen; 1882 struct grid *gd = back_s->grid; 1883 u_int px, py; 1884 1885 py = screen_hsize(back_s) + data->cy - data->oy; 1886 px = window_copy_find_length(wp, py); 1887 1888 if (data->cx == px && s->sel.lineflag == LINE_SEL_NONE) { 1889 if (data->screen.sel.flag && data->rectflag) 1890 px = screen_size_x(back_s); 1891 if (gd->linedata[py].flags & GRID_LINE_WRAPPED) { 1892 while (py < gd->sy + gd->hsize && 1893 gd->linedata[py].flags & GRID_LINE_WRAPPED) { 1894 window_copy_cursor_down(wp, 0); 1895 py = screen_hsize(back_s) 1896 + data->cy - data->oy; 1897 } 1898 px = window_copy_find_length(wp, py); 1899 } 1900 } 1901 window_copy_update_cursor(wp, px, data->cy); 1902 1903 if (window_copy_update_selection(wp, 1)) 1904 window_copy_redraw_lines(wp, data->cy, 1); 1905 } 1906 1907 static void 1908 window_copy_other_end(struct window_pane *wp) 1909 { 1910 struct window_copy_mode_data *data = wp->modedata; 1911 struct screen *s = &data->screen; 1912 u_int selx, sely, cy, yy, hsize; 1913 1914 if (!s->sel.flag && s->sel.lineflag == LINE_SEL_NONE) 1915 return; 1916 1917 if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT) 1918 s->sel.lineflag = LINE_SEL_RIGHT_LEFT; 1919 else if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT) 1920 s->sel.lineflag = LINE_SEL_LEFT_RIGHT; 1921 1922 switch (data->cursordrag) { 1923 case CURSORDRAG_NONE: 1924 case CURSORDRAG_SEL: 1925 data->cursordrag = CURSORDRAG_ENDSEL; 1926 break; 1927 case CURSORDRAG_ENDSEL: 1928 data->cursordrag = CURSORDRAG_SEL; 1929 break; 1930 } 1931 1932 selx = data->endselx; 1933 sely = data->endsely; 1934 if (data->cursordrag == CURSORDRAG_SEL) { 1935 selx = data->selx; 1936 sely = data->sely; 1937 } 1938 1939 cy = data->cy; 1940 yy = screen_hsize(data->backing) + data->cy - data->oy; 1941 1942 data->cx = selx; 1943 1944 hsize = screen_hsize(data->backing); 1945 if (sely < hsize - data->oy) { /* above */ 1946 data->oy = hsize - sely; 1947 data->cy = 0; 1948 } else if (sely > hsize - data->oy + screen_size_y(s)) { /* below */ 1949 data->oy = hsize - sely + screen_size_y(s) - 1; 1950 data->cy = screen_size_y(s) - 1; 1951 } else 1952 data->cy = cy + sely - yy; 1953 1954 window_copy_update_selection(wp, 1); 1955 window_copy_redraw_screen(wp); 1956 } 1957 1958 static void 1959 window_copy_cursor_left(struct window_pane *wp) 1960 { 1961 struct window_copy_mode_data *data = wp->modedata; 1962 u_int py, cx; 1963 struct grid_cell gc; 1964 1965 py = screen_hsize(data->backing) + data->cy - data->oy; 1966 cx = data->cx; 1967 while (cx > 0) { 1968 grid_get_cell(data->backing->grid, cx, py, &gc); 1969 if (~gc.flags & GRID_FLAG_PADDING) 1970 break; 1971 cx--; 1972 } 1973 if (cx == 0 && py > 0) { 1974 window_copy_cursor_up(wp, 0); 1975 window_copy_cursor_end_of_line(wp); 1976 } else if (cx > 0) { 1977 window_copy_update_cursor(wp, cx - 1, data->cy); 1978 if (window_copy_update_selection(wp, 1)) 1979 window_copy_redraw_lines(wp, data->cy, 1); 1980 } 1981 } 1982 1983 static void 1984 window_copy_cursor_right(struct window_pane *wp) 1985 { 1986 struct window_copy_mode_data *data = wp->modedata; 1987 u_int px, py, yy, cx, cy; 1988 struct grid_cell gc; 1989 1990 py = screen_hsize(data->backing) + data->cy - data->oy; 1991 yy = screen_hsize(data->backing) + screen_size_y(data->backing) - 1; 1992 if (data->screen.sel.flag && data->rectflag) 1993 px = screen_size_x(&data->screen); 1994 else 1995 px = window_copy_find_length(wp, py); 1996 1997 if (data->cx >= px && py < yy) { 1998 window_copy_cursor_start_of_line(wp); 1999 window_copy_cursor_down(wp, 0); 2000 } else if (data->cx < px) { 2001 cx = data->cx + 1; 2002 cy = screen_hsize(data->backing) + data->cy - data->oy; 2003 while (cx < px) { 2004 grid_get_cell(data->backing->grid, cx, cy, &gc); 2005 if (~gc.flags & GRID_FLAG_PADDING) 2006 break; 2007 cx++; 2008 } 2009 window_copy_update_cursor(wp, cx, data->cy); 2010 if (window_copy_update_selection(wp, 1)) 2011 window_copy_redraw_lines(wp, data->cy, 1); 2012 } 2013 } 2014 2015 static void 2016 window_copy_cursor_up(struct window_pane *wp, int scroll_only) 2017 { 2018 struct window_copy_mode_data *data = wp->modedata; 2019 struct screen *s = &data->screen; 2020 u_int ox, oy, px, py; 2021 2022 oy = screen_hsize(data->backing) + data->cy - data->oy; 2023 ox = window_copy_find_length(wp, oy); 2024 if (data->cx != ox) { 2025 data->lastcx = data->cx; 2026 data->lastsx = ox; 2027 } 2028 2029 if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely) 2030 window_copy_other_end(wp); 2031 2032 data->cx = data->lastcx; 2033 if (scroll_only || data->cy == 0) { 2034 window_copy_scroll_down(wp, 1); 2035 if (scroll_only) { 2036 if (data->cy == screen_size_y(s) - 1) 2037 window_copy_redraw_lines(wp, data->cy, 1); 2038 else 2039 window_copy_redraw_lines(wp, data->cy, 2); 2040 } 2041 } else { 2042 window_copy_update_cursor(wp, data->cx, data->cy - 1); 2043 if (window_copy_update_selection(wp, 1)) { 2044 if (data->cy == screen_size_y(s) - 1) 2045 window_copy_redraw_lines(wp, data->cy, 1); 2046 else 2047 window_copy_redraw_lines(wp, data->cy, 2); 2048 } 2049 } 2050 2051 if (!data->screen.sel.flag || !data->rectflag) { 2052 py = screen_hsize(data->backing) + data->cy - data->oy; 2053 px = window_copy_find_length(wp, py); 2054 if ((data->cx >= data->lastsx && data->cx != px) || 2055 data->cx > px) 2056 window_copy_cursor_end_of_line(wp); 2057 } 2058 2059 if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT) 2060 window_copy_cursor_end_of_line(wp); 2061 else if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT) 2062 window_copy_cursor_start_of_line(wp); 2063 } 2064 2065 static void 2066 window_copy_cursor_down(struct window_pane *wp, int scroll_only) 2067 { 2068 struct window_copy_mode_data *data = wp->modedata; 2069 struct screen *s = &data->screen; 2070 u_int ox, oy, px, py; 2071 2072 oy = screen_hsize(data->backing) + data->cy - data->oy; 2073 ox = window_copy_find_length(wp, oy); 2074 if (data->cx != ox) { 2075 data->lastcx = data->cx; 2076 data->lastsx = ox; 2077 } 2078 2079 if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely) 2080 window_copy_other_end(wp); 2081 2082 data->cx = data->lastcx; 2083 if (scroll_only || data->cy == screen_size_y(s) - 1) { 2084 window_copy_scroll_up(wp, 1); 2085 if (scroll_only && data->cy > 0) 2086 window_copy_redraw_lines(wp, data->cy - 1, 2); 2087 } else { 2088 window_copy_update_cursor(wp, data->cx, data->cy + 1); 2089 if (window_copy_update_selection(wp, 1)) 2090 window_copy_redraw_lines(wp, data->cy - 1, 2); 2091 } 2092 2093 if (!data->screen.sel.flag || !data->rectflag) { 2094 py = screen_hsize(data->backing) + data->cy - data->oy; 2095 px = window_copy_find_length(wp, py); 2096 if ((data->cx >= data->lastsx && data->cx != px) || 2097 data->cx > px) 2098 window_copy_cursor_end_of_line(wp); 2099 } 2100 2101 if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT) 2102 window_copy_cursor_end_of_line(wp); 2103 else if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT) 2104 window_copy_cursor_start_of_line(wp); 2105 } 2106 2107 static void 2108 window_copy_cursor_jump(struct window_pane *wp) 2109 { 2110 struct window_copy_mode_data *data = wp->modedata; 2111 struct screen *back_s = data->backing; 2112 struct grid_cell gc; 2113 u_int px, py, xx; 2114 2115 px = data->cx + 1; 2116 py = screen_hsize(back_s) + data->cy - data->oy; 2117 xx = window_copy_find_length(wp, py); 2118 2119 while (px < xx) { 2120 grid_get_cell(back_s->grid, px, py, &gc); 2121 if (!(gc.flags & GRID_FLAG_PADDING) && 2122 gc.data.size == 1 && *gc.data.data == data->jumpchar) { 2123 window_copy_update_cursor(wp, px, data->cy); 2124 if (window_copy_update_selection(wp, 1)) 2125 window_copy_redraw_lines(wp, data->cy, 1); 2126 return; 2127 } 2128 px++; 2129 } 2130 } 2131 2132 static void 2133 window_copy_cursor_jump_back(struct window_pane *wp) 2134 { 2135 struct window_copy_mode_data *data = wp->modedata; 2136 struct screen *back_s = data->backing; 2137 struct grid_cell gc; 2138 u_int px, py; 2139 2140 px = data->cx; 2141 py = screen_hsize(back_s) + data->cy - data->oy; 2142 2143 if (px > 0) 2144 px--; 2145 2146 for (;;) { 2147 grid_get_cell(back_s->grid, px, py, &gc); 2148 if (!(gc.flags & GRID_FLAG_PADDING) && 2149 gc.data.size == 1 && *gc.data.data == data->jumpchar) { 2150 window_copy_update_cursor(wp, px, data->cy); 2151 if (window_copy_update_selection(wp, 1)) 2152 window_copy_redraw_lines(wp, data->cy, 1); 2153 return; 2154 } 2155 if (px == 0) 2156 break; 2157 px--; 2158 } 2159 } 2160 2161 static void 2162 window_copy_cursor_jump_to(struct window_pane *wp, int jump_again) 2163 { 2164 struct window_copy_mode_data *data = wp->modedata; 2165 struct screen *back_s = data->backing; 2166 struct grid_cell gc; 2167 u_int px, py, xx; 2168 2169 px = data->cx + 1 + jump_again; 2170 py = screen_hsize(back_s) + data->cy - data->oy; 2171 xx = window_copy_find_length(wp, py); 2172 2173 while (px < xx) { 2174 grid_get_cell(back_s->grid, px, py, &gc); 2175 if (!(gc.flags & GRID_FLAG_PADDING) && 2176 gc.data.size == 1 && *gc.data.data == data->jumpchar) { 2177 window_copy_update_cursor(wp, px - 1, data->cy); 2178 if (window_copy_update_selection(wp, 1)) 2179 window_copy_redraw_lines(wp, data->cy, 1); 2180 return; 2181 } 2182 px++; 2183 } 2184 } 2185 2186 static void 2187 window_copy_cursor_jump_to_back(struct window_pane *wp, int jump_again) 2188 { 2189 struct window_copy_mode_data *data = wp->modedata; 2190 struct screen *back_s = data->backing; 2191 struct grid_cell gc; 2192 u_int px, py; 2193 2194 px = data->cx; 2195 py = screen_hsize(back_s) + data->cy - data->oy; 2196 2197 if (px > 0) 2198 px--; 2199 2200 if (jump_again && px > 0) 2201 px--; 2202 2203 for (;;) { 2204 grid_get_cell(back_s->grid, px, py, &gc); 2205 if (!(gc.flags & GRID_FLAG_PADDING) && 2206 gc.data.size == 1 && *gc.data.data == data->jumpchar) { 2207 window_copy_update_cursor(wp, px + 1, data->cy); 2208 if (window_copy_update_selection(wp, 1)) 2209 window_copy_redraw_lines(wp, data->cy, 1); 2210 return; 2211 } 2212 if (px == 0) 2213 break; 2214 px--; 2215 } 2216 } 2217 2218 static void 2219 window_copy_cursor_next_word(struct window_pane *wp, const char *separators) 2220 { 2221 struct window_copy_mode_data *data = wp->modedata; 2222 struct screen *back_s = data->backing; 2223 u_int px, py, xx, yy; 2224 int expected = 0; 2225 2226 px = data->cx; 2227 py = screen_hsize(back_s) + data->cy - data->oy; 2228 xx = window_copy_find_length(wp, py); 2229 yy = screen_hsize(back_s) + screen_size_y(back_s) - 1; 2230 2231 /* 2232 * First skip past any nonword characters and then any word characters. 2233 * 2234 * expected is initially set to 0 for the former and then 1 for the 2235 * latter. 2236 */ 2237 do { 2238 while (px > xx || 2239 window_copy_in_set(wp, px, py, separators) == expected) { 2240 /* Move down if we're past the end of the line. */ 2241 if (px > xx) { 2242 if (py == yy) 2243 return; 2244 window_copy_cursor_down(wp, 0); 2245 px = 0; 2246 2247 py = screen_hsize(back_s) + data->cy - data->oy; 2248 xx = window_copy_find_length(wp, py); 2249 } else 2250 px++; 2251 } 2252 expected = !expected; 2253 } while (expected == 1); 2254 2255 window_copy_update_cursor(wp, px, data->cy); 2256 if (window_copy_update_selection(wp, 1)) 2257 window_copy_redraw_lines(wp, data->cy, 1); 2258 } 2259 2260 static void 2261 window_copy_cursor_next_word_end(struct window_pane *wp, 2262 const char *separators) 2263 { 2264 struct window_copy_mode_data *data = wp->modedata; 2265 struct options *oo = wp->window->options; 2266 struct screen *back_s = data->backing; 2267 u_int px, py, xx, yy; 2268 int keys, expected = 1; 2269 2270 px = data->cx; 2271 py = screen_hsize(back_s) + data->cy - data->oy; 2272 xx = window_copy_find_length(wp, py); 2273 yy = screen_hsize(back_s) + screen_size_y(back_s) - 1; 2274 2275 keys = options_get_number(oo, "mode-keys"); 2276 if (keys == MODEKEY_VI && !window_copy_in_set(wp, px, py, separators)) 2277 px++; 2278 2279 /* 2280 * First skip past any word characters, then any nonword characters. 2281 * 2282 * expected is initially set to 1 for the former and then 0 for the 2283 * latter. 2284 */ 2285 do { 2286 while (px > xx || 2287 window_copy_in_set(wp, px, py, separators) == expected) { 2288 /* Move down if we're past the end of the line. */ 2289 if (px > xx) { 2290 if (py == yy) 2291 return; 2292 window_copy_cursor_down(wp, 0); 2293 px = 0; 2294 2295 py = screen_hsize(back_s) + data->cy - data->oy; 2296 xx = window_copy_find_length(wp, py); 2297 } else 2298 px++; 2299 } 2300 expected = !expected; 2301 } while (expected == 0); 2302 2303 if (keys == MODEKEY_VI && px != 0) 2304 px--; 2305 2306 window_copy_update_cursor(wp, px, data->cy); 2307 if (window_copy_update_selection(wp, 1)) 2308 window_copy_redraw_lines(wp, data->cy, 1); 2309 } 2310 2311 /* Move to the previous place where a word begins. */ 2312 static void 2313 window_copy_cursor_previous_word(struct window_pane *wp, 2314 const char *separators) 2315 { 2316 struct window_copy_mode_data *data = wp->modedata; 2317 u_int px, py; 2318 2319 px = data->cx; 2320 py = screen_hsize(data->backing) + data->cy - data->oy; 2321 2322 /* Move back to the previous word character. */ 2323 for (;;) { 2324 if (px > 0) { 2325 px--; 2326 if (!window_copy_in_set(wp, px, py, separators)) 2327 break; 2328 } else { 2329 if (data->cy == 0 && 2330 (screen_hsize(data->backing) == 0 || 2331 data->oy >= screen_hsize(data->backing) - 1)) 2332 goto out; 2333 window_copy_cursor_up(wp, 0); 2334 2335 py = screen_hsize(data->backing) + data->cy - data->oy; 2336 px = window_copy_find_length(wp, py); 2337 } 2338 } 2339 2340 /* Move back to the beginning of this word. */ 2341 while (px > 0 && !window_copy_in_set(wp, px - 1, py, separators)) 2342 px--; 2343 2344 out: 2345 window_copy_update_cursor(wp, px, data->cy); 2346 if (window_copy_update_selection(wp, 1)) 2347 window_copy_redraw_lines(wp, data->cy, 1); 2348 } 2349 2350 static void 2351 window_copy_scroll_up(struct window_pane *wp, u_int ny) 2352 { 2353 struct window_copy_mode_data *data = wp->modedata; 2354 struct screen *s = &data->screen; 2355 struct screen_write_ctx ctx; 2356 2357 if (data->oy < ny) 2358 ny = data->oy; 2359 if (ny == 0) 2360 return; 2361 data->oy -= ny; 2362 2363 window_copy_update_selection(wp, 0); 2364 2365 screen_write_start(&ctx, wp, NULL); 2366 screen_write_cursormove(&ctx, 0, 0); 2367 screen_write_deleteline(&ctx, ny, 8); 2368 window_copy_write_lines(wp, &ctx, screen_size_y(s) - ny, ny); 2369 window_copy_write_line(wp, &ctx, 0); 2370 if (screen_size_y(s) > 1) 2371 window_copy_write_line(wp, &ctx, 1); 2372 if (screen_size_y(s) > 3) 2373 window_copy_write_line(wp, &ctx, screen_size_y(s) - 2); 2374 if (s->sel.flag && screen_size_y(s) > ny) 2375 window_copy_write_line(wp, &ctx, screen_size_y(s) - ny - 1); 2376 screen_write_cursormove(&ctx, data->cx, data->cy); 2377 screen_write_stop(&ctx); 2378 } 2379 2380 static void 2381 window_copy_scroll_down(struct window_pane *wp, u_int ny) 2382 { 2383 struct window_copy_mode_data *data = wp->modedata; 2384 struct screen *s = &data->screen; 2385 struct screen_write_ctx ctx; 2386 2387 if (ny > screen_hsize(data->backing)) 2388 return; 2389 2390 if (data->oy > screen_hsize(data->backing) - ny) 2391 ny = screen_hsize(data->backing) - data->oy; 2392 if (ny == 0) 2393 return; 2394 data->oy += ny; 2395 2396 window_copy_update_selection(wp, 0); 2397 2398 screen_write_start(&ctx, wp, NULL); 2399 screen_write_cursormove(&ctx, 0, 0); 2400 screen_write_insertline(&ctx, ny, 8); 2401 window_copy_write_lines(wp, &ctx, 0, ny); 2402 if (s->sel.flag && screen_size_y(s) > ny) 2403 window_copy_write_line(wp, &ctx, ny); 2404 else if (ny == 1) /* nuke position */ 2405 window_copy_write_line(wp, &ctx, 1); 2406 screen_write_cursormove(&ctx, data->cx, data->cy); 2407 screen_write_stop(&ctx); 2408 } 2409 2410 void 2411 window_copy_add_formats(struct window_pane *wp, struct format_tree *ft) 2412 { 2413 struct window_copy_mode_data *data = wp->modedata; 2414 struct screen *s = &data->screen; 2415 2416 if (wp->mode != &window_copy_mode) 2417 return; 2418 2419 format_add(ft, "selection_present", "%d", s->sel.flag); 2420 format_add(ft, "scroll_position", "%d", data->oy); 2421 } 2422 2423 static void 2424 window_copy_rectangle_toggle(struct window_pane *wp) 2425 { 2426 struct window_copy_mode_data *data = wp->modedata; 2427 u_int px, py; 2428 2429 data->rectflag = !data->rectflag; 2430 2431 py = screen_hsize(data->backing) + data->cy - data->oy; 2432 px = window_copy_find_length(wp, py); 2433 if (data->cx > px) 2434 window_copy_update_cursor(wp, px, data->cy); 2435 2436 window_copy_update_selection(wp, 1); 2437 window_copy_redraw_screen(wp); 2438 } 2439 2440 static void 2441 window_copy_move_mouse(struct mouse_event *m) 2442 { 2443 struct window_pane *wp; 2444 u_int x, y; 2445 2446 wp = cmd_mouse_pane(m, NULL, NULL); 2447 if (wp == NULL || wp->mode != &window_copy_mode) 2448 return; 2449 2450 if (cmd_mouse_at(wp, m, &x, &y, 1) != 0) 2451 return; 2452 2453 window_copy_update_cursor(wp, x, y); 2454 } 2455 2456 void 2457 window_copy_start_drag(struct client *c, struct mouse_event *m) 2458 { 2459 struct window_pane *wp; 2460 u_int x, y; 2461 2462 if (c == NULL) 2463 return; 2464 2465 wp = cmd_mouse_pane(m, NULL, NULL); 2466 if (wp == NULL || wp->mode != &window_copy_mode) 2467 return; 2468 2469 if (cmd_mouse_at(wp, m, &x, &y, 1) != 0) 2470 return; 2471 2472 c->tty.mouse_drag_update = window_copy_drag_update; 2473 c->tty.mouse_drag_release = NULL; /* will fire MouseDragEnd key */ 2474 2475 window_copy_update_cursor(wp, x, y); 2476 window_copy_start_selection(wp); 2477 window_copy_redraw_screen(wp); 2478 } 2479 2480 static void 2481 window_copy_drag_update(__unused struct client *c, struct mouse_event *m) 2482 { 2483 struct window_pane *wp; 2484 struct window_copy_mode_data *data; 2485 u_int x, y, old_cy; 2486 2487 wp = cmd_mouse_pane(m, NULL, NULL); 2488 if (wp == NULL || wp->mode != &window_copy_mode) 2489 return; 2490 data = wp->modedata; 2491 2492 if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) 2493 return; 2494 old_cy = data->cy; 2495 2496 window_copy_update_cursor(wp, x, y); 2497 if (window_copy_update_selection(wp, 1)) 2498 window_copy_redraw_selection(wp, old_cy); 2499 } 2500