1 /* $OpenBSD: window-copy.c,v 1.29 2009/10/11 07:01:10 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_STARTSELECTION: 282 window_copy_start_selection(wp); 283 window_copy_redraw_screen(wp); 284 break; 285 case MODEKEYCOPY_CLEARSELECTION: 286 screen_clear_selection(&data->screen); 287 window_copy_redraw_screen(wp); 288 break; 289 case MODEKEYCOPY_COPYSELECTION: 290 if (c != NULL && c->session != NULL) { 291 window_copy_copy_selection(wp, c); 292 window_pane_reset_mode(wp); 293 } 294 break; 295 case MODEKEYCOPY_STARTOFLINE: 296 window_copy_cursor_start_of_line(wp); 297 break; 298 case MODEKEYCOPY_BACKTOINDENTATION: 299 window_copy_cursor_back_to_indentation(wp); 300 break; 301 case MODEKEYCOPY_ENDOFLINE: 302 window_copy_cursor_end_of_line(wp); 303 break; 304 case MODEKEYCOPY_NEXTWORD: 305 window_copy_cursor_next_word(wp); 306 break; 307 case MODEKEYCOPY_PREVIOUSWORD: 308 window_copy_cursor_previous_word(wp); 309 break; 310 case MODEKEYCOPY_SEARCHUP: 311 data->inputtype = WINDOW_COPY_SEARCHUP; 312 data->inputprompt = "Search Up"; 313 goto input_on; 314 case MODEKEYCOPY_SEARCHDOWN: 315 data->inputtype = WINDOW_COPY_SEARCHDOWN; 316 data->inputprompt = "Search Down"; 317 goto input_on; 318 case MODEKEYCOPY_SEARCHAGAIN: 319 switch (data->searchtype) { 320 case WINDOW_COPY_OFF: 321 case WINDOW_COPY_GOTOLINE: 322 break; 323 case WINDOW_COPY_SEARCHUP: 324 window_copy_search_up(wp, data->searchstr); 325 break; 326 case WINDOW_COPY_SEARCHDOWN: 327 window_copy_search_down(wp, data->searchstr); 328 break; 329 } 330 break; 331 case MODEKEYCOPY_GOTOLINE: 332 data->inputtype = WINDOW_COPY_GOTOLINE; 333 data->inputprompt = "Goto Line"; 334 *data->inputstr = '\0'; 335 goto input_on; 336 default: 337 break; 338 } 339 340 return; 341 342 input_on: 343 keys = options_get_number(&wp->window->options, "mode-keys"); 344 if (keys == MODEKEY_EMACS) 345 mode_key_init(&data->mdata, &mode_key_tree_emacs_edit); 346 else 347 mode_key_init(&data->mdata, &mode_key_tree_vi_edit); 348 349 window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); 350 return; 351 352 input_off: 353 keys = options_get_number(&wp->window->options, "mode-keys"); 354 if (keys == MODEKEY_EMACS) 355 mode_key_init(&data->mdata, &mode_key_tree_emacs_copy); 356 else 357 mode_key_init(&data->mdata, &mode_key_tree_vi_copy); 358 359 data->inputtype = WINDOW_COPY_OFF; 360 data->inputprompt = NULL; 361 362 window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); 363 } 364 365 int 366 window_copy_key_input(struct window_pane *wp, int key) 367 { 368 struct window_copy_mode_data *data = wp->modedata; 369 struct screen *s = &data->screen; 370 size_t inputlen; 371 372 switch (mode_key_lookup(&data->mdata, key)) { 373 case MODEKEYEDIT_CANCEL: 374 return (-1); 375 case MODEKEYEDIT_BACKSPACE: 376 inputlen = strlen(data->inputstr); 377 if (inputlen > 0) 378 data->inputstr[inputlen - 1] = '\0'; 379 break; 380 case MODEKEYEDIT_DELETELINE: 381 *data->inputstr = '\0'; 382 break; 383 case MODEKEYEDIT_ENTER: 384 switch (data->inputtype) { 385 case WINDOW_COPY_OFF: 386 break; 387 case WINDOW_COPY_SEARCHUP: 388 window_copy_search_up(wp, data->inputstr); 389 data->searchtype = data->inputtype; 390 data->searchstr = xstrdup(data->inputstr); 391 break; 392 case WINDOW_COPY_SEARCHDOWN: 393 window_copy_search_down(wp, data->inputstr); 394 data->searchtype = data->inputtype; 395 data->searchstr = xstrdup(data->inputstr); 396 break; 397 case WINDOW_COPY_GOTOLINE: 398 window_copy_goto_line(wp, data->inputstr); 399 *data->inputstr = '\0'; 400 break; 401 } 402 return (1); 403 case MODEKEY_OTHER: 404 if (key < 32 || key > 126) 405 break; 406 inputlen = strlen(data->inputstr) + 2; 407 408 data->inputstr = xrealloc(data->inputstr, 1, inputlen); 409 data->inputstr[inputlen - 2] = key; 410 data->inputstr[inputlen - 1] = '\0'; 411 break; 412 default: 413 break; 414 } 415 416 window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); 417 return (0); 418 } 419 420 void 421 window_copy_mouse( 422 struct window_pane *wp, unused struct client *c, struct mouse_event *m) 423 { 424 struct window_copy_mode_data *data = wp->modedata; 425 struct screen *s = &data->screen; 426 427 if ((m->b & 3) == 3) 428 return; 429 if (m->x >= screen_size_x(s)) 430 return; 431 if (m->y >= screen_size_y(s)) 432 return; 433 434 window_copy_update_cursor(wp, m->x, m->y); 435 if (window_copy_update_selection(wp)) 436 window_copy_redraw_screen(wp); 437 } 438 439 void 440 window_copy_scroll_to(struct window_pane *wp, u_int px, u_int py) 441 { 442 struct window_copy_mode_data *data = wp->modedata; 443 struct screen *s = &wp->base; 444 struct grid *gd = s->grid; 445 u_int offset, gap; 446 447 data->cx = px; 448 449 gap = gd->sy / 4; 450 if (py < gd->sy) { 451 offset = 0; 452 data->cy = py; 453 } else if (py > gd->hsize + gd->sy - gap) { 454 offset = gd->hsize; 455 data->cy = py - gd->hsize; 456 } else { 457 offset = py + gap - gd->sy; 458 data->cy = py - offset; 459 } 460 data->oy = gd->hsize - offset; 461 462 window_copy_redraw_screen(wp); 463 } 464 465 int 466 window_copy_search_compare( 467 struct grid *gd, u_int px, u_int py, struct grid *sgd, u_int spx) 468 { 469 const struct grid_cell *gc, *sgc; 470 const struct grid_utf8 *gu, *sgu; 471 472 gc = grid_peek_cell(gd, px, py); 473 sgc = grid_peek_cell(sgd, spx, 0); 474 475 if ((gc->flags & GRID_FLAG_UTF8) != (sgc->flags & GRID_FLAG_UTF8)) 476 return (0); 477 478 if (gc->flags & GRID_FLAG_UTF8) { 479 gu = grid_peek_utf8(gd, px, py); 480 sgu = grid_peek_utf8(sgd, spx, 0); 481 if (memcmp(gu->data, sgu->data, UTF8_SIZE) == 0) 482 return (1); 483 } else { 484 if (gc->data == sgc->data) 485 return (1); 486 } 487 return (0); 488 } 489 490 int 491 window_copy_search_lr(struct grid *gd, 492 struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last) 493 { 494 u_int ax, bx, px; 495 496 for (ax = first; ax < last; ax++) { 497 if (ax + sgd->sx >= gd->sx) 498 break; 499 for (bx = 0; bx < sgd->sx; bx++) { 500 px = ax + bx; 501 if (!window_copy_search_compare(gd, px, py, sgd, bx)) 502 break; 503 } 504 if (bx == sgd->sx) { 505 *ppx = ax; 506 return (1); 507 } 508 } 509 return (0); 510 } 511 512 int 513 window_copy_search_rl(struct grid *gd, 514 struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last) 515 { 516 u_int ax, bx, px; 517 518 for (ax = last + 1; ax > first; ax--) { 519 if (gd->sx - (ax - 1) < sgd->sx) 520 continue; 521 for (bx = 0; bx < sgd->sx; bx++) { 522 px = ax - 1 + bx; 523 if (!window_copy_search_compare(gd, px, py, sgd, bx)) 524 break; 525 } 526 if (bx == sgd->sx) { 527 *ppx = ax - 1; 528 return (1); 529 } 530 } 531 return (0); 532 } 533 534 void 535 window_copy_search_up(struct window_pane *wp, const char *searchstr) 536 { 537 struct window_copy_mode_data *data = wp->modedata; 538 struct screen *s = &wp->base, ss; 539 struct screen_write_ctx ctx; 540 struct grid *gd = s->grid, *sgd; 541 struct grid_cell gc; 542 size_t searchlen; 543 u_int i, last, fx, fy, px; 544 int utf8flag, n, wrapped; 545 546 if (*searchstr == '\0') 547 return; 548 utf8flag = options_get_number(&wp->window->options, "utf8"); 549 searchlen = screen_write_strlen(utf8flag, "%s", searchstr); 550 551 screen_init(&ss, searchlen, 1, 0); 552 screen_write_start(&ctx, NULL, &ss); 553 memcpy(&gc, &grid_default_cell, sizeof gc); 554 screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr); 555 screen_write_stop(&ctx); 556 557 fx = data->cx; 558 fy = gd->hsize - data->oy + data->cy; 559 560 if (fx == 0) { 561 if (fy == 0) 562 return; 563 fx = gd->sx - 1; 564 fy--; 565 } else 566 fx--; 567 n = wrapped = 0; 568 569 retry: 570 sgd = ss.grid; 571 for (i = fy + 1; i > 0; i--) { 572 last = screen_size_x(s); 573 if (i == fy + 1) 574 last = fx; 575 n = window_copy_search_rl(gd, sgd, &px, i - 1, 0, last); 576 if (n) { 577 window_copy_scroll_to(wp, px, i - 1); 578 break; 579 } 580 } 581 if (!n && !wrapped) { 582 fx = gd->sx - 1; 583 fy = gd->hsize + gd->sy - 1; 584 wrapped = 1; 585 goto retry; 586 } 587 588 screen_free(&ss); 589 } 590 591 void 592 window_copy_search_down(struct window_pane *wp, const char *searchstr) 593 { 594 struct window_copy_mode_data *data = wp->modedata; 595 struct screen *s = &wp->base, ss; 596 struct screen_write_ctx ctx; 597 struct grid *gd = s->grid, *sgd; 598 struct grid_cell gc; 599 size_t searchlen; 600 u_int i, first, fx, fy, px; 601 int utf8flag, n, wrapped; 602 603 if (*searchstr == '\0') 604 return; 605 utf8flag = options_get_number(&wp->window->options, "utf8"); 606 searchlen = screen_write_strlen(utf8flag, "%s", searchstr); 607 608 screen_init(&ss, searchlen, 1, 0); 609 screen_write_start(&ctx, NULL, &ss); 610 memcpy(&gc, &grid_default_cell, sizeof gc); 611 screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr); 612 screen_write_stop(&ctx); 613 614 fx = data->cx; 615 fy = gd->hsize - data->oy + data->cy; 616 617 if (fx == gd->sx - 1) { 618 if (fy == gd->hsize + gd->sy) 619 return; 620 fx = 0; 621 fy++; 622 } else 623 fx++; 624 n = wrapped = 0; 625 626 retry: 627 sgd = ss.grid; 628 for (i = fy + 1; i < gd->hsize + gd->sy; i++) { 629 first = 0; 630 if (i == fy + 1) 631 first = fx; 632 n = window_copy_search_lr(gd, sgd, &px, i - 1, first, gd->sx); 633 if (n) { 634 window_copy_scroll_to(wp, px, i - 1); 635 break; 636 } 637 } 638 if (!n && !wrapped) { 639 fx = 0; 640 fy = 0; 641 wrapped = 1; 642 goto retry; 643 } 644 645 screen_free(&ss); 646 } 647 648 void 649 window_copy_goto_line(struct window_pane *wp, const char *linestr) 650 { 651 struct window_copy_mode_data *data = wp->modedata; 652 const char *errstr; 653 u_int lineno; 654 655 lineno = strtonum(linestr, 0, screen_hsize(&wp->base), &errstr); 656 if (errstr != NULL) 657 return; 658 659 data->oy = lineno; 660 window_copy_redraw_screen(wp); 661 } 662 663 void 664 window_copy_write_line( 665 struct window_pane *wp, struct screen_write_ctx *ctx, u_int py) 666 { 667 struct window_copy_mode_data *data = wp->modedata; 668 struct screen *s = &data->screen; 669 struct options *oo = &wp->window->options; 670 struct grid_cell gc; 671 char hdr[32]; 672 size_t last, xoff = 0, size = 0; 673 674 memcpy(&gc, &grid_default_cell, sizeof gc); 675 colour_set_fg(&gc, options_get_number(oo, "mode-fg")); 676 colour_set_bg(&gc, options_get_number(oo, "mode-bg")); 677 gc.attr |= options_get_number(oo, "mode-attr"); 678 679 last = screen_size_y(s) - 1; 680 if (py == 0) { 681 size = xsnprintf(hdr, sizeof hdr, 682 "[%u/%u]", data->oy, screen_hsize(&wp->base)); 683 screen_write_cursormove(ctx, screen_size_x(s) - size, 0); 684 screen_write_puts(ctx, &gc, "%s", hdr); 685 } else if (py == last && data->inputtype != WINDOW_COPY_OFF) { 686 xoff = size = xsnprintf(hdr, sizeof hdr, 687 "%s: %s", data->inputprompt, data->inputstr); 688 screen_write_cursormove(ctx, 0, last); 689 screen_write_puts(ctx, &gc, "%s", hdr); 690 } else 691 size = 0; 692 693 screen_write_cursormove(ctx, xoff, py); 694 screen_write_copy(ctx, &wp->base, xoff, (screen_hsize(&wp->base) - 695 data->oy) + py, screen_size_x(s) - size, 1); 696 697 if (py == data->cy && data->cx == screen_size_x(s)) { 698 memcpy(&gc, &grid_default_cell, sizeof gc); 699 screen_write_cursormove(ctx, screen_size_x(s) - 1, py); 700 screen_write_putc(ctx, &gc, '$'); 701 } 702 } 703 704 void 705 window_copy_write_lines( 706 struct window_pane *wp, struct screen_write_ctx *ctx, u_int py, u_int ny) 707 { 708 u_int yy; 709 710 for (yy = py; yy < py + ny; yy++) 711 window_copy_write_line(wp, ctx, py); 712 } 713 714 void 715 window_copy_redraw_lines(struct window_pane *wp, u_int py, u_int ny) 716 { 717 struct window_copy_mode_data *data = wp->modedata; 718 struct screen_write_ctx ctx; 719 u_int i; 720 721 screen_write_start(&ctx, wp, NULL); 722 for (i = py; i < py + ny; i++) 723 window_copy_write_line(wp, &ctx, i); 724 screen_write_cursormove(&ctx, data->cx, data->cy); 725 screen_write_stop(&ctx); 726 } 727 728 void 729 window_copy_redraw_screen(struct window_pane *wp) 730 { 731 struct window_copy_mode_data *data = wp->modedata; 732 733 window_copy_redraw_lines(wp, 0, screen_size_y(&data->screen)); 734 } 735 736 void 737 window_copy_update_cursor(struct window_pane *wp, u_int cx, u_int cy) 738 { 739 struct window_copy_mode_data *data = wp->modedata; 740 struct screen *s = &data->screen; 741 struct screen_write_ctx ctx; 742 u_int old_cx, old_cy; 743 744 old_cx = data->cx; old_cy = data->cy; 745 data->cx = cx; data->cy = cy; 746 if (old_cx == screen_size_x(s)) 747 window_copy_redraw_lines(wp, old_cy, 1); 748 if (data->cx == screen_size_x(s)) 749 window_copy_redraw_lines(wp, data->cy, 1); 750 else { 751 screen_write_start(&ctx, wp, NULL); 752 screen_write_cursormove(&ctx, data->cx, data->cy); 753 screen_write_stop(&ctx); 754 } 755 } 756 757 void 758 window_copy_start_selection(struct window_pane *wp) 759 { 760 struct window_copy_mode_data *data = wp->modedata; 761 struct screen *s = &data->screen; 762 763 data->selx = data->cx; 764 data->sely = screen_hsize(&wp->base) + data->cy - data->oy; 765 766 s->sel.flag = 1; 767 window_copy_update_selection(wp); 768 } 769 770 int 771 window_copy_update_selection(struct window_pane *wp) 772 { 773 struct window_copy_mode_data *data = wp->modedata; 774 struct screen *s = &data->screen; 775 struct options *oo = &wp->window->options; 776 struct grid_cell gc; 777 u_int sx, sy, ty; 778 779 if (!s->sel.flag) 780 return (0); 781 782 /* Set colours. */ 783 memcpy(&gc, &grid_default_cell, sizeof gc); 784 colour_set_fg(&gc, options_get_number(oo, "mode-fg")); 785 colour_set_bg(&gc, options_get_number(oo, "mode-bg")); 786 gc.attr |= options_get_number(oo, "mode-attr"); 787 788 /* Find top of screen. */ 789 ty = screen_hsize(&wp->base) - data->oy; 790 791 /* Adjust the selection. */ 792 sx = data->selx; 793 sy = data->sely; 794 if (sy < ty) { /* above screen */ 795 sx = 0; 796 sy = 0; 797 } else if (sy > ty + screen_size_y(s) - 1) { /* below screen */ 798 sx = screen_size_x(s) - 1; 799 sy = screen_size_y(s) - 1; 800 } else 801 sy -= ty; 802 sy = screen_hsize(s) + sy; 803 804 screen_set_selection( 805 s, sx, sy, data->cx, screen_hsize(s) + data->cy, &gc); 806 return (1); 807 } 808 809 void 810 window_copy_copy_selection(struct window_pane *wp, struct client *c) 811 { 812 struct window_copy_mode_data *data = wp->modedata; 813 struct screen *s = &data->screen; 814 char *buf; 815 size_t off; 816 u_int i, xx, yy, sx, sy, ex, ey, limit; 817 818 if (!s->sel.flag) 819 return; 820 821 buf = xmalloc(1); 822 off = 0; 823 824 *buf = '\0'; 825 826 /* 827 * The selection extends from selx,sely to (adjusted) cx,cy on 828 * the base screen. 829 */ 830 831 /* Find start and end. */ 832 xx = data->cx; 833 yy = screen_hsize(&wp->base) + data->cy - data->oy; 834 if (yy < data->sely || (yy == data->sely && xx < data->selx)) { 835 sx = xx; sy = yy; 836 ex = data->selx; ey = data->sely; 837 } else { 838 sx = data->selx; sy = data->sely; 839 ex = xx; ey = yy; 840 } 841 842 /* Trim ex to end of line. */ 843 xx = window_copy_find_length(wp, ey); 844 if (ex > xx) 845 ex = xx; 846 847 /* Copy the lines. */ 848 if (sy == ey) 849 window_copy_copy_line(wp, &buf, &off, sy, sx, ex); 850 else { 851 xx = screen_size_x(s); 852 window_copy_copy_line(wp, &buf, &off, sy, sx, xx); 853 if (ey - sy > 1) { 854 for (i = sy + 1; i < ey; i++) 855 window_copy_copy_line(wp, &buf, &off, i, 0, xx); 856 } 857 window_copy_copy_line(wp, &buf, &off, ey, 0, ex); 858 } 859 860 /* Don't bother if no data. */ 861 if (off == 0) { 862 xfree(buf); 863 return; 864 } 865 off--; /* remove final \n */ 866 867 /* Add the buffer to the stack. */ 868 limit = options_get_number(&c->session->options, "buffer-limit"); 869 paste_add(&c->session->buffers, buf, off, limit); 870 } 871 872 void 873 window_copy_copy_line(struct window_pane *wp, 874 char **buf, size_t *off, u_int sy, u_int sx, u_int ex) 875 { 876 struct grid *gd = wp->base.grid; 877 const struct grid_cell *gc; 878 const struct grid_utf8 *gu; 879 struct grid_line *gl; 880 u_int i, j, xx, wrapped = 0; 881 882 if (sx > ex) 883 return; 884 885 /* 886 * Work out if the line was wrapped at the screen edge and all of it is 887 * on screen. 888 */ 889 gl = &gd->linedata[sy]; 890 if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx) 891 wrapped = 1; 892 893 /* If the line was wrapped, don't strip spaces (use the full length). */ 894 if (wrapped) 895 xx = gl->cellsize; 896 else 897 xx = window_copy_find_length(wp, sy); 898 if (ex > xx) 899 ex = xx; 900 if (sx > xx) 901 sx = xx; 902 903 if (sx < ex) { 904 for (i = sx; i < ex; i++) { 905 gc = grid_peek_cell(gd, i, sy); 906 if (gc->flags & GRID_FLAG_PADDING) 907 continue; 908 if (!(gc->flags & GRID_FLAG_UTF8)) { 909 *buf = xrealloc(*buf, 1, (*off) + 1); 910 (*buf)[(*off)++] = gc->data; 911 } else { 912 gu = grid_peek_utf8(gd, i, sy); 913 *buf = xrealloc(*buf, 1, (*off) + UTF8_SIZE); 914 for (j = 0; j < UTF8_SIZE; j++) { 915 if (gu->data[j] == 0xff) 916 break; 917 (*buf)[(*off)++] = gu->data[j]; 918 } 919 } 920 } 921 } 922 923 /* Only add a newline if the line wasn't wrapped. */ 924 if (!wrapped) { 925 *buf = xrealloc(*buf, 1, (*off) + 1); 926 (*buf)[(*off)++] = '\n'; 927 } 928 } 929 930 int 931 window_copy_is_space(struct window_pane *wp, u_int px, u_int py) 932 { 933 const struct grid_cell *gc; 934 const char *spaces = " -_@"; 935 936 gc = grid_peek_cell(wp->base.grid, px, py); 937 if (gc->flags & (GRID_FLAG_PADDING|GRID_FLAG_UTF8)) 938 return (0); 939 if (gc->data == 0x00 || gc->data == 0x7f) 940 return (0); 941 return (strchr(spaces, gc->data) != NULL); 942 } 943 944 u_int 945 window_copy_find_length(struct window_pane *wp, u_int py) 946 { 947 const struct grid_cell *gc; 948 u_int px; 949 950 /* 951 * If the pane has been resized, its grid can contain old overlong 952 * lines. grid_peek_cell does not allow accessing cells beyond the 953 * width of the grid, and screen_write_copy treats them as spaces, so 954 * ignore them here too. 955 */ 956 px = wp->base.grid->linedata[py].cellsize; 957 if (px > screen_size_x(&wp->base)) 958 px = screen_size_x(&wp->base); 959 while (px > 0) { 960 gc = grid_peek_cell(wp->base.grid, px - 1, py); 961 if (gc->flags & GRID_FLAG_UTF8) 962 break; 963 if (gc->data != ' ') 964 break; 965 px--; 966 } 967 return (px); 968 } 969 970 void 971 window_copy_cursor_start_of_line(struct window_pane *wp) 972 { 973 struct window_copy_mode_data *data = wp->modedata; 974 975 window_copy_update_cursor(wp, 0, data->cy); 976 if (window_copy_update_selection(wp)) 977 window_copy_redraw_lines(wp, data->cy, 1); 978 } 979 980 void 981 window_copy_cursor_back_to_indentation(struct window_pane *wp) 982 { 983 struct window_copy_mode_data *data = wp->modedata; 984 u_int px, py, xx; 985 const struct grid_cell *gc; 986 987 px = 0; 988 py = screen_hsize(&wp->base) + data->cy - data->oy; 989 xx = window_copy_find_length(wp, py); 990 991 /* 992 * Don't use window_copy_is_space because that treats some word 993 * delimiters as spaces. 994 */ 995 while (px < xx) { 996 gc = grid_peek_cell(wp->base.grid, px, py); 997 if (gc->flags & GRID_FLAG_UTF8) 998 break; 999 if (gc->data != ' ') 1000 break; 1001 px++; 1002 } 1003 1004 window_copy_update_cursor(wp, px, data->cy); 1005 if (window_copy_update_selection(wp)) 1006 window_copy_redraw_lines(wp, data->cy, 1); 1007 } 1008 1009 void 1010 window_copy_cursor_end_of_line(struct window_pane *wp) 1011 { 1012 struct window_copy_mode_data *data = wp->modedata; 1013 u_int px, py; 1014 1015 py = screen_hsize(&wp->base) + data->cy - data->oy; 1016 px = window_copy_find_length(wp, py); 1017 1018 window_copy_update_cursor(wp, px, data->cy); 1019 if (window_copy_update_selection(wp)) 1020 window_copy_redraw_lines(wp, data->cy, 1); 1021 } 1022 1023 void 1024 window_copy_cursor_left(struct window_pane *wp) 1025 { 1026 struct window_copy_mode_data *data = wp->modedata; 1027 1028 if (data->cx == 0) { 1029 window_copy_cursor_up(wp, 0); 1030 window_copy_cursor_end_of_line(wp); 1031 } else { 1032 window_copy_update_cursor(wp, data->cx - 1, data->cy); 1033 if (window_copy_update_selection(wp)) 1034 window_copy_redraw_lines(wp, data->cy, 1); 1035 } 1036 } 1037 1038 void 1039 window_copy_cursor_right(struct window_pane *wp) 1040 { 1041 struct window_copy_mode_data *data = wp->modedata; 1042 u_int px, py; 1043 1044 py = screen_hsize(&wp->base) + data->cy - data->oy; 1045 px = window_copy_find_length(wp, py); 1046 1047 if (data->cx >= px) { 1048 window_copy_cursor_start_of_line(wp); 1049 window_copy_cursor_down(wp, 0); 1050 } else { 1051 window_copy_update_cursor(wp, data->cx + 1, data->cy); 1052 if (window_copy_update_selection(wp)) 1053 window_copy_redraw_lines(wp, data->cy, 1); 1054 } 1055 } 1056 1057 void 1058 window_copy_cursor_up(struct window_pane *wp, int scroll_only) 1059 { 1060 struct window_copy_mode_data *data = wp->modedata; 1061 u_int ox, oy, px, py; 1062 1063 oy = screen_hsize(&wp->base) + data->cy - data->oy; 1064 ox = window_copy_find_length(wp, oy); 1065 if (ox != 0) { 1066 data->lastcx = data->cx; 1067 data->lastsx = ox; 1068 } 1069 1070 data->cx = data->lastcx; 1071 if (scroll_only || data->cy == 0) { 1072 window_copy_scroll_down(wp, 1); 1073 if (scroll_only && window_copy_update_selection(wp)) 1074 window_copy_redraw_lines(wp, data->cy, 2); 1075 } else { 1076 window_copy_update_cursor(wp, data->cx, data->cy - 1); 1077 if (window_copy_update_selection(wp)) 1078 window_copy_redraw_lines(wp, data->cy, 2); 1079 } 1080 1081 py = screen_hsize(&wp->base) + data->cy - data->oy; 1082 px = window_copy_find_length(wp, py); 1083 if (data->cx >= data->lastsx || data->cx > px) 1084 window_copy_cursor_end_of_line(wp); 1085 } 1086 1087 void 1088 window_copy_cursor_down(struct window_pane *wp, int scroll_only) 1089 { 1090 struct window_copy_mode_data *data = wp->modedata; 1091 struct screen *s = &data->screen; 1092 u_int ox, oy, px, py; 1093 1094 oy = screen_hsize(&wp->base) + data->cy - data->oy; 1095 ox = window_copy_find_length(wp, oy); 1096 if (ox != 0) { 1097 data->lastcx = data->cx; 1098 data->lastsx = ox; 1099 } 1100 1101 data->cx = data->lastcx; 1102 if (scroll_only || data->cy == screen_size_y(s) - 1) { 1103 window_copy_scroll_up(wp, 1); 1104 if (scroll_only && window_copy_update_selection(wp)) 1105 window_copy_redraw_lines(wp, data->cy - 1, 2); 1106 } else { 1107 window_copy_update_cursor(wp, data->cx, data->cy + 1); 1108 if (window_copy_update_selection(wp)) 1109 window_copy_redraw_lines(wp, data->cy - 1, 2); 1110 } 1111 1112 py = screen_hsize(&wp->base) + data->cy - data->oy; 1113 px = window_copy_find_length(wp, py); 1114 if (data->cx >= data->lastsx || data->cx > px) 1115 window_copy_cursor_end_of_line(wp); 1116 } 1117 1118 void 1119 window_copy_cursor_next_word(struct window_pane *wp) 1120 { 1121 struct window_copy_mode_data *data = wp->modedata; 1122 struct screen *s = &data->screen; 1123 u_int px, py, xx, skip; 1124 1125 px = data->cx; 1126 py = screen_hsize(&wp->base) + data->cy - data->oy; 1127 xx = window_copy_find_length(wp, py); 1128 1129 skip = 1; 1130 if (px < xx) { 1131 /* If currently on a space, skip space. */ 1132 if (window_copy_is_space(wp, px, py)) 1133 skip = 0; 1134 } else 1135 skip = 0; 1136 for (;;) { 1137 if (px >= xx) { 1138 if (skip) { 1139 px = xx; 1140 break; 1141 } 1142 1143 while (px >= xx) { 1144 if (data->cy == screen_size_y(s) - 1) { 1145 if (data->oy == 0) 1146 goto out; 1147 } 1148 1149 px = 0; 1150 window_copy_cursor_down(wp, 0); 1151 1152 py =screen_hsize( 1153 &wp->base) + data->cy - data->oy; 1154 xx = window_copy_find_length(wp, py); 1155 } 1156 } 1157 1158 if (skip) { 1159 /* Currently skipping non-space (until space). */ 1160 if (window_copy_is_space(wp, px, py)) 1161 break; 1162 } else { 1163 /* Currently skipping space (until non-space). */ 1164 if (!window_copy_is_space(wp, px, py)) 1165 skip = 1; 1166 } 1167 1168 px++; 1169 } 1170 1171 out: 1172 window_copy_update_cursor(wp, px, data->cy); 1173 if (window_copy_update_selection(wp)) 1174 window_copy_redraw_lines(wp, data->cy, 1); 1175 } 1176 1177 /* Move to the previous place where a word begins. */ 1178 void 1179 window_copy_cursor_previous_word(struct window_pane *wp) 1180 { 1181 struct window_copy_mode_data *data = wp->modedata; 1182 u_int px, py; 1183 1184 px = data->cx; 1185 py = screen_hsize(&wp->base) + data->cy - data->oy; 1186 1187 /* Move back to the previous word character. */ 1188 for (;;) { 1189 if (px > 0) { 1190 px--; 1191 if (!window_copy_is_space(wp, px, py)) 1192 break; 1193 } else { 1194 if (data->cy == 0 && 1195 (screen_hsize(&wp->base) == 0 || 1196 data->oy >= screen_hsize(&wp->base) - 1)) 1197 goto out; 1198 window_copy_cursor_up(wp, 0); 1199 1200 py = screen_hsize( 1201 &wp->base) + data->cy - data->oy; 1202 px = window_copy_find_length(wp, py); 1203 } 1204 } 1205 1206 /* Move back to the beginning of this word. */ 1207 while (px > 0 && !window_copy_is_space(wp, px - 1, py)) 1208 px--; 1209 1210 out: 1211 window_copy_update_cursor(wp, px, data->cy); 1212 if (window_copy_update_selection(wp)) 1213 window_copy_redraw_lines(wp, data->cy, 1); 1214 } 1215 1216 void 1217 window_copy_scroll_up(struct window_pane *wp, u_int ny) 1218 { 1219 struct window_copy_mode_data *data = wp->modedata; 1220 struct screen *s = &data->screen; 1221 struct screen_write_ctx ctx; 1222 1223 if (data->oy < ny) 1224 ny = data->oy; 1225 if (ny == 0) 1226 return; 1227 data->oy -= ny; 1228 window_copy_update_selection(wp); 1229 1230 screen_write_start(&ctx, wp, NULL); 1231 screen_write_cursormove(&ctx, 0, 0); 1232 screen_write_deleteline(&ctx, ny); 1233 window_copy_write_lines(wp, &ctx, screen_size_y(s) - ny, ny); 1234 window_copy_write_line(wp, &ctx, 0); 1235 window_copy_write_line(wp, &ctx, 1); 1236 if (s->sel.flag && screen_size_y(s) > ny) 1237 window_copy_write_line(wp, &ctx, screen_size_y(s) - ny - 1); 1238 screen_write_cursormove(&ctx, data->cx, data->cy); 1239 screen_write_stop(&ctx); 1240 } 1241 1242 void 1243 window_copy_scroll_down(struct window_pane *wp, u_int ny) 1244 { 1245 struct window_copy_mode_data *data = wp->modedata; 1246 struct screen *s = &data->screen; 1247 struct screen_write_ctx ctx; 1248 1249 if (ny > screen_hsize(&wp->base)) 1250 return; 1251 1252 if (data->oy > screen_hsize(&wp->base) - ny) 1253 ny = screen_hsize(&wp->base) - data->oy; 1254 if (ny == 0) 1255 return; 1256 data->oy += ny; 1257 window_copy_update_selection(wp); 1258 1259 screen_write_start(&ctx, wp, NULL); 1260 screen_write_cursormove(&ctx, 0, 0); 1261 screen_write_insertline(&ctx, ny); 1262 window_copy_write_lines(wp, &ctx, 0, ny); 1263 if (s->sel.flag && screen_size_y(s) > ny) 1264 window_copy_write_line(wp, &ctx, ny); 1265 else if (ny == 1) /* nuke position */ 1266 window_copy_write_line(wp, &ctx, 1); 1267 screen_write_cursormove(&ctx, data->cx, data->cy); 1268 screen_write_stop(&ctx); 1269 } 1270