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