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