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