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