1 /* $OpenBSD: window-copy.c,v 1.33 2009/11/18 17:03:16 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> 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 <stdlib.h> 22 #include <string.h> 23 24 #include "tmux.h" 25 26 struct screen *window_copy_init(struct window_pane *); 27 void window_copy_free(struct window_pane *); 28 void window_copy_resize(struct window_pane *, u_int, u_int); 29 void window_copy_key(struct window_pane *, struct client *, int); 30 int window_copy_key_input(struct window_pane *, int); 31 void window_copy_mouse( 32 struct window_pane *, struct client *, struct mouse_event *); 33 34 void window_copy_redraw_lines(struct window_pane *, u_int, u_int); 35 void window_copy_redraw_screen(struct window_pane *); 36 void window_copy_write_line( 37 struct window_pane *, struct screen_write_ctx *, u_int); 38 void window_copy_write_lines( 39 struct window_pane *, struct screen_write_ctx *, u_int, u_int); 40 41 void window_copy_scroll_to(struct window_pane *, u_int, u_int); 42 int window_copy_search_compare( 43 struct grid *, u_int, u_int, struct grid *, u_int); 44 int window_copy_search_lr( 45 struct grid *, struct grid *, u_int *, u_int, u_int, u_int); 46 int window_copy_search_rl( 47 struct grid *, struct grid *, u_int *, u_int, u_int, u_int); 48 void window_copy_search_up(struct window_pane *, const char *); 49 void window_copy_search_down(struct window_pane *, const char *); 50 void window_copy_goto_line(struct window_pane *, const char *); 51 void window_copy_update_cursor(struct window_pane *, u_int, u_int); 52 void window_copy_start_selection(struct window_pane *); 53 int window_copy_update_selection(struct window_pane *); 54 void window_copy_copy_selection(struct window_pane *, struct client *); 55 void window_copy_copy_line( 56 struct window_pane *, char **, size_t *, u_int, u_int, u_int); 57 int window_copy_is_space(struct window_pane *, u_int, u_int); 58 u_int window_copy_find_length(struct window_pane *, u_int); 59 void window_copy_cursor_start_of_line(struct window_pane *); 60 void window_copy_cursor_back_to_indentation(struct window_pane *); 61 void window_copy_cursor_end_of_line(struct window_pane *); 62 void window_copy_cursor_left(struct window_pane *); 63 void window_copy_cursor_right(struct window_pane *); 64 void window_copy_cursor_up(struct window_pane *, int); 65 void window_copy_cursor_down(struct window_pane *, int); 66 void window_copy_cursor_next_word(struct window_pane *); 67 void window_copy_cursor_previous_word(struct window_pane *); 68 void window_copy_scroll_up(struct window_pane *, u_int); 69 void window_copy_scroll_down(struct window_pane *, u_int); 70 71 const struct window_mode window_copy_mode = { 72 window_copy_init, 73 window_copy_free, 74 window_copy_resize, 75 window_copy_key, 76 window_copy_mouse, 77 NULL, 78 }; 79 80 enum window_copy_input_type { 81 WINDOW_COPY_OFF, 82 WINDOW_COPY_SEARCHUP, 83 WINDOW_COPY_SEARCHDOWN, 84 WINDOW_COPY_GOTOLINE, 85 }; 86 87 struct window_copy_mode_data { 88 struct screen screen; 89 90 struct mode_key_data mdata; 91 92 u_int oy; 93 94 u_int selx; 95 u_int sely; 96 97 u_int cx; 98 u_int cy; 99 100 u_int lastcx; /* position in last line with content */ 101 u_int lastsx; /* size of last line with content */ 102 103 enum window_copy_input_type inputtype; 104 const char *inputprompt; 105 char *inputstr; 106 107 enum window_copy_input_type searchtype; 108 char *searchstr; 109 }; 110 111 struct screen * 112 window_copy_init(struct window_pane *wp) 113 { 114 struct window_copy_mode_data *data; 115 struct screen *s; 116 struct screen_write_ctx ctx; 117 u_int i; 118 int keys; 119 120 wp->modedata = data = xmalloc(sizeof *data); 121 data->oy = 0; 122 data->cx = wp->base.cx; 123 data->cy = wp->base.cy; 124 125 data->lastcx = 0; 126 data->lastsx = 0; 127 128 data->inputtype = WINDOW_COPY_OFF; 129 data->inputprompt = NULL; 130 data->inputstr = xstrdup(""); 131 132 data->searchtype = WINDOW_COPY_OFF; 133 data->searchstr = NULL; 134 135 s = &data->screen; 136 screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); 137 if (options_get_number(&wp->window->options, "mode-mouse")) 138 s->mode |= MODE_MOUSE; 139 140 keys = options_get_number(&wp->window->options, "mode-keys"); 141 if (keys == MODEKEY_EMACS) 142 mode_key_init(&data->mdata, &mode_key_tree_emacs_copy); 143 else 144 mode_key_init(&data->mdata, &mode_key_tree_vi_copy); 145 146 s->cx = data->cx; 147 s->cy = data->cy; 148 149 screen_write_start(&ctx, NULL, s); 150 for (i = 0; i < screen_size_y(s); i++) 151 window_copy_write_line(wp, &ctx, i); 152 screen_write_cursormove(&ctx, data->cx, data->cy); 153 screen_write_stop(&ctx); 154 155 return (s); 156 } 157 158 void 159 window_copy_free(struct window_pane *wp) 160 { 161 struct window_copy_mode_data *data = wp->modedata; 162 163 if (data->searchstr != NULL) 164 xfree(data->searchstr); 165 xfree(data->inputstr); 166 167 screen_free(&data->screen); 168 169 xfree(data); 170 } 171 172 void 173 window_copy_pageup(struct window_pane *wp) 174 { 175 struct window_copy_mode_data *data = wp->modedata; 176 struct screen *s = &data->screen; 177 u_int n; 178 179 n = 1; 180 if (screen_size_y(s) > 2) 181 n = screen_size_y(s) - 2; 182 if (data->oy + n > screen_hsize(&wp->base)) 183 data->oy = screen_hsize(&wp->base); 184 else 185 data->oy += n; 186 window_copy_update_selection(wp); 187 window_copy_redraw_screen(wp); 188 } 189 190 void 191 window_copy_resize(struct window_pane *wp, u_int sx, u_int sy) 192 { 193 struct window_copy_mode_data *data = wp->modedata; 194 struct screen *s = &data->screen; 195 struct screen_write_ctx ctx; 196 197 screen_resize(s, sx, sy); 198 199 if (data->cy > sy - 1) 200 data->cy = sy - 1; 201 if (data->cx > sx) 202 data->cx = sx; 203 204 screen_clear_selection(&data->screen); 205 206 screen_write_start(&ctx, NULL, s); 207 window_copy_write_lines(wp, &ctx, 0, screen_size_y(s) - 1); 208 screen_write_stop(&ctx); 209 210 window_copy_redraw_screen(wp); 211 } 212 213 void 214 window_copy_key(struct window_pane *wp, struct client *c, int key) 215 { 216 struct window_copy_mode_data *data = wp->modedata; 217 struct screen *s = &data->screen; 218 u_int n; 219 int keys; 220 221 if (data->inputtype != WINDOW_COPY_OFF) { 222 if (window_copy_key_input(wp, key) != 0) 223 goto input_off; 224 return; 225 } 226 227 switch (mode_key_lookup(&data->mdata, key)) { 228 case MODEKEYCOPY_CANCEL: 229 window_pane_reset_mode(wp); 230 break; 231 case MODEKEYCOPY_LEFT: 232 window_copy_cursor_left(wp); 233 return; 234 case MODEKEYCOPY_RIGHT: 235 window_copy_cursor_right(wp); 236 return; 237 case MODEKEYCOPY_UP: 238 window_copy_cursor_up(wp, 0); 239 return; 240 case MODEKEYCOPY_DOWN: 241 window_copy_cursor_down(wp, 0); 242 return; 243 case MODEKEYCOPY_SCROLLUP: 244 window_copy_cursor_up(wp, 1); 245 break; 246 case MODEKEYCOPY_SCROLLDOWN: 247 window_copy_cursor_down(wp, 1); 248 break; 249 case MODEKEYCOPY_PREVIOUSPAGE: 250 window_copy_pageup(wp); 251 break; 252 case MODEKEYCOPY_NEXTPAGE: 253 n = 1; 254 if (screen_size_y(s) > 2) 255 n = screen_size_y(s) - 2; 256 if (data->oy < n) 257 data->oy = 0; 258 else 259 data->oy -= n; 260 window_copy_update_selection(wp); 261 window_copy_redraw_screen(wp); 262 break; 263 case MODEKEYCOPY_HALFPAGEUP: 264 n = screen_size_y(s) / 2; 265 if (data->oy + n > screen_hsize(&wp->base)) 266 data->oy = screen_hsize(&wp->base); 267 else 268 data->oy += n; 269 window_copy_update_selection(wp); 270 window_copy_redraw_screen(wp); 271 break; 272 case MODEKEYCOPY_HALFPAGEDOWN: 273 n = screen_size_y(s) / 2; 274 if (data->oy < n) 275 data->oy = 0; 276 else 277 data->oy -= n; 278 window_copy_update_selection(wp); 279 window_copy_redraw_screen(wp); 280 break; 281 case MODEKEYCOPY_TOPLINE: 282 data->cx = 0; 283 data->cy = 0; 284 window_copy_update_selection(wp); 285 window_copy_redraw_screen(wp); 286 break; 287 case MODEKEYCOPY_MIDDLELINE: 288 data->cx = 0; 289 data->cy = (screen_size_y(s) - 1) / 2; 290 window_copy_update_selection(wp); 291 window_copy_redraw_screen(wp); 292 break; 293 case MODEKEYCOPY_BOTTOMLINE: 294 data->cx = 0; 295 data->cy = screen_size_y(s) - 1; 296 window_copy_update_selection(wp); 297 window_copy_redraw_screen(wp); 298 break; 299 case MODEKEYCOPY_STARTSELECTION: 300 window_copy_start_selection(wp); 301 window_copy_redraw_screen(wp); 302 break; 303 case MODEKEYCOPY_CLEARSELECTION: 304 screen_clear_selection(&data->screen); 305 window_copy_redraw_screen(wp); 306 break; 307 case MODEKEYCOPY_COPYSELECTION: 308 if (c != NULL && c->session != NULL) { 309 window_copy_copy_selection(wp, c); 310 window_pane_reset_mode(wp); 311 } 312 break; 313 case MODEKEYCOPY_STARTOFLINE: 314 window_copy_cursor_start_of_line(wp); 315 break; 316 case MODEKEYCOPY_BACKTOINDENTATION: 317 window_copy_cursor_back_to_indentation(wp); 318 break; 319 case MODEKEYCOPY_ENDOFLINE: 320 window_copy_cursor_end_of_line(wp); 321 break; 322 case MODEKEYCOPY_NEXTWORD: 323 window_copy_cursor_next_word(wp); 324 break; 325 case MODEKEYCOPY_PREVIOUSWORD: 326 window_copy_cursor_previous_word(wp); 327 break; 328 case MODEKEYCOPY_SEARCHUP: 329 data->inputtype = WINDOW_COPY_SEARCHUP; 330 data->inputprompt = "Search Up"; 331 goto input_on; 332 case MODEKEYCOPY_SEARCHDOWN: 333 data->inputtype = WINDOW_COPY_SEARCHDOWN; 334 data->inputprompt = "Search Down"; 335 goto input_on; 336 case MODEKEYCOPY_SEARCHAGAIN: 337 switch (data->searchtype) { 338 case WINDOW_COPY_OFF: 339 case WINDOW_COPY_GOTOLINE: 340 break; 341 case WINDOW_COPY_SEARCHUP: 342 window_copy_search_up(wp, data->searchstr); 343 break; 344 case WINDOW_COPY_SEARCHDOWN: 345 window_copy_search_down(wp, data->searchstr); 346 break; 347 } 348 break; 349 case MODEKEYCOPY_GOTOLINE: 350 data->inputtype = WINDOW_COPY_GOTOLINE; 351 data->inputprompt = "Goto Line"; 352 *data->inputstr = '\0'; 353 goto input_on; 354 default: 355 break; 356 } 357 358 return; 359 360 input_on: 361 keys = options_get_number(&wp->window->options, "mode-keys"); 362 if (keys == MODEKEY_EMACS) 363 mode_key_init(&data->mdata, &mode_key_tree_emacs_edit); 364 else 365 mode_key_init(&data->mdata, &mode_key_tree_vi_edit); 366 367 window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); 368 return; 369 370 input_off: 371 keys = options_get_number(&wp->window->options, "mode-keys"); 372 if (keys == MODEKEY_EMACS) 373 mode_key_init(&data->mdata, &mode_key_tree_emacs_copy); 374 else 375 mode_key_init(&data->mdata, &mode_key_tree_vi_copy); 376 377 data->inputtype = WINDOW_COPY_OFF; 378 data->inputprompt = NULL; 379 380 window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); 381 } 382 383 int 384 window_copy_key_input(struct window_pane *wp, int key) 385 { 386 struct window_copy_mode_data *data = wp->modedata; 387 struct screen *s = &data->screen; 388 size_t inputlen; 389 390 switch (mode_key_lookup(&data->mdata, key)) { 391 case MODEKEYEDIT_CANCEL: 392 return (-1); 393 case MODEKEYEDIT_BACKSPACE: 394 inputlen = strlen(data->inputstr); 395 if (inputlen > 0) 396 data->inputstr[inputlen - 1] = '\0'; 397 break; 398 case MODEKEYEDIT_DELETELINE: 399 *data->inputstr = '\0'; 400 break; 401 case MODEKEYEDIT_ENTER: 402 switch (data->inputtype) { 403 case WINDOW_COPY_OFF: 404 break; 405 case WINDOW_COPY_SEARCHUP: 406 window_copy_search_up(wp, data->inputstr); 407 data->searchtype = data->inputtype; 408 data->searchstr = xstrdup(data->inputstr); 409 break; 410 case WINDOW_COPY_SEARCHDOWN: 411 window_copy_search_down(wp, data->inputstr); 412 data->searchtype = data->inputtype; 413 data->searchstr = xstrdup(data->inputstr); 414 break; 415 case WINDOW_COPY_GOTOLINE: 416 window_copy_goto_line(wp, data->inputstr); 417 *data->inputstr = '\0'; 418 break; 419 } 420 return (1); 421 case MODEKEY_OTHER: 422 if (key < 32 || key > 126) 423 break; 424 inputlen = strlen(data->inputstr) + 2; 425 426 data->inputstr = xrealloc(data->inputstr, 1, inputlen); 427 data->inputstr[inputlen - 2] = key; 428 data->inputstr[inputlen - 1] = '\0'; 429 break; 430 default: 431 break; 432 } 433 434 window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); 435 return (0); 436 } 437 438 void 439 window_copy_mouse( 440 struct window_pane *wp, unused struct client *c, struct mouse_event *m) 441 { 442 struct window_copy_mode_data *data = wp->modedata; 443 struct screen *s = &data->screen; 444 445 if ((m->b & 3) == 3) 446 return; 447 if (m->x >= screen_size_x(s)) 448 return; 449 if (m->y >= screen_size_y(s)) 450 return; 451 452 window_copy_update_cursor(wp, m->x, m->y); 453 if (window_copy_update_selection(wp)) 454 window_copy_redraw_screen(wp); 455 } 456 457 void 458 window_copy_scroll_to(struct window_pane *wp, u_int px, u_int py) 459 { 460 struct window_copy_mode_data *data = wp->modedata; 461 struct screen *s = &wp->base; 462 struct grid *gd = s->grid; 463 u_int offset, gap; 464 465 data->cx = px; 466 467 gap = gd->sy / 4; 468 if (py < gd->sy) { 469 offset = 0; 470 data->cy = py; 471 } else if (py > gd->hsize + gd->sy - gap) { 472 offset = gd->hsize; 473 data->cy = py - gd->hsize; 474 } else { 475 offset = py + gap - gd->sy; 476 data->cy = py - offset; 477 } 478 data->oy = gd->hsize - offset; 479 480 window_copy_redraw_screen(wp); 481 } 482 483 int 484 window_copy_search_compare( 485 struct grid *gd, u_int px, u_int py, struct grid *sgd, u_int spx) 486 { 487 const struct grid_cell *gc, *sgc; 488 const struct grid_utf8 *gu, *sgu; 489 490 gc = grid_peek_cell(gd, px, py); 491 sgc = grid_peek_cell(sgd, spx, 0); 492 493 if ((gc->flags & GRID_FLAG_UTF8) != (sgc->flags & GRID_FLAG_UTF8)) 494 return (0); 495 496 if (gc->flags & GRID_FLAG_UTF8) { 497 gu = grid_peek_utf8(gd, px, py); 498 sgu = grid_peek_utf8(sgd, spx, 0); 499 if (grid_utf8_compare(gu, sgu)) 500 return (1); 501 } else { 502 if (gc->data == sgc->data) 503 return (1); 504 } 505 return (0); 506 } 507 508 int 509 window_copy_search_lr(struct grid *gd, 510 struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last) 511 { 512 u_int ax, bx, px; 513 514 for (ax = first; ax < last; ax++) { 515 if (ax + sgd->sx >= gd->sx) 516 break; 517 for (bx = 0; bx < sgd->sx; bx++) { 518 px = ax + bx; 519 if (!window_copy_search_compare(gd, px, py, sgd, bx)) 520 break; 521 } 522 if (bx == sgd->sx) { 523 *ppx = ax; 524 return (1); 525 } 526 } 527 return (0); 528 } 529 530 int 531 window_copy_search_rl(struct grid *gd, 532 struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last) 533 { 534 u_int ax, bx, px; 535 536 for (ax = last + 1; ax > first; ax--) { 537 if (gd->sx - (ax - 1) < sgd->sx) 538 continue; 539 for (bx = 0; bx < sgd->sx; bx++) { 540 px = ax - 1 + bx; 541 if (!window_copy_search_compare(gd, px, py, sgd, bx)) 542 break; 543 } 544 if (bx == sgd->sx) { 545 *ppx = ax - 1; 546 return (1); 547 } 548 } 549 return (0); 550 } 551 552 void 553 window_copy_search_up(struct window_pane *wp, const char *searchstr) 554 { 555 struct window_copy_mode_data *data = wp->modedata; 556 struct screen *s = &wp->base, ss; 557 struct screen_write_ctx ctx; 558 struct grid *gd = s->grid, *sgd; 559 struct grid_cell gc; 560 size_t searchlen; 561 u_int i, last, fx, fy, px; 562 int utf8flag, n, wrapped; 563 564 if (*searchstr == '\0') 565 return; 566 utf8flag = options_get_number(&wp->window->options, "utf8"); 567 searchlen = screen_write_strlen(utf8flag, "%s", searchstr); 568 569 screen_init(&ss, searchlen, 1, 0); 570 screen_write_start(&ctx, NULL, &ss); 571 memcpy(&gc, &grid_default_cell, sizeof gc); 572 screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr); 573 screen_write_stop(&ctx); 574 575 fx = data->cx; 576 fy = gd->hsize - data->oy + data->cy; 577 578 if (fx == 0) { 579 if (fy == 0) 580 return; 581 fx = gd->sx - 1; 582 fy--; 583 } else 584 fx--; 585 n = wrapped = 0; 586 587 retry: 588 sgd = ss.grid; 589 for (i = fy + 1; i > 0; i--) { 590 last = screen_size_x(s); 591 if (i == fy + 1) 592 last = fx; 593 n = window_copy_search_rl(gd, sgd, &px, i - 1, 0, last); 594 if (n) { 595 window_copy_scroll_to(wp, px, i - 1); 596 break; 597 } 598 } 599 if (!n && !wrapped) { 600 fx = gd->sx - 1; 601 fy = gd->hsize + gd->sy - 1; 602 wrapped = 1; 603 goto retry; 604 } 605 606 screen_free(&ss); 607 } 608 609 void 610 window_copy_search_down(struct window_pane *wp, const char *searchstr) 611 { 612 struct window_copy_mode_data *data = wp->modedata; 613 struct screen *s = &wp->base, ss; 614 struct screen_write_ctx ctx; 615 struct grid *gd = s->grid, *sgd; 616 struct grid_cell gc; 617 size_t searchlen; 618 u_int i, first, fx, fy, px; 619 int utf8flag, n, wrapped; 620 621 if (*searchstr == '\0') 622 return; 623 utf8flag = options_get_number(&wp->window->options, "utf8"); 624 searchlen = screen_write_strlen(utf8flag, "%s", searchstr); 625 626 screen_init(&ss, searchlen, 1, 0); 627 screen_write_start(&ctx, NULL, &ss); 628 memcpy(&gc, &grid_default_cell, sizeof gc); 629 screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr); 630 screen_write_stop(&ctx); 631 632 fx = data->cx; 633 fy = gd->hsize - data->oy + data->cy; 634 635 if (fx == gd->sx - 1) { 636 if (fy == gd->hsize + gd->sy) 637 return; 638 fx = 0; 639 fy++; 640 } else 641 fx++; 642 n = wrapped = 0; 643 644 retry: 645 sgd = ss.grid; 646 for (i = fy + 1; i < gd->hsize + gd->sy; i++) { 647 first = 0; 648 if (i == fy + 1) 649 first = fx; 650 n = window_copy_search_lr(gd, sgd, &px, i - 1, first, gd->sx); 651 if (n) { 652 window_copy_scroll_to(wp, px, i - 1); 653 break; 654 } 655 } 656 if (!n && !wrapped) { 657 fx = 0; 658 fy = 0; 659 wrapped = 1; 660 goto retry; 661 } 662 663 screen_free(&ss); 664 } 665 666 void 667 window_copy_goto_line(struct window_pane *wp, const char *linestr) 668 { 669 struct window_copy_mode_data *data = wp->modedata; 670 const char *errstr; 671 u_int lineno; 672 673 lineno = strtonum(linestr, 0, screen_hsize(&wp->base), &errstr); 674 if (errstr != NULL) 675 return; 676 677 data->oy = lineno; 678 window_copy_redraw_screen(wp); 679 } 680 681 void 682 window_copy_write_line( 683 struct window_pane *wp, struct screen_write_ctx *ctx, u_int py) 684 { 685 struct window_copy_mode_data *data = wp->modedata; 686 struct screen *s = &data->screen; 687 struct options *oo = &wp->window->options; 688 struct grid_cell gc; 689 char hdr[32]; 690 size_t last, xoff = 0, size = 0; 691 692 memcpy(&gc, &grid_default_cell, sizeof gc); 693 colour_set_fg(&gc, options_get_number(oo, "mode-fg")); 694 colour_set_bg(&gc, options_get_number(oo, "mode-bg")); 695 gc.attr |= options_get_number(oo, "mode-attr"); 696 697 last = screen_size_y(s) - 1; 698 if (py == 0) { 699 size = xsnprintf(hdr, sizeof hdr, 700 "[%u/%u]", data->oy, screen_hsize(&wp->base)); 701 screen_write_cursormove(ctx, screen_size_x(s) - size, 0); 702 screen_write_puts(ctx, &gc, "%s", hdr); 703 } else if (py == last && data->inputtype != WINDOW_COPY_OFF) { 704 xoff = size = xsnprintf(hdr, sizeof hdr, 705 "%s: %s", data->inputprompt, data->inputstr); 706 screen_write_cursormove(ctx, 0, last); 707 screen_write_puts(ctx, &gc, "%s", hdr); 708 } else 709 size = 0; 710 711 screen_write_cursormove(ctx, xoff, py); 712 screen_write_copy(ctx, &wp->base, xoff, (screen_hsize(&wp->base) - 713 data->oy) + py, screen_size_x(s) - size, 1); 714 715 if (py == data->cy && data->cx == screen_size_x(s)) { 716 memcpy(&gc, &grid_default_cell, sizeof gc); 717 screen_write_cursormove(ctx, screen_size_x(s) - 1, py); 718 screen_write_putc(ctx, &gc, '$'); 719 } 720 } 721 722 void 723 window_copy_write_lines( 724 struct window_pane *wp, struct screen_write_ctx *ctx, u_int py, u_int ny) 725 { 726 u_int yy; 727 728 for (yy = py; yy < py + ny; yy++) 729 window_copy_write_line(wp, ctx, py); 730 } 731 732 void 733 window_copy_redraw_lines(struct window_pane *wp, u_int py, u_int ny) 734 { 735 struct window_copy_mode_data *data = wp->modedata; 736 struct screen_write_ctx ctx; 737 u_int i; 738 739 screen_write_start(&ctx, wp, NULL); 740 for (i = py; i < py + ny; i++) 741 window_copy_write_line(wp, &ctx, i); 742 screen_write_cursormove(&ctx, data->cx, data->cy); 743 screen_write_stop(&ctx); 744 } 745 746 void 747 window_copy_redraw_screen(struct window_pane *wp) 748 { 749 struct window_copy_mode_data *data = wp->modedata; 750 751 window_copy_redraw_lines(wp, 0, screen_size_y(&data->screen)); 752 } 753 754 void 755 window_copy_update_cursor(struct window_pane *wp, u_int cx, u_int cy) 756 { 757 struct window_copy_mode_data *data = wp->modedata; 758 struct screen *s = &data->screen; 759 struct screen_write_ctx ctx; 760 u_int old_cx, old_cy; 761 762 old_cx = data->cx; old_cy = data->cy; 763 data->cx = cx; data->cy = cy; 764 if (old_cx == screen_size_x(s)) 765 window_copy_redraw_lines(wp, old_cy, 1); 766 if (data->cx == screen_size_x(s)) 767 window_copy_redraw_lines(wp, data->cy, 1); 768 else { 769 screen_write_start(&ctx, wp, NULL); 770 screen_write_cursormove(&ctx, data->cx, data->cy); 771 screen_write_stop(&ctx); 772 } 773 } 774 775 void 776 window_copy_start_selection(struct window_pane *wp) 777 { 778 struct window_copy_mode_data *data = wp->modedata; 779 struct screen *s = &data->screen; 780 781 data->selx = data->cx; 782 data->sely = screen_hsize(&wp->base) + data->cy - data->oy; 783 784 s->sel.flag = 1; 785 window_copy_update_selection(wp); 786 } 787 788 int 789 window_copy_update_selection(struct window_pane *wp) 790 { 791 struct window_copy_mode_data *data = wp->modedata; 792 struct screen *s = &data->screen; 793 struct options *oo = &wp->window->options; 794 struct grid_cell gc; 795 u_int sx, sy, ty; 796 797 if (!s->sel.flag) 798 return (0); 799 800 /* Set colours. */ 801 memcpy(&gc, &grid_default_cell, sizeof gc); 802 colour_set_fg(&gc, options_get_number(oo, "mode-fg")); 803 colour_set_bg(&gc, options_get_number(oo, "mode-bg")); 804 gc.attr |= options_get_number(oo, "mode-attr"); 805 806 /* Find top of screen. */ 807 ty = screen_hsize(&wp->base) - data->oy; 808 809 /* Adjust the selection. */ 810 sx = data->selx; 811 sy = data->sely; 812 if (sy < ty) { /* above screen */ 813 sx = 0; 814 sy = 0; 815 } else if (sy > ty + screen_size_y(s) - 1) { /* below screen */ 816 sx = screen_size_x(s) - 1; 817 sy = screen_size_y(s) - 1; 818 } else 819 sy -= ty; 820 sy = screen_hsize(s) + sy; 821 822 screen_set_selection( 823 s, sx, sy, data->cx, screen_hsize(s) + data->cy, &gc); 824 return (1); 825 } 826 827 void 828 window_copy_copy_selection(struct window_pane *wp, struct client *c) 829 { 830 struct window_copy_mode_data *data = wp->modedata; 831 struct screen *s = &data->screen; 832 char *buf; 833 size_t off; 834 u_int i, xx, yy, sx, sy, ex, ey, limit; 835 836 if (!s->sel.flag) 837 return; 838 839 buf = xmalloc(1); 840 off = 0; 841 842 *buf = '\0'; 843 844 /* 845 * The selection extends from selx,sely to (adjusted) cx,cy on 846 * the base screen. 847 */ 848 849 /* Find start and end. */ 850 xx = data->cx; 851 yy = screen_hsize(&wp->base) + data->cy - data->oy; 852 if (yy < data->sely || (yy == data->sely && xx < data->selx)) { 853 sx = xx; sy = yy; 854 ex = data->selx; ey = data->sely; 855 } else { 856 sx = data->selx; sy = data->sely; 857 ex = xx; ey = yy; 858 } 859 860 /* Trim ex to end of line. */ 861 xx = window_copy_find_length(wp, ey); 862 if (ex > xx) 863 ex = xx; 864 865 /* Copy the lines. */ 866 if (sy == ey) 867 window_copy_copy_line(wp, &buf, &off, sy, sx, ex); 868 else { 869 xx = screen_size_x(s); 870 window_copy_copy_line(wp, &buf, &off, sy, sx, xx); 871 if (ey - sy > 1) { 872 for (i = sy + 1; i < ey; i++) 873 window_copy_copy_line(wp, &buf, &off, i, 0, xx); 874 } 875 window_copy_copy_line(wp, &buf, &off, ey, 0, ex); 876 } 877 878 /* Don't bother if no data. */ 879 if (off == 0) { 880 xfree(buf); 881 return; 882 } 883 off--; /* remove final \n */ 884 885 /* Add the buffer to the stack. */ 886 limit = options_get_number(&c->session->options, "buffer-limit"); 887 paste_add(&c->session->buffers, buf, off, limit); 888 } 889 890 void 891 window_copy_copy_line(struct window_pane *wp, 892 char **buf, size_t *off, u_int sy, u_int sx, u_int ex) 893 { 894 struct grid *gd = wp->base.grid; 895 const struct grid_cell *gc; 896 const struct grid_utf8 *gu; 897 struct grid_line *gl; 898 u_int i, xx, wrapped = 0; 899 size_t size; 900 901 if (sx > ex) 902 return; 903 904 /* 905 * Work out if the line was wrapped at the screen edge and all of it is 906 * on screen. 907 */ 908 gl = &gd->linedata[sy]; 909 if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx) 910 wrapped = 1; 911 912 /* If the line was wrapped, don't strip spaces (use the full length). */ 913 if (wrapped) 914 xx = gl->cellsize; 915 else 916 xx = window_copy_find_length(wp, sy); 917 if (ex > xx) 918 ex = xx; 919 if (sx > xx) 920 sx = xx; 921 922 if (sx < ex) { 923 for (i = sx; i < ex; i++) { 924 gc = grid_peek_cell(gd, i, sy); 925 if (gc->flags & GRID_FLAG_PADDING) 926 continue; 927 if (!(gc->flags & GRID_FLAG_UTF8)) { 928 *buf = xrealloc(*buf, 1, (*off) + 1); 929 (*buf)[(*off)++] = gc->data; 930 } else { 931 gu = grid_peek_utf8(gd, i, sy); 932 size = grid_utf8_size(gu); 933 *buf = xrealloc(*buf, 1, (*off) + size); 934 *off += grid_utf8_copy(gu, *buf + *off, size); 935 } 936 } 937 } 938 939 /* Only add a newline if the line wasn't wrapped. */ 940 if (!wrapped) { 941 *buf = xrealloc(*buf, 1, (*off) + 1); 942 (*buf)[(*off)++] = '\n'; 943 } 944 } 945 946 int 947 window_copy_is_space(struct window_pane *wp, u_int px, u_int py) 948 { 949 const struct grid_cell *gc; 950 const char *spaces = " -_@"; 951 952 gc = grid_peek_cell(wp->base.grid, px, py); 953 if (gc->flags & (GRID_FLAG_PADDING|GRID_FLAG_UTF8)) 954 return (0); 955 if (gc->data == 0x00 || gc->data == 0x7f) 956 return (0); 957 return (strchr(spaces, gc->data) != NULL); 958 } 959 960 u_int 961 window_copy_find_length(struct window_pane *wp, u_int py) 962 { 963 const struct grid_cell *gc; 964 u_int px; 965 966 /* 967 * If the pane has been resized, its grid can contain old overlong 968 * lines. grid_peek_cell does not allow accessing cells beyond the 969 * width of the grid, and screen_write_copy treats them as spaces, so 970 * ignore them here too. 971 */ 972 px = wp->base.grid->linedata[py].cellsize; 973 if (px > screen_size_x(&wp->base)) 974 px = screen_size_x(&wp->base); 975 while (px > 0) { 976 gc = grid_peek_cell(wp->base.grid, px - 1, py); 977 if (gc->flags & GRID_FLAG_UTF8) 978 break; 979 if (gc->data != ' ') 980 break; 981 px--; 982 } 983 return (px); 984 } 985 986 void 987 window_copy_cursor_start_of_line(struct window_pane *wp) 988 { 989 struct window_copy_mode_data *data = wp->modedata; 990 991 window_copy_update_cursor(wp, 0, data->cy); 992 if (window_copy_update_selection(wp)) 993 window_copy_redraw_lines(wp, data->cy, 1); 994 } 995 996 void 997 window_copy_cursor_back_to_indentation(struct window_pane *wp) 998 { 999 struct window_copy_mode_data *data = wp->modedata; 1000 u_int px, py, xx; 1001 const struct grid_cell *gc; 1002 1003 px = 0; 1004 py = screen_hsize(&wp->base) + data->cy - data->oy; 1005 xx = window_copy_find_length(wp, py); 1006 1007 /* 1008 * Don't use window_copy_is_space because that treats some word 1009 * delimiters as spaces. 1010 */ 1011 while (px < xx) { 1012 gc = grid_peek_cell(wp->base.grid, px, py); 1013 if (gc->flags & GRID_FLAG_UTF8) 1014 break; 1015 if (gc->data != ' ') 1016 break; 1017 px++; 1018 } 1019 1020 window_copy_update_cursor(wp, px, data->cy); 1021 if (window_copy_update_selection(wp)) 1022 window_copy_redraw_lines(wp, data->cy, 1); 1023 } 1024 1025 void 1026 window_copy_cursor_end_of_line(struct window_pane *wp) 1027 { 1028 struct window_copy_mode_data *data = wp->modedata; 1029 u_int px, py; 1030 1031 py = screen_hsize(&wp->base) + data->cy - data->oy; 1032 px = window_copy_find_length(wp, py); 1033 1034 window_copy_update_cursor(wp, px, data->cy); 1035 if (window_copy_update_selection(wp)) 1036 window_copy_redraw_lines(wp, data->cy, 1); 1037 } 1038 1039 void 1040 window_copy_cursor_left(struct window_pane *wp) 1041 { 1042 struct window_copy_mode_data *data = wp->modedata; 1043 1044 if (data->cx == 0) { 1045 window_copy_cursor_up(wp, 0); 1046 window_copy_cursor_end_of_line(wp); 1047 } else { 1048 window_copy_update_cursor(wp, data->cx - 1, data->cy); 1049 if (window_copy_update_selection(wp)) 1050 window_copy_redraw_lines(wp, data->cy, 1); 1051 } 1052 } 1053 1054 void 1055 window_copy_cursor_right(struct window_pane *wp) 1056 { 1057 struct window_copy_mode_data *data = wp->modedata; 1058 u_int px, py; 1059 1060 py = screen_hsize(&wp->base) + data->cy - data->oy; 1061 px = window_copy_find_length(wp, py); 1062 1063 if (data->cx >= px) { 1064 window_copy_cursor_start_of_line(wp); 1065 window_copy_cursor_down(wp, 0); 1066 } else { 1067 window_copy_update_cursor(wp, data->cx + 1, data->cy); 1068 if (window_copy_update_selection(wp)) 1069 window_copy_redraw_lines(wp, data->cy, 1); 1070 } 1071 } 1072 1073 void 1074 window_copy_cursor_up(struct window_pane *wp, int scroll_only) 1075 { 1076 struct window_copy_mode_data *data = wp->modedata; 1077 u_int ox, oy, px, py; 1078 1079 oy = screen_hsize(&wp->base) + data->cy - data->oy; 1080 ox = window_copy_find_length(wp, oy); 1081 if (ox != 0) { 1082 data->lastcx = data->cx; 1083 data->lastsx = ox; 1084 } 1085 1086 data->cx = data->lastcx; 1087 if (scroll_only || data->cy == 0) { 1088 window_copy_scroll_down(wp, 1); 1089 if (scroll_only) 1090 window_copy_redraw_lines(wp, data->cy, 2); 1091 } else { 1092 window_copy_update_cursor(wp, data->cx, data->cy - 1); 1093 if (window_copy_update_selection(wp)) 1094 window_copy_redraw_lines(wp, data->cy, 2); 1095 } 1096 1097 py = screen_hsize(&wp->base) + data->cy - data->oy; 1098 px = window_copy_find_length(wp, py); 1099 if (data->cx >= data->lastsx || data->cx > px) 1100 window_copy_cursor_end_of_line(wp); 1101 } 1102 1103 void 1104 window_copy_cursor_down(struct window_pane *wp, int scroll_only) 1105 { 1106 struct window_copy_mode_data *data = wp->modedata; 1107 struct screen *s = &data->screen; 1108 u_int ox, oy, px, py; 1109 1110 oy = screen_hsize(&wp->base) + data->cy - data->oy; 1111 ox = window_copy_find_length(wp, oy); 1112 if (ox != 0) { 1113 data->lastcx = data->cx; 1114 data->lastsx = ox; 1115 } 1116 1117 data->cx = data->lastcx; 1118 if (scroll_only || data->cy == screen_size_y(s) - 1) { 1119 window_copy_scroll_up(wp, 1); 1120 if (scroll_only && data->cy > 0) 1121 window_copy_redraw_lines(wp, data->cy - 1, 2); 1122 } else { 1123 window_copy_update_cursor(wp, data->cx, data->cy + 1); 1124 if (window_copy_update_selection(wp)) 1125 window_copy_redraw_lines(wp, data->cy - 1, 2); 1126 } 1127 1128 py = screen_hsize(&wp->base) + data->cy - data->oy; 1129 px = window_copy_find_length(wp, py); 1130 if (data->cx >= data->lastsx || data->cx > px) 1131 window_copy_cursor_end_of_line(wp); 1132 } 1133 1134 void 1135 window_copy_cursor_next_word(struct window_pane *wp) 1136 { 1137 struct window_copy_mode_data *data = wp->modedata; 1138 struct screen *s = &data->screen; 1139 u_int px, py, xx, skip; 1140 1141 px = data->cx; 1142 py = screen_hsize(&wp->base) + data->cy - data->oy; 1143 xx = window_copy_find_length(wp, py); 1144 1145 skip = 1; 1146 if (px < xx) { 1147 /* If currently on a space, skip space. */ 1148 if (window_copy_is_space(wp, px, py)) 1149 skip = 0; 1150 } else 1151 skip = 0; 1152 for (;;) { 1153 if (px >= xx) { 1154 if (skip) { 1155 px = xx; 1156 break; 1157 } 1158 1159 while (px >= xx) { 1160 if (data->cy == screen_size_y(s) - 1) { 1161 if (data->oy == 0) 1162 goto out; 1163 } 1164 1165 px = 0; 1166 window_copy_cursor_down(wp, 0); 1167 1168 py =screen_hsize( 1169 &wp->base) + data->cy - data->oy; 1170 xx = window_copy_find_length(wp, py); 1171 } 1172 } 1173 1174 if (skip) { 1175 /* Currently skipping non-space (until space). */ 1176 if (window_copy_is_space(wp, px, py)) 1177 break; 1178 } else { 1179 /* Currently skipping space (until non-space). */ 1180 if (!window_copy_is_space(wp, px, py)) 1181 skip = 1; 1182 } 1183 1184 px++; 1185 } 1186 1187 out: 1188 window_copy_update_cursor(wp, px, data->cy); 1189 if (window_copy_update_selection(wp)) 1190 window_copy_redraw_lines(wp, data->cy, 1); 1191 } 1192 1193 /* Move to the previous place where a word begins. */ 1194 void 1195 window_copy_cursor_previous_word(struct window_pane *wp) 1196 { 1197 struct window_copy_mode_data *data = wp->modedata; 1198 u_int px, py; 1199 1200 px = data->cx; 1201 py = screen_hsize(&wp->base) + data->cy - data->oy; 1202 1203 /* Move back to the previous word character. */ 1204 for (;;) { 1205 if (px > 0) { 1206 px--; 1207 if (!window_copy_is_space(wp, px, py)) 1208 break; 1209 } else { 1210 if (data->cy == 0 && 1211 (screen_hsize(&wp->base) == 0 || 1212 data->oy >= screen_hsize(&wp->base) - 1)) 1213 goto out; 1214 window_copy_cursor_up(wp, 0); 1215 1216 py = screen_hsize( 1217 &wp->base) + data->cy - data->oy; 1218 px = window_copy_find_length(wp, py); 1219 } 1220 } 1221 1222 /* Move back to the beginning of this word. */ 1223 while (px > 0 && !window_copy_is_space(wp, px - 1, py)) 1224 px--; 1225 1226 out: 1227 window_copy_update_cursor(wp, px, data->cy); 1228 if (window_copy_update_selection(wp)) 1229 window_copy_redraw_lines(wp, data->cy, 1); 1230 } 1231 1232 void 1233 window_copy_scroll_up(struct window_pane *wp, u_int ny) 1234 { 1235 struct window_copy_mode_data *data = wp->modedata; 1236 struct screen *s = &data->screen; 1237 struct screen_write_ctx ctx; 1238 1239 if (data->oy < ny) 1240 ny = data->oy; 1241 if (ny == 0) 1242 return; 1243 data->oy -= ny; 1244 window_copy_update_selection(wp); 1245 1246 screen_write_start(&ctx, wp, NULL); 1247 screen_write_cursormove(&ctx, 0, 0); 1248 screen_write_deleteline(&ctx, ny); 1249 window_copy_write_lines(wp, &ctx, screen_size_y(s) - ny, ny); 1250 window_copy_write_line(wp, &ctx, 0); 1251 if (screen_size_y(s) > 1) 1252 window_copy_write_line(wp, &ctx, 1); 1253 if (screen_size_y(s) > 3) 1254 window_copy_write_line(wp, &ctx, screen_size_y(s) - 2); 1255 if (s->sel.flag && screen_size_y(s) > ny) 1256 window_copy_write_line(wp, &ctx, screen_size_y(s) - ny - 1); 1257 screen_write_cursormove(&ctx, data->cx, data->cy); 1258 screen_write_stop(&ctx); 1259 } 1260 1261 void 1262 window_copy_scroll_down(struct window_pane *wp, u_int ny) 1263 { 1264 struct window_copy_mode_data *data = wp->modedata; 1265 struct screen *s = &data->screen; 1266 struct screen_write_ctx ctx; 1267 1268 if (ny > screen_hsize(&wp->base)) 1269 return; 1270 1271 if (data->oy > screen_hsize(&wp->base) - ny) 1272 ny = screen_hsize(&wp->base) - data->oy; 1273 if (ny == 0) 1274 return; 1275 data->oy += ny; 1276 window_copy_update_selection(wp); 1277 1278 screen_write_start(&ctx, wp, NULL); 1279 screen_write_cursormove(&ctx, 0, 0); 1280 screen_write_insertline(&ctx, ny); 1281 window_copy_write_lines(wp, &ctx, 0, ny); 1282 if (s->sel.flag && screen_size_y(s) > ny) 1283 window_copy_write_line(wp, &ctx, ny); 1284 else if (ny == 1) /* nuke position */ 1285 window_copy_write_line(wp, &ctx, 1); 1286 screen_write_cursormove(&ctx, data->cx, data->cy); 1287 screen_write_stop(&ctx); 1288 } 1289