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