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