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