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