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