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