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