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