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