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