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