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