1 /* $OpenBSD$ */ 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_mode_entry *); 28 static void window_copy_command(struct window_mode_entry *, struct client *, 29 struct session *, struct winlink *, struct args *, 30 struct mouse_event *); 31 static struct screen *window_copy_init(struct window_mode_entry *, 32 struct cmd_find_state *, struct args *); 33 static struct screen *window_copy_view_init(struct window_mode_entry *, 34 struct cmd_find_state *, struct args *); 35 static void window_copy_free(struct window_mode_entry *); 36 static void window_copy_resize(struct window_mode_entry *, u_int, u_int); 37 static void window_copy_formats(struct window_mode_entry *, 38 struct format_tree *); 39 static void window_copy_pageup1(struct window_mode_entry *, int); 40 static int window_copy_pagedown(struct window_mode_entry *, int, int); 41 static void window_copy_next_paragraph(struct window_mode_entry *); 42 static void window_copy_previous_paragraph(struct window_mode_entry *); 43 44 static void window_copy_redraw_selection(struct window_mode_entry *, u_int); 45 static void window_copy_redraw_lines(struct window_mode_entry *, u_int, 46 u_int); 47 static void window_copy_redraw_screen(struct window_mode_entry *); 48 static void window_copy_write_line(struct window_mode_entry *, 49 struct screen_write_ctx *, u_int); 50 static void window_copy_write_lines(struct window_mode_entry *, 51 struct screen_write_ctx *, u_int, u_int); 52 53 static void window_copy_scroll_to(struct window_mode_entry *, u_int, u_int); 54 static int window_copy_search_compare(struct grid *, u_int, u_int, 55 struct grid *, u_int, int); 56 static int window_copy_search_lr(struct grid *, struct grid *, u_int *, 57 u_int, u_int, u_int, int); 58 static int window_copy_search_rl(struct grid *, struct grid *, u_int *, 59 u_int, u_int, u_int, int); 60 static int window_copy_search_marks(struct window_mode_entry *, 61 struct screen *); 62 static void window_copy_clear_marks(struct window_mode_entry *); 63 static void window_copy_move_left(struct screen *, u_int *, u_int *); 64 static void window_copy_move_right(struct screen *, u_int *, u_int *); 65 static int window_copy_is_lowercase(const char *); 66 static int window_copy_search_jump(struct window_mode_entry *, 67 struct grid *, struct grid *, u_int, u_int, u_int, int, int, 68 int); 69 static int window_copy_search(struct window_mode_entry *, int); 70 static int window_copy_search_up(struct window_mode_entry *); 71 static int window_copy_search_down(struct window_mode_entry *); 72 static void window_copy_goto_line(struct window_mode_entry *, const char *); 73 static void window_copy_update_cursor(struct window_mode_entry *, u_int, 74 u_int); 75 static void window_copy_start_selection(struct window_mode_entry *); 76 static int window_copy_adjust_selection(struct window_mode_entry *, 77 u_int *, u_int *); 78 static int window_copy_set_selection(struct window_mode_entry *, int); 79 static int window_copy_update_selection(struct window_mode_entry *, int); 80 static void window_copy_synchronize_cursor(struct window_mode_entry *); 81 static void *window_copy_get_selection(struct window_mode_entry *, size_t *); 82 static void window_copy_copy_buffer(struct window_mode_entry *, 83 const char *, void *, size_t); 84 static void window_copy_copy_pipe(struct window_mode_entry *, 85 struct session *, const char *, const char *); 86 static void window_copy_copy_selection(struct window_mode_entry *, 87 const char *); 88 static void window_copy_append_selection(struct window_mode_entry *); 89 static void window_copy_clear_selection(struct window_mode_entry *); 90 static void window_copy_copy_line(struct window_mode_entry *, char **, 91 size_t *, u_int, u_int, u_int); 92 static int window_copy_in_set(struct window_mode_entry *, u_int, u_int, 93 const char *); 94 static u_int window_copy_find_length(struct window_mode_entry *, u_int); 95 static void window_copy_cursor_start_of_line(struct window_mode_entry *); 96 static void window_copy_cursor_back_to_indentation( 97 struct window_mode_entry *); 98 static void window_copy_cursor_end_of_line(struct window_mode_entry *); 99 static void window_copy_other_end(struct window_mode_entry *); 100 static void window_copy_cursor_left(struct window_mode_entry *); 101 static void window_copy_cursor_right(struct window_mode_entry *); 102 static void window_copy_cursor_up(struct window_mode_entry *, int); 103 static void window_copy_cursor_down(struct window_mode_entry *, int); 104 static void window_copy_cursor_jump(struct window_mode_entry *); 105 static void window_copy_cursor_jump_back(struct window_mode_entry *); 106 static void window_copy_cursor_jump_to(struct window_mode_entry *); 107 static void window_copy_cursor_jump_to_back(struct window_mode_entry *); 108 static void window_copy_cursor_next_word(struct window_mode_entry *, 109 const char *); 110 static void window_copy_cursor_next_word_end(struct window_mode_entry *, 111 const char *); 112 static void window_copy_cursor_previous_word(struct window_mode_entry *, 113 const char *); 114 static void window_copy_scroll_up(struct window_mode_entry *, u_int); 115 static void window_copy_scroll_down(struct window_mode_entry *, u_int); 116 static void window_copy_rectangle_toggle(struct window_mode_entry *); 117 static void window_copy_move_mouse(struct mouse_event *); 118 static void window_copy_drag_update(struct client *, struct mouse_event *); 119 static void window_copy_drag_release(struct client *, struct mouse_event *); 120 121 const struct window_mode window_copy_mode = { 122 .name = "copy-mode", 123 124 .init = window_copy_init, 125 .free = window_copy_free, 126 .resize = window_copy_resize, 127 .key_table = window_copy_key_table, 128 .command = window_copy_command, 129 .formats = window_copy_formats, 130 }; 131 132 const struct window_mode window_view_mode = { 133 .name = "view-mode", 134 135 .init = window_copy_view_init, 136 .free = window_copy_free, 137 .resize = window_copy_resize, 138 .key_table = window_copy_key_table, 139 .command = window_copy_command, 140 .formats = window_copy_formats, 141 }; 142 143 enum { 144 WINDOW_COPY_OFF, 145 WINDOW_COPY_SEARCHUP, 146 WINDOW_COPY_SEARCHDOWN, 147 WINDOW_COPY_JUMPFORWARD, 148 WINDOW_COPY_JUMPBACKWARD, 149 WINDOW_COPY_JUMPTOFORWARD, 150 WINDOW_COPY_JUMPTOBACKWARD, 151 }; 152 153 enum { 154 WINDOW_COPY_REL_POS_ABOVE, 155 WINDOW_COPY_REL_POS_ON_SCREEN, 156 WINDOW_COPY_REL_POS_BELOW, 157 }; 158 159 enum window_copy_cmd_action { 160 WINDOW_COPY_CMD_NOTHING, 161 WINDOW_COPY_CMD_REDRAW, 162 WINDOW_COPY_CMD_CANCEL, 163 }; 164 165 struct window_copy_cmd_state { 166 struct window_mode_entry *wme; 167 struct args *args; 168 struct mouse_event *m; 169 170 struct client *c; 171 struct session *s; 172 struct winlink *wl; 173 }; 174 175 /* 176 * Copy mode's visible screen (the "screen" field) is filled from one of two 177 * sources: the original contents of the pane (used when we actually enter via 178 * the "copy-mode" command, to copy the contents of the current pane), or else 179 * a series of lines containing the output from an output-writing tmux command 180 * (such as any of the "show-*" or "list-*" commands). 181 * 182 * In either case, the full content of the copy-mode grid is pointed at by the 183 * "backing" field, and is copied into "screen" as needed (that is, when 184 * scrolling occurs). When copy-mode is backed by a pane, backing points 185 * directly at that pane's screen structure (&wp->base); when backed by a list 186 * of output-lines from a command, it points at a newly-allocated screen 187 * structure (which is deallocated when the mode ends). 188 */ 189 struct window_copy_mode_data { 190 struct screen screen; 191 192 struct screen *backing; 193 int backing_written; /* backing display started */ 194 195 u_int oy; /* number of lines scrolled up */ 196 197 u_int selx; /* beginning of selection */ 198 u_int sely; 199 200 u_int endselx; /* end of selection */ 201 u_int endsely; 202 203 enum { 204 CURSORDRAG_NONE, /* selection is independent of cursor */ 205 CURSORDRAG_ENDSEL, /* end is synchronized with cursor */ 206 CURSORDRAG_SEL, /* start is synchronized with cursor */ 207 } cursordrag; 208 209 int modekeys; 210 enum { 211 LINE_SEL_NONE, 212 LINE_SEL_LEFT_RIGHT, 213 LINE_SEL_RIGHT_LEFT, 214 } lineflag; /* line selection mode */ 215 int rectflag; /* in rectangle copy mode? */ 216 int scroll_exit; /* exit on scroll to end? */ 217 218 u_int cx; 219 u_int cy; 220 221 u_int lastcx; /* position in last line w/ content */ 222 u_int lastsx; /* size of last line w/ content */ 223 224 int searchtype; 225 char *searchstr; 226 bitstr_t *searchmark; 227 u_int searchcount; 228 int searchthis; 229 int searchx; 230 int searchy; 231 int searcho; 232 233 int jumptype; 234 char jumpchar; 235 236 struct event dragtimer; 237 #define WINDOW_COPY_DRAG_REPEAT_TIME 50000 238 }; 239 240 static void 241 window_copy_scroll_timer(__unused int fd, __unused short events, void *arg) 242 { 243 struct window_mode_entry *wme = arg; 244 struct window_pane *wp = wme->wp; 245 struct window_copy_mode_data *data = wme->data; 246 struct timeval tv = { 247 .tv_usec = WINDOW_COPY_DRAG_REPEAT_TIME 248 }; 249 250 evtimer_del(&data->dragtimer); 251 252 if (TAILQ_FIRST(&wp->modes) != wme) 253 return; 254 255 if (data->cy == 0) { 256 evtimer_add(&data->dragtimer, &tv); 257 window_copy_cursor_up(wme, 1); 258 } else if (data->cy == screen_size_y(&data->screen) - 1) { 259 evtimer_add(&data->dragtimer, &tv); 260 window_copy_cursor_down(wme, 1); 261 } 262 } 263 264 static struct window_copy_mode_data * 265 window_copy_common_init(struct window_mode_entry *wme) 266 { 267 struct window_pane *wp = wme->wp; 268 struct window_copy_mode_data *data; 269 struct screen *base = &wp->base; 270 271 wme->data = data = xcalloc(1, sizeof *data); 272 273 data->cursordrag = CURSORDRAG_NONE; 274 data->lineflag = LINE_SEL_NONE; 275 276 if (wp->searchstr != NULL) { 277 data->searchtype = WINDOW_COPY_SEARCHUP; 278 data->searchstr = xstrdup(wp->searchstr); 279 } else { 280 data->searchtype = WINDOW_COPY_OFF; 281 data->searchstr = NULL; 282 } 283 data->searchmark = NULL; 284 data->searchx = data->searchy = data->searcho = -1; 285 286 data->jumptype = WINDOW_COPY_OFF; 287 data->jumpchar = '\0'; 288 289 screen_init(&data->screen, screen_size_x(base), screen_size_y(base), 0); 290 data->modekeys = options_get_number(wp->window->options, "mode-keys"); 291 292 evtimer_set(&data->dragtimer, window_copy_scroll_timer, wme); 293 294 return (data); 295 } 296 297 static struct screen * 298 window_copy_init(struct window_mode_entry *wme, 299 __unused struct cmd_find_state *fs, struct args *args) 300 { 301 struct window_pane *wp = wme->wp; 302 struct window_copy_mode_data *data; 303 struct screen_write_ctx ctx; 304 u_int i; 305 306 data = window_copy_common_init(wme); 307 308 if (wp->fd != -1 && wp->disabled++ == 0) 309 bufferevent_disable(wp->event, EV_READ|EV_WRITE); 310 311 data->backing = &wp->base; 312 data->cx = data->backing->cx; 313 data->cy = data->backing->cy; 314 315 data->scroll_exit = args_has(args, 'e'); 316 317 data->screen.cx = data->cx; 318 data->screen.cy = data->cy; 319 320 screen_write_start(&ctx, NULL, &data->screen); 321 for (i = 0; i < screen_size_y(&data->screen); i++) 322 window_copy_write_line(wme, &ctx, i); 323 screen_write_cursormove(&ctx, data->cx, data->cy, 0); 324 screen_write_stop(&ctx); 325 326 return (&data->screen); 327 } 328 329 static struct screen * 330 window_copy_view_init(struct window_mode_entry *wme, 331 __unused struct cmd_find_state *fs, __unused struct args *args) 332 { 333 struct window_pane *wp = wme->wp; 334 struct window_copy_mode_data *data; 335 struct screen *base = &wp->base; 336 struct screen *s; 337 338 data = window_copy_common_init(wme); 339 340 data->backing = s = xmalloc(sizeof *data->backing); 341 screen_init(s, screen_size_x(base), screen_size_y(base), UINT_MAX); 342 343 return (&data->screen); 344 } 345 346 static void 347 window_copy_free(struct window_mode_entry *wme) 348 { 349 struct window_pane *wp = wme->wp; 350 struct window_copy_mode_data *data = wme->data; 351 352 evtimer_del(&data->dragtimer); 353 354 if (wp->fd != -1 && --wp->disabled == 0) 355 bufferevent_enable(wp->event, EV_READ|EV_WRITE); 356 357 free(data->searchmark); 358 free(data->searchstr); 359 360 if (data->backing != &wp->base) { 361 screen_free(data->backing); 362 free(data->backing); 363 } 364 screen_free(&data->screen); 365 366 free(data); 367 } 368 369 void 370 window_copy_add(struct window_pane *wp, const char *fmt, ...) 371 { 372 va_list ap; 373 374 va_start(ap, fmt); 375 window_copy_vadd(wp, fmt, ap); 376 va_end(ap); 377 } 378 379 void 380 window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap) 381 { 382 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); 383 struct window_copy_mode_data *data = wme->data; 384 struct screen *backing = data->backing; 385 struct screen_write_ctx back_ctx, ctx; 386 struct grid_cell gc; 387 u_int old_hsize, old_cy; 388 389 if (backing == &wp->base) 390 return; 391 392 memcpy(&gc, &grid_default_cell, sizeof gc); 393 394 old_hsize = screen_hsize(data->backing); 395 screen_write_start(&back_ctx, NULL, backing); 396 if (data->backing_written) { 397 /* 398 * On the second or later line, do a CRLF before writing 399 * (so it's on a new line). 400 */ 401 screen_write_carriagereturn(&back_ctx); 402 screen_write_linefeed(&back_ctx, 0, 8); 403 } else 404 data->backing_written = 1; 405 old_cy = backing->cy; 406 screen_write_vnputs(&back_ctx, 0, &gc, fmt, ap); 407 screen_write_stop(&back_ctx); 408 409 data->oy += screen_hsize(data->backing) - old_hsize; 410 411 screen_write_start(&ctx, wp, &data->screen); 412 413 /* 414 * If the history has changed, draw the top line. 415 * (If there's any history at all, it has changed.) 416 */ 417 if (screen_hsize(data->backing)) 418 window_copy_redraw_lines(wme, 0, 1); 419 420 /* Write the new lines. */ 421 window_copy_redraw_lines(wme, old_cy, backing->cy - old_cy + 1); 422 423 screen_write_stop(&ctx); 424 } 425 426 void 427 window_copy_pageup(struct window_pane *wp, int half_page) 428 { 429 window_copy_pageup1(TAILQ_FIRST(&wp->modes), half_page); 430 } 431 432 static void 433 window_copy_pageup1(struct window_mode_entry *wme, int half_page) 434 { 435 struct window_copy_mode_data *data = wme->data; 436 struct screen *s = &data->screen; 437 u_int n, ox, oy, px, py; 438 439 oy = screen_hsize(data->backing) + data->cy - data->oy; 440 ox = window_copy_find_length(wme, oy); 441 442 if (data->cx != ox) { 443 data->lastcx = data->cx; 444 data->lastsx = ox; 445 } 446 data->cx = data->lastcx; 447 448 n = 1; 449 if (screen_size_y(s) > 2) { 450 if (half_page) 451 n = screen_size_y(s) / 2; 452 else 453 n = screen_size_y(s) - 2; 454 } 455 456 if (data->oy + n > screen_hsize(data->backing)) { 457 data->oy = screen_hsize(data->backing); 458 if (data->cy < n) 459 data->cy = 0; 460 else 461 data->cy -= n; 462 } else 463 data->oy += n; 464 465 if (data->screen.sel == NULL || !data->rectflag) { 466 py = screen_hsize(data->backing) + data->cy - data->oy; 467 px = window_copy_find_length(wme, py); 468 if ((data->cx >= data->lastsx && data->cx != px) || 469 data->cx > px) 470 window_copy_cursor_end_of_line(wme); 471 } 472 473 window_copy_update_selection(wme, 1); 474 window_copy_redraw_screen(wme); 475 } 476 477 static int 478 window_copy_pagedown(struct window_mode_entry *wme, int half_page, 479 int scroll_exit) 480 { 481 struct window_copy_mode_data *data = wme->data; 482 struct screen *s = &data->screen; 483 u_int n, ox, oy, px, py; 484 485 oy = screen_hsize(data->backing) + data->cy - data->oy; 486 ox = window_copy_find_length(wme, oy); 487 488 if (data->cx != ox) { 489 data->lastcx = data->cx; 490 data->lastsx = ox; 491 } 492 data->cx = data->lastcx; 493 494 n = 1; 495 if (screen_size_y(s) > 2) { 496 if (half_page) 497 n = screen_size_y(s) / 2; 498 else 499 n = screen_size_y(s) - 2; 500 } 501 502 if (data->oy < n) { 503 data->oy = 0; 504 if (data->cy + (n - data->oy) >= screen_size_y(data->backing)) 505 data->cy = screen_size_y(data->backing) - 1; 506 else 507 data->cy += n - data->oy; 508 } else 509 data->oy -= n; 510 511 if (data->screen.sel == NULL || !data->rectflag) { 512 py = screen_hsize(data->backing) + data->cy - data->oy; 513 px = window_copy_find_length(wme, py); 514 if ((data->cx >= data->lastsx && data->cx != px) || 515 data->cx > px) 516 window_copy_cursor_end_of_line(wme); 517 } 518 519 if (scroll_exit && data->oy == 0) 520 return (1); 521 window_copy_update_selection(wme, 1); 522 window_copy_redraw_screen(wme); 523 return (0); 524 } 525 526 static void 527 window_copy_previous_paragraph(struct window_mode_entry *wme) 528 { 529 struct window_copy_mode_data *data = wme->data; 530 u_int oy; 531 532 oy = screen_hsize(data->backing) + data->cy - data->oy; 533 534 while (oy > 0 && window_copy_find_length(wme, oy) == 0) 535 oy--; 536 537 while (oy > 0 && window_copy_find_length(wme, oy) > 0) 538 oy--; 539 540 window_copy_scroll_to(wme, 0, oy); 541 } 542 543 static void 544 window_copy_next_paragraph(struct window_mode_entry *wme) 545 { 546 struct window_copy_mode_data *data = wme->data; 547 struct screen *s = &data->screen; 548 u_int maxy, ox, oy; 549 550 oy = screen_hsize(data->backing) + data->cy - data->oy; 551 maxy = screen_hsize(data->backing) + screen_size_y(s) - 1; 552 553 while (oy < maxy && window_copy_find_length(wme, oy) == 0) 554 oy++; 555 556 while (oy < maxy && window_copy_find_length(wme, oy) > 0) 557 oy++; 558 559 ox = window_copy_find_length(wme, oy); 560 window_copy_scroll_to(wme, ox, oy); 561 } 562 563 static void 564 window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft) 565 { 566 struct window_copy_mode_data *data = wme->data; 567 568 format_add(ft, "selection_present", "%d", data->screen.sel != NULL); 569 format_add(ft, "scroll_position", "%d", data->oy); 570 format_add(ft, "rectangle_toggle", "%d", data->rectflag); 571 } 572 573 static void 574 window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy) 575 { 576 struct window_pane *wp = wme->wp; 577 struct window_copy_mode_data *data = wme->data; 578 struct screen *s = &data->screen; 579 struct screen_write_ctx ctx; 580 int search; 581 582 screen_resize(s, sx, sy, 1); 583 if (data->backing != &wp->base) 584 screen_resize(data->backing, sx, sy, 1); 585 586 if (data->cy > sy - 1) 587 data->cy = sy - 1; 588 if (data->cx > sx) 589 data->cx = sx; 590 if (data->oy > screen_hsize(data->backing)) 591 data->oy = screen_hsize(data->backing); 592 593 search = (data->searchmark != NULL); 594 window_copy_clear_selection(wme); 595 window_copy_clear_marks(wme); 596 597 screen_write_start(&ctx, NULL, s); 598 window_copy_write_lines(wme, &ctx, 0, screen_size_y(s) - 1); 599 screen_write_stop(&ctx); 600 601 if (search) 602 window_copy_search_marks(wme, NULL); 603 data->searchx = data->cx; 604 data->searchy = data->cy; 605 data->searcho = data->oy; 606 607 window_copy_redraw_screen(wme); 608 } 609 610 static const char * 611 window_copy_key_table(struct window_mode_entry *wme) 612 { 613 struct window_pane *wp = wme->wp; 614 615 if (options_get_number(wp->window->options, "mode-keys") == MODEKEY_VI) 616 return ("copy-mode-vi"); 617 return ("copy-mode"); 618 } 619 620 static enum window_copy_cmd_action 621 window_copy_cmd_append_selection(struct window_copy_cmd_state *cs) 622 { 623 struct window_mode_entry *wme = cs->wme; 624 struct session *s = cs->s; 625 626 if (s != NULL) 627 window_copy_append_selection(wme); 628 window_copy_clear_selection(wme); 629 return (WINDOW_COPY_CMD_REDRAW); 630 } 631 632 static enum window_copy_cmd_action 633 window_copy_cmd_append_selection_and_cancel(struct window_copy_cmd_state *cs) 634 { 635 struct window_mode_entry *wme = cs->wme; 636 struct session *s = cs->s; 637 638 if (s != NULL) 639 window_copy_append_selection(wme); 640 window_copy_clear_selection(wme); 641 return (WINDOW_COPY_CMD_CANCEL); 642 } 643 644 static enum window_copy_cmd_action 645 window_copy_cmd_back_to_indentation(struct window_copy_cmd_state *cs) 646 { 647 struct window_mode_entry *wme = cs->wme; 648 649 window_copy_cursor_back_to_indentation(wme); 650 return (WINDOW_COPY_CMD_NOTHING); 651 } 652 653 static enum window_copy_cmd_action 654 window_copy_cmd_begin_selection(struct window_copy_cmd_state *cs) 655 { 656 struct window_mode_entry *wme = cs->wme; 657 struct client *c = cs->c; 658 struct mouse_event *m = cs->m; 659 struct window_copy_mode_data *data = wme->data; 660 661 if (m != NULL) { 662 window_copy_start_drag(c, m); 663 return (WINDOW_COPY_CMD_NOTHING); 664 } 665 666 data->lineflag = LINE_SEL_NONE; 667 window_copy_start_selection(wme); 668 return (WINDOW_COPY_CMD_REDRAW); 669 } 670 671 static enum window_copy_cmd_action 672 window_copy_cmd_stop_selection(struct window_copy_cmd_state *cs) 673 { 674 struct window_mode_entry *wme = cs->wme; 675 struct window_copy_mode_data *data = wme->data; 676 677 data->cursordrag = CURSORDRAG_NONE; 678 data->lineflag = LINE_SEL_NONE; 679 return (WINDOW_COPY_CMD_NOTHING); 680 } 681 682 static enum window_copy_cmd_action 683 window_copy_cmd_bottom_line(struct window_copy_cmd_state *cs) 684 { 685 struct window_mode_entry *wme = cs->wme; 686 struct window_copy_mode_data *data = wme->data; 687 688 data->cx = 0; 689 data->cy = screen_size_y(&data->screen) - 1; 690 691 window_copy_update_selection(wme, 1); 692 return (WINDOW_COPY_CMD_REDRAW); 693 } 694 695 static enum window_copy_cmd_action 696 window_copy_cmd_cancel(__unused struct window_copy_cmd_state *cs) 697 { 698 return (WINDOW_COPY_CMD_CANCEL); 699 } 700 701 static enum window_copy_cmd_action 702 window_copy_cmd_clear_selection(struct window_copy_cmd_state *cs) 703 { 704 struct window_mode_entry *wme = cs->wme; 705 706 window_copy_clear_selection(wme); 707 return (WINDOW_COPY_CMD_REDRAW); 708 } 709 710 static enum window_copy_cmd_action 711 window_copy_cmd_copy_end_of_line(struct window_copy_cmd_state *cs) 712 { 713 struct window_mode_entry *wme = cs->wme; 714 struct client *c = cs->c; 715 struct session *s = cs->s; 716 struct winlink *wl = cs->wl; 717 struct window_pane *wp = wme->wp; 718 u_int np = wme->prefix; 719 char *prefix = NULL; 720 721 if (cs->args->argc == 2) 722 prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp); 723 724 window_copy_start_selection(wme); 725 for (; np > 1; np--) 726 window_copy_cursor_down(wme, 0); 727 window_copy_cursor_end_of_line(wme); 728 729 if (s != NULL) { 730 window_copy_copy_selection(wme, prefix); 731 732 free(prefix); 733 return (WINDOW_COPY_CMD_CANCEL); 734 } 735 736 free(prefix); 737 return (WINDOW_COPY_CMD_REDRAW); 738 } 739 740 static enum window_copy_cmd_action 741 window_copy_cmd_copy_line(struct window_copy_cmd_state *cs) 742 { 743 struct window_mode_entry *wme = cs->wme; 744 struct client *c = cs->c; 745 struct session *s = cs->s; 746 struct winlink *wl = cs->wl; 747 struct window_pane *wp = wme->wp; 748 u_int np = wme->prefix; 749 char *prefix = NULL; 750 751 if (cs->args->argc == 2) 752 prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp); 753 754 window_copy_cursor_start_of_line(wme); 755 window_copy_start_selection(wme); 756 for (; np > 1; np--) 757 window_copy_cursor_down(wme, 0); 758 window_copy_cursor_end_of_line(wme); 759 760 if (s != NULL) { 761 window_copy_copy_selection(wme, prefix); 762 763 free(prefix); 764 return (WINDOW_COPY_CMD_CANCEL); 765 } 766 767 free(prefix); 768 return (WINDOW_COPY_CMD_REDRAW); 769 } 770 771 static enum window_copy_cmd_action 772 window_copy_cmd_copy_selection_no_clear(struct window_copy_cmd_state *cs) 773 { 774 struct window_mode_entry *wme = cs->wme; 775 struct client *c = cs->c; 776 struct session *s = cs->s; 777 struct winlink *wl = cs->wl; 778 struct window_pane *wp = wme->wp; 779 char *prefix = NULL; 780 781 if (cs->args->argc == 2) 782 prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp); 783 784 if (s != NULL) 785 window_copy_copy_selection(wme, prefix); 786 787 free(prefix); 788 return (WINDOW_COPY_CMD_NOTHING); 789 } 790 791 static enum window_copy_cmd_action 792 window_copy_cmd_copy_selection(struct window_copy_cmd_state *cs) 793 { 794 struct window_mode_entry *wme = cs->wme; 795 796 window_copy_cmd_copy_selection_no_clear(cs); 797 window_copy_clear_selection(wme); 798 return (WINDOW_COPY_CMD_REDRAW); 799 } 800 801 static enum window_copy_cmd_action 802 window_copy_cmd_copy_selection_and_cancel(struct window_copy_cmd_state *cs) 803 { 804 struct window_mode_entry *wme = cs->wme; 805 806 window_copy_cmd_copy_selection_no_clear(cs); 807 window_copy_clear_selection(wme); 808 return (WINDOW_COPY_CMD_CANCEL); 809 } 810 811 static enum window_copy_cmd_action 812 window_copy_cmd_cursor_down(struct window_copy_cmd_state *cs) 813 { 814 struct window_mode_entry *wme = cs->wme; 815 u_int np = wme->prefix; 816 817 for (; np != 0; np--) 818 window_copy_cursor_down(wme, 0); 819 return (WINDOW_COPY_CMD_NOTHING); 820 } 821 822 static enum window_copy_cmd_action 823 window_copy_cmd_cursor_left(struct window_copy_cmd_state *cs) 824 { 825 struct window_mode_entry *wme = cs->wme; 826 u_int np = wme->prefix; 827 828 for (; np != 0; np--) 829 window_copy_cursor_left(wme); 830 return (WINDOW_COPY_CMD_NOTHING); 831 } 832 833 static enum window_copy_cmd_action 834 window_copy_cmd_cursor_right(struct window_copy_cmd_state *cs) 835 { 836 struct window_mode_entry *wme = cs->wme; 837 u_int np = wme->prefix; 838 839 for (; np != 0; np--) 840 window_copy_cursor_right(wme); 841 return (WINDOW_COPY_CMD_NOTHING); 842 } 843 844 static enum window_copy_cmd_action 845 window_copy_cmd_cursor_up(struct window_copy_cmd_state *cs) 846 { 847 struct window_mode_entry *wme = cs->wme; 848 u_int np = wme->prefix; 849 850 for (; np != 0; np--) 851 window_copy_cursor_up(wme, 0); 852 return (WINDOW_COPY_CMD_NOTHING); 853 } 854 855 static enum window_copy_cmd_action 856 window_copy_cmd_end_of_line(struct window_copy_cmd_state *cs) 857 { 858 struct window_mode_entry *wme = cs->wme; 859 860 window_copy_cursor_end_of_line(wme); 861 return (WINDOW_COPY_CMD_NOTHING); 862 } 863 864 static enum window_copy_cmd_action 865 window_copy_cmd_halfpage_down(struct window_copy_cmd_state *cs) 866 { 867 struct window_mode_entry *wme = cs->wme; 868 struct window_copy_mode_data *data = wme->data; 869 u_int np = wme->prefix; 870 871 for (; np != 0; np--) { 872 if (window_copy_pagedown(wme, 1, data->scroll_exit)) 873 return (WINDOW_COPY_CMD_CANCEL); 874 } 875 return (WINDOW_COPY_CMD_NOTHING); 876 } 877 878 static enum window_copy_cmd_action 879 window_copy_cmd_halfpage_down_and_cancel(struct window_copy_cmd_state *cs) 880 { 881 882 struct window_mode_entry *wme = cs->wme; 883 u_int np = wme->prefix; 884 885 for (; np != 0; np--) { 886 if (window_copy_pagedown(wme, 1, 1)) 887 return (WINDOW_COPY_CMD_CANCEL); 888 } 889 return (WINDOW_COPY_CMD_NOTHING); 890 } 891 892 static enum window_copy_cmd_action 893 window_copy_cmd_halfpage_up(struct window_copy_cmd_state *cs) 894 { 895 struct window_mode_entry *wme = cs->wme; 896 u_int np = wme->prefix; 897 898 for (; np != 0; np--) 899 window_copy_pageup1(wme, 1); 900 return (WINDOW_COPY_CMD_NOTHING); 901 } 902 903 static enum window_copy_cmd_action 904 window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs) 905 { 906 struct window_mode_entry *wme = cs->wme; 907 struct window_copy_mode_data *data = wme->data; 908 u_int oy; 909 910 oy = screen_hsize(data->backing) + data->cy - data->oy; 911 if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely) 912 window_copy_other_end(wme); 913 914 data->cy = screen_size_y(&data->screen) - 1; 915 data->cx = window_copy_find_length(wme, data->cy); 916 data->oy = 0; 917 918 window_copy_update_selection(wme, 1); 919 return (WINDOW_COPY_CMD_REDRAW); 920 } 921 922 static enum window_copy_cmd_action 923 window_copy_cmd_history_top(struct window_copy_cmd_state *cs) 924 { 925 struct window_mode_entry *wme = cs->wme; 926 struct window_copy_mode_data *data = wme->data; 927 u_int oy; 928 929 oy = screen_hsize(data->backing) + data->cy - data->oy; 930 if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely) 931 window_copy_other_end(wme); 932 933 data->cy = 0; 934 data->cx = 0; 935 data->oy = screen_hsize(data->backing); 936 937 window_copy_update_selection(wme, 1); 938 return (WINDOW_COPY_CMD_REDRAW); 939 } 940 941 static enum window_copy_cmd_action 942 window_copy_cmd_jump_again(struct window_copy_cmd_state *cs) 943 { 944 struct window_mode_entry *wme = cs->wme; 945 struct window_copy_mode_data *data = wme->data; 946 u_int np = wme->prefix; 947 948 switch (data->jumptype) { 949 case WINDOW_COPY_JUMPFORWARD: 950 for (; np != 0; np--) 951 window_copy_cursor_jump(wme); 952 break; 953 case WINDOW_COPY_JUMPBACKWARD: 954 for (; np != 0; np--) 955 window_copy_cursor_jump_back(wme); 956 break; 957 case WINDOW_COPY_JUMPTOFORWARD: 958 for (; np != 0; np--) 959 window_copy_cursor_jump_to(wme); 960 break; 961 case WINDOW_COPY_JUMPTOBACKWARD: 962 for (; np != 0; np--) 963 window_copy_cursor_jump_to_back(wme); 964 break; 965 } 966 return (WINDOW_COPY_CMD_NOTHING); 967 } 968 969 static enum window_copy_cmd_action 970 window_copy_cmd_jump_reverse(struct window_copy_cmd_state *cs) 971 { 972 struct window_mode_entry *wme = cs->wme; 973 struct window_copy_mode_data *data = wme->data; 974 u_int np = wme->prefix; 975 976 switch (data->jumptype) { 977 case WINDOW_COPY_JUMPFORWARD: 978 for (; np != 0; np--) 979 window_copy_cursor_jump_back(wme); 980 break; 981 case WINDOW_COPY_JUMPBACKWARD: 982 for (; np != 0; np--) 983 window_copy_cursor_jump(wme); 984 break; 985 case WINDOW_COPY_JUMPTOFORWARD: 986 for (; np != 0; np--) 987 window_copy_cursor_jump_to_back(wme); 988 break; 989 case WINDOW_COPY_JUMPTOBACKWARD: 990 for (; np != 0; np--) 991 window_copy_cursor_jump_to(wme); 992 break; 993 } 994 return (WINDOW_COPY_CMD_NOTHING); 995 } 996 997 static enum window_copy_cmd_action 998 window_copy_cmd_middle_line(struct window_copy_cmd_state *cs) 999 { 1000 struct window_mode_entry *wme = cs->wme; 1001 struct window_copy_mode_data *data = wme->data; 1002 1003 data->cx = 0; 1004 data->cy = (screen_size_y(&data->screen) - 1) / 2; 1005 1006 window_copy_update_selection(wme, 1); 1007 return (WINDOW_COPY_CMD_REDRAW); 1008 } 1009 1010 static enum window_copy_cmd_action 1011 window_copy_cmd_previous_matching_bracket(struct window_copy_cmd_state *cs) 1012 { 1013 struct window_mode_entry *wme = cs->wme; 1014 u_int np = wme->prefix; 1015 struct window_copy_mode_data *data = wme->data; 1016 struct screen *s = data->backing; 1017 char open[] = "{[(", close[] = "}])"; 1018 char tried, found, start, *cp; 1019 u_int px, py, xx, n; 1020 struct grid_cell gc; 1021 int failed; 1022 1023 for (; np != 0; np--) { 1024 /* Get cursor position and line length. */ 1025 px = data->cx; 1026 py = screen_hsize(s) + data->cy - data->oy; 1027 xx = window_copy_find_length(wme, py); 1028 if (xx == 0) 1029 break; 1030 1031 /* 1032 * Get the current character. If not on a bracket, try the 1033 * previous. If still not, then behave like previous-word. 1034 */ 1035 tried = 0; 1036 retry: 1037 grid_get_cell(s->grid, px, py, &gc); 1038 if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING)) 1039 cp = NULL; 1040 else { 1041 found = *gc.data.data; 1042 cp = strchr(close, found); 1043 } 1044 if (cp == NULL) { 1045 if (data->modekeys == MODEKEY_EMACS) { 1046 if (!tried && px > 0) { 1047 px--; 1048 tried = 1; 1049 goto retry; 1050 } 1051 window_copy_cursor_previous_word(wme, "}]) "); 1052 } 1053 continue; 1054 } 1055 start = open[cp - close]; 1056 1057 /* Walk backward until the matching bracket is reached. */ 1058 n = 1; 1059 failed = 0; 1060 do { 1061 if (px == 0) { 1062 if (py == 0) { 1063 failed = 1; 1064 break; 1065 } 1066 do { 1067 py--; 1068 xx = window_copy_find_length(wme, py); 1069 } while (xx == 0 && py > 0); 1070 if (xx == 0 && py == 0) { 1071 failed = 1; 1072 break; 1073 } 1074 px = xx - 1; 1075 } else 1076 px--; 1077 1078 grid_get_cell(s->grid, px, py, &gc); 1079 if (gc.data.size == 1 && 1080 (~gc.flags & GRID_FLAG_PADDING)) { 1081 if (*gc.data.data == found) 1082 n++; 1083 else if (*gc.data.data == start) 1084 n--; 1085 } 1086 } while (n != 0); 1087 1088 /* Move the cursor to the found location if any. */ 1089 if (!failed) 1090 window_copy_scroll_to(wme, px, py); 1091 } 1092 1093 return (WINDOW_COPY_CMD_NOTHING); 1094 } 1095 1096 1097 static enum window_copy_cmd_action 1098 window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state *cs) 1099 { 1100 struct window_mode_entry *wme = cs->wme; 1101 u_int np = wme->prefix; 1102 struct window_copy_mode_data *data = wme->data; 1103 struct screen *s = data->backing; 1104 char open[] = "{[(", close[] = "}])"; 1105 char tried, found, end, *cp; 1106 u_int px, py, xx, yy, sx, sy, n; 1107 struct grid_cell gc; 1108 int failed; 1109 struct grid_line *gl; 1110 1111 for (; np != 0; np--) { 1112 /* Get cursor position and line length. */ 1113 px = data->cx; 1114 py = screen_hsize(s) + data->cy - data->oy; 1115 xx = window_copy_find_length(wme, py); 1116 yy = screen_hsize(s) + screen_size_y(s) - 1; 1117 if (xx == 0) 1118 break; 1119 1120 /* 1121 * Get the current character. If not on a bracket, try the 1122 * next. If still not, then behave like next-word. 1123 */ 1124 tried = 0; 1125 retry: 1126 grid_get_cell(s->grid, px, py, &gc); 1127 if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING)) 1128 cp = NULL; 1129 else { 1130 found = *gc.data.data; 1131 1132 /* 1133 * In vi mode, attempt to move to previous bracket if a 1134 * closing bracket is found first. If this fails, 1135 * return to the original cursor position. 1136 */ 1137 cp = strchr(close, found); 1138 if (cp != NULL && data->modekeys == MODEKEY_VI) { 1139 sx = data->cx; 1140 sy = screen_hsize(s) + data->cy - data->oy; 1141 1142 window_copy_scroll_to(wme, px, py); 1143 window_copy_cmd_previous_matching_bracket(cs); 1144 1145 px = data->cx; 1146 py = screen_hsize(s) + data->cy - data->oy; 1147 grid_get_cell(s->grid, px, py, &gc); 1148 if (gc.data.size != 1 || 1149 (gc.flags & GRID_FLAG_PADDING) || 1150 strchr(close, *gc.data.data) == NULL) 1151 window_copy_scroll_to(wme, sx, sy); 1152 break; 1153 } 1154 1155 cp = strchr(open, found); 1156 } 1157 if (cp == NULL) { 1158 if (data->modekeys == MODEKEY_EMACS) { 1159 if (!tried && px <= xx) { 1160 px++; 1161 tried = 1; 1162 goto retry; 1163 } 1164 window_copy_cursor_next_word_end(wme, "{[( "); 1165 continue; 1166 } 1167 /* For vi, continue searching for bracket until EOL. */ 1168 if (px > xx) { 1169 if (py == yy) 1170 continue; 1171 gl = grid_get_line(s->grid, py); 1172 if (~gl->flags & GRID_LINE_WRAPPED) 1173 continue; 1174 if (gl->cellsize > s->grid->sx) 1175 continue; 1176 px = 0; 1177 py++; 1178 xx = window_copy_find_length(wme, py); 1179 } else 1180 px++; 1181 goto retry; 1182 } 1183 end = close[cp - open]; 1184 1185 /* Walk forward until the matching bracket is reached. */ 1186 n = 1; 1187 failed = 0; 1188 do { 1189 if (px > xx) { 1190 if (py == yy) { 1191 failed = 1; 1192 break; 1193 } 1194 px = 0; 1195 py++; 1196 xx = window_copy_find_length(wme, py); 1197 } else 1198 px++; 1199 1200 grid_get_cell(s->grid, px, py, &gc); 1201 if (gc.data.size == 1 && 1202 (~gc.flags & GRID_FLAG_PADDING)) { 1203 if (*gc.data.data == found) 1204 n++; 1205 else if (*gc.data.data == end) 1206 n--; 1207 } 1208 } while (n != 0); 1209 1210 /* Move the cursor to the found location if any. */ 1211 if (!failed) 1212 window_copy_scroll_to(wme, px, py); 1213 } 1214 1215 return (WINDOW_COPY_CMD_NOTHING); 1216 } 1217 1218 static enum window_copy_cmd_action 1219 window_copy_cmd_next_paragraph(struct window_copy_cmd_state *cs) 1220 { 1221 struct window_mode_entry *wme = cs->wme; 1222 u_int np = wme->prefix; 1223 1224 for (; np != 0; np--) 1225 window_copy_next_paragraph(wme); 1226 return (WINDOW_COPY_CMD_NOTHING); 1227 } 1228 1229 static enum window_copy_cmd_action 1230 window_copy_cmd_next_space(struct window_copy_cmd_state *cs) 1231 { 1232 struct window_mode_entry *wme = cs->wme; 1233 u_int np = wme->prefix; 1234 1235 for (; np != 0; np--) 1236 window_copy_cursor_next_word(wme, " "); 1237 return (WINDOW_COPY_CMD_NOTHING); 1238 } 1239 1240 static enum window_copy_cmd_action 1241 window_copy_cmd_next_space_end(struct window_copy_cmd_state *cs) 1242 { 1243 struct window_mode_entry *wme = cs->wme; 1244 u_int np = wme->prefix; 1245 1246 for (; np != 0; np--) 1247 window_copy_cursor_next_word_end(wme, " "); 1248 return (WINDOW_COPY_CMD_NOTHING); 1249 } 1250 1251 static enum window_copy_cmd_action 1252 window_copy_cmd_next_word(struct window_copy_cmd_state *cs) 1253 { 1254 struct window_mode_entry *wme = cs->wme; 1255 struct session *s = cs->s; 1256 u_int np = wme->prefix; 1257 const char *ws; 1258 1259 ws = options_get_string(s->options, "word-separators"); 1260 for (; np != 0; np--) 1261 window_copy_cursor_next_word(wme, ws); 1262 return (WINDOW_COPY_CMD_NOTHING); 1263 } 1264 1265 static enum window_copy_cmd_action 1266 window_copy_cmd_next_word_end(struct window_copy_cmd_state *cs) 1267 { 1268 struct window_mode_entry *wme = cs->wme; 1269 struct session *s = cs->s; 1270 u_int np = wme->prefix; 1271 const char *ws; 1272 1273 ws = options_get_string(s->options, "word-separators"); 1274 for (; np != 0; np--) 1275 window_copy_cursor_next_word_end(wme, ws); 1276 return (WINDOW_COPY_CMD_NOTHING); 1277 } 1278 1279 static enum window_copy_cmd_action 1280 window_copy_cmd_other_end(struct window_copy_cmd_state *cs) 1281 { 1282 struct window_mode_entry *wme = cs->wme; 1283 u_int np = wme->prefix; 1284 1285 if ((np % 2) != 0) 1286 window_copy_other_end(wme); 1287 return (WINDOW_COPY_CMD_NOTHING); 1288 } 1289 1290 static enum window_copy_cmd_action 1291 window_copy_cmd_page_down(struct window_copy_cmd_state *cs) 1292 { 1293 struct window_mode_entry *wme = cs->wme; 1294 struct window_copy_mode_data *data = wme->data; 1295 u_int np = wme->prefix; 1296 1297 for (; np != 0; np--) { 1298 if (window_copy_pagedown(wme, 0, data->scroll_exit)) 1299 return (WINDOW_COPY_CMD_CANCEL); 1300 } 1301 return (WINDOW_COPY_CMD_NOTHING); 1302 } 1303 1304 static enum window_copy_cmd_action 1305 window_copy_cmd_page_down_and_cancel(struct window_copy_cmd_state *cs) 1306 { 1307 struct window_mode_entry *wme = cs->wme; 1308 u_int np = wme->prefix; 1309 1310 for (; np != 0; np--) { 1311 if (window_copy_pagedown(wme, 0, 1)) 1312 return (WINDOW_COPY_CMD_CANCEL); 1313 } 1314 return (WINDOW_COPY_CMD_NOTHING); 1315 } 1316 1317 static enum window_copy_cmd_action 1318 window_copy_cmd_page_up(struct window_copy_cmd_state *cs) 1319 { 1320 struct window_mode_entry *wme = cs->wme; 1321 u_int np = wme->prefix; 1322 1323 for (; np != 0; np--) 1324 window_copy_pageup1(wme, 0); 1325 return (WINDOW_COPY_CMD_NOTHING); 1326 } 1327 1328 static enum window_copy_cmd_action 1329 window_copy_cmd_previous_paragraph(struct window_copy_cmd_state *cs) 1330 { 1331 struct window_mode_entry *wme = cs->wme; 1332 u_int np = wme->prefix; 1333 1334 for (; np != 0; np--) 1335 window_copy_previous_paragraph(wme); 1336 return (WINDOW_COPY_CMD_NOTHING); 1337 } 1338 1339 static enum window_copy_cmd_action 1340 window_copy_cmd_previous_space(struct window_copy_cmd_state *cs) 1341 { 1342 struct window_mode_entry *wme = cs->wme; 1343 u_int np = wme->prefix; 1344 1345 for (; np != 0; np--) 1346 window_copy_cursor_previous_word(wme, " "); 1347 return (WINDOW_COPY_CMD_NOTHING); 1348 } 1349 1350 static enum window_copy_cmd_action 1351 window_copy_cmd_previous_word(struct window_copy_cmd_state *cs) 1352 { 1353 struct window_mode_entry *wme = cs->wme; 1354 struct session *s = cs->s; 1355 u_int np = wme->prefix; 1356 const char *ws; 1357 1358 ws = options_get_string(s->options, "word-separators"); 1359 for (; np != 0; np--) 1360 window_copy_cursor_previous_word(wme, ws); 1361 return (WINDOW_COPY_CMD_NOTHING); 1362 } 1363 1364 static enum window_copy_cmd_action 1365 window_copy_cmd_rectangle_toggle(struct window_copy_cmd_state *cs) 1366 { 1367 struct window_mode_entry *wme = cs->wme; 1368 struct window_copy_mode_data *data = wme->data; 1369 1370 data->lineflag = LINE_SEL_NONE; 1371 window_copy_rectangle_toggle(wme); 1372 1373 return (WINDOW_COPY_CMD_NOTHING); 1374 } 1375 1376 static enum window_copy_cmd_action 1377 window_copy_cmd_scroll_down(struct window_copy_cmd_state *cs) 1378 { 1379 struct window_mode_entry *wme = cs->wme; 1380 struct window_copy_mode_data *data = wme->data; 1381 u_int np = wme->prefix; 1382 1383 for (; np != 0; np--) 1384 window_copy_cursor_down(wme, 1); 1385 if (data->scroll_exit && data->oy == 0) 1386 return (WINDOW_COPY_CMD_CANCEL); 1387 return (WINDOW_COPY_CMD_NOTHING); 1388 } 1389 1390 static enum window_copy_cmd_action 1391 window_copy_cmd_scroll_down_and_cancel(struct window_copy_cmd_state *cs) 1392 { 1393 struct window_mode_entry *wme = cs->wme; 1394 struct window_copy_mode_data *data = wme->data; 1395 u_int np = wme->prefix; 1396 1397 for (; np != 0; np--) 1398 window_copy_cursor_down(wme, 1); 1399 if (data->oy == 0) 1400 return (WINDOW_COPY_CMD_CANCEL); 1401 return (WINDOW_COPY_CMD_NOTHING); 1402 } 1403 1404 static enum window_copy_cmd_action 1405 window_copy_cmd_scroll_up(struct window_copy_cmd_state *cs) 1406 { 1407 struct window_mode_entry *wme = cs->wme; 1408 u_int np = wme->prefix; 1409 1410 for (; np != 0; np--) 1411 window_copy_cursor_up(wme, 1); 1412 return (WINDOW_COPY_CMD_NOTHING); 1413 } 1414 1415 static enum window_copy_cmd_action 1416 window_copy_cmd_search_again(struct window_copy_cmd_state *cs) 1417 { 1418 struct window_mode_entry *wme = cs->wme; 1419 struct window_copy_mode_data *data = wme->data; 1420 u_int np = wme->prefix; 1421 1422 if (data->searchtype == WINDOW_COPY_SEARCHUP) { 1423 for (; np != 0; np--) 1424 window_copy_search_up(wme); 1425 } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { 1426 for (; np != 0; np--) 1427 window_copy_search_down(wme); 1428 } 1429 return (WINDOW_COPY_CMD_NOTHING); 1430 } 1431 1432 static enum window_copy_cmd_action 1433 window_copy_cmd_search_reverse(struct window_copy_cmd_state *cs) 1434 { 1435 struct window_mode_entry *wme = cs->wme; 1436 struct window_copy_mode_data *data = wme->data; 1437 u_int np = wme->prefix; 1438 1439 if (data->searchtype == WINDOW_COPY_SEARCHUP) { 1440 for (; np != 0; np--) 1441 window_copy_search_down(wme); 1442 } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { 1443 for (; np != 0; np--) 1444 window_copy_search_up(wme); 1445 } 1446 return (WINDOW_COPY_CMD_NOTHING); 1447 } 1448 1449 static enum window_copy_cmd_action 1450 window_copy_cmd_select_line(struct window_copy_cmd_state *cs) 1451 { 1452 struct window_mode_entry *wme = cs->wme; 1453 struct window_copy_mode_data *data = wme->data; 1454 u_int np = wme->prefix; 1455 1456 data->lineflag = LINE_SEL_LEFT_RIGHT; 1457 data->rectflag = 0; 1458 1459 window_copy_cursor_start_of_line(wme); 1460 window_copy_start_selection(wme); 1461 for (; np > 1; np--) 1462 window_copy_cursor_down(wme, 0); 1463 window_copy_cursor_end_of_line(wme); 1464 1465 return (WINDOW_COPY_CMD_REDRAW); 1466 } 1467 1468 static enum window_copy_cmd_action 1469 window_copy_cmd_select_word(struct window_copy_cmd_state *cs) 1470 { 1471 struct window_mode_entry *wme = cs->wme; 1472 struct session *s = cs->s; 1473 struct window_copy_mode_data *data = wme->data; 1474 const char *ws; 1475 1476 data->lineflag = LINE_SEL_LEFT_RIGHT; 1477 data->rectflag = 0; 1478 1479 ws = options_get_string(s->options, "word-separators"); 1480 window_copy_cursor_previous_word(wme, ws); 1481 window_copy_start_selection(wme); 1482 window_copy_cursor_next_word_end(wme, ws); 1483 1484 return (WINDOW_COPY_CMD_REDRAW); 1485 } 1486 1487 static enum window_copy_cmd_action 1488 window_copy_cmd_start_of_line(struct window_copy_cmd_state *cs) 1489 { 1490 struct window_mode_entry *wme = cs->wme; 1491 1492 window_copy_cursor_start_of_line(wme); 1493 return (WINDOW_COPY_CMD_NOTHING); 1494 } 1495 1496 static enum window_copy_cmd_action 1497 window_copy_cmd_top_line(struct window_copy_cmd_state *cs) 1498 { 1499 struct window_mode_entry *wme = cs->wme; 1500 struct window_copy_mode_data *data = wme->data; 1501 1502 data->cx = 0; 1503 data->cy = 0; 1504 1505 window_copy_update_selection(wme, 1); 1506 return (WINDOW_COPY_CMD_REDRAW); 1507 } 1508 1509 static enum window_copy_cmd_action 1510 window_copy_cmd_copy_pipe_no_clear(struct window_copy_cmd_state *cs) 1511 { 1512 struct window_mode_entry *wme = cs->wme; 1513 struct client *c = cs->c; 1514 struct session *s = cs->s; 1515 struct winlink *wl = cs->wl; 1516 struct window_pane *wp = wme->wp; 1517 char *command = NULL; 1518 char *prefix = NULL; 1519 1520 if (cs->args->argc == 3) 1521 prefix = format_single(NULL, cs->args->argv[2], c, s, wl, wp); 1522 1523 if (s != NULL && *cs->args->argv[1] != '\0') { 1524 command = format_single(NULL, cs->args->argv[1], c, s, wl, wp); 1525 window_copy_copy_pipe(wme, s, prefix, command); 1526 free(command); 1527 } 1528 1529 free(prefix); 1530 return (WINDOW_COPY_CMD_NOTHING); 1531 } 1532 1533 static enum window_copy_cmd_action 1534 window_copy_cmd_copy_pipe(struct window_copy_cmd_state *cs) 1535 { 1536 struct window_mode_entry *wme = cs->wme; 1537 1538 window_copy_cmd_copy_pipe_no_clear(cs); 1539 window_copy_clear_selection(wme); 1540 return (WINDOW_COPY_CMD_REDRAW); 1541 } 1542 1543 static enum window_copy_cmd_action 1544 window_copy_cmd_copy_pipe_and_cancel(struct window_copy_cmd_state *cs) 1545 { 1546 struct window_mode_entry *wme = cs->wme; 1547 1548 window_copy_cmd_copy_pipe_no_clear(cs); 1549 window_copy_clear_selection(wme); 1550 return (WINDOW_COPY_CMD_CANCEL); 1551 } 1552 1553 static enum window_copy_cmd_action 1554 window_copy_cmd_goto_line(struct window_copy_cmd_state *cs) 1555 { 1556 struct window_mode_entry *wme = cs->wme; 1557 const char *argument = cs->args->argv[1]; 1558 1559 if (*argument != '\0') 1560 window_copy_goto_line(wme, argument); 1561 return (WINDOW_COPY_CMD_NOTHING); 1562 } 1563 1564 static enum window_copy_cmd_action 1565 window_copy_cmd_jump_backward(struct window_copy_cmd_state *cs) 1566 { 1567 struct window_mode_entry *wme = cs->wme; 1568 struct window_copy_mode_data *data = wme->data; 1569 u_int np = wme->prefix; 1570 const char *argument = cs->args->argv[1]; 1571 1572 if (*argument != '\0') { 1573 data->jumptype = WINDOW_COPY_JUMPBACKWARD; 1574 data->jumpchar = *argument; 1575 for (; np != 0; np--) 1576 window_copy_cursor_jump_back(wme); 1577 } 1578 return (WINDOW_COPY_CMD_NOTHING); 1579 } 1580 1581 static enum window_copy_cmd_action 1582 window_copy_cmd_jump_forward(struct window_copy_cmd_state *cs) 1583 { 1584 struct window_mode_entry *wme = cs->wme; 1585 struct window_copy_mode_data *data = wme->data; 1586 u_int np = wme->prefix; 1587 const char *argument = cs->args->argv[1]; 1588 1589 if (*argument != '\0') { 1590 data->jumptype = WINDOW_COPY_JUMPFORWARD; 1591 data->jumpchar = *argument; 1592 for (; np != 0; np--) 1593 window_copy_cursor_jump(wme); 1594 } 1595 return (WINDOW_COPY_CMD_NOTHING); 1596 } 1597 1598 static enum window_copy_cmd_action 1599 window_copy_cmd_jump_to_backward(struct window_copy_cmd_state *cs) 1600 { 1601 struct window_mode_entry *wme = cs->wme; 1602 struct window_copy_mode_data *data = wme->data; 1603 u_int np = wme->prefix; 1604 const char *argument = cs->args->argv[1]; 1605 1606 if (*argument != '\0') { 1607 data->jumptype = WINDOW_COPY_JUMPTOBACKWARD; 1608 data->jumpchar = *argument; 1609 for (; np != 0; np--) 1610 window_copy_cursor_jump_to_back(wme); 1611 } 1612 return (WINDOW_COPY_CMD_NOTHING); 1613 } 1614 1615 static enum window_copy_cmd_action 1616 window_copy_cmd_jump_to_forward(struct window_copy_cmd_state *cs) 1617 { 1618 struct window_mode_entry *wme = cs->wme; 1619 struct window_copy_mode_data *data = wme->data; 1620 u_int np = wme->prefix; 1621 const char *argument = cs->args->argv[1]; 1622 1623 if (*argument != '\0') { 1624 data->jumptype = WINDOW_COPY_JUMPTOFORWARD; 1625 data->jumpchar = *argument; 1626 for (; np != 0; np--) 1627 window_copy_cursor_jump_to(wme); 1628 } 1629 return (WINDOW_COPY_CMD_NOTHING); 1630 } 1631 1632 static enum window_copy_cmd_action 1633 window_copy_cmd_search_backward(struct window_copy_cmd_state *cs) 1634 { 1635 struct window_mode_entry *wme = cs->wme; 1636 struct window_copy_mode_data *data = wme->data; 1637 u_int np = wme->prefix; 1638 const char *argument = cs->args->argv[1]; 1639 1640 if (*argument != '\0') { 1641 data->searchtype = WINDOW_COPY_SEARCHUP; 1642 free(data->searchstr); 1643 data->searchstr = xstrdup(argument); 1644 for (; np != 0; np--) 1645 window_copy_search_up(wme); 1646 } 1647 return (WINDOW_COPY_CMD_NOTHING); 1648 } 1649 1650 static enum window_copy_cmd_action 1651 window_copy_cmd_search_forward(struct window_copy_cmd_state *cs) 1652 { 1653 struct window_mode_entry *wme = cs->wme; 1654 struct window_copy_mode_data *data = wme->data; 1655 u_int np = wme->prefix; 1656 const char *argument = cs->args->argv[1]; 1657 1658 if (*argument != '\0') { 1659 data->searchtype = WINDOW_COPY_SEARCHDOWN; 1660 free(data->searchstr); 1661 data->searchstr = xstrdup(argument); 1662 for (; np != 0; np--) 1663 window_copy_search_down(wme); 1664 } 1665 return (WINDOW_COPY_CMD_NOTHING); 1666 } 1667 1668 static enum window_copy_cmd_action 1669 window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) 1670 { 1671 struct window_mode_entry *wme = cs->wme; 1672 struct window_copy_mode_data *data = wme->data; 1673 const char *argument = cs->args->argv[1]; 1674 const char *ss = data->searchstr; 1675 char prefix; 1676 enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING; 1677 1678 prefix = *argument++; 1679 if (data->searchx == -1 || data->searchy == -1) { 1680 data->searchx = data->cx; 1681 data->searchy = data->cy; 1682 data->searcho = data->oy; 1683 } else if (ss != NULL && strcmp(argument, ss) != 0) { 1684 data->cx = data->searchx; 1685 data->cy = data->searchy; 1686 data->oy = data->searcho; 1687 action = WINDOW_COPY_CMD_REDRAW; 1688 } 1689 if (*argument == '\0') { 1690 window_copy_clear_marks(wme); 1691 return (WINDOW_COPY_CMD_REDRAW); 1692 } 1693 switch (prefix) { 1694 case '=': 1695 case '-': 1696 data->searchtype = WINDOW_COPY_SEARCHUP; 1697 free(data->searchstr); 1698 data->searchstr = xstrdup(argument); 1699 if (!window_copy_search_up(wme)) { 1700 window_copy_clear_marks(wme); 1701 return (WINDOW_COPY_CMD_REDRAW); 1702 } 1703 break; 1704 case '+': 1705 data->searchtype = WINDOW_COPY_SEARCHDOWN; 1706 free(data->searchstr); 1707 data->searchstr = xstrdup(argument); 1708 if (!window_copy_search_down(wme)) { 1709 window_copy_clear_marks(wme); 1710 return (WINDOW_COPY_CMD_REDRAW); 1711 } 1712 break; 1713 } 1714 return (action); 1715 } 1716 1717 static enum window_copy_cmd_action 1718 window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) 1719 { 1720 struct window_mode_entry *wme = cs->wme; 1721 struct window_copy_mode_data *data = wme->data; 1722 const char *argument = cs->args->argv[1]; 1723 const char *ss = data->searchstr; 1724 char prefix; 1725 enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING; 1726 1727 prefix = *argument++; 1728 if (data->searchx == -1 || data->searchy == -1) { 1729 data->searchx = data->cx; 1730 data->searchy = data->cy; 1731 data->searcho = data->oy; 1732 } else if (ss != NULL && strcmp(argument, ss) != 0) { 1733 data->cx = data->searchx; 1734 data->cy = data->searchy; 1735 data->oy = data->searcho; 1736 action = WINDOW_COPY_CMD_REDRAW; 1737 } 1738 if (*argument == '\0') { 1739 window_copy_clear_marks(wme); 1740 return (WINDOW_COPY_CMD_REDRAW); 1741 } 1742 switch (prefix) { 1743 case '=': 1744 case '+': 1745 data->searchtype = WINDOW_COPY_SEARCHDOWN; 1746 free(data->searchstr); 1747 data->searchstr = xstrdup(argument); 1748 if (!window_copy_search_down(wme)) { 1749 window_copy_clear_marks(wme); 1750 return (WINDOW_COPY_CMD_REDRAW); 1751 } 1752 break; 1753 case '-': 1754 data->searchtype = WINDOW_COPY_SEARCHUP; 1755 free(data->searchstr); 1756 data->searchstr = xstrdup(argument); 1757 if (!window_copy_search_up(wme)) { 1758 window_copy_clear_marks(wme); 1759 return (WINDOW_COPY_CMD_REDRAW); 1760 } 1761 } 1762 return (action); 1763 } 1764 1765 static const struct { 1766 const char *command; 1767 int minargs; 1768 int maxargs; 1769 enum window_copy_cmd_action (*f)(struct window_copy_cmd_state *); 1770 } window_copy_cmd_table[] = { 1771 { "append-selection", 0, 0, 1772 window_copy_cmd_append_selection }, 1773 { "append-selection-and-cancel", 0, 0, 1774 window_copy_cmd_append_selection_and_cancel }, 1775 { "back-to-indentation", 0, 0, 1776 window_copy_cmd_back_to_indentation }, 1777 { "begin-selection", 0, 0, 1778 window_copy_cmd_begin_selection }, 1779 { "bottom-line", 0, 0, 1780 window_copy_cmd_bottom_line }, 1781 { "cancel", 0, 0, 1782 window_copy_cmd_cancel }, 1783 { "clear-selection", 0, 0, 1784 window_copy_cmd_clear_selection }, 1785 { "copy-end-of-line", 0, 1, 1786 window_copy_cmd_copy_end_of_line }, 1787 { "copy-line", 0, 1, 1788 window_copy_cmd_copy_line }, 1789 { "copy-pipe-no-clear", 1, 2, 1790 window_copy_cmd_copy_pipe_no_clear }, 1791 { "copy-pipe", 1, 2, 1792 window_copy_cmd_copy_pipe }, 1793 { "copy-pipe-and-cancel", 1, 2, 1794 window_copy_cmd_copy_pipe_and_cancel }, 1795 { "copy-selection-no-clear", 0, 1, 1796 window_copy_cmd_copy_selection_no_clear }, 1797 { "copy-selection", 0, 1, 1798 window_copy_cmd_copy_selection }, 1799 { "copy-selection-and-cancel", 0, 1, 1800 window_copy_cmd_copy_selection_and_cancel }, 1801 { "cursor-down", 0, 0, 1802 window_copy_cmd_cursor_down }, 1803 { "cursor-left", 0, 0, 1804 window_copy_cmd_cursor_left }, 1805 { "cursor-right", 0, 0, 1806 window_copy_cmd_cursor_right }, 1807 { "cursor-up", 0, 0, 1808 window_copy_cmd_cursor_up }, 1809 { "end-of-line", 0, 0, 1810 window_copy_cmd_end_of_line }, 1811 { "goto-line", 1, 1, 1812 window_copy_cmd_goto_line }, 1813 { "halfpage-down", 0, 0, 1814 window_copy_cmd_halfpage_down }, 1815 { "halfpage-down-and-cancel", 0, 0, 1816 window_copy_cmd_halfpage_down_and_cancel }, 1817 { "halfpage-up", 0, 0, 1818 window_copy_cmd_halfpage_up }, 1819 { "history-bottom", 0, 0, 1820 window_copy_cmd_history_bottom }, 1821 { "history-top", 0, 0, 1822 window_copy_cmd_history_top }, 1823 { "jump-again", 0, 0, 1824 window_copy_cmd_jump_again }, 1825 { "jump-backward", 1, 1, 1826 window_copy_cmd_jump_backward }, 1827 { "jump-forward", 1, 1, 1828 window_copy_cmd_jump_forward }, 1829 { "jump-reverse", 0, 0, 1830 window_copy_cmd_jump_reverse }, 1831 { "jump-to-backward", 1, 1, 1832 window_copy_cmd_jump_to_backward }, 1833 { "jump-to-forward", 1, 1, 1834 window_copy_cmd_jump_to_forward }, 1835 { "middle-line", 0, 0, 1836 window_copy_cmd_middle_line }, 1837 { "next-matching-bracket", 0, 0, 1838 window_copy_cmd_next_matching_bracket }, 1839 { "next-paragraph", 0, 0, 1840 window_copy_cmd_next_paragraph }, 1841 { "next-space", 0, 0, 1842 window_copy_cmd_next_space }, 1843 { "next-space-end", 0, 0, 1844 window_copy_cmd_next_space_end }, 1845 { "next-word", 0, 0, 1846 window_copy_cmd_next_word }, 1847 { "next-word-end", 0, 0, 1848 window_copy_cmd_next_word_end }, 1849 { "other-end", 0, 0, 1850 window_copy_cmd_other_end }, 1851 { "page-down", 0, 0, 1852 window_copy_cmd_page_down }, 1853 { "page-down-and-cancel", 0, 0, 1854 window_copy_cmd_page_down_and_cancel }, 1855 { "page-up", 0, 0, 1856 window_copy_cmd_page_up }, 1857 { "previous-matching-bracket", 0, 0, 1858 window_copy_cmd_previous_matching_bracket }, 1859 { "previous-paragraph", 0, 0, 1860 window_copy_cmd_previous_paragraph }, 1861 { "previous-space", 0, 0, 1862 window_copy_cmd_previous_space }, 1863 { "previous-word", 0, 0, 1864 window_copy_cmd_previous_word }, 1865 { "rectangle-toggle", 0, 0, 1866 window_copy_cmd_rectangle_toggle }, 1867 { "scroll-down", 0, 0, 1868 window_copy_cmd_scroll_down }, 1869 { "scroll-down-and-cancel", 0, 0, 1870 window_copy_cmd_scroll_down_and_cancel }, 1871 { "scroll-up", 0, 0, 1872 window_copy_cmd_scroll_up }, 1873 { "search-again", 0, 0, 1874 window_copy_cmd_search_again }, 1875 { "search-backward", 1, 1, 1876 window_copy_cmd_search_backward }, 1877 { "search-backward-incremental", 1, 1, 1878 window_copy_cmd_search_backward_incremental }, 1879 { "search-forward", 1, 1, 1880 window_copy_cmd_search_forward }, 1881 { "search-forward-incremental", 1, 1, 1882 window_copy_cmd_search_forward_incremental }, 1883 { "search-reverse", 0, 0, 1884 window_copy_cmd_search_reverse }, 1885 { "select-line", 0, 0, 1886 window_copy_cmd_select_line }, 1887 { "select-word", 0, 0, 1888 window_copy_cmd_select_word }, 1889 { "start-of-line", 0, 0, 1890 window_copy_cmd_start_of_line }, 1891 { "stop-selection", 0, 0, 1892 window_copy_cmd_stop_selection }, 1893 { "top-line", 0, 0, 1894 window_copy_cmd_top_line }, 1895 }; 1896 1897 static void 1898 window_copy_command(struct window_mode_entry *wme, struct client *c, 1899 struct session *s, struct winlink *wl, struct args *args, 1900 struct mouse_event *m) 1901 { 1902 struct window_copy_mode_data *data = wme->data; 1903 struct window_copy_cmd_state cs; 1904 enum window_copy_cmd_action action; 1905 const char *command; 1906 u_int i; 1907 1908 if (args->argc == 0) 1909 return; 1910 command = args->argv[0]; 1911 1912 if (m != NULL && m->valid && !MOUSE_WHEEL(m->b)) 1913 window_copy_move_mouse(m); 1914 1915 cs.wme = wme; 1916 cs.args = args; 1917 cs.m = m; 1918 1919 cs.c = c; 1920 cs.s = s; 1921 cs.wl = wl; 1922 1923 action = WINDOW_COPY_CMD_NOTHING; 1924 for (i = 0; i < nitems(window_copy_cmd_table); i++) { 1925 if (strcmp(window_copy_cmd_table[i].command, command) == 0) { 1926 if (args->argc - 1 < window_copy_cmd_table[i].minargs || 1927 args->argc - 1 > window_copy_cmd_table[i].maxargs) 1928 break; 1929 action = window_copy_cmd_table[i].f (&cs); 1930 break; 1931 } 1932 } 1933 1934 if (strncmp(command, "search-", 7) != 0 && data->searchmark != NULL) { 1935 window_copy_clear_marks(wme); 1936 if (action == WINDOW_COPY_CMD_NOTHING) 1937 action = WINDOW_COPY_CMD_REDRAW; 1938 data->searchx = data->searchy = -1; 1939 } 1940 wme->prefix = 1; 1941 1942 if (action == WINDOW_COPY_CMD_CANCEL) 1943 window_pane_reset_mode(wme->wp); 1944 else if (action == WINDOW_COPY_CMD_REDRAW) 1945 window_copy_redraw_screen(wme); 1946 } 1947 1948 static void 1949 window_copy_scroll_to(struct window_mode_entry *wme, u_int px, u_int py) 1950 { 1951 struct window_copy_mode_data *data = wme->data; 1952 struct grid *gd = data->backing->grid; 1953 u_int offset, gap; 1954 1955 data->cx = px; 1956 1957 if (py >= gd->hsize - data->oy && py < gd->hsize - data->oy + gd->sy) 1958 data->cy = py - (gd->hsize - data->oy); 1959 else { 1960 gap = gd->sy / 4; 1961 if (py < gd->sy) { 1962 offset = 0; 1963 data->cy = py; 1964 } else if (py > gd->hsize + gd->sy - gap) { 1965 offset = gd->hsize; 1966 data->cy = py - gd->hsize; 1967 } else { 1968 offset = py + gap - gd->sy; 1969 data->cy = py - offset; 1970 } 1971 data->oy = gd->hsize - offset; 1972 } 1973 1974 window_copy_update_selection(wme, 1); 1975 window_copy_redraw_screen(wme); 1976 } 1977 1978 static int 1979 window_copy_search_compare(struct grid *gd, u_int px, u_int py, 1980 struct grid *sgd, u_int spx, int cis) 1981 { 1982 struct grid_cell gc, sgc; 1983 const struct utf8_data *ud, *sud; 1984 1985 grid_get_cell(gd, px, py, &gc); 1986 ud = &gc.data; 1987 grid_get_cell(sgd, spx, 0, &sgc); 1988 sud = &sgc.data; 1989 1990 if (ud->size != sud->size || ud->width != sud->width) 1991 return (0); 1992 1993 if (cis && ud->size == 1) 1994 return (tolower(ud->data[0]) == sud->data[0]); 1995 1996 return (memcmp(ud->data, sud->data, ud->size) == 0); 1997 } 1998 1999 static int 2000 window_copy_search_lr(struct grid *gd, 2001 struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis) 2002 { 2003 u_int ax, bx, px; 2004 int matched; 2005 2006 for (ax = first; ax < last; ax++) { 2007 if (ax + sgd->sx > gd->sx) 2008 break; 2009 for (bx = 0; bx < sgd->sx; bx++) { 2010 px = ax + bx; 2011 matched = window_copy_search_compare(gd, px, py, sgd, 2012 bx, cis); 2013 if (!matched) 2014 break; 2015 } 2016 if (bx == sgd->sx) { 2017 *ppx = ax; 2018 return (1); 2019 } 2020 } 2021 return (0); 2022 } 2023 2024 static int 2025 window_copy_search_rl(struct grid *gd, 2026 struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis) 2027 { 2028 u_int ax, bx, px; 2029 int matched; 2030 2031 for (ax = last + 1; ax > first; ax--) { 2032 if (gd->sx - (ax - 1) < sgd->sx) 2033 continue; 2034 for (bx = 0; bx < sgd->sx; bx++) { 2035 px = ax - 1 + bx; 2036 matched = window_copy_search_compare(gd, px, py, sgd, 2037 bx, cis); 2038 if (!matched) 2039 break; 2040 } 2041 if (bx == sgd->sx) { 2042 *ppx = ax - 1; 2043 return (1); 2044 } 2045 } 2046 return (0); 2047 } 2048 2049 static void 2050 window_copy_move_left(struct screen *s, u_int *fx, u_int *fy) 2051 { 2052 if (*fx == 0) { /* left */ 2053 if (*fy == 0) /* top */ 2054 return; 2055 *fx = screen_size_x(s) - 1; 2056 *fy = *fy - 1; 2057 } else 2058 *fx = *fx - 1; 2059 } 2060 2061 static void 2062 window_copy_move_right(struct screen *s, u_int *fx, u_int *fy) 2063 { 2064 if (*fx == screen_size_x(s) - 1) { /* right */ 2065 if (*fy == screen_hsize(s) + screen_size_y(s)) /* bottom */ 2066 return; 2067 *fx = 0; 2068 *fy = *fy + 1; 2069 } else 2070 *fx = *fx + 1; 2071 } 2072 2073 static int 2074 window_copy_is_lowercase(const char *ptr) 2075 { 2076 while (*ptr != '\0') { 2077 if (*ptr != tolower((u_char)*ptr)) 2078 return (0); 2079 ++ptr; 2080 } 2081 return (1); 2082 } 2083 2084 /* 2085 * Search for text stored in sgd starting from position fx,fy up to endline. If 2086 * found, jump to it. If cis then ignore case. The direction is 0 for searching 2087 * up, down otherwise. If wrap then go to begin/end of grid and try again if 2088 * not found. 2089 */ 2090 static int 2091 window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, 2092 struct grid *sgd, u_int fx, u_int fy, u_int endline, int cis, int wrap, 2093 int direction) 2094 { 2095 u_int i, px; 2096 int found; 2097 2098 found = 0; 2099 if (direction) { 2100 for (i = fy; i <= endline; i++) { 2101 found = window_copy_search_lr(gd, sgd, &px, i, fx, 2102 gd->sx, cis); 2103 if (found) 2104 break; 2105 fx = 0; 2106 } 2107 } else { 2108 for (i = fy + 1; endline < i; i--) { 2109 found = window_copy_search_rl(gd, sgd, &px, i - 1, 0, 2110 fx, cis); 2111 if (found) { 2112 i--; 2113 break; 2114 } 2115 fx = gd->sx; 2116 } 2117 } 2118 2119 if (found) { 2120 window_copy_scroll_to(wme, px, i); 2121 return (1); 2122 } 2123 if (wrap) { 2124 return (window_copy_search_jump(wme, gd, sgd, 2125 direction ? 0 : gd->sx - 1, 2126 direction ? 0 : gd->hsize + gd->sy - 1, fy, cis, 0, 2127 direction)); 2128 } 2129 return (0); 2130 } 2131 2132 /* 2133 * Search in for text searchstr. If direction is 0 then search up, otherwise 2134 * down. 2135 */ 2136 static int 2137 window_copy_search(struct window_mode_entry *wme, int direction) 2138 { 2139 struct window_pane *wp = wme->wp; 2140 struct window_copy_mode_data *data = wme->data; 2141 struct screen *s = data->backing, ss; 2142 struct screen_write_ctx ctx; 2143 struct grid *gd = s->grid; 2144 u_int fx, fy, endline; 2145 int wrapflag, cis, found; 2146 2147 free(wp->searchstr); 2148 wp->searchstr = xstrdup(data->searchstr); 2149 2150 fx = data->cx; 2151 fy = screen_hsize(data->backing) - data->oy + data->cy; 2152 2153 screen_init(&ss, screen_write_strlen("%s", data->searchstr), 1, 0); 2154 screen_write_start(&ctx, NULL, &ss); 2155 screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", data->searchstr); 2156 screen_write_stop(&ctx); 2157 2158 if (direction) 2159 window_copy_move_right(s, &fx, &fy); 2160 else 2161 window_copy_move_left(s, &fx, &fy); 2162 2163 wrapflag = options_get_number(wp->window->options, "wrap-search"); 2164 cis = window_copy_is_lowercase(data->searchstr); 2165 2166 if (direction) 2167 endline = gd->hsize + gd->sy - 1; 2168 else 2169 endline = 0; 2170 found = window_copy_search_jump(wme, gd, ss.grid, fx, fy, endline, cis, 2171 wrapflag, direction); 2172 2173 if (window_copy_search_marks(wme, &ss)) 2174 window_copy_redraw_screen(wme); 2175 2176 screen_free(&ss); 2177 return (found); 2178 } 2179 2180 static int 2181 window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp) 2182 { 2183 struct window_copy_mode_data *data = wme->data; 2184 struct screen *s = data->backing, ss; 2185 struct screen_write_ctx ctx; 2186 struct grid *gd = s->grid; 2187 int found, cis, which = -1; 2188 u_int px, py, b, nfound = 0, width; 2189 2190 if (ssp == NULL) { 2191 width = screen_write_strlen("%s", data->searchstr); 2192 screen_init(&ss, width, 1, 0); 2193 screen_write_start(&ctx, NULL, &ss); 2194 screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", 2195 data->searchstr); 2196 screen_write_stop(&ctx); 2197 ssp = &ss; 2198 } else 2199 width = screen_size_x(ssp); 2200 2201 cis = window_copy_is_lowercase(data->searchstr); 2202 2203 free(data->searchmark); 2204 data->searchmark = bit_alloc((gd->hsize + gd->sy) * gd->sx); 2205 2206 for (py = 0; py < gd->hsize + gd->sy; py++) { 2207 px = 0; 2208 for (;;) { 2209 found = window_copy_search_lr(gd, ssp->grid, &px, py, 2210 px, gd->sx, cis); 2211 if (!found) 2212 break; 2213 2214 nfound++; 2215 if (px == data->cx && py == gd->hsize + data->cy - data->oy) 2216 which = nfound; 2217 2218 b = (py * gd->sx) + px; 2219 bit_nset(data->searchmark, b, b + width - 1); 2220 2221 px++; 2222 } 2223 } 2224 2225 if (which != -1) 2226 data->searchthis = 1 + nfound - which; 2227 else 2228 data->searchthis = -1; 2229 data->searchcount = nfound; 2230 2231 if (ssp == &ss) 2232 screen_free(&ss); 2233 return (nfound); 2234 } 2235 2236 static void 2237 window_copy_clear_marks(struct window_mode_entry *wme) 2238 { 2239 struct window_copy_mode_data *data = wme->data; 2240 2241 free(data->searchmark); 2242 data->searchmark = NULL; 2243 } 2244 2245 static int 2246 window_copy_search_up(struct window_mode_entry *wme) 2247 { 2248 return (window_copy_search(wme, 0)); 2249 } 2250 2251 static int 2252 window_copy_search_down(struct window_mode_entry *wme) 2253 { 2254 return (window_copy_search(wme, 1)); 2255 } 2256 2257 static void 2258 window_copy_goto_line(struct window_mode_entry *wme, const char *linestr) 2259 { 2260 struct window_copy_mode_data *data = wme->data; 2261 const char *errstr; 2262 int lineno; 2263 2264 lineno = strtonum(linestr, -1, INT_MAX, &errstr); 2265 if (errstr != NULL) 2266 return; 2267 if (lineno < 0 || (u_int)lineno > screen_hsize(data->backing)) 2268 lineno = screen_hsize(data->backing); 2269 2270 data->oy = lineno; 2271 window_copy_update_selection(wme, 1); 2272 window_copy_redraw_screen(wme); 2273 } 2274 2275 static void 2276 window_copy_write_line(struct window_mode_entry *wme, 2277 struct screen_write_ctx *ctx, u_int py) 2278 { 2279 struct window_pane *wp = wme->wp; 2280 struct window_copy_mode_data *data = wme->data; 2281 struct screen *s = &data->screen; 2282 struct options *oo = wp->window->options; 2283 struct grid_cell gc; 2284 char hdr[512]; 2285 size_t size = 0; 2286 2287 style_apply(&gc, oo, "mode-style"); 2288 gc.flags |= GRID_FLAG_NOPALETTE; 2289 2290 if (py == 0 && s->rupper < s->rlower) { 2291 if (data->searchmark == NULL) { 2292 size = xsnprintf(hdr, sizeof hdr, 2293 "[%u/%u]", data->oy, screen_hsize(data->backing)); 2294 } else { 2295 if (data->searchthis == -1) { 2296 size = xsnprintf(hdr, sizeof hdr, 2297 "(%u results) [%d/%u]", data->searchcount, 2298 data->oy, screen_hsize(data->backing)); 2299 } else { 2300 size = xsnprintf(hdr, sizeof hdr, 2301 "(%u/%u results) [%d/%u]", data->searchthis, 2302 data->searchcount, data->oy, 2303 screen_hsize(data->backing)); 2304 } 2305 } 2306 if (size > screen_size_x(s)) 2307 size = screen_size_x(s); 2308 screen_write_cursormove(ctx, screen_size_x(s) - size, 0, 0); 2309 screen_write_puts(ctx, &gc, "%s", hdr); 2310 } else 2311 size = 0; 2312 2313 if (size < screen_size_x(s)) { 2314 screen_write_cursormove(ctx, 0, py, 0); 2315 screen_write_copy(ctx, data->backing, 0, 2316 (screen_hsize(data->backing) - data->oy) + py, 2317 screen_size_x(s) - size, 1, data->searchmark, &gc); 2318 } 2319 2320 if (py == data->cy && data->cx == screen_size_x(s)) { 2321 memcpy(&gc, &grid_default_cell, sizeof gc); 2322 screen_write_cursormove(ctx, screen_size_x(s) - 1, py, 0); 2323 screen_write_putc(ctx, &gc, '$'); 2324 } 2325 } 2326 2327 static void 2328 window_copy_write_lines(struct window_mode_entry *wme, 2329 struct screen_write_ctx *ctx, u_int py, u_int ny) 2330 { 2331 u_int yy; 2332 2333 for (yy = py; yy < py + ny; yy++) 2334 window_copy_write_line(wme, ctx, py); 2335 } 2336 2337 static void 2338 window_copy_redraw_selection(struct window_mode_entry *wme, u_int old_y) 2339 { 2340 struct window_copy_mode_data *data = wme->data; 2341 u_int new_y, start, end; 2342 2343 new_y = data->cy; 2344 if (old_y <= new_y) { 2345 start = old_y; 2346 end = new_y; 2347 } else { 2348 start = new_y; 2349 end = old_y; 2350 } 2351 window_copy_redraw_lines(wme, start, end - start + 1); 2352 } 2353 2354 static void 2355 window_copy_redraw_lines(struct window_mode_entry *wme, u_int py, u_int ny) 2356 { 2357 struct window_pane *wp = wme->wp; 2358 struct window_copy_mode_data *data = wme->data; 2359 struct screen_write_ctx ctx; 2360 u_int i; 2361 2362 screen_write_start(&ctx, wp, NULL); 2363 for (i = py; i < py + ny; i++) 2364 window_copy_write_line(wme, &ctx, i); 2365 screen_write_cursormove(&ctx, data->cx, data->cy, 0); 2366 screen_write_stop(&ctx); 2367 } 2368 2369 static void 2370 window_copy_redraw_screen(struct window_mode_entry *wme) 2371 { 2372 struct window_copy_mode_data *data = wme->data; 2373 2374 window_copy_redraw_lines(wme, 0, screen_size_y(&data->screen)); 2375 } 2376 2377 static void 2378 window_copy_synchronize_cursor(struct window_mode_entry *wme) 2379 { 2380 struct window_copy_mode_data *data = wme->data; 2381 u_int xx, yy; 2382 2383 xx = data->cx; 2384 yy = screen_hsize(data->backing) + data->cy - data->oy; 2385 2386 switch (data->cursordrag) { 2387 case CURSORDRAG_ENDSEL: 2388 data->endselx = xx; 2389 data->endsely = yy; 2390 break; 2391 case CURSORDRAG_SEL: 2392 data->selx = xx; 2393 data->sely = yy; 2394 break; 2395 case CURSORDRAG_NONE: 2396 break; 2397 } 2398 } 2399 2400 static void 2401 window_copy_update_cursor(struct window_mode_entry *wme, u_int cx, u_int cy) 2402 { 2403 struct window_pane *wp = wme->wp; 2404 struct window_copy_mode_data *data = wme->data; 2405 struct screen *s = &data->screen; 2406 struct screen_write_ctx ctx; 2407 u_int old_cx, old_cy; 2408 2409 old_cx = data->cx; old_cy = data->cy; 2410 data->cx = cx; data->cy = cy; 2411 if (old_cx == screen_size_x(s)) 2412 window_copy_redraw_lines(wme, old_cy, 1); 2413 if (data->cx == screen_size_x(s)) 2414 window_copy_redraw_lines(wme, data->cy, 1); 2415 else { 2416 screen_write_start(&ctx, wp, NULL); 2417 screen_write_cursormove(&ctx, data->cx, data->cy, 0); 2418 screen_write_stop(&ctx); 2419 } 2420 } 2421 2422 static void 2423 window_copy_start_selection(struct window_mode_entry *wme) 2424 { 2425 struct window_copy_mode_data *data = wme->data; 2426 2427 data->selx = data->cx; 2428 data->sely = screen_hsize(data->backing) + data->cy - data->oy; 2429 2430 data->endselx = data->selx; 2431 data->endsely = data->sely; 2432 2433 data->cursordrag = CURSORDRAG_ENDSEL; 2434 2435 window_copy_set_selection(wme, 1); 2436 } 2437 2438 static int 2439 window_copy_adjust_selection(struct window_mode_entry *wme, u_int *selx, 2440 u_int *sely) 2441 { 2442 struct window_copy_mode_data *data = wme->data; 2443 struct screen *s = &data->screen; 2444 u_int sx, sy, ty; 2445 int relpos; 2446 2447 sx = *selx; 2448 sy = *sely; 2449 2450 ty = screen_hsize(data->backing) - data->oy; 2451 if (sy < ty) { 2452 relpos = WINDOW_COPY_REL_POS_ABOVE; 2453 if (!data->rectflag) 2454 sx = 0; 2455 sy = 0; 2456 } else if (sy > ty + screen_size_y(s) - 1) { 2457 relpos = WINDOW_COPY_REL_POS_BELOW; 2458 if (!data->rectflag) 2459 sx = screen_size_x(s) - 1; 2460 sy = screen_size_y(s) - 1; 2461 } else { 2462 relpos = WINDOW_COPY_REL_POS_ON_SCREEN; 2463 sy -= ty; 2464 } 2465 2466 *selx = sx; 2467 *sely = sy; 2468 return (relpos); 2469 } 2470 2471 static int 2472 window_copy_update_selection(struct window_mode_entry *wme, int may_redraw) 2473 { 2474 struct window_copy_mode_data *data = wme->data; 2475 struct screen *s = &data->screen; 2476 2477 if (s->sel == NULL && data->lineflag == LINE_SEL_NONE) 2478 return (0); 2479 return (window_copy_set_selection(wme, may_redraw)); 2480 } 2481 2482 static int 2483 window_copy_set_selection(struct window_mode_entry *wme, int may_redraw) 2484 { 2485 struct window_pane *wp = wme->wp; 2486 struct window_copy_mode_data *data = wme->data; 2487 struct screen *s = &data->screen; 2488 struct options *oo = wp->window->options; 2489 struct grid_cell gc; 2490 u_int sx, sy, cy, endsx, endsy; 2491 int startrelpos, endrelpos; 2492 2493 window_copy_synchronize_cursor(wme); 2494 2495 /* Adjust the selection. */ 2496 sx = data->selx; 2497 sy = data->sely; 2498 startrelpos = window_copy_adjust_selection(wme, &sx, &sy); 2499 2500 /* Adjust the end of selection. */ 2501 endsx = data->endselx; 2502 endsy = data->endsely; 2503 endrelpos = window_copy_adjust_selection(wme, &endsx, &endsy); 2504 2505 /* Selection is outside of the current screen */ 2506 if (startrelpos == endrelpos && 2507 startrelpos != WINDOW_COPY_REL_POS_ON_SCREEN) { 2508 screen_hide_selection(s); 2509 return (0); 2510 } 2511 2512 /* Set colours and selection. */ 2513 style_apply(&gc, oo, "mode-style"); 2514 gc.flags |= GRID_FLAG_NOPALETTE; 2515 screen_set_selection(s, sx, sy, endsx, endsy, data->rectflag, 2516 data->modekeys, &gc); 2517 2518 if (data->rectflag && may_redraw) { 2519 /* 2520 * Can't rely on the caller to redraw the right lines for 2521 * rectangle selection - find the highest line and the number 2522 * of lines, and redraw just past that in both directions 2523 */ 2524 cy = data->cy; 2525 if (data->cursordrag == CURSORDRAG_ENDSEL) { 2526 if (sy < cy) 2527 window_copy_redraw_lines(wme, sy, cy - sy + 1); 2528 else 2529 window_copy_redraw_lines(wme, cy, sy - cy + 1); 2530 } else { 2531 if (endsy < cy) { 2532 window_copy_redraw_lines(wme, endsy, 2533 cy - endsy + 1); 2534 } else { 2535 window_copy_redraw_lines(wme, cy, 2536 endsy - cy + 1); 2537 } 2538 } 2539 } 2540 2541 return (1); 2542 } 2543 2544 static void * 2545 window_copy_get_selection(struct window_mode_entry *wme, size_t *len) 2546 { 2547 struct window_pane *wp = wme->wp; 2548 struct window_copy_mode_data *data = wme->data; 2549 struct screen *s = &data->screen; 2550 char *buf; 2551 size_t off; 2552 u_int i, xx, yy, sx, sy, ex, ey, ey_last; 2553 u_int firstsx, lastex, restex, restsx, selx; 2554 int keys; 2555 2556 if (data->screen.sel == NULL && data->lineflag == LINE_SEL_NONE) 2557 return (NULL); 2558 2559 buf = xmalloc(1); 2560 off = 0; 2561 2562 *buf = '\0'; 2563 2564 /* 2565 * The selection extends from selx,sely to (adjusted) cx,cy on 2566 * the base screen. 2567 */ 2568 2569 /* Find start and end. */ 2570 xx = data->endselx; 2571 yy = data->endsely; 2572 if (yy < data->sely || (yy == data->sely && xx < data->selx)) { 2573 sx = xx; sy = yy; 2574 ex = data->selx; ey = data->sely; 2575 } else { 2576 sx = data->selx; sy = data->sely; 2577 ex = xx; ey = yy; 2578 } 2579 2580 /* Trim ex to end of line. */ 2581 ey_last = window_copy_find_length(wme, ey); 2582 if (ex > ey_last) 2583 ex = ey_last; 2584 2585 /* 2586 * Deal with rectangle-copy if necessary; four situations: start of 2587 * first line (firstsx), end of last line (lastex), start (restsx) and 2588 * end (restex) of all other lines. 2589 */ 2590 xx = screen_size_x(s); 2591 2592 /* 2593 * Behave according to mode-keys. If it is emacs, copy like emacs, 2594 * keeping the top-left-most character, and dropping the 2595 * bottom-right-most, regardless of copy direction. If it is vi, also 2596 * keep bottom-right-most character. 2597 */ 2598 keys = options_get_number(wp->window->options, "mode-keys"); 2599 if (data->rectflag) { 2600 /* 2601 * Need to ignore the column with the cursor in it, which for 2602 * rectangular copy means knowing which side the cursor is on. 2603 */ 2604 if (data->cursordrag == CURSORDRAG_ENDSEL) 2605 selx = data->selx; 2606 else 2607 selx = data->endselx; 2608 if (selx < data->cx) { 2609 /* Selection start is on the left. */ 2610 if (keys == MODEKEY_EMACS) { 2611 lastex = data->cx; 2612 restex = data->cx; 2613 } 2614 else { 2615 lastex = data->cx + 1; 2616 restex = data->cx + 1; 2617 } 2618 firstsx = selx; 2619 restsx = selx; 2620 } else { 2621 /* Cursor is on the left. */ 2622 lastex = selx + 1; 2623 restex = selx + 1; 2624 firstsx = data->cx; 2625 restsx = data->cx; 2626 } 2627 } else { 2628 if (keys == MODEKEY_EMACS) 2629 lastex = ex; 2630 else 2631 lastex = ex + 1; 2632 restex = xx; 2633 firstsx = sx; 2634 restsx = 0; 2635 } 2636 2637 /* Copy the lines. */ 2638 for (i = sy; i <= ey; i++) { 2639 window_copy_copy_line(wme, &buf, &off, i, 2640 (i == sy ? firstsx : restsx), 2641 (i == ey ? lastex : restex)); 2642 } 2643 2644 /* Don't bother if no data. */ 2645 if (off == 0) { 2646 free(buf); 2647 return (NULL); 2648 } 2649 if (keys == MODEKEY_EMACS || lastex <= ey_last) 2650 off -= 1; /* remove final \n (unless at end in vi mode) */ 2651 *len = off; 2652 return (buf); 2653 } 2654 2655 static void 2656 window_copy_copy_buffer(struct window_mode_entry *wme, const char *prefix, 2657 void *buf, size_t len) 2658 { 2659 struct window_pane *wp = wme->wp; 2660 struct screen_write_ctx ctx; 2661 2662 if (options_get_number(global_options, "set-clipboard") != 0) { 2663 screen_write_start(&ctx, wp, NULL); 2664 screen_write_setselection(&ctx, buf, len); 2665 screen_write_stop(&ctx); 2666 notify_pane("pane-set-clipboard", wp); 2667 } 2668 2669 paste_add(prefix, buf, len); 2670 } 2671 2672 static void 2673 window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s, 2674 const char *prefix, const char *command) 2675 { 2676 void *buf; 2677 size_t len; 2678 struct job *job; 2679 2680 buf = window_copy_get_selection(wme, &len); 2681 if (buf == NULL) 2682 return; 2683 2684 job = job_run(command, s, NULL, NULL, NULL, NULL, NULL, JOB_NOWAIT); 2685 bufferevent_write(job_get_event(job), buf, len); 2686 window_copy_copy_buffer(wme, prefix, buf, len); 2687 } 2688 2689 static void 2690 window_copy_copy_selection(struct window_mode_entry *wme, const char *prefix) 2691 { 2692 char *buf; 2693 size_t len; 2694 2695 buf = window_copy_get_selection(wme, &len); 2696 if (buf != NULL) 2697 window_copy_copy_buffer(wme, prefix, buf, len); 2698 } 2699 2700 static void 2701 window_copy_append_selection(struct window_mode_entry *wme) 2702 { 2703 struct window_pane *wp = wme->wp; 2704 char *buf; 2705 struct paste_buffer *pb; 2706 const char *bufdata, *bufname = NULL; 2707 size_t len, bufsize; 2708 struct screen_write_ctx ctx; 2709 2710 buf = window_copy_get_selection(wme, &len); 2711 if (buf == NULL) 2712 return; 2713 2714 if (options_get_number(global_options, "set-clipboard") != 0) { 2715 screen_write_start(&ctx, wp, NULL); 2716 screen_write_setselection(&ctx, (u_char *)buf, len); 2717 screen_write_stop(&ctx); 2718 notify_pane("pane-set-clipboard", wp); 2719 } 2720 2721 pb = paste_get_top(&bufname); 2722 if (pb != NULL) { 2723 bufdata = paste_buffer_data(pb, &bufsize); 2724 buf = xrealloc(buf, len + bufsize); 2725 memmove(buf + bufsize, buf, len); 2726 memcpy(buf, bufdata, bufsize); 2727 len += bufsize; 2728 } 2729 if (paste_set(buf, len, bufname, NULL) != 0) 2730 free(buf); 2731 } 2732 2733 static void 2734 window_copy_copy_line(struct window_mode_entry *wme, char **buf, size_t *off, 2735 u_int sy, u_int sx, u_int ex) 2736 { 2737 struct window_copy_mode_data *data = wme->data; 2738 struct grid *gd = data->backing->grid; 2739 struct grid_cell gc; 2740 struct grid_line *gl; 2741 struct utf8_data ud; 2742 u_int i, xx, wrapped = 0; 2743 const char *s; 2744 2745 if (sx > ex) 2746 return; 2747 2748 /* 2749 * Work out if the line was wrapped at the screen edge and all of it is 2750 * on screen. 2751 */ 2752 gl = grid_get_line(gd, sy); 2753 if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx) 2754 wrapped = 1; 2755 2756 /* If the line was wrapped, don't strip spaces (use the full length). */ 2757 if (wrapped) 2758 xx = gl->cellsize; 2759 else 2760 xx = window_copy_find_length(wme, sy); 2761 if (ex > xx) 2762 ex = xx; 2763 if (sx > xx) 2764 sx = xx; 2765 2766 if (sx < ex) { 2767 for (i = sx; i < ex; i++) { 2768 grid_get_cell(gd, i, sy, &gc); 2769 if (gc.flags & GRID_FLAG_PADDING) 2770 continue; 2771 utf8_copy(&ud, &gc.data); 2772 if (ud.size == 1 && (gc.attr & GRID_ATTR_CHARSET)) { 2773 s = tty_acs_get(NULL, ud.data[0]); 2774 if (s != NULL && strlen(s) <= sizeof ud.data) { 2775 ud.size = strlen(s); 2776 memcpy(ud.data, s, ud.size); 2777 } 2778 } 2779 2780 *buf = xrealloc(*buf, (*off) + ud.size); 2781 memcpy(*buf + *off, ud.data, ud.size); 2782 *off += ud.size; 2783 } 2784 } 2785 2786 /* Only add a newline if the line wasn't wrapped. */ 2787 if (!wrapped || ex != xx) { 2788 *buf = xrealloc(*buf, (*off) + 1); 2789 (*buf)[(*off)++] = '\n'; 2790 } 2791 } 2792 2793 static void 2794 window_copy_clear_selection(struct window_mode_entry *wme) 2795 { 2796 struct window_copy_mode_data *data = wme->data; 2797 u_int px, py; 2798 2799 screen_clear_selection(&data->screen); 2800 2801 data->cursordrag = CURSORDRAG_NONE; 2802 data->lineflag = LINE_SEL_NONE; 2803 2804 py = screen_hsize(data->backing) + data->cy - data->oy; 2805 px = window_copy_find_length(wme, py); 2806 if (data->cx > px) 2807 window_copy_update_cursor(wme, px, data->cy); 2808 } 2809 2810 static int 2811 window_copy_in_set(struct window_mode_entry *wme, u_int px, u_int py, 2812 const char *set) 2813 { 2814 struct window_copy_mode_data *data = wme->data; 2815 struct grid_cell gc; 2816 2817 grid_get_cell(data->backing->grid, px, py, &gc); 2818 if (gc.flags & GRID_FLAG_PADDING) 2819 return (0); 2820 return (utf8_cstrhas(set, &gc.data)); 2821 } 2822 2823 static u_int 2824 window_copy_find_length(struct window_mode_entry *wme, u_int py) 2825 { 2826 struct window_copy_mode_data *data = wme->data; 2827 2828 return (grid_line_length(data->backing->grid, py)); 2829 } 2830 2831 static void 2832 window_copy_cursor_start_of_line(struct window_mode_entry *wme) 2833 { 2834 struct window_copy_mode_data *data = wme->data; 2835 struct screen *back_s = data->backing; 2836 struct grid *gd = back_s->grid; 2837 u_int py; 2838 2839 if (data->cx == 0 && data->lineflag == LINE_SEL_NONE) { 2840 py = screen_hsize(back_s) + data->cy - data->oy; 2841 while (py > 0 && 2842 grid_get_line(gd, py - 1)->flags & GRID_LINE_WRAPPED) { 2843 window_copy_cursor_up(wme, 0); 2844 py = screen_hsize(back_s) + data->cy - data->oy; 2845 } 2846 } 2847 window_copy_update_cursor(wme, 0, data->cy); 2848 if (window_copy_update_selection(wme, 1)) 2849 window_copy_redraw_lines(wme, data->cy, 1); 2850 } 2851 2852 static void 2853 window_copy_cursor_back_to_indentation(struct window_mode_entry *wme) 2854 { 2855 struct window_copy_mode_data *data = wme->data; 2856 u_int px, py, xx; 2857 struct grid_cell gc; 2858 2859 px = 0; 2860 py = screen_hsize(data->backing) + data->cy - data->oy; 2861 xx = window_copy_find_length(wme, py); 2862 2863 while (px < xx) { 2864 grid_get_cell(data->backing->grid, px, py, &gc); 2865 if (gc.data.size != 1 || *gc.data.data != ' ') 2866 break; 2867 px++; 2868 } 2869 2870 window_copy_update_cursor(wme, px, data->cy); 2871 if (window_copy_update_selection(wme, 1)) 2872 window_copy_redraw_lines(wme, data->cy, 1); 2873 } 2874 2875 static void 2876 window_copy_cursor_end_of_line(struct window_mode_entry *wme) 2877 { 2878 struct window_copy_mode_data *data = wme->data; 2879 struct screen *back_s = data->backing; 2880 struct grid *gd = back_s->grid; 2881 struct grid_line *gl; 2882 u_int px, py; 2883 2884 py = screen_hsize(back_s) + data->cy - data->oy; 2885 px = window_copy_find_length(wme, py); 2886 2887 if (data->cx == px && data->lineflag == LINE_SEL_NONE) { 2888 if (data->screen.sel != NULL && data->rectflag) 2889 px = screen_size_x(back_s); 2890 gl = grid_get_line(gd, py); 2891 if (gl->flags & GRID_LINE_WRAPPED) { 2892 while (py < gd->sy + gd->hsize) { 2893 gl = grid_get_line(gd, py); 2894 if (~gl->flags & GRID_LINE_WRAPPED) 2895 break; 2896 window_copy_cursor_down(wme, 0); 2897 py = screen_hsize(back_s) + data->cy - data->oy; 2898 } 2899 px = window_copy_find_length(wme, py); 2900 } 2901 } 2902 window_copy_update_cursor(wme, px, data->cy); 2903 2904 if (window_copy_update_selection(wme, 1)) 2905 window_copy_redraw_lines(wme, data->cy, 1); 2906 } 2907 2908 static void 2909 window_copy_other_end(struct window_mode_entry *wme) 2910 { 2911 struct window_copy_mode_data *data = wme->data; 2912 struct screen *s = &data->screen; 2913 u_int selx, sely, cy, yy, hsize; 2914 2915 if (s->sel == NULL && data->lineflag == LINE_SEL_NONE) 2916 return; 2917 2918 if (data->lineflag == LINE_SEL_LEFT_RIGHT) 2919 data->lineflag = LINE_SEL_RIGHT_LEFT; 2920 else if (data->lineflag == LINE_SEL_RIGHT_LEFT) 2921 data->lineflag = LINE_SEL_LEFT_RIGHT; 2922 2923 switch (data->cursordrag) { 2924 case CURSORDRAG_NONE: 2925 case CURSORDRAG_SEL: 2926 data->cursordrag = CURSORDRAG_ENDSEL; 2927 break; 2928 case CURSORDRAG_ENDSEL: 2929 data->cursordrag = CURSORDRAG_SEL; 2930 break; 2931 } 2932 2933 selx = data->endselx; 2934 sely = data->endsely; 2935 if (data->cursordrag == CURSORDRAG_SEL) { 2936 selx = data->selx; 2937 sely = data->sely; 2938 } 2939 2940 cy = data->cy; 2941 yy = screen_hsize(data->backing) + data->cy - data->oy; 2942 2943 data->cx = selx; 2944 2945 hsize = screen_hsize(data->backing); 2946 if (sely < hsize - data->oy) { /* above */ 2947 data->oy = hsize - sely; 2948 data->cy = 0; 2949 } else if (sely > hsize - data->oy + screen_size_y(s)) { /* below */ 2950 data->oy = hsize - sely + screen_size_y(s) - 1; 2951 data->cy = screen_size_y(s) - 1; 2952 } else 2953 data->cy = cy + sely - yy; 2954 2955 window_copy_update_selection(wme, 1); 2956 window_copy_redraw_screen(wme); 2957 } 2958 2959 static void 2960 window_copy_cursor_left(struct window_mode_entry *wme) 2961 { 2962 struct window_copy_mode_data *data = wme->data; 2963 u_int py, cx; 2964 struct grid_cell gc; 2965 2966 py = screen_hsize(data->backing) + data->cy - data->oy; 2967 cx = data->cx; 2968 while (cx > 0) { 2969 grid_get_cell(data->backing->grid, cx, py, &gc); 2970 if (~gc.flags & GRID_FLAG_PADDING) 2971 break; 2972 cx--; 2973 } 2974 if (cx == 0 && py > 0) { 2975 window_copy_cursor_up(wme, 0); 2976 window_copy_cursor_end_of_line(wme); 2977 } else if (cx > 0) { 2978 window_copy_update_cursor(wme, cx - 1, data->cy); 2979 if (window_copy_update_selection(wme, 1)) 2980 window_copy_redraw_lines(wme, data->cy, 1); 2981 } 2982 } 2983 2984 static void 2985 window_copy_cursor_right(struct window_mode_entry *wme) 2986 { 2987 struct window_copy_mode_data *data = wme->data; 2988 u_int px, py, yy, cx, cy; 2989 struct grid_cell gc; 2990 2991 py = screen_hsize(data->backing) + data->cy - data->oy; 2992 yy = screen_hsize(data->backing) + screen_size_y(data->backing) - 1; 2993 if (data->screen.sel != NULL && data->rectflag) 2994 px = screen_size_x(&data->screen); 2995 else 2996 px = window_copy_find_length(wme, py); 2997 2998 if (data->cx >= px && py < yy) { 2999 window_copy_cursor_start_of_line(wme); 3000 window_copy_cursor_down(wme, 0); 3001 } else if (data->cx < px) { 3002 cx = data->cx + 1; 3003 cy = screen_hsize(data->backing) + data->cy - data->oy; 3004 while (cx < px) { 3005 grid_get_cell(data->backing->grid, cx, cy, &gc); 3006 if (~gc.flags & GRID_FLAG_PADDING) 3007 break; 3008 cx++; 3009 } 3010 window_copy_update_cursor(wme, cx, data->cy); 3011 if (window_copy_update_selection(wme, 1)) 3012 window_copy_redraw_lines(wme, data->cy, 1); 3013 } 3014 } 3015 3016 static void 3017 window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only) 3018 { 3019 struct window_copy_mode_data *data = wme->data; 3020 struct screen *s = &data->screen; 3021 u_int ox, oy, px, py; 3022 3023 oy = screen_hsize(data->backing) + data->cy - data->oy; 3024 ox = window_copy_find_length(wme, oy); 3025 if (data->cx != ox) { 3026 data->lastcx = data->cx; 3027 data->lastsx = ox; 3028 } 3029 3030 if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely) 3031 window_copy_other_end(wme); 3032 3033 if (scroll_only || data->cy == 0) { 3034 data->cx = data->lastcx; 3035 window_copy_scroll_down(wme, 1); 3036 if (scroll_only) { 3037 if (data->cy == screen_size_y(s) - 1) 3038 window_copy_redraw_lines(wme, data->cy, 1); 3039 else 3040 window_copy_redraw_lines(wme, data->cy, 2); 3041 } 3042 } else { 3043 window_copy_update_cursor(wme, data->lastcx, data->cy - 1); 3044 if (window_copy_update_selection(wme, 1)) { 3045 if (data->cy == screen_size_y(s) - 1) 3046 window_copy_redraw_lines(wme, data->cy, 1); 3047 else 3048 window_copy_redraw_lines(wme, data->cy, 2); 3049 } 3050 } 3051 3052 if (data->screen.sel == NULL || !data->rectflag) { 3053 py = screen_hsize(data->backing) + data->cy - data->oy; 3054 px = window_copy_find_length(wme, py); 3055 if ((data->cx >= data->lastsx && data->cx != px) || 3056 data->cx > px) 3057 window_copy_cursor_end_of_line(wme); 3058 } 3059 3060 if (data->lineflag == LINE_SEL_LEFT_RIGHT) 3061 window_copy_cursor_end_of_line(wme); 3062 else if (data->lineflag == LINE_SEL_RIGHT_LEFT) 3063 window_copy_cursor_start_of_line(wme); 3064 } 3065 3066 static void 3067 window_copy_cursor_down(struct window_mode_entry *wme, int scroll_only) 3068 { 3069 struct window_copy_mode_data *data = wme->data; 3070 struct screen *s = &data->screen; 3071 u_int ox, oy, px, py; 3072 3073 oy = screen_hsize(data->backing) + data->cy - data->oy; 3074 ox = window_copy_find_length(wme, oy); 3075 if (data->cx != ox) { 3076 data->lastcx = data->cx; 3077 data->lastsx = ox; 3078 } 3079 3080 if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely) 3081 window_copy_other_end(wme); 3082 3083 if (scroll_only || data->cy == screen_size_y(s) - 1) { 3084 data->cx = data->lastcx; 3085 window_copy_scroll_up(wme, 1); 3086 if (scroll_only && data->cy > 0) 3087 window_copy_redraw_lines(wme, data->cy - 1, 2); 3088 } else { 3089 window_copy_update_cursor(wme, data->lastcx, data->cy + 1); 3090 if (window_copy_update_selection(wme, 1)) 3091 window_copy_redraw_lines(wme, data->cy - 1, 2); 3092 } 3093 3094 if (data->screen.sel == NULL || !data->rectflag) { 3095 py = screen_hsize(data->backing) + data->cy - data->oy; 3096 px = window_copy_find_length(wme, py); 3097 if ((data->cx >= data->lastsx && data->cx != px) || 3098 data->cx > px) 3099 window_copy_cursor_end_of_line(wme); 3100 } 3101 3102 if (data->lineflag == LINE_SEL_LEFT_RIGHT) 3103 window_copy_cursor_end_of_line(wme); 3104 else if (data->lineflag == LINE_SEL_RIGHT_LEFT) 3105 window_copy_cursor_start_of_line(wme); 3106 } 3107 3108 static void 3109 window_copy_cursor_jump(struct window_mode_entry *wme) 3110 { 3111 struct window_copy_mode_data *data = wme->data; 3112 struct screen *back_s = data->backing; 3113 struct grid_cell gc; 3114 u_int px, py, xx; 3115 3116 px = data->cx + 1; 3117 py = screen_hsize(back_s) + data->cy - data->oy; 3118 xx = window_copy_find_length(wme, py); 3119 3120 while (px < xx) { 3121 grid_get_cell(back_s->grid, px, py, &gc); 3122 if (!(gc.flags & GRID_FLAG_PADDING) && 3123 gc.data.size == 1 && *gc.data.data == data->jumpchar) { 3124 window_copy_update_cursor(wme, px, data->cy); 3125 if (window_copy_update_selection(wme, 1)) 3126 window_copy_redraw_lines(wme, data->cy, 1); 3127 return; 3128 } 3129 px++; 3130 } 3131 } 3132 3133 static void 3134 window_copy_cursor_jump_back(struct window_mode_entry *wme) 3135 { 3136 struct window_copy_mode_data *data = wme->data; 3137 struct screen *back_s = data->backing; 3138 struct grid_cell gc; 3139 u_int px, py; 3140 3141 px = data->cx; 3142 py = screen_hsize(back_s) + data->cy - data->oy; 3143 3144 if (px > 0) 3145 px--; 3146 3147 for (;;) { 3148 grid_get_cell(back_s->grid, px, py, &gc); 3149 if (!(gc.flags & GRID_FLAG_PADDING) && 3150 gc.data.size == 1 && *gc.data.data == data->jumpchar) { 3151 window_copy_update_cursor(wme, px, data->cy); 3152 if (window_copy_update_selection(wme, 1)) 3153 window_copy_redraw_lines(wme, data->cy, 1); 3154 return; 3155 } 3156 if (px == 0) 3157 break; 3158 px--; 3159 } 3160 } 3161 3162 static void 3163 window_copy_cursor_jump_to(struct window_mode_entry *wme) 3164 { 3165 struct window_copy_mode_data *data = wme->data; 3166 struct screen *back_s = data->backing; 3167 struct grid_cell gc; 3168 u_int px, py, xx; 3169 3170 px = data->cx + 2; 3171 py = screen_hsize(back_s) + data->cy - data->oy; 3172 xx = window_copy_find_length(wme, py); 3173 3174 while (px < xx) { 3175 grid_get_cell(back_s->grid, px, py, &gc); 3176 if (!(gc.flags & GRID_FLAG_PADDING) && 3177 gc.data.size == 1 && *gc.data.data == data->jumpchar) { 3178 window_copy_update_cursor(wme, px - 1, data->cy); 3179 if (window_copy_update_selection(wme, 1)) 3180 window_copy_redraw_lines(wme, data->cy, 1); 3181 return; 3182 } 3183 px++; 3184 } 3185 } 3186 3187 static void 3188 window_copy_cursor_jump_to_back(struct window_mode_entry *wme) 3189 { 3190 struct window_copy_mode_data *data = wme->data; 3191 struct screen *back_s = data->backing; 3192 struct grid_cell gc; 3193 u_int px, py; 3194 3195 px = data->cx; 3196 py = screen_hsize(back_s) + data->cy - data->oy; 3197 3198 if (px > 0) 3199 px--; 3200 3201 if (px > 0) 3202 px--; 3203 3204 for (;;) { 3205 grid_get_cell(back_s->grid, px, py, &gc); 3206 if (!(gc.flags & GRID_FLAG_PADDING) && 3207 gc.data.size == 1 && *gc.data.data == data->jumpchar) { 3208 window_copy_update_cursor(wme, px + 1, data->cy); 3209 if (window_copy_update_selection(wme, 1)) 3210 window_copy_redraw_lines(wme, data->cy, 1); 3211 return; 3212 } 3213 if (px == 0) 3214 break; 3215 px--; 3216 } 3217 } 3218 3219 static void 3220 window_copy_cursor_next_word(struct window_mode_entry *wme, 3221 const char *separators) 3222 { 3223 struct window_copy_mode_data *data = wme->data; 3224 struct screen *back_s = data->backing; 3225 u_int px, py, xx, yy; 3226 int expected = 0; 3227 3228 px = data->cx; 3229 py = screen_hsize(back_s) + data->cy - data->oy; 3230 xx = window_copy_find_length(wme, py); 3231 yy = screen_hsize(back_s) + screen_size_y(back_s) - 1; 3232 3233 /* 3234 * First skip past any nonword characters and then any word characters. 3235 * 3236 * expected is initially set to 0 for the former and then 1 for the 3237 * latter. 3238 */ 3239 do { 3240 while (px > xx || 3241 window_copy_in_set(wme, px, py, separators) == expected) { 3242 /* Move down if we're past the end of the line. */ 3243 if (px > xx) { 3244 if (py == yy) 3245 return; 3246 window_copy_cursor_down(wme, 0); 3247 px = 0; 3248 3249 py = screen_hsize(back_s) + data->cy - data->oy; 3250 xx = window_copy_find_length(wme, py); 3251 } else 3252 px++; 3253 } 3254 expected = !expected; 3255 } while (expected == 1); 3256 3257 window_copy_update_cursor(wme, px, data->cy); 3258 if (window_copy_update_selection(wme, 1)) 3259 window_copy_redraw_lines(wme, data->cy, 1); 3260 } 3261 3262 static void 3263 window_copy_cursor_next_word_end(struct window_mode_entry *wme, 3264 const char *separators) 3265 { 3266 struct window_pane *wp = wme->wp; 3267 struct window_copy_mode_data *data = wme->data; 3268 struct options *oo = wp->window->options; 3269 struct screen *back_s = data->backing; 3270 u_int px, py, xx, yy; 3271 int keys, expected = 1; 3272 3273 px = data->cx; 3274 py = screen_hsize(back_s) + data->cy - data->oy; 3275 xx = window_copy_find_length(wme, py); 3276 yy = screen_hsize(back_s) + screen_size_y(back_s) - 1; 3277 3278 keys = options_get_number(oo, "mode-keys"); 3279 if (keys == MODEKEY_VI && !window_copy_in_set(wme, px, py, separators)) 3280 px++; 3281 3282 /* 3283 * First skip past any word characters, then any nonword characters. 3284 * 3285 * expected is initially set to 1 for the former and then 0 for the 3286 * latter. 3287 */ 3288 do { 3289 while (px > xx || 3290 window_copy_in_set(wme, px, py, separators) == expected) { 3291 /* Move down if we're past the end of the line. */ 3292 if (px > xx) { 3293 if (py == yy) 3294 return; 3295 window_copy_cursor_down(wme, 0); 3296 px = 0; 3297 3298 py = screen_hsize(back_s) + data->cy - data->oy; 3299 xx = window_copy_find_length(wme, py); 3300 } else 3301 px++; 3302 } 3303 expected = !expected; 3304 } while (expected == 0); 3305 3306 if (keys == MODEKEY_VI && px != 0) 3307 px--; 3308 3309 window_copy_update_cursor(wme, px, data->cy); 3310 if (window_copy_update_selection(wme, 1)) 3311 window_copy_redraw_lines(wme, data->cy, 1); 3312 } 3313 3314 /* Move to the previous place where a word begins. */ 3315 static void 3316 window_copy_cursor_previous_word(struct window_mode_entry *wme, 3317 const char *separators) 3318 { 3319 struct window_copy_mode_data *data = wme->data; 3320 u_int px, py; 3321 3322 px = data->cx; 3323 py = screen_hsize(data->backing) + data->cy - data->oy; 3324 3325 /* Move back to the previous word character. */ 3326 for (;;) { 3327 if (px > 0) { 3328 px--; 3329 if (!window_copy_in_set(wme, px, py, separators)) 3330 break; 3331 } else { 3332 if (data->cy == 0 && 3333 (screen_hsize(data->backing) == 0 || 3334 data->oy >= screen_hsize(data->backing) - 1)) 3335 goto out; 3336 window_copy_cursor_up(wme, 0); 3337 3338 py = screen_hsize(data->backing) + data->cy - data->oy; 3339 px = window_copy_find_length(wme, py); 3340 3341 /* Stop if separator at EOL. */ 3342 if (px > 0 && 3343 window_copy_in_set(wme, px - 1, py, separators)) 3344 break; 3345 } 3346 } 3347 3348 /* Move back to the beginning of this word. */ 3349 while (px > 0 && !window_copy_in_set(wme, px - 1, py, separators)) 3350 px--; 3351 3352 out: 3353 window_copy_update_cursor(wme, px, data->cy); 3354 if (window_copy_update_selection(wme, 1)) 3355 window_copy_redraw_lines(wme, data->cy, 1); 3356 } 3357 3358 static void 3359 window_copy_scroll_up(struct window_mode_entry *wme, u_int ny) 3360 { 3361 struct window_pane *wp = wme->wp; 3362 struct window_copy_mode_data *data = wme->data; 3363 struct screen *s = &data->screen; 3364 struct screen_write_ctx ctx; 3365 3366 if (data->oy < ny) 3367 ny = data->oy; 3368 if (ny == 0) 3369 return; 3370 data->oy -= ny; 3371 3372 window_copy_update_selection(wme, 0); 3373 3374 screen_write_start(&ctx, wp, NULL); 3375 screen_write_cursormove(&ctx, 0, 0, 0); 3376 screen_write_deleteline(&ctx, ny, 8); 3377 window_copy_write_lines(wme, &ctx, screen_size_y(s) - ny, ny); 3378 window_copy_write_line(wme, &ctx, 0); 3379 if (screen_size_y(s) > 1) 3380 window_copy_write_line(wme, &ctx, 1); 3381 if (screen_size_y(s) > 3) 3382 window_copy_write_line(wme, &ctx, screen_size_y(s) - 2); 3383 if (s->sel != NULL && screen_size_y(s) > ny) 3384 window_copy_write_line(wme, &ctx, screen_size_y(s) - ny - 1); 3385 screen_write_cursormove(&ctx, data->cx, data->cy, 0); 3386 screen_write_stop(&ctx); 3387 } 3388 3389 static void 3390 window_copy_scroll_down(struct window_mode_entry *wme, u_int ny) 3391 { 3392 struct window_pane *wp = wme->wp; 3393 struct window_copy_mode_data *data = wme->data; 3394 struct screen *s = &data->screen; 3395 struct screen_write_ctx ctx; 3396 3397 if (ny > screen_hsize(data->backing)) 3398 return; 3399 3400 if (data->oy > screen_hsize(data->backing) - ny) 3401 ny = screen_hsize(data->backing) - data->oy; 3402 if (ny == 0) 3403 return; 3404 data->oy += ny; 3405 3406 window_copy_update_selection(wme, 0); 3407 3408 screen_write_start(&ctx, wp, NULL); 3409 screen_write_cursormove(&ctx, 0, 0, 0); 3410 screen_write_insertline(&ctx, ny, 8); 3411 window_copy_write_lines(wme, &ctx, 0, ny); 3412 if (s->sel != NULL && screen_size_y(s) > ny) 3413 window_copy_write_line(wme, &ctx, ny); 3414 else if (ny == 1) /* nuke position */ 3415 window_copy_write_line(wme, &ctx, 1); 3416 screen_write_cursormove(&ctx, data->cx, data->cy, 0); 3417 screen_write_stop(&ctx); 3418 } 3419 3420 static void 3421 window_copy_rectangle_toggle(struct window_mode_entry *wme) 3422 { 3423 struct window_copy_mode_data *data = wme->data; 3424 u_int px, py; 3425 3426 data->rectflag = !data->rectflag; 3427 3428 py = screen_hsize(data->backing) + data->cy - data->oy; 3429 px = window_copy_find_length(wme, py); 3430 if (data->cx > px) 3431 window_copy_update_cursor(wme, px, data->cy); 3432 3433 window_copy_update_selection(wme, 1); 3434 window_copy_redraw_screen(wme); 3435 } 3436 3437 static void 3438 window_copy_move_mouse(struct mouse_event *m) 3439 { 3440 struct window_pane *wp; 3441 struct window_mode_entry *wme; 3442 u_int x, y; 3443 3444 wp = cmd_mouse_pane(m, NULL, NULL); 3445 if (wp == NULL) 3446 return; 3447 wme = TAILQ_FIRST(&wp->modes); 3448 if (wme == NULL) 3449 return; 3450 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode) 3451 return; 3452 3453 if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) 3454 return; 3455 3456 window_copy_update_cursor(wme, x, y); 3457 } 3458 3459 void 3460 window_copy_start_drag(struct client *c, struct mouse_event *m) 3461 { 3462 struct window_pane *wp; 3463 struct window_mode_entry *wme; 3464 u_int x, y; 3465 3466 if (c == NULL) 3467 return; 3468 3469 wp = cmd_mouse_pane(m, NULL, NULL); 3470 if (wp == NULL) 3471 return; 3472 wme = TAILQ_FIRST(&wp->modes); 3473 if (wme == NULL) 3474 return; 3475 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode) 3476 return; 3477 3478 if (cmd_mouse_at(wp, m, &x, &y, 1) != 0) 3479 return; 3480 3481 c->tty.mouse_drag_update = window_copy_drag_update; 3482 c->tty.mouse_drag_release = window_copy_drag_release; 3483 3484 window_copy_update_cursor(wme, x, y); 3485 window_copy_start_selection(wme); 3486 window_copy_redraw_screen(wme); 3487 3488 window_copy_drag_update(c, m); 3489 } 3490 3491 static void 3492 window_copy_drag_update(struct client *c, struct mouse_event *m) 3493 { 3494 struct window_pane *wp; 3495 struct window_mode_entry *wme; 3496 struct window_copy_mode_data *data; 3497 u_int x, y, old_cx, old_cy; 3498 struct timeval tv = { 3499 .tv_usec = WINDOW_COPY_DRAG_REPEAT_TIME 3500 }; 3501 3502 if (c == NULL) 3503 return; 3504 3505 wp = cmd_mouse_pane(m, NULL, NULL); 3506 if (wp == NULL) 3507 return; 3508 wme = TAILQ_FIRST(&wp->modes); 3509 if (wme == NULL) 3510 return; 3511 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode) 3512 return; 3513 3514 data = wme->data; 3515 evtimer_del(&data->dragtimer); 3516 3517 if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) 3518 return; 3519 old_cx = data->cx; 3520 old_cy = data->cy; 3521 3522 window_copy_update_cursor(wme, x, y); 3523 if (window_copy_update_selection(wme, 1)) 3524 window_copy_redraw_selection(wme, old_cy); 3525 if (old_cy != data->cy || old_cx == data->cx) { 3526 if (y == 0) { 3527 evtimer_add(&data->dragtimer, &tv); 3528 window_copy_cursor_up(wme, 1); 3529 } else if (y == screen_size_y(&data->screen) - 1) { 3530 evtimer_add(&data->dragtimer, &tv); 3531 window_copy_cursor_down(wme, 1); 3532 } 3533 } 3534 } 3535 3536 static void 3537 window_copy_drag_release(struct client *c, struct mouse_event *m) 3538 { 3539 struct window_pane *wp; 3540 struct window_mode_entry *wme; 3541 struct window_copy_mode_data *data; 3542 3543 if (c == NULL) 3544 return; 3545 3546 wp = cmd_mouse_pane(m, NULL, NULL); 3547 if (wp == NULL) 3548 return; 3549 wme = TAILQ_FIRST(&wp->modes); 3550 if (wme == NULL) 3551 return; 3552 if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode) 3553 return; 3554 3555 data = wme->data; 3556 evtimer_del(&data->dragtimer); 3557 } 3558