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