window-copy.c revision 1.2
1/* $Id: window-copy.c,v 1.2 2011/08/17 19:28:36 jmmv 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 26struct screen *window_copy_init(struct window_pane *); 27void window_copy_free(struct window_pane *); 28void window_copy_resize(struct window_pane *, u_int, u_int); 29void window_copy_key(struct window_pane *, struct session *, int); 30int window_copy_key_input(struct window_pane *, int); 31int window_copy_key_numeric_prefix(struct window_pane *, int); 32void window_copy_mouse( 33 struct window_pane *, struct session *, struct mouse_event *); 34 35void window_copy_redraw_lines(struct window_pane *, u_int, u_int); 36void window_copy_redraw_screen(struct window_pane *); 37void window_copy_write_line( 38 struct window_pane *, struct screen_write_ctx *, u_int); 39void window_copy_write_lines( 40 struct window_pane *, struct screen_write_ctx *, u_int, u_int); 41 42void window_copy_scroll_to(struct window_pane *, u_int, u_int); 43int window_copy_search_compare( 44 struct grid *, u_int, u_int, struct grid *, u_int); 45int window_copy_search_lr( 46 struct grid *, struct grid *, u_int *, u_int, u_int, u_int); 47int window_copy_search_rl( 48 struct grid *, struct grid *, u_int *, u_int, u_int, u_int); 49void window_copy_search_up(struct window_pane *, const char *); 50void window_copy_search_down(struct window_pane *, const char *); 51void window_copy_goto_line(struct window_pane *, const char *); 52void window_copy_update_cursor(struct window_pane *, u_int, u_int); 53void window_copy_start_selection(struct window_pane *); 54int window_copy_update_selection(struct window_pane *); 55void window_copy_copy_selection(struct window_pane *); 56void window_copy_clear_selection(struct window_pane *); 57void window_copy_copy_line( 58 struct window_pane *, char **, size_t *, u_int, u_int, u_int); 59int window_copy_in_set(struct window_pane *, u_int, u_int, const char *); 60u_int window_copy_find_length(struct window_pane *, u_int); 61void window_copy_cursor_start_of_line(struct window_pane *); 62void window_copy_cursor_back_to_indentation(struct window_pane *); 63void window_copy_cursor_end_of_line(struct window_pane *); 64void window_copy_cursor_left(struct window_pane *); 65void window_copy_cursor_right(struct window_pane *); 66void window_copy_cursor_up(struct window_pane *, int); 67void window_copy_cursor_down(struct window_pane *, int); 68void window_copy_cursor_jump(struct window_pane *); 69void window_copy_cursor_jump_back(struct window_pane *); 70void window_copy_cursor_next_word(struct window_pane *, const char *); 71void window_copy_cursor_next_word_end(struct window_pane *, const char *); 72void window_copy_cursor_previous_word(struct window_pane *, const char *); 73void window_copy_scroll_up(struct window_pane *, u_int); 74void window_copy_scroll_down(struct window_pane *, u_int); 75void window_copy_rectangle_toggle(struct window_pane *); 76 77const struct window_mode window_copy_mode = { 78 window_copy_init, 79 window_copy_free, 80 window_copy_resize, 81 window_copy_key, 82 window_copy_mouse, 83 NULL, 84}; 85 86enum window_copy_input_type { 87 WINDOW_COPY_OFF, 88 WINDOW_COPY_NUMERICPREFIX, 89 WINDOW_COPY_SEARCHUP, 90 WINDOW_COPY_SEARCHDOWN, 91 WINDOW_COPY_JUMPFORWARD, 92 WINDOW_COPY_JUMPBACK, 93 WINDOW_COPY_GOTOLINE, 94}; 95 96/* 97 * Copy-mode's visible screen (the "screen" field) is filled from one of 98 * two sources: the original contents of the pane (used when we 99 * actually enter via the "copy-mode" command, to copy the contents of 100 * the current pane), or else a series of lines containing the output 101 * from an output-writing tmux command (such as any of the "show-*" or 102 * "list-*" commands). 103 * 104 * In either case, the full content of the copy-mode grid is pointed at 105 * by the "backing" field, and is copied into "screen" as needed (that 106 * is, when scrolling occurs). When copy-mode is backed by a pane, 107 * backing points directly at that pane's screen structure (&wp->base); 108 * when backed by a list of output-lines from a command, it points at 109 * a newly-allocated screen structure (which is deallocated when the 110 * mode ends). 111 */ 112struct window_copy_mode_data { 113 struct screen screen; 114 115 struct screen *backing; 116 int backing_written; /* backing display has started */ 117 118 struct mode_key_data mdata; 119 120 u_int oy; 121 122 u_int selx; 123 u_int sely; 124 125 u_int rectflag; /* are we in rectangle copy mode? */ 126 127 u_int cx; 128 u_int cy; 129 130 u_int lastcx; /* position in last line with content */ 131 u_int lastsx; /* size of last line with content */ 132 133 enum window_copy_input_type inputtype; 134 const char *inputprompt; 135 char *inputstr; 136 137 u_int numprefix; 138 139 enum window_copy_input_type searchtype; 140 char *searchstr; 141 142 enum window_copy_input_type jumptype; 143 char jumpchar; 144}; 145 146struct screen * 147window_copy_init(struct window_pane *wp) 148{ 149 struct window_copy_mode_data *data; 150 struct screen *s; 151 int keys; 152 153 wp->modedata = data = xmalloc(sizeof *data); 154 data->oy = 0; 155 data->cx = 0; 156 data->cy = 0; 157 158 data->lastcx = 0; 159 data->lastsx = 0; 160 161 data->backing_written = 0; 162 163 data->rectflag = 0; 164 165 data->inputtype = WINDOW_COPY_OFF; 166 data->inputprompt = NULL; 167 data->inputstr = xstrdup(""); 168 data->numprefix = 0; 169 170 data->searchtype = WINDOW_COPY_OFF; 171 data->searchstr = NULL; 172 173 if (wp->fd != -1) 174 bufferevent_disable(wp->event, EV_READ|EV_WRITE); 175 176 data->jumptype = WINDOW_COPY_OFF; 177 data->jumpchar = '\0'; 178 179 s = &data->screen; 180 screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); 181 if (options_get_number(&wp->window->options, "mode-mouse")) 182 s->mode |= MODE_MOUSE_STANDARD; 183 184 keys = options_get_number(&wp->window->options, "mode-keys"); 185 if (keys == MODEKEY_EMACS) 186 mode_key_init(&data->mdata, &mode_key_tree_emacs_copy); 187 else 188 mode_key_init(&data->mdata, &mode_key_tree_vi_copy); 189 190 data->backing = NULL; 191 192 return (s); 193} 194 195void 196window_copy_init_from_pane(struct window_pane *wp) 197{ 198 struct window_copy_mode_data *data = wp->modedata; 199 struct screen *s = &data->screen; 200 struct screen_write_ctx ctx; 201 u_int i; 202 203 if (wp->mode != &window_copy_mode) 204 fatalx("not in copy mode"); 205 206 data->backing = &wp->base; 207 data->cx = data->backing->cx; 208 data->cy = data->backing->cy; 209 210 s->cx = data->cx; 211 s->cy = data->cy; 212 213 screen_write_start(&ctx, NULL, s); 214 for (i = 0; i < screen_size_y(s); i++) 215 window_copy_write_line(wp, &ctx, i); 216 screen_write_cursormove(&ctx, data->cx, data->cy); 217 screen_write_stop(&ctx); 218} 219 220void 221window_copy_init_for_output(struct window_pane *wp) 222{ 223 struct window_copy_mode_data *data = wp->modedata; 224 225 data->backing = xmalloc(sizeof *data->backing); 226 screen_init(data->backing, screen_size_x(&wp->base), 227 screen_size_y(&wp->base), UINT_MAX); 228 data->backing->mode &= ~MODE_WRAP; 229} 230 231void 232window_copy_free(struct window_pane *wp) 233{ 234 struct window_copy_mode_data *data = wp->modedata; 235 236 if (wp->fd != -1) 237 bufferevent_enable(wp->event, EV_READ|EV_WRITE); 238 239 if (data->searchstr != NULL) 240 xfree(data->searchstr); 241 xfree(data->inputstr); 242 243 if (data->backing != &wp->base) { 244 screen_free(data->backing); 245 xfree(data->backing); 246 } 247 screen_free(&data->screen); 248 249 xfree(data); 250} 251 252void 253window_copy_add(struct window_pane *wp, const char *fmt, ...) 254{ 255 va_list ap; 256 257 va_start(ap, fmt); 258 window_copy_vadd(wp, fmt, ap); 259 va_end(ap); 260} 261 262void 263window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap) 264{ 265 struct window_copy_mode_data *data = wp->modedata; 266 struct screen *backing = data->backing; 267 struct screen_write_ctx back_ctx, ctx; 268 struct grid_cell gc; 269 int utf8flag; 270 u_int old_hsize; 271 272 if (backing == &wp->base) 273 return; 274 275 utf8flag = options_get_number(&wp->window->options, "utf8"); 276 memcpy(&gc, &grid_default_cell, sizeof gc); 277 278 old_hsize = screen_hsize(data->backing); 279 screen_write_start(&back_ctx, NULL, backing); 280 if (data->backing_written) { 281 /* 282 * On the second or later line, do a CRLF before writing 283 * (so it's on a new line). 284 */ 285 screen_write_carriagereturn(&back_ctx); 286 screen_write_linefeed(&back_ctx, 0); 287 } else 288 data->backing_written = 1; 289 screen_write_vnputs(&back_ctx, 0, &gc, utf8flag, fmt, ap); 290 screen_write_stop(&back_ctx); 291 292 data->oy += screen_hsize(data->backing) - old_hsize; 293 294 screen_write_start(&ctx, wp, &data->screen); 295 296 /* 297 * If the history has changed, draw the top line. 298 * (If there's any history at all, it has changed.) 299 */ 300 if (screen_hsize(data->backing)) 301 window_copy_redraw_lines(wp, 0, 1); 302 303 /* Write the line, if it's visible. */ 304 if (backing->cy + data->oy < screen_size_y(backing)) 305 window_copy_redraw_lines(wp, backing->cy, 1); 306 307 screen_write_stop(&ctx); 308} 309 310void 311window_copy_pageup(struct window_pane *wp) 312{ 313 struct window_copy_mode_data *data = wp->modedata; 314 struct screen *s = &data->screen; 315 u_int n; 316 317 n = 1; 318 if (screen_size_y(s) > 2) 319 n = screen_size_y(s) - 2; 320 if (data->oy + n > screen_hsize(data->backing)) 321 data->oy = screen_hsize(data->backing); 322 else 323 data->oy += n; 324 window_copy_update_selection(wp); 325 window_copy_redraw_screen(wp); 326} 327 328void 329window_copy_resize(struct window_pane *wp, u_int sx, u_int sy) 330{ 331 struct window_copy_mode_data *data = wp->modedata; 332 struct screen *s = &data->screen; 333 struct screen_write_ctx ctx; 334 335 screen_resize(s, sx, sy); 336 if (data->backing != &wp->base) 337 screen_resize(data->backing, sx, sy); 338 339 if (data->cy > sy - 1) 340 data->cy = sy - 1; 341 if (data->cx > sx) 342 data->cx = sx; 343 if (data->oy > screen_hsize(data->backing)) 344 data->oy = screen_hsize(data->backing); 345 346 window_copy_clear_selection(wp); 347 348 screen_write_start(&ctx, NULL, s); 349 window_copy_write_lines(wp, &ctx, 0, screen_size_y(s) - 1); 350 screen_write_stop(&ctx); 351 352 window_copy_redraw_screen(wp); 353} 354 355void 356window_copy_key(struct window_pane *wp, struct session *sess, int key) 357{ 358 const char *word_separators; 359 struct window_copy_mode_data *data = wp->modedata; 360 struct screen *s = &data->screen; 361 u_int n, np; 362 int keys; 363 enum mode_key_cmd cmd; 364 365 np = data->numprefix; 366 if (np == 0) 367 np = 1; 368 369 if (data->inputtype == WINDOW_COPY_JUMPFORWARD || 370 data->inputtype == WINDOW_COPY_JUMPBACK) { 371 /* Ignore keys with modifiers. */ 372 if ((key & KEYC_MASK_MOD) == 0) { 373 data->jumpchar = key; 374 if (data->inputtype == WINDOW_COPY_JUMPFORWARD) { 375 for (; np != 0; np--) 376 window_copy_cursor_jump(wp); 377 } else { 378 for (; np != 0; np--) 379 window_copy_cursor_jump_back(wp); 380 } 381 } 382 data->jumptype = data->inputtype; 383 data->inputtype = WINDOW_COPY_OFF; 384 window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); 385 return; 386 } else if (data->inputtype == WINDOW_COPY_NUMERICPREFIX) { 387 if (window_copy_key_numeric_prefix(wp, key) == 0) 388 return; 389 data->inputtype = WINDOW_COPY_OFF; 390 window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); 391 } else if (data->inputtype != WINDOW_COPY_OFF) { 392 if (window_copy_key_input(wp, key) != 0) 393 goto input_off; 394 return; 395 } 396 397 cmd = mode_key_lookup(&data->mdata, key); 398 switch (cmd) { 399 case MODEKEYCOPY_CANCEL: 400 window_pane_reset_mode(wp); 401 return; 402 case MODEKEYCOPY_LEFT: 403 for (; np != 0; np--) 404 window_copy_cursor_left(wp); 405 break; 406 case MODEKEYCOPY_RIGHT: 407 for (; np != 0; np--) 408 window_copy_cursor_right(wp); 409 break; 410 case MODEKEYCOPY_UP: 411 for (; np != 0; np--) 412 window_copy_cursor_up(wp, 0); 413 break; 414 case MODEKEYCOPY_DOWN: 415 for (; np != 0; np--) 416 window_copy_cursor_down(wp, 0); 417 break; 418 case MODEKEYCOPY_SCROLLUP: 419 for (; np != 0; np--) 420 window_copy_cursor_up(wp, 1); 421 break; 422 case MODEKEYCOPY_SCROLLDOWN: 423 for (; np != 0; np--) 424 window_copy_cursor_down(wp, 1); 425 break; 426 case MODEKEYCOPY_PREVIOUSPAGE: 427 for (; np != 0; np--) 428 window_copy_pageup(wp); 429 break; 430 case MODEKEYCOPY_NEXTPAGE: 431 n = 1; 432 if (screen_size_y(s) > 2) 433 n = screen_size_y(s) - 2; 434 for (; np != 0; np--) { 435 if (data->oy < n) 436 data->oy = 0; 437 else 438 data->oy -= n; 439 } 440 window_copy_update_selection(wp); 441 window_copy_redraw_screen(wp); 442 break; 443 case MODEKEYCOPY_HALFPAGEUP: 444 n = screen_size_y(s) / 2; 445 for (; np != 0; np--) { 446 if (data->oy + n > screen_hsize(data->backing)) 447 data->oy = screen_hsize(data->backing); 448 else 449 data->oy += n; 450 } 451 window_copy_update_selection(wp); 452 window_copy_redraw_screen(wp); 453 break; 454 case MODEKEYCOPY_HALFPAGEDOWN: 455 n = screen_size_y(s) / 2; 456 for (; np != 0; np--) { 457 if (data->oy < n) 458 data->oy = 0; 459 else 460 data->oy -= n; 461 } 462 window_copy_update_selection(wp); 463 window_copy_redraw_screen(wp); 464 break; 465 case MODEKEYCOPY_TOPLINE: 466 data->cx = 0; 467 data->cy = 0; 468 window_copy_update_selection(wp); 469 window_copy_redraw_screen(wp); 470 break; 471 case MODEKEYCOPY_MIDDLELINE: 472 data->cx = 0; 473 data->cy = (screen_size_y(s) - 1) / 2; 474 window_copy_update_selection(wp); 475 window_copy_redraw_screen(wp); 476 break; 477 case MODEKEYCOPY_BOTTOMLINE: 478 data->cx = 0; 479 data->cy = screen_size_y(s) - 1; 480 window_copy_update_selection(wp); 481 window_copy_redraw_screen(wp); 482 break; 483 case MODEKEYCOPY_HISTORYTOP: 484 data->cx = 0; 485 data->cy = 0; 486 data->oy = screen_hsize(data->backing); 487 window_copy_update_selection(wp); 488 window_copy_redraw_screen(wp); 489 break; 490 case MODEKEYCOPY_HISTORYBOTTOM: 491 data->cx = 0; 492 data->cy = screen_size_y(s) - 1; 493 data->oy = 0; 494 window_copy_update_selection(wp); 495 window_copy_redraw_screen(wp); 496 break; 497 case MODEKEYCOPY_STARTSELECTION: 498 window_copy_start_selection(wp); 499 window_copy_redraw_screen(wp); 500 break; 501 case MODEKEYCOPY_COPYLINE: 502 case MODEKEYCOPY_SELECTLINE: 503 window_copy_cursor_start_of_line(wp); 504 /* FALLTHROUGH */ 505 case MODEKEYCOPY_COPYENDOFLINE: 506 window_copy_start_selection(wp); 507 for (; np > 1; np--) 508 window_copy_cursor_down(wp, 0); 509 window_copy_cursor_end_of_line(wp); 510 window_copy_redraw_screen(wp); 511 512 /* If a copy command then copy the selection and exit. */ 513 if (sess != NULL && 514 (cmd == MODEKEYCOPY_COPYLINE || 515 cmd == MODEKEYCOPY_COPYENDOFLINE)) { 516 window_copy_copy_selection(wp); 517 window_pane_reset_mode(wp); 518 return; 519 } 520 break; 521 case MODEKEYCOPY_CLEARSELECTION: 522 window_copy_clear_selection(wp); 523 window_copy_redraw_screen(wp); 524 break; 525 case MODEKEYCOPY_COPYSELECTION: 526 if (sess != NULL) { 527 window_copy_copy_selection(wp); 528 window_pane_reset_mode(wp); 529 return; 530 } 531 break; 532 case MODEKEYCOPY_STARTOFLINE: 533 window_copy_cursor_start_of_line(wp); 534 break; 535 case MODEKEYCOPY_BACKTOINDENTATION: 536 window_copy_cursor_back_to_indentation(wp); 537 break; 538 case MODEKEYCOPY_ENDOFLINE: 539 window_copy_cursor_end_of_line(wp); 540 break; 541 case MODEKEYCOPY_NEXTSPACE: 542 for (; np != 0; np--) 543 window_copy_cursor_next_word(wp, " "); 544 break; 545 case MODEKEYCOPY_NEXTSPACEEND: 546 for (; np != 0; np--) 547 window_copy_cursor_next_word_end(wp, " "); 548 break; 549 case MODEKEYCOPY_NEXTWORD: 550 word_separators = 551 options_get_string(&wp->window->options, "word-separators"); 552 for (; np != 0; np--) 553 window_copy_cursor_next_word(wp, word_separators); 554 break; 555 case MODEKEYCOPY_NEXTWORDEND: 556 word_separators = 557 options_get_string(&wp->window->options, "word-separators"); 558 for (; np != 0; np--) 559 window_copy_cursor_next_word_end(wp, word_separators); 560 break; 561 case MODEKEYCOPY_PREVIOUSSPACE: 562 for (; np != 0; np--) 563 window_copy_cursor_previous_word(wp, " "); 564 break; 565 case MODEKEYCOPY_PREVIOUSWORD: 566 word_separators = 567 options_get_string(&wp->window->options, "word-separators"); 568 for (; np != 0; np--) 569 window_copy_cursor_previous_word(wp, word_separators); 570 break; 571 case MODEKEYCOPY_JUMP: 572 data->inputtype = WINDOW_COPY_JUMPFORWARD; 573 data->inputprompt = "Jump Forward"; 574 *data->inputstr = '\0'; 575 window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); 576 return; /* skip numprefix reset */ 577 case MODEKEYCOPY_JUMPAGAIN: 578 if (data->jumptype == WINDOW_COPY_JUMPFORWARD) { 579 for (; np != 0; np--) 580 window_copy_cursor_jump(wp); 581 } else if (data->jumptype == WINDOW_COPY_JUMPBACK) { 582 for (; np != 0; np--) 583 window_copy_cursor_jump_back(wp); 584 } 585 break; 586 case MODEKEYCOPY_JUMPREVERSE: 587 if (data->jumptype == WINDOW_COPY_JUMPFORWARD) { 588 for (; np != 0; np--) 589 window_copy_cursor_jump_back(wp); 590 } else if (data->jumptype == WINDOW_COPY_JUMPBACK) { 591 for (; np != 0; np--) 592 window_copy_cursor_jump(wp); 593 } 594 break; 595 case MODEKEYCOPY_JUMPBACK: 596 data->inputtype = WINDOW_COPY_JUMPBACK; 597 data->inputprompt = "Jump Back"; 598 *data->inputstr = '\0'; 599 window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); 600 return; /* skip numprefix reset */ 601 case MODEKEYCOPY_SEARCHUP: 602 data->inputtype = WINDOW_COPY_SEARCHUP; 603 data->inputprompt = "Search Up"; 604 goto input_on; 605 case MODEKEYCOPY_SEARCHDOWN: 606 data->inputtype = WINDOW_COPY_SEARCHDOWN; 607 data->inputprompt = "Search Down"; 608 goto input_on; 609 case MODEKEYCOPY_SEARCHAGAIN: 610 case MODEKEYCOPY_SEARCHREVERSE: 611 switch (data->searchtype) { 612 case WINDOW_COPY_OFF: 613 case WINDOW_COPY_GOTOLINE: 614 case WINDOW_COPY_JUMPFORWARD: 615 case WINDOW_COPY_JUMPBACK: 616 case WINDOW_COPY_NUMERICPREFIX: 617 break; 618 case WINDOW_COPY_SEARCHUP: 619 if (cmd == MODEKEYCOPY_SEARCHAGAIN) { 620 for (; np != 0; np--) { 621 window_copy_search_up( 622 wp, data->searchstr); 623 } 624 } else { 625 for (; np != 0; np--) { 626 window_copy_search_down( 627 wp, data->searchstr); 628 } 629 } 630 break; 631 case WINDOW_COPY_SEARCHDOWN: 632 if (cmd == MODEKEYCOPY_SEARCHAGAIN) { 633 for (; np != 0; np--) { 634 window_copy_search_down( 635 wp, data->searchstr); 636 } 637 } else { 638 for (; np != 0; np--) { 639 window_copy_search_up( 640 wp, data->searchstr); 641 } 642 } 643 break; 644 } 645 break; 646 case MODEKEYCOPY_GOTOLINE: 647 data->inputtype = WINDOW_COPY_GOTOLINE; 648 data->inputprompt = "Goto Line"; 649 *data->inputstr = '\0'; 650 goto input_on; 651 case MODEKEYCOPY_STARTNUMBERPREFIX: 652 key &= KEYC_MASK_KEY; 653 if (key >= '0' && key <= '9') { 654 data->inputtype = WINDOW_COPY_NUMERICPREFIX; 655 data->numprefix = 0; 656 window_copy_key_numeric_prefix(wp, key); 657 return; 658 } 659 break; 660 case MODEKEYCOPY_RECTANGLETOGGLE: 661 window_copy_rectangle_toggle(wp); 662 break; 663 default: 664 break; 665 } 666 667 data->numprefix = 0; 668 return; 669 670input_on: 671 keys = options_get_number(&wp->window->options, "mode-keys"); 672 if (keys == MODEKEY_EMACS) 673 mode_key_init(&data->mdata, &mode_key_tree_emacs_edit); 674 else 675 mode_key_init(&data->mdata, &mode_key_tree_vi_edit); 676 677 window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); 678 return; 679 680input_off: 681 keys = options_get_number(&wp->window->options, "mode-keys"); 682 if (keys == MODEKEY_EMACS) 683 mode_key_init(&data->mdata, &mode_key_tree_emacs_copy); 684 else 685 mode_key_init(&data->mdata, &mode_key_tree_vi_copy); 686 687 data->inputtype = WINDOW_COPY_OFF; 688 data->inputprompt = NULL; 689 690 window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); 691} 692 693int 694window_copy_key_input(struct window_pane *wp, int key) 695{ 696 struct window_copy_mode_data *data = wp->modedata; 697 struct screen *s = &data->screen; 698 size_t inputlen; 699 u_int np; 700 701 switch (mode_key_lookup(&data->mdata, key)) { 702 case MODEKEYEDIT_CANCEL: 703 data->numprefix = 0; 704 return (-1); 705 case MODEKEYEDIT_BACKSPACE: 706 inputlen = strlen(data->inputstr); 707 if (inputlen > 0) 708 data->inputstr[inputlen - 1] = '\0'; 709 break; 710 case MODEKEYEDIT_DELETELINE: 711 *data->inputstr = '\0'; 712 break; 713 case MODEKEYEDIT_ENTER: 714 np = data->numprefix; 715 if (np == 0) 716 np = 1; 717 718 switch (data->inputtype) { 719 case WINDOW_COPY_OFF: 720 case WINDOW_COPY_JUMPFORWARD: 721 case WINDOW_COPY_JUMPBACK: 722 case WINDOW_COPY_NUMERICPREFIX: 723 break; 724 case WINDOW_COPY_SEARCHUP: 725 for (; np != 0; np--) 726 window_copy_search_up(wp, data->inputstr); 727 data->searchtype = data->inputtype; 728 data->searchstr = xstrdup(data->inputstr); 729 break; 730 case WINDOW_COPY_SEARCHDOWN: 731 for (; np != 0; np--) 732 window_copy_search_down(wp, data->inputstr); 733 data->searchtype = data->inputtype; 734 data->searchstr = xstrdup(data->inputstr); 735 break; 736 case WINDOW_COPY_GOTOLINE: 737 window_copy_goto_line(wp, data->inputstr); 738 *data->inputstr = '\0'; 739 break; 740 } 741 data->numprefix = 0; 742 return (1); 743 case MODEKEY_OTHER: 744 if (key < 32 || key > 126) 745 break; 746 inputlen = strlen(data->inputstr) + 2; 747 748 data->inputstr = xrealloc(data->inputstr, 1, inputlen); 749 data->inputstr[inputlen - 2] = key; 750 data->inputstr[inputlen - 1] = '\0'; 751 break; 752 default: 753 break; 754 } 755 756 window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); 757 return (0); 758} 759 760int 761window_copy_key_numeric_prefix(struct window_pane *wp, int key) 762{ 763 struct window_copy_mode_data *data = wp->modedata; 764 struct screen *s = &data->screen; 765 766 key &= KEYC_MASK_KEY; 767 if (key < '0' || key > '9') 768 return 1; 769 770 if (data->numprefix >= 100) /* no more than three digits */ 771 return 0; 772 data->numprefix = data->numprefix * 10 + key - '0'; 773 774 window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); 775 return 0; 776} 777 778/* ARGSUSED */ 779void 780window_copy_mouse( 781 struct window_pane *wp, struct session *sess, struct mouse_event *m) 782{ 783 struct window_copy_mode_data *data = wp->modedata; 784 struct screen *s = &data->screen; 785 u_int i, old_cy; 786 787 if (m->x >= screen_size_x(s)) 788 return; 789 if (m->y >= screen_size_y(s)) 790 return; 791 792 /* If mouse wheel (buttons 4 and 5), scroll. */ 793 if ((m->b & MOUSE_45)) { 794 if ((m->b & MOUSE_BUTTON) == MOUSE_1) { 795 for (i = 0; i < 5; i++) 796 window_copy_cursor_up(wp, 0); 797 } else if ((m->b & MOUSE_BUTTON) == MOUSE_2) { 798 old_cy = data->cy; 799 for (i = 0; i < 5; i++) 800 window_copy_cursor_down(wp, 0); 801 if (old_cy == data->cy) 802 goto reset_mode; 803 } 804 return; 805 } 806 807 /* 808 * If already reading motion, move the cursor while buttons are still 809 * pressed, or stop the selection on their release. 810 */ 811 if (s->mode & MODE_MOUSE_BUTTON) { 812 if ((m->b & MOUSE_BUTTON) != MOUSE_UP) { 813 window_copy_update_cursor(wp, m->x, m->y); 814 if (window_copy_update_selection(wp)) 815 window_copy_redraw_screen(wp); 816 return; 817 } 818 goto reset_mode; 819 } 820 821 /* Otherwise if other buttons pressed, start selection and motion. */ 822 if ((m->b & MOUSE_BUTTON) != MOUSE_UP) { 823 s->mode &= ~MODE_MOUSE_STANDARD; 824 s->mode |= MODE_MOUSE_BUTTON; 825 826 window_copy_update_cursor(wp, m->x, m->y); 827 window_copy_start_selection(wp); 828 window_copy_redraw_screen(wp); 829 } 830 831 return; 832 833reset_mode: 834 s->mode &= ~MODE_MOUSE_BUTTON; 835 s->mode |= MODE_MOUSE_STANDARD; 836 if (sess != NULL) { 837 window_copy_copy_selection(wp); 838 window_pane_reset_mode(wp); 839 } 840} 841 842void 843window_copy_scroll_to(struct window_pane *wp, u_int px, u_int py) 844{ 845 struct window_copy_mode_data *data = wp->modedata; 846 struct grid *gd = data->backing->grid; 847 u_int offset, gap; 848 849 data->cx = px; 850 851 gap = gd->sy / 4; 852 if (py < gd->sy) { 853 offset = 0; 854 data->cy = py; 855 } else if (py > gd->hsize + gd->sy - gap) { 856 offset = gd->hsize; 857 data->cy = py - gd->hsize; 858 } else { 859 offset = py + gap - gd->sy; 860 data->cy = py - offset; 861 } 862 data->oy = gd->hsize - offset; 863 864 window_copy_update_selection(wp); 865 window_copy_redraw_screen(wp); 866} 867 868int 869window_copy_search_compare( 870 struct grid *gd, u_int px, u_int py, struct grid *sgd, u_int spx) 871{ 872 const struct grid_cell *gc, *sgc; 873 const struct grid_utf8 *gu, *sgu; 874 875 gc = grid_peek_cell(gd, px, py); 876 sgc = grid_peek_cell(sgd, spx, 0); 877 878 if ((gc->flags & GRID_FLAG_UTF8) != (sgc->flags & GRID_FLAG_UTF8)) 879 return (0); 880 881 if (gc->flags & GRID_FLAG_UTF8) { 882 gu = grid_peek_utf8(gd, px, py); 883 sgu = grid_peek_utf8(sgd, spx, 0); 884 if (grid_utf8_compare(gu, sgu)) 885 return (1); 886 } else { 887 if (gc->data == sgc->data) 888 return (1); 889 } 890 return (0); 891} 892 893int 894window_copy_search_lr(struct grid *gd, 895 struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last) 896{ 897 u_int ax, bx, px; 898 899 for (ax = first; ax < last; ax++) { 900 if (ax + sgd->sx >= gd->sx) 901 break; 902 for (bx = 0; bx < sgd->sx; bx++) { 903 px = ax + bx; 904 if (!window_copy_search_compare(gd, px, py, sgd, bx)) 905 break; 906 } 907 if (bx == sgd->sx) { 908 *ppx = ax; 909 return (1); 910 } 911 } 912 return (0); 913} 914 915int 916window_copy_search_rl(struct grid *gd, 917 struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last) 918{ 919 u_int ax, bx, px; 920 921 for (ax = last + 1; ax > first; ax--) { 922 if (gd->sx - (ax - 1) < sgd->sx) 923 continue; 924 for (bx = 0; bx < sgd->sx; bx++) { 925 px = ax - 1 + bx; 926 if (!window_copy_search_compare(gd, px, py, sgd, bx)) 927 break; 928 } 929 if (bx == sgd->sx) { 930 *ppx = ax - 1; 931 return (1); 932 } 933 } 934 return (0); 935} 936 937void 938window_copy_search_up(struct window_pane *wp, const char *searchstr) 939{ 940 struct window_copy_mode_data *data = wp->modedata; 941 struct screen *s = data->backing, ss; 942 struct screen_write_ctx ctx; 943 struct grid *gd = s->grid, *sgd; 944 struct grid_cell gc; 945 size_t searchlen; 946 u_int i, last, fx, fy, px; 947 int utf8flag, n, wrapped; 948 949 if (*searchstr == '\0') 950 return; 951 utf8flag = options_get_number(&wp->window->options, "utf8"); 952 searchlen = screen_write_strlen(utf8flag, "%s", searchstr); 953 954 screen_init(&ss, searchlen, 1, 0); 955 screen_write_start(&ctx, NULL, &ss); 956 memcpy(&gc, &grid_default_cell, sizeof gc); 957 screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr); 958 screen_write_stop(&ctx); 959 960 fx = data->cx; 961 fy = gd->hsize - data->oy + data->cy; 962 963 if (fx == 0) { 964 if (fy == 0) 965 return; 966 fx = gd->sx - 1; 967 fy--; 968 } else 969 fx--; 970 n = wrapped = 0; 971 972retry: 973 sgd = ss.grid; 974 for (i = fy + 1; i > 0; i--) { 975 last = screen_size_x(s); 976 if (i == fy + 1) 977 last = fx; 978 n = window_copy_search_rl(gd, sgd, &px, i - 1, 0, last); 979 if (n) { 980 window_copy_scroll_to(wp, px, i - 1); 981 break; 982 } 983 } 984 if (!n && !wrapped) { 985 fx = gd->sx - 1; 986 fy = gd->hsize + gd->sy - 1; 987 wrapped = 1; 988 goto retry; 989 } 990 991 screen_free(&ss); 992} 993 994void 995window_copy_search_down(struct window_pane *wp, const char *searchstr) 996{ 997 struct window_copy_mode_data *data = wp->modedata; 998 struct screen *s = data->backing, ss; 999 struct screen_write_ctx ctx; 1000 struct grid *gd = s->grid, *sgd; 1001 struct grid_cell gc; 1002 size_t searchlen; 1003 u_int i, first, fx, fy, px; 1004 int utf8flag, n, wrapped; 1005 1006 if (*searchstr == '\0') 1007 return; 1008 utf8flag = options_get_number(&wp->window->options, "utf8"); 1009 searchlen = screen_write_strlen(utf8flag, "%s", searchstr); 1010 1011 screen_init(&ss, searchlen, 1, 0); 1012 screen_write_start(&ctx, NULL, &ss); 1013 memcpy(&gc, &grid_default_cell, sizeof gc); 1014 screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr); 1015 screen_write_stop(&ctx); 1016 1017 fx = data->cx; 1018 fy = gd->hsize - data->oy + data->cy; 1019 1020 if (fx == gd->sx - 1) { 1021 if (fy == gd->hsize + gd->sy) 1022 return; 1023 fx = 0; 1024 fy++; 1025 } else 1026 fx++; 1027 n = wrapped = 0; 1028 1029retry: 1030 sgd = ss.grid; 1031 for (i = fy + 1; i < gd->hsize + gd->sy; i++) { 1032 first = 0; 1033 if (i == fy + 1) 1034 first = fx; 1035 n = window_copy_search_lr(gd, sgd, &px, i - 1, first, gd->sx); 1036 if (n) { 1037 window_copy_scroll_to(wp, px, i - 1); 1038 break; 1039 } 1040 } 1041 if (!n && !wrapped) { 1042 fx = 0; 1043 fy = 0; 1044 wrapped = 1; 1045 goto retry; 1046 } 1047 1048 screen_free(&ss); 1049} 1050 1051void 1052window_copy_goto_line(struct window_pane *wp, const char *linestr) 1053{ 1054 struct window_copy_mode_data *data = wp->modedata; 1055 const char *errstr; 1056 u_int lineno; 1057 1058 lineno = strtonum(linestr, 0, screen_hsize(data->backing), &errstr); 1059 if (errstr != NULL) 1060 return; 1061 1062 data->oy = lineno; 1063 window_copy_update_selection(wp); 1064 window_copy_redraw_screen(wp); 1065} 1066 1067void 1068window_copy_write_line( 1069 struct window_pane *wp, struct screen_write_ctx *ctx, u_int py) 1070{ 1071 struct window_copy_mode_data *data = wp->modedata; 1072 struct screen *s = &data->screen; 1073 struct options *oo = &wp->window->options; 1074 struct grid_cell gc; 1075 char hdr[32]; 1076 size_t last, xoff = 0, size = 0; 1077 1078 memcpy(&gc, &grid_default_cell, sizeof gc); 1079 colour_set_fg(&gc, options_get_number(oo, "mode-fg")); 1080 colour_set_bg(&gc, options_get_number(oo, "mode-bg")); 1081 gc.attr |= options_get_number(oo, "mode-attr"); 1082 1083 last = screen_size_y(s) - 1; 1084 if (py == 0) { 1085 size = xsnprintf(hdr, sizeof hdr, 1086 "[%u/%u]", data->oy, screen_hsize(data->backing)); 1087 if (size > screen_size_x(s)) 1088 size = screen_size_x(s); 1089 screen_write_cursormove(ctx, screen_size_x(s) - size, 0); 1090 screen_write_puts(ctx, &gc, "%s", hdr); 1091 } else if (py == last && data->inputtype != WINDOW_COPY_OFF) { 1092 if (data->inputtype == WINDOW_COPY_NUMERICPREFIX) { 1093 xoff = size = xsnprintf(hdr, sizeof hdr, 1094 "Repeat: %u", data->numprefix); 1095 } else { 1096 xoff = size = xsnprintf(hdr, sizeof hdr, 1097 "%s: %s", data->inputprompt, data->inputstr); 1098 } 1099 screen_write_cursormove(ctx, 0, last); 1100 screen_write_puts(ctx, &gc, "%s", hdr); 1101 } else 1102 size = 0; 1103 1104 screen_write_cursormove(ctx, xoff, py); 1105 screen_write_copy(ctx, data->backing, xoff, 1106 (screen_hsize(data->backing) - data->oy) + py, 1107 screen_size_x(s) - size, 1); 1108 1109 if (py == data->cy && data->cx == screen_size_x(s)) { 1110 memcpy(&gc, &grid_default_cell, sizeof gc); 1111 screen_write_cursormove(ctx, screen_size_x(s) - 1, py); 1112 screen_write_putc(ctx, &gc, '$'); 1113 } 1114} 1115 1116void 1117window_copy_write_lines( 1118 struct window_pane *wp, struct screen_write_ctx *ctx, u_int py, u_int ny) 1119{ 1120 u_int yy; 1121 1122 for (yy = py; yy < py + ny; yy++) 1123 window_copy_write_line(wp, ctx, py); 1124} 1125 1126void 1127window_copy_redraw_lines(struct window_pane *wp, u_int py, u_int ny) 1128{ 1129 struct window_copy_mode_data *data = wp->modedata; 1130 struct screen_write_ctx ctx; 1131 u_int i; 1132 1133 screen_write_start(&ctx, wp, NULL); 1134 for (i = py; i < py + ny; i++) 1135 window_copy_write_line(wp, &ctx, i); 1136 screen_write_cursormove(&ctx, data->cx, data->cy); 1137 screen_write_stop(&ctx); 1138} 1139 1140void 1141window_copy_redraw_screen(struct window_pane *wp) 1142{ 1143 struct window_copy_mode_data *data = wp->modedata; 1144 1145 window_copy_redraw_lines(wp, 0, screen_size_y(&data->screen)); 1146} 1147 1148void 1149window_copy_update_cursor(struct window_pane *wp, u_int cx, u_int cy) 1150{ 1151 struct window_copy_mode_data *data = wp->modedata; 1152 struct screen *s = &data->screen; 1153 struct screen_write_ctx ctx; 1154 u_int old_cx, old_cy; 1155 1156 old_cx = data->cx; old_cy = data->cy; 1157 data->cx = cx; data->cy = cy; 1158 if (old_cx == screen_size_x(s)) 1159 window_copy_redraw_lines(wp, old_cy, 1); 1160 if (data->cx == screen_size_x(s)) 1161 window_copy_redraw_lines(wp, data->cy, 1); 1162 else { 1163 screen_write_start(&ctx, wp, NULL); 1164 screen_write_cursormove(&ctx, data->cx, data->cy); 1165 screen_write_stop(&ctx); 1166 } 1167} 1168 1169void 1170window_copy_start_selection(struct window_pane *wp) 1171{ 1172 struct window_copy_mode_data *data = wp->modedata; 1173 struct screen *s = &data->screen; 1174 1175 data->selx = data->cx; 1176 data->sely = screen_hsize(data->backing) + data->cy - data->oy; 1177 1178 s->sel.flag = 1; 1179 window_copy_update_selection(wp); 1180} 1181 1182int 1183window_copy_update_selection(struct window_pane *wp) 1184{ 1185 struct window_copy_mode_data *data = wp->modedata; 1186 struct screen *s = &data->screen; 1187 struct options *oo = &wp->window->options; 1188 struct grid_cell gc; 1189 u_int sx, sy, ty, cy; 1190 1191 if (!s->sel.flag) 1192 return (0); 1193 1194 /* Set colours. */ 1195 memcpy(&gc, &grid_default_cell, sizeof gc); 1196 colour_set_fg(&gc, options_get_number(oo, "mode-fg")); 1197 colour_set_bg(&gc, options_get_number(oo, "mode-bg")); 1198 gc.attr |= options_get_number(oo, "mode-attr"); 1199 1200 /* Find top of screen. */ 1201 ty = screen_hsize(data->backing) - data->oy; 1202 1203 /* Adjust the selection. */ 1204 sx = data->selx; 1205 sy = data->sely; 1206 if (sy < ty) { /* above screen */ 1207 if (!data->rectflag) 1208 sx = 0; 1209 sy = 0; 1210 } else if (sy > ty + screen_size_y(s) - 1) { /* below screen */ 1211 if (!data->rectflag) 1212 sx = screen_size_x(s) - 1; 1213 sy = screen_size_y(s) - 1; 1214 } else 1215 sy -= ty; 1216 sy = screen_hsize(s) + sy; 1217 1218 screen_set_selection(s, 1219 sx, sy, data->cx, screen_hsize(s) + data->cy, data->rectflag, &gc); 1220 1221 if (data->rectflag) { 1222 /* 1223 * Can't rely on the caller to redraw the right lines for 1224 * rectangle selection - find the highest line and the number 1225 * of lines, and redraw just past that in both directions 1226 */ 1227 cy = data->cy; 1228 if (sy < cy) 1229 window_copy_redraw_lines(wp, sy, cy - sy + 1); 1230 else 1231 window_copy_redraw_lines(wp, cy, sy - cy + 1); 1232 } 1233 1234 return (1); 1235} 1236 1237void 1238window_copy_copy_selection(struct window_pane *wp) 1239{ 1240 struct window_copy_mode_data *data = wp->modedata; 1241 struct screen *s = &data->screen; 1242 char *buf; 1243 size_t off; 1244 u_int i, xx, yy, sx, sy, ex, ey, limit; 1245 u_int firstsx, lastex, restex, restsx; 1246 int keys; 1247 1248 if (!s->sel.flag) 1249 return; 1250 1251 buf = xmalloc(1); 1252 off = 0; 1253 1254 *buf = '\0'; 1255 1256 /* 1257 * The selection extends from selx,sely to (adjusted) cx,cy on 1258 * the base screen. 1259 */ 1260 1261 /* Find start and end. */ 1262 xx = data->cx; 1263 yy = screen_hsize(data->backing) + data->cy - data->oy; 1264 if (yy < data->sely || (yy == data->sely && xx < data->selx)) { 1265 sx = xx; sy = yy; 1266 ex = data->selx; ey = data->sely; 1267 } else { 1268 sx = data->selx; sy = data->sely; 1269 ex = xx; ey = yy; 1270 } 1271 1272 /* Trim ex to end of line. */ 1273 xx = window_copy_find_length(wp, ey); 1274 if (ex > xx) 1275 ex = xx; 1276 1277 /* 1278 * Deal with rectangle-copy if necessary; four situations: start of 1279 * first line (firstsx), end of last line (lastex), start (restsx) and 1280 * end (restex) of all other lines. 1281 */ 1282 xx = screen_size_x(s); 1283 1284 /* 1285 * Behave according to mode-keys. If it is emacs, copy like emacs, 1286 * keeping the top-left-most character, and dropping the 1287 * bottom-right-most, regardless of copy direction. If it is vi, also 1288 * keep bottom-right-most character. 1289 */ 1290 keys = options_get_number(&wp->window->options, "mode-keys"); 1291 if (data->rectflag) { 1292 /* 1293 * Need to ignore the column with the cursor in it, which for 1294 * rectangular copy means knowing which side the cursor is on. 1295 */ 1296 if (data->selx < data->cx) { 1297 /* Selection start is on the left. */ 1298 if (keys == MODEKEY_EMACS) { 1299 lastex = data->cx; 1300 restex = data->cx; 1301 } 1302 else { 1303 lastex = data->cx + 1; 1304 restex = data->cx + 1; 1305 } 1306 firstsx = data->selx; 1307 restsx = data->selx; 1308 } else { 1309 /* Cursor is on the left. */ 1310 lastex = data->selx + 1; 1311 restex = data->selx + 1; 1312 firstsx = data->cx; 1313 restsx = data->cx; 1314 } 1315 } else { 1316 if (keys == MODEKEY_EMACS) 1317 lastex = ex; 1318 else 1319 lastex = ex + 1; 1320 restex = xx; 1321 firstsx = sx; 1322 restsx = 0; 1323 } 1324 1325 /* Copy the lines. */ 1326 if (sy == ey) 1327 window_copy_copy_line(wp, &buf, &off, sy, firstsx, lastex); 1328 else { 1329 window_copy_copy_line(wp, &buf, &off, sy, firstsx, restex); 1330 if (ey - sy > 1) { 1331 for (i = sy + 1; i < ey; i++) { 1332 window_copy_copy_line( 1333 wp, &buf, &off, i, restsx, restex); 1334 } 1335 } 1336 window_copy_copy_line(wp, &buf, &off, ey, restsx, lastex); 1337 } 1338 1339 /* Don't bother if no data. */ 1340 if (off == 0) { 1341 xfree(buf); 1342 return; 1343 } 1344 off--; /* remove final \n */ 1345 1346 if (options_get_number(&global_options, "set-clipboard")) 1347 screen_write_setselection(&wp->ictx.ctx, (u_char *)buf, off); 1348 1349 /* Add the buffer to the stack. */ 1350 limit = options_get_number(&global_options, "buffer-limit"); 1351 paste_add(&global_buffers, buf, off, limit); 1352} 1353 1354void 1355window_copy_copy_line(struct window_pane *wp, 1356 char **buf, size_t *off, u_int sy, u_int sx, u_int ex) 1357{ 1358 struct window_copy_mode_data *data = wp->modedata; 1359 struct grid *gd = data->backing->grid; 1360 const struct grid_cell *gc; 1361 const struct grid_utf8 *gu; 1362 struct grid_line *gl; 1363 u_int i, xx, wrapped = 0; 1364 size_t size; 1365 1366 if (sx > ex) 1367 return; 1368 1369 /* 1370 * Work out if the line was wrapped at the screen edge and all of it is 1371 * on screen. 1372 */ 1373 gl = &gd->linedata[sy]; 1374 if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx) 1375 wrapped = 1; 1376 1377 /* If the line was wrapped, don't strip spaces (use the full length). */ 1378 if (wrapped) 1379 xx = gl->cellsize; 1380 else 1381 xx = window_copy_find_length(wp, sy); 1382 if (ex > xx) 1383 ex = xx; 1384 if (sx > xx) 1385 sx = xx; 1386 1387 if (sx < ex) { 1388 for (i = sx; i < ex; i++) { 1389 gc = grid_peek_cell(gd, i, sy); 1390 if (gc->flags & GRID_FLAG_PADDING) 1391 continue; 1392 if (!(gc->flags & GRID_FLAG_UTF8)) { 1393 *buf = xrealloc(*buf, 1, (*off) + 1); 1394 (*buf)[(*off)++] = gc->data; 1395 } else { 1396 gu = grid_peek_utf8(gd, i, sy); 1397 size = grid_utf8_size(gu); 1398 *buf = xrealloc(*buf, 1, (*off) + size); 1399 *off += grid_utf8_copy(gu, *buf + *off, size); 1400 } 1401 } 1402 } 1403 1404 /* Only add a newline if the line wasn't wrapped. */ 1405 if (!wrapped || ex != xx) { 1406 *buf = xrealloc(*buf, 1, (*off) + 1); 1407 (*buf)[(*off)++] = '\n'; 1408 } 1409} 1410 1411void 1412window_copy_clear_selection(struct window_pane *wp) 1413{ 1414 struct window_copy_mode_data *data = wp->modedata; 1415 u_int px, py; 1416 1417 screen_clear_selection(&data->screen); 1418 1419 py = screen_hsize(data->backing) + data->cy - data->oy; 1420 px = window_copy_find_length(wp, py); 1421 if (data->cx > px) 1422 window_copy_update_cursor(wp, px, data->cy); 1423} 1424 1425int 1426window_copy_in_set(struct window_pane *wp, u_int px, u_int py, const char *set) 1427{ 1428 struct window_copy_mode_data *data = wp->modedata; 1429 const struct grid_cell *gc; 1430 1431 gc = grid_peek_cell(data->backing->grid, px, py); 1432 if (gc->flags & (GRID_FLAG_PADDING|GRID_FLAG_UTF8)) 1433 return (0); 1434 if (gc->data == 0x00 || gc->data == 0x7f) 1435 return (0); 1436 return (strchr(set, gc->data) != NULL); 1437} 1438 1439u_int 1440window_copy_find_length(struct window_pane *wp, u_int py) 1441{ 1442 struct window_copy_mode_data *data = wp->modedata; 1443 struct screen *s = data->backing; 1444 const struct grid_cell *gc; 1445 u_int px; 1446 1447 /* 1448 * If the pane has been resized, its grid can contain old overlong 1449 * lines. grid_peek_cell does not allow accessing cells beyond the 1450 * width of the grid, and screen_write_copy treats them as spaces, so 1451 * ignore them here too. 1452 */ 1453 px = s->grid->linedata[py].cellsize; 1454 if (px > screen_size_x(s)) 1455 px = screen_size_x(s); 1456 while (px > 0) { 1457 gc = grid_peek_cell(s->grid, px - 1, py); 1458 if (gc->flags & GRID_FLAG_UTF8) 1459 break; 1460 if (gc->data != ' ') 1461 break; 1462 px--; 1463 } 1464 return (px); 1465} 1466 1467void 1468window_copy_cursor_start_of_line(struct window_pane *wp) 1469{ 1470 struct window_copy_mode_data *data = wp->modedata; 1471 struct screen *back_s = data->backing; 1472 struct grid *gd = back_s->grid; 1473 u_int py; 1474 1475 if (data->cx == 0) { 1476 py = screen_hsize(back_s) + data->cy - data->oy; 1477 while (py > 0 && gd->linedata[py-1].flags & GRID_LINE_WRAPPED) { 1478 window_copy_cursor_up(wp, 0); 1479 py = screen_hsize(back_s) + data->cy - data->oy; 1480 } 1481 } 1482 window_copy_update_cursor(wp, 0, data->cy); 1483 if (window_copy_update_selection(wp)) 1484 window_copy_redraw_lines(wp, data->cy, 1); 1485} 1486 1487void 1488window_copy_cursor_back_to_indentation(struct window_pane *wp) 1489{ 1490 struct window_copy_mode_data *data = wp->modedata; 1491 u_int px, py, xx; 1492 const struct grid_cell *gc; 1493 1494 px = 0; 1495 py = screen_hsize(data->backing) + data->cy - data->oy; 1496 xx = window_copy_find_length(wp, py); 1497 1498 while (px < xx) { 1499 gc = grid_peek_cell(data->backing->grid, px, py); 1500 if (gc->flags & GRID_FLAG_UTF8) 1501 break; 1502 if (gc->data != ' ') 1503 break; 1504 px++; 1505 } 1506 1507 window_copy_update_cursor(wp, px, data->cy); 1508 if (window_copy_update_selection(wp)) 1509 window_copy_redraw_lines(wp, data->cy, 1); 1510} 1511 1512void 1513window_copy_cursor_end_of_line(struct window_pane *wp) 1514{ 1515 struct window_copy_mode_data *data = wp->modedata; 1516 struct screen *back_s = data->backing; 1517 struct grid *gd = back_s->grid; 1518 u_int px, py; 1519 1520 py = screen_hsize(back_s) + data->cy - data->oy; 1521 px = window_copy_find_length(wp, py); 1522 1523 if (data->cx == px) { 1524 if (data->screen.sel.flag && data->rectflag) 1525 px = screen_size_x(back_s); 1526 if (gd->linedata[py].flags & GRID_LINE_WRAPPED) { 1527 while (py < gd->sy + gd->hsize && 1528 gd->linedata[py].flags & GRID_LINE_WRAPPED) { 1529 window_copy_cursor_down(wp, 0); 1530 py = screen_hsize(back_s) 1531 + data->cy - data->oy; 1532 } 1533 px = window_copy_find_length(wp, py); 1534 } 1535 } 1536 window_copy_update_cursor(wp, px, data->cy); 1537 1538 if (window_copy_update_selection(wp)) 1539 window_copy_redraw_lines(wp, data->cy, 1); 1540} 1541 1542void 1543window_copy_cursor_left(struct window_pane *wp) 1544{ 1545 struct window_copy_mode_data *data = wp->modedata; 1546 1547 if (data->cx == 0) { 1548 window_copy_cursor_up(wp, 0); 1549 window_copy_cursor_end_of_line(wp); 1550 } else { 1551 window_copy_update_cursor(wp, data->cx - 1, data->cy); 1552 if (window_copy_update_selection(wp)) 1553 window_copy_redraw_lines(wp, data->cy, 1); 1554 } 1555} 1556 1557void 1558window_copy_cursor_right(struct window_pane *wp) 1559{ 1560 struct window_copy_mode_data *data = wp->modedata; 1561 u_int px, py; 1562 1563 if (data->screen.sel.flag && data->rectflag) 1564 px = screen_size_x(&data->screen); 1565 else { 1566 py = screen_hsize(data->backing) + data->cy - data->oy; 1567 px = window_copy_find_length(wp, py); 1568 } 1569 1570 if (data->cx >= px) { 1571 window_copy_cursor_start_of_line(wp); 1572 window_copy_cursor_down(wp, 0); 1573 } else { 1574 window_copy_update_cursor(wp, data->cx + 1, data->cy); 1575 if (window_copy_update_selection(wp)) 1576 window_copy_redraw_lines(wp, data->cy, 1); 1577 } 1578} 1579 1580void 1581window_copy_cursor_up(struct window_pane *wp, int scroll_only) 1582{ 1583 struct window_copy_mode_data *data = wp->modedata; 1584 struct screen *s = &data->screen; 1585 u_int ox, oy, px, py; 1586 1587 oy = screen_hsize(data->backing) + data->cy - data->oy; 1588 ox = window_copy_find_length(wp, oy); 1589 if (ox != 0) { 1590 data->lastcx = data->cx; 1591 data->lastsx = ox; 1592 } 1593 1594 data->cx = data->lastcx; 1595 if (scroll_only || data->cy == 0) { 1596 window_copy_scroll_down(wp, 1); 1597 if (scroll_only) { 1598 if (data->cy == screen_size_y(s) - 1) 1599 window_copy_redraw_lines(wp, data->cy, 1); 1600 else 1601 window_copy_redraw_lines(wp, data->cy, 2); 1602 } 1603 } else { 1604 window_copy_update_cursor(wp, data->cx, data->cy - 1); 1605 if (window_copy_update_selection(wp)) { 1606 if (data->cy == screen_size_y(s) - 1) 1607 window_copy_redraw_lines(wp, data->cy, 1); 1608 else 1609 window_copy_redraw_lines(wp, data->cy, 2); 1610 } 1611 } 1612 1613 if (!data->screen.sel.flag || !data->rectflag) { 1614 py = screen_hsize(data->backing) + data->cy - data->oy; 1615 px = window_copy_find_length(wp, py); 1616 if ((data->cx >= data->lastsx && data->cx != px) || 1617 data->cx > px) 1618 window_copy_cursor_end_of_line(wp); 1619 } 1620} 1621 1622void 1623window_copy_cursor_down(struct window_pane *wp, int scroll_only) 1624{ 1625 struct window_copy_mode_data *data = wp->modedata; 1626 struct screen *s = &data->screen; 1627 u_int ox, oy, px, py; 1628 1629 oy = screen_hsize(data->backing) + data->cy - data->oy; 1630 ox = window_copy_find_length(wp, oy); 1631 if (ox != 0) { 1632 data->lastcx = data->cx; 1633 data->lastsx = ox; 1634 } 1635 1636 data->cx = data->lastcx; 1637 if (scroll_only || data->cy == screen_size_y(s) - 1) { 1638 window_copy_scroll_up(wp, 1); 1639 if (scroll_only && data->cy > 0) 1640 window_copy_redraw_lines(wp, data->cy - 1, 2); 1641 } else { 1642 window_copy_update_cursor(wp, data->cx, data->cy + 1); 1643 if (window_copy_update_selection(wp)) 1644 window_copy_redraw_lines(wp, data->cy - 1, 2); 1645 } 1646 1647 if (!data->screen.sel.flag || !data->rectflag) { 1648 py = screen_hsize(data->backing) + data->cy - data->oy; 1649 px = window_copy_find_length(wp, py); 1650 if ((data->cx >= data->lastsx && data->cx != px) || 1651 data->cx > px) 1652 window_copy_cursor_end_of_line(wp); 1653 } 1654} 1655 1656void 1657window_copy_cursor_jump(struct window_pane *wp) 1658{ 1659 struct window_copy_mode_data *data = wp->modedata; 1660 struct screen *back_s = data->backing; 1661 const struct grid_cell *gc; 1662 u_int px, py, xx; 1663 1664 px = data->cx + 1; 1665 py = screen_hsize(back_s) + data->cy - data->oy; 1666 xx = window_copy_find_length(wp, py); 1667 1668 while (px < xx) { 1669 gc = grid_peek_cell(back_s->grid, px, py); 1670 if ((gc->flags & (GRID_FLAG_PADDING|GRID_FLAG_UTF8)) == 0 1671 && gc->data == data->jumpchar) { 1672 1673 window_copy_update_cursor(wp, px, data->cy); 1674 if (window_copy_update_selection(wp)) 1675 window_copy_redraw_lines(wp, data->cy, 1); 1676 return; 1677 } 1678 px++; 1679 } 1680} 1681 1682void 1683window_copy_cursor_jump_back(struct window_pane *wp) 1684{ 1685 struct window_copy_mode_data *data = wp->modedata; 1686 struct screen *back_s = data->backing; 1687 const struct grid_cell *gc; 1688 u_int px, py; 1689 1690 px = data->cx; 1691 py = screen_hsize(back_s) + data->cy - data->oy; 1692 1693 if (px > 0) 1694 px--; 1695 1696 for (;;) { 1697 gc = grid_peek_cell(back_s->grid, px, py); 1698 if ((gc->flags & (GRID_FLAG_PADDING|GRID_FLAG_UTF8)) == 0 1699 && gc->data == data->jumpchar) { 1700 1701 window_copy_update_cursor(wp, px, data->cy); 1702 if (window_copy_update_selection(wp)) 1703 window_copy_redraw_lines(wp, data->cy, 1); 1704 return; 1705 } 1706 if (px == 0) 1707 break; 1708 px--; 1709 } 1710} 1711 1712void 1713window_copy_cursor_next_word(struct window_pane *wp, const char *separators) 1714{ 1715 struct window_copy_mode_data *data = wp->modedata; 1716 struct screen *back_s = data->backing; 1717 u_int px, py, xx, yy; 1718 int expected = 0; 1719 1720 px = data->cx; 1721 py = screen_hsize(back_s) + data->cy - data->oy; 1722 xx = window_copy_find_length(wp, py); 1723 yy = screen_hsize(back_s) + screen_size_y(back_s) - 1; 1724 1725 /* 1726 * First skip past any nonword characters and then any word characters. 1727 * 1728 * expected is initially set to 0 for the former and then 1 for the 1729 * latter. 1730 */ 1731 do { 1732 while (px > xx || 1733 window_copy_in_set(wp, px, py, separators) == expected) { 1734 /* Move down if we're past the end of the line. */ 1735 if (px > xx) { 1736 if (py == yy) 1737 return; 1738 window_copy_cursor_down(wp, 0); 1739 px = 0; 1740 1741 py = screen_hsize(back_s) + data->cy - data->oy; 1742 xx = window_copy_find_length(wp, py); 1743 } else 1744 px++; 1745 } 1746 expected = !expected; 1747 } while (expected == 1); 1748 1749 window_copy_update_cursor(wp, px, data->cy); 1750 if (window_copy_update_selection(wp)) 1751 window_copy_redraw_lines(wp, data->cy, 1); 1752} 1753 1754void 1755window_copy_cursor_next_word_end(struct window_pane *wp, const char *separators) 1756{ 1757 struct window_copy_mode_data *data = wp->modedata; 1758 struct screen *back_s = data->backing; 1759 u_int px, py, xx, yy; 1760 int expected = 1; 1761 1762 px = data->cx; 1763 py = screen_hsize(back_s) + data->cy - data->oy; 1764 xx = window_copy_find_length(wp, py); 1765 yy = screen_hsize(back_s) + screen_size_y(back_s) - 1; 1766 1767 /* 1768 * First skip past any word characters, then any nonword characters. 1769 * 1770 * expected is initially set to 1 for the former and then 0 for the 1771 * latter. 1772 */ 1773 do { 1774 while (px > xx || 1775 window_copy_in_set(wp, px, py, separators) == expected) { 1776 /* Move down if we're past the end of the line. */ 1777 if (px > xx) { 1778 if (py == yy) 1779 return; 1780 window_copy_cursor_down(wp, 0); 1781 px = 0; 1782 1783 py = screen_hsize(back_s) + data->cy - data->oy; 1784 xx = window_copy_find_length(wp, py); 1785 } else 1786 px++; 1787 } 1788 expected = !expected; 1789 } while (expected == 0); 1790 1791 window_copy_update_cursor(wp, px, data->cy); 1792 if (window_copy_update_selection(wp)) 1793 window_copy_redraw_lines(wp, data->cy, 1); 1794} 1795 1796/* Move to the previous place where a word begins. */ 1797void 1798window_copy_cursor_previous_word(struct window_pane *wp, const char *separators) 1799{ 1800 struct window_copy_mode_data *data = wp->modedata; 1801 u_int px, py; 1802 1803 px = data->cx; 1804 py = screen_hsize(data->backing) + data->cy - data->oy; 1805 1806 /* Move back to the previous word character. */ 1807 for (;;) { 1808 if (px > 0) { 1809 px--; 1810 if (!window_copy_in_set(wp, px, py, separators)) 1811 break; 1812 } else { 1813 if (data->cy == 0 && 1814 (screen_hsize(data->backing) == 0 || 1815 data->oy >= screen_hsize(data->backing) - 1)) 1816 goto out; 1817 window_copy_cursor_up(wp, 0); 1818 1819 py = screen_hsize(data->backing) + data->cy - data->oy; 1820 px = window_copy_find_length(wp, py); 1821 } 1822 } 1823 1824 /* Move back to the beginning of this word. */ 1825 while (px > 0 && !window_copy_in_set(wp, px - 1, py, separators)) 1826 px--; 1827 1828out: 1829 window_copy_update_cursor(wp, px, data->cy); 1830 if (window_copy_update_selection(wp)) 1831 window_copy_redraw_lines(wp, data->cy, 1); 1832} 1833 1834void 1835window_copy_scroll_up(struct window_pane *wp, u_int ny) 1836{ 1837 struct window_copy_mode_data *data = wp->modedata; 1838 struct screen *s = &data->screen; 1839 struct screen_write_ctx ctx; 1840 1841 if (data->oy < ny) 1842 ny = data->oy; 1843 if (ny == 0) 1844 return; 1845 data->oy -= ny; 1846 1847 screen_write_start(&ctx, wp, NULL); 1848 screen_write_cursormove(&ctx, 0, 0); 1849 screen_write_deleteline(&ctx, ny); 1850 window_copy_write_lines(wp, &ctx, screen_size_y(s) - ny, ny); 1851 window_copy_write_line(wp, &ctx, 0); 1852 if (screen_size_y(s) > 1) 1853 window_copy_write_line(wp, &ctx, 1); 1854 if (screen_size_y(s) > 3) 1855 window_copy_write_line(wp, &ctx, screen_size_y(s) - 2); 1856 if (s->sel.flag && screen_size_y(s) > ny) { 1857 window_copy_update_selection(wp); 1858 window_copy_write_line(wp, &ctx, screen_size_y(s) - ny - 1); 1859 } 1860 screen_write_cursormove(&ctx, data->cx, data->cy); 1861 window_copy_update_selection(wp); 1862 screen_write_stop(&ctx); 1863} 1864 1865void 1866window_copy_scroll_down(struct window_pane *wp, u_int ny) 1867{ 1868 struct window_copy_mode_data *data = wp->modedata; 1869 struct screen *s = &data->screen; 1870 struct screen_write_ctx ctx; 1871 1872 if (ny > screen_hsize(data->backing)) 1873 return; 1874 1875 if (data->oy > screen_hsize(data->backing) - ny) 1876 ny = screen_hsize(data->backing) - data->oy; 1877 if (ny == 0) 1878 return; 1879 data->oy += ny; 1880 1881 screen_write_start(&ctx, wp, NULL); 1882 screen_write_cursormove(&ctx, 0, 0); 1883 screen_write_insertline(&ctx, ny); 1884 window_copy_write_lines(wp, &ctx, 0, ny); 1885 if (s->sel.flag && screen_size_y(s) > ny) { 1886 window_copy_update_selection(wp); 1887 window_copy_write_line(wp, &ctx, ny); 1888 } else if (ny == 1) /* nuke position */ 1889 window_copy_write_line(wp, &ctx, 1); 1890 screen_write_cursormove(&ctx, data->cx, data->cy); 1891 window_copy_update_selection(wp); 1892 screen_write_stop(&ctx); 1893} 1894 1895void 1896window_copy_rectangle_toggle(struct window_pane *wp) 1897{ 1898 struct window_copy_mode_data *data = wp->modedata; 1899 u_int px, py; 1900 1901 data->rectflag = !data->rectflag; 1902 1903 py = screen_hsize(data->backing) + data->cy - data->oy; 1904 px = window_copy_find_length(wp, py); 1905 if (data->cx > px) 1906 window_copy_update_cursor(wp, px, data->cy); 1907 1908 window_copy_update_selection(wp); 1909 window_copy_redraw_screen(wp); 1910} 1911