4 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
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.
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.
19 #include <sys/types.h>
26 struct screen
*window_copy_init(struct window_pane
*);
27 void window_copy_free(struct window_pane
*);
28 void window_copy_resize(struct window_pane
*, u_int
, u_int
);
29 void window_copy_key(struct window_pane
*, struct session
*, int);
30 int window_copy_key_input(struct window_pane
*, int);
31 int window_copy_key_numeric_prefix(struct window_pane
*, int);
32 void window_copy_mouse(
33 struct window_pane
*, struct session
*, struct mouse_event
*);
35 void window_copy_redraw_lines(struct window_pane
*, u_int
, u_int
);
36 void window_copy_redraw_screen(struct window_pane
*);
37 void window_copy_write_line(
38 struct window_pane
*, struct screen_write_ctx
*, u_int
);
39 void window_copy_write_lines(
40 struct window_pane
*, struct screen_write_ctx
*, u_int
, u_int
);
42 void window_copy_scroll_to(struct window_pane
*, u_int
, u_int
);
43 int window_copy_search_compare(
44 struct grid
*, u_int
, u_int
, struct grid
*, u_int
);
45 int window_copy_search_lr(
46 struct grid
*, struct grid
*, u_int
*, u_int
, u_int
, u_int
);
47 int window_copy_search_rl(
48 struct grid
*, struct grid
*, u_int
*, u_int
, u_int
, u_int
);
49 void window_copy_search_up(struct window_pane
*, const char *);
50 void window_copy_search_down(struct window_pane
*, const char *);
51 void window_copy_goto_line(struct window_pane
*, const char *);
52 void window_copy_update_cursor(struct window_pane
*, u_int
, u_int
);
53 void window_copy_start_selection(struct window_pane
*);
54 int window_copy_update_selection(struct window_pane
*);
55 void *window_copy_get_selection(struct window_pane
*, size_t *);
56 void window_copy_copy_buffer(struct window_pane
*, int, void *, size_t);
57 void window_copy_copy_pipe(
58 struct window_pane
*, struct session
*, int, const char *);
59 void window_copy_copy_selection(struct window_pane
*, int);
60 void window_copy_clear_selection(struct window_pane
*);
61 void window_copy_copy_line(
62 struct window_pane
*, char **, size_t *, u_int
, u_int
, u_int
);
63 int window_copy_in_set(struct window_pane
*, u_int
, u_int
, const char *);
64 u_int
window_copy_find_length(struct window_pane
*, u_int
);
65 void window_copy_cursor_start_of_line(struct window_pane
*);
66 void window_copy_cursor_back_to_indentation(struct window_pane
*);
67 void window_copy_cursor_end_of_line(struct window_pane
*);
68 void window_copy_cursor_left(struct window_pane
*);
69 void window_copy_cursor_right(struct window_pane
*);
70 void window_copy_cursor_up(struct window_pane
*, int);
71 void window_copy_cursor_down(struct window_pane
*, int);
72 void window_copy_cursor_jump(struct window_pane
*);
73 void window_copy_cursor_jump_back(struct window_pane
*);
74 void window_copy_cursor_jump_to(struct window_pane
*);
75 void window_copy_cursor_jump_to_back(struct window_pane
*);
76 void window_copy_cursor_next_word(struct window_pane
*, const char *);
77 void window_copy_cursor_next_word_end(struct window_pane
*, const char *);
78 void window_copy_cursor_previous_word(struct window_pane
*, const char *);
79 void window_copy_scroll_up(struct window_pane
*, u_int
);
80 void window_copy_scroll_down(struct window_pane
*, u_int
);
81 void window_copy_rectangle_toggle(struct window_pane
*);
83 const struct window_mode window_copy_mode
= {
92 enum window_copy_input_type
{
94 WINDOW_COPY_NUMERICPREFIX
,
96 WINDOW_COPY_SEARCHDOWN
,
97 WINDOW_COPY_JUMPFORWARD
,
99 WINDOW_COPY_JUMPTOFORWARD
,
100 WINDOW_COPY_JUMPTOBACK
,
101 WINDOW_COPY_GOTOLINE
,
105 * Copy-mode's visible screen (the "screen" field) is filled from one of
106 * two sources: the original contents of the pane (used when we
107 * actually enter via the "copy-mode" command, to copy the contents of
108 * the current pane), or else a series of lines containing the output
109 * from an output-writing tmux command (such as any of the "show-*" or
110 * "list-*" commands).
112 * In either case, the full content of the copy-mode grid is pointed at
113 * by the "backing" field, and is copied into "screen" as needed (that
114 * is, when scrolling occurs). When copy-mode is backed by a pane,
115 * backing points directly at that pane's screen structure (&wp->base);
116 * when backed by a list of output-lines from a command, it points at
117 * a newly-allocated screen structure (which is deallocated when the
120 struct window_copy_mode_data
{
121 struct screen screen
;
123 struct screen
*backing
;
124 int backing_written
; /* backing display has started */
126 struct mode_key_data mdata
;
133 u_int rectflag
; /* are we in rectangle copy mode? */
138 u_int lastcx
; /* position in last line with content */
139 u_int lastsx
; /* size of last line with content */
141 enum window_copy_input_type inputtype
;
142 const char *inputprompt
;
147 enum window_copy_input_type searchtype
;
150 enum window_copy_input_type jumptype
;
155 window_copy_init(struct window_pane
*wp
)
157 struct window_copy_mode_data
*data
;
161 wp
->modedata
= data
= xmalloc(sizeof *data
);
169 data
->backing_written
= 0;
173 data
->inputtype
= WINDOW_COPY_OFF
;
174 data
->inputprompt
= NULL
;
175 data
->inputstr
= xstrdup("");
176 data
->numprefix
= -1;
178 data
->searchtype
= WINDOW_COPY_OFF
;
179 data
->searchstr
= NULL
;
182 bufferevent_disable(wp
->event
, EV_READ
|EV_WRITE
);
184 data
->jumptype
= WINDOW_COPY_OFF
;
185 data
->jumpchar
= '\0';
188 screen_init(s
, screen_size_x(&wp
->base
), screen_size_y(&wp
->base
), 0);
189 if (options_get_number(&wp
->window
->options
, "mode-mouse"))
190 s
->mode
|= MODE_MOUSE_STANDARD
;
192 keys
= options_get_number(&wp
->window
->options
, "mode-keys");
193 if (keys
== MODEKEY_EMACS
)
194 mode_key_init(&data
->mdata
, &mode_key_tree_emacs_copy
);
196 mode_key_init(&data
->mdata
, &mode_key_tree_vi_copy
);
198 data
->backing
= NULL
;
204 window_copy_init_from_pane(struct window_pane
*wp
)
206 struct window_copy_mode_data
*data
= wp
->modedata
;
207 struct screen
*s
= &data
->screen
;
208 struct screen_write_ctx ctx
;
211 if (wp
->mode
!= &window_copy_mode
)
212 fatalx("not in copy mode");
214 data
->backing
= &wp
->base
;
215 data
->cx
= data
->backing
->cx
;
216 data
->cy
= data
->backing
->cy
;
221 screen_write_start(&ctx
, NULL
, s
);
222 for (i
= 0; i
< screen_size_y(s
); i
++)
223 window_copy_write_line(wp
, &ctx
, i
);
224 screen_write_cursormove(&ctx
, data
->cx
, data
->cy
);
225 screen_write_stop(&ctx
);
229 window_copy_init_for_output(struct window_pane
*wp
)
231 struct window_copy_mode_data
*data
= wp
->modedata
;
233 data
->backing
= xmalloc(sizeof *data
->backing
);
234 screen_init(data
->backing
, screen_size_x(&wp
->base
),
235 screen_size_y(&wp
->base
), UINT_MAX
);
236 data
->backing
->mode
&= ~MODE_WRAP
;
240 window_copy_free(struct window_pane
*wp
)
242 struct window_copy_mode_data
*data
= wp
->modedata
;
245 bufferevent_enable(wp
->event
, EV_READ
|EV_WRITE
);
247 free(data
->searchstr
);
248 free(data
->inputstr
);
250 if (data
->backing
!= &wp
->base
) {
251 screen_free(data
->backing
);
254 screen_free(&data
->screen
);
260 window_copy_add(struct window_pane
*wp
, const char *fmt
, ...)
265 window_copy_vadd(wp
, fmt
, ap
);
270 window_copy_vadd(struct window_pane
*wp
, const char *fmt
, va_list ap
)
272 struct window_copy_mode_data
*data
= wp
->modedata
;
273 struct screen
*backing
= data
->backing
;
274 struct screen_write_ctx back_ctx
, ctx
;
279 if (backing
== &wp
->base
)
282 utf8flag
= options_get_number(&wp
->window
->options
, "utf8");
283 memcpy(&gc
, &grid_default_cell
, sizeof gc
);
285 old_hsize
= screen_hsize(data
->backing
);
286 screen_write_start(&back_ctx
, NULL
, backing
);
287 if (data
->backing_written
) {
289 * On the second or later line, do a CRLF before writing
290 * (so it's on a new line).
292 screen_write_carriagereturn(&back_ctx
);
293 screen_write_linefeed(&back_ctx
, 0);
295 data
->backing_written
= 1;
296 screen_write_vnputs(&back_ctx
, 0, &gc
, utf8flag
, fmt
, ap
);
297 screen_write_stop(&back_ctx
);
299 data
->oy
+= screen_hsize(data
->backing
) - old_hsize
;
301 screen_write_start(&ctx
, wp
, &data
->screen
);
304 * If the history has changed, draw the top line.
305 * (If there's any history at all, it has changed.)
307 if (screen_hsize(data
->backing
))
308 window_copy_redraw_lines(wp
, 0, 1);
310 /* Write the line, if it's visible. */
311 if (backing
->cy
+ data
->oy
< screen_size_y(backing
))
312 window_copy_redraw_lines(wp
, backing
->cy
, 1);
314 screen_write_stop(&ctx
);
318 window_copy_pageup(struct window_pane
*wp
)
320 struct window_copy_mode_data
*data
= wp
->modedata
;
321 struct screen
*s
= &data
->screen
;
325 if (screen_size_y(s
) > 2)
326 n
= screen_size_y(s
) - 2;
327 if (data
->oy
+ n
> screen_hsize(data
->backing
))
328 data
->oy
= screen_hsize(data
->backing
);
331 window_copy_update_selection(wp
);
332 window_copy_redraw_screen(wp
);
336 window_copy_resize(struct window_pane
*wp
, u_int sx
, u_int sy
)
338 struct window_copy_mode_data
*data
= wp
->modedata
;
339 struct screen
*s
= &data
->screen
;
340 struct screen_write_ctx ctx
;
342 screen_resize(s
, sx
, sy
, 0);
343 if (data
->backing
!= &wp
->base
)
344 screen_resize(data
->backing
, sx
, sy
, 0);
346 if (data
->cy
> sy
- 1)
350 if (data
->oy
> screen_hsize(data
->backing
))
351 data
->oy
= screen_hsize(data
->backing
);
353 window_copy_clear_selection(wp
);
355 screen_write_start(&ctx
, NULL
, s
);
356 window_copy_write_lines(wp
, &ctx
, 0, screen_size_y(s
) - 1);
357 screen_write_stop(&ctx
);
359 window_copy_redraw_screen(wp
);
363 window_copy_key(struct window_pane
*wp
, struct session
*sess
, int key
)
365 const char *word_separators
;
366 struct window_copy_mode_data
*data
= wp
->modedata
;
367 struct screen
*s
= &data
->screen
;
370 enum mode_key_cmd cmd
;
373 np
= data
->numprefix
;
377 if (data
->inputtype
== WINDOW_COPY_JUMPFORWARD
||
378 data
->inputtype
== WINDOW_COPY_JUMPBACK
||
379 data
->inputtype
== WINDOW_COPY_JUMPTOFORWARD
||
380 data
->inputtype
== WINDOW_COPY_JUMPTOBACK
) {
381 /* Ignore keys with modifiers. */
382 if ((key
& KEYC_MASK_MOD
) == 0) {
383 data
->jumpchar
= key
;
384 if (data
->inputtype
== WINDOW_COPY_JUMPFORWARD
) {
385 for (; np
!= 0; np
--)
386 window_copy_cursor_jump(wp
);
387 } else if (data
->inputtype
== WINDOW_COPY_JUMPBACK
) {
388 for (; np
!= 0; np
--)
389 window_copy_cursor_jump_back(wp
);
390 } else if (data
->inputtype
== WINDOW_COPY_JUMPTOFORWARD
) {
391 for (; np
!= 0; np
--)
392 window_copy_cursor_jump_to(wp
);
393 } else if (data
->inputtype
== WINDOW_COPY_JUMPTOBACK
) {
394 for (; np
!= 0; np
--)
395 window_copy_cursor_jump_to_back(wp
);
398 data
->jumptype
= data
->inputtype
;
399 data
->inputtype
= WINDOW_COPY_OFF
;
400 window_copy_redraw_lines(wp
, screen_size_y(s
) - 1, 1);
402 } else if (data
->inputtype
== WINDOW_COPY_NUMERICPREFIX
) {
403 if (window_copy_key_numeric_prefix(wp
, key
) == 0)
405 data
->inputtype
= WINDOW_COPY_OFF
;
406 window_copy_redraw_lines(wp
, screen_size_y(s
) - 1, 1);
407 } else if (data
->inputtype
!= WINDOW_COPY_OFF
) {
408 if (window_copy_key_input(wp
, key
) != 0)
413 cmd
= mode_key_lookup(&data
->mdata
, key
, &arg
);
415 case MODEKEYCOPY_CANCEL
:
416 window_pane_reset_mode(wp
);
418 case MODEKEYCOPY_LEFT
:
419 for (; np
!= 0; np
--)
420 window_copy_cursor_left(wp
);
422 case MODEKEYCOPY_RIGHT
:
423 for (; np
!= 0; np
--)
424 window_copy_cursor_right(wp
);
427 for (; np
!= 0; np
--)
428 window_copy_cursor_up(wp
, 0);
430 case MODEKEYCOPY_DOWN
:
431 for (; np
!= 0; np
--)
432 window_copy_cursor_down(wp
, 0);
434 case MODEKEYCOPY_SCROLLUP
:
435 for (; np
!= 0; np
--)
436 window_copy_cursor_up(wp
, 1);
438 case MODEKEYCOPY_SCROLLDOWN
:
439 for (; np
!= 0; np
--)
440 window_copy_cursor_down(wp
, 1);
442 case MODEKEYCOPY_PREVIOUSPAGE
:
443 for (; np
!= 0; np
--)
444 window_copy_pageup(wp
);
446 case MODEKEYCOPY_NEXTPAGE
:
448 if (screen_size_y(s
) > 2)
449 n
= screen_size_y(s
) - 2;
450 for (; np
!= 0; np
--) {
456 window_copy_update_selection(wp
);
457 window_copy_redraw_screen(wp
);
459 case MODEKEYCOPY_HALFPAGEUP
:
460 n
= screen_size_y(s
) / 2;
461 for (; np
!= 0; np
--) {
462 if (data
->oy
+ n
> screen_hsize(data
->backing
))
463 data
->oy
= screen_hsize(data
->backing
);
467 window_copy_update_selection(wp
);
468 window_copy_redraw_screen(wp
);
470 case MODEKEYCOPY_HALFPAGEDOWN
:
471 n
= screen_size_y(s
) / 2;
472 for (; np
!= 0; np
--) {
478 window_copy_update_selection(wp
);
479 window_copy_redraw_screen(wp
);
481 case MODEKEYCOPY_TOPLINE
:
484 window_copy_update_selection(wp
);
485 window_copy_redraw_screen(wp
);
487 case MODEKEYCOPY_MIDDLELINE
:
489 data
->cy
= (screen_size_y(s
) - 1) / 2;
490 window_copy_update_selection(wp
);
491 window_copy_redraw_screen(wp
);
493 case MODEKEYCOPY_BOTTOMLINE
:
495 data
->cy
= screen_size_y(s
) - 1;
496 window_copy_update_selection(wp
);
497 window_copy_redraw_screen(wp
);
499 case MODEKEYCOPY_HISTORYTOP
:
502 data
->oy
= screen_hsize(data
->backing
);
503 window_copy_update_selection(wp
);
504 window_copy_redraw_screen(wp
);
506 case MODEKEYCOPY_HISTORYBOTTOM
:
508 data
->cy
= screen_size_y(s
) - 1;
510 window_copy_update_selection(wp
);
511 window_copy_redraw_screen(wp
);
513 case MODEKEYCOPY_STARTSELECTION
:
514 window_copy_start_selection(wp
);
515 window_copy_redraw_screen(wp
);
517 case MODEKEYCOPY_COPYLINE
:
518 case MODEKEYCOPY_SELECTLINE
:
519 window_copy_cursor_start_of_line(wp
);
521 case MODEKEYCOPY_COPYENDOFLINE
:
522 window_copy_start_selection(wp
);
524 window_copy_cursor_down(wp
, 0);
525 window_copy_cursor_end_of_line(wp
);
526 window_copy_redraw_screen(wp
);
528 /* If a copy command then copy the selection and exit. */
530 (cmd
== MODEKEYCOPY_COPYLINE
||
531 cmd
== MODEKEYCOPY_COPYENDOFLINE
)) {
532 window_copy_copy_selection(wp
, -1);
533 window_pane_reset_mode(wp
);
537 case MODEKEYCOPY_CLEARSELECTION
:
538 window_copy_clear_selection(wp
);
539 window_copy_redraw_screen(wp
);
541 case MODEKEYCOPY_COPYPIPE
:
543 window_copy_copy_pipe(wp
, sess
, data
->numprefix
, arg
);
544 window_pane_reset_mode(wp
);
548 case MODEKEYCOPY_COPYSELECTION
:
550 window_copy_copy_selection(wp
, data
->numprefix
);
551 window_pane_reset_mode(wp
);
555 case MODEKEYCOPY_STARTOFLINE
:
556 window_copy_cursor_start_of_line(wp
);
558 case MODEKEYCOPY_BACKTOINDENTATION
:
559 window_copy_cursor_back_to_indentation(wp
);
561 case MODEKEYCOPY_ENDOFLINE
:
562 window_copy_cursor_end_of_line(wp
);
564 case MODEKEYCOPY_NEXTSPACE
:
565 for (; np
!= 0; np
--)
566 window_copy_cursor_next_word(wp
, " ");
568 case MODEKEYCOPY_NEXTSPACEEND
:
569 for (; np
!= 0; np
--)
570 window_copy_cursor_next_word_end(wp
, " ");
572 case MODEKEYCOPY_NEXTWORD
:
574 options_get_string(&sess
->options
, "word-separators");
575 for (; np
!= 0; np
--)
576 window_copy_cursor_next_word(wp
, word_separators
);
578 case MODEKEYCOPY_NEXTWORDEND
:
580 options_get_string(&sess
->options
, "word-separators");
581 for (; np
!= 0; np
--)
582 window_copy_cursor_next_word_end(wp
, word_separators
);
584 case MODEKEYCOPY_PREVIOUSSPACE
:
585 for (; np
!= 0; np
--)
586 window_copy_cursor_previous_word(wp
, " ");
588 case MODEKEYCOPY_PREVIOUSWORD
:
590 options_get_string(&sess
->options
, "word-separators");
591 for (; np
!= 0; np
--)
592 window_copy_cursor_previous_word(wp
, word_separators
);
594 case MODEKEYCOPY_JUMP
:
595 data
->inputtype
= WINDOW_COPY_JUMPFORWARD
;
596 data
->inputprompt
= "Jump Forward";
597 *data
->inputstr
= '\0';
598 window_copy_redraw_lines(wp
, screen_size_y(s
) - 1, 1);
599 return; /* skip numprefix reset */
600 case MODEKEYCOPY_JUMPAGAIN
:
601 if (data
->jumptype
== WINDOW_COPY_JUMPFORWARD
) {
602 for (; np
!= 0; np
--)
603 window_copy_cursor_jump(wp
);
604 } else if (data
->jumptype
== WINDOW_COPY_JUMPBACK
) {
605 for (; np
!= 0; np
--)
606 window_copy_cursor_jump_back(wp
);
607 } else if (data
->jumptype
== WINDOW_COPY_JUMPTOFORWARD
) {
608 for (; np
!= 0; np
--)
609 window_copy_cursor_jump_to(wp
);
610 } else if (data
->jumptype
== WINDOW_COPY_JUMPTOBACK
) {
611 for (; np
!= 0; np
--)
612 window_copy_cursor_jump_to_back(wp
);
615 case MODEKEYCOPY_JUMPREVERSE
:
616 if (data
->jumptype
== WINDOW_COPY_JUMPFORWARD
) {
617 for (; np
!= 0; np
--)
618 window_copy_cursor_jump_back(wp
);
619 } else if (data
->jumptype
== WINDOW_COPY_JUMPBACK
) {
620 for (; np
!= 0; np
--)
621 window_copy_cursor_jump(wp
);
622 } else if (data
->jumptype
== WINDOW_COPY_JUMPTOFORWARD
) {
623 for (; np
!= 0; np
--)
624 window_copy_cursor_jump_to_back(wp
);
625 } else if (data
->jumptype
== WINDOW_COPY_JUMPTOBACK
) {
626 for (; np
!= 0; np
--)
627 window_copy_cursor_jump_to(wp
);
630 case MODEKEYCOPY_JUMPBACK
:
631 data
->inputtype
= WINDOW_COPY_JUMPBACK
;
632 data
->inputprompt
= "Jump Back";
633 *data
->inputstr
= '\0';
634 window_copy_redraw_lines(wp
, screen_size_y(s
) - 1, 1);
635 return; /* skip numprefix reset */
636 case MODEKEYCOPY_JUMPTO
:
637 data
->inputtype
= WINDOW_COPY_JUMPTOFORWARD
;
638 data
->inputprompt
= "Jump To";
639 *data
->inputstr
= '\0';
640 window_copy_redraw_lines(wp
, screen_size_y(s
) - 1, 1);
641 return; /* skip numprefix reset */
642 case MODEKEYCOPY_JUMPTOBACK
:
643 data
->inputtype
= WINDOW_COPY_JUMPTOBACK
;
644 data
->inputprompt
= "Jump To Back";
645 *data
->inputstr
= '\0';
646 window_copy_redraw_lines(wp
, screen_size_y(s
) - 1, 1);
647 return; /* skip numprefix reset */
648 case MODEKEYCOPY_SEARCHUP
:
649 data
->inputtype
= WINDOW_COPY_SEARCHUP
;
650 data
->inputprompt
= "Search Up";
652 case MODEKEYCOPY_SEARCHDOWN
:
653 data
->inputtype
= WINDOW_COPY_SEARCHDOWN
;
654 data
->inputprompt
= "Search Down";
656 case MODEKEYCOPY_SEARCHAGAIN
:
657 case MODEKEYCOPY_SEARCHREVERSE
:
658 switch (data
->searchtype
) {
659 case WINDOW_COPY_OFF
:
660 case WINDOW_COPY_GOTOLINE
:
661 case WINDOW_COPY_JUMPFORWARD
:
662 case WINDOW_COPY_JUMPBACK
:
663 case WINDOW_COPY_JUMPTOFORWARD
:
664 case WINDOW_COPY_JUMPTOBACK
:
665 case WINDOW_COPY_NUMERICPREFIX
:
667 case WINDOW_COPY_SEARCHUP
:
668 if (cmd
== MODEKEYCOPY_SEARCHAGAIN
) {
669 for (; np
!= 0; np
--) {
670 window_copy_search_up(
671 wp
, data
->searchstr
);
674 for (; np
!= 0; np
--) {
675 window_copy_search_down(
676 wp
, data
->searchstr
);
680 case WINDOW_COPY_SEARCHDOWN
:
681 if (cmd
== MODEKEYCOPY_SEARCHAGAIN
) {
682 for (; np
!= 0; np
--) {
683 window_copy_search_down(
684 wp
, data
->searchstr
);
687 for (; np
!= 0; np
--) {
688 window_copy_search_up(
689 wp
, data
->searchstr
);
695 case MODEKEYCOPY_GOTOLINE
:
696 data
->inputtype
= WINDOW_COPY_GOTOLINE
;
697 data
->inputprompt
= "Goto Line";
698 *data
->inputstr
= '\0';
700 case MODEKEYCOPY_STARTNUMBERPREFIX
:
701 key
&= KEYC_MASK_KEY
;
702 if (key
>= '0' && key
<= '9') {
703 data
->inputtype
= WINDOW_COPY_NUMERICPREFIX
;
705 window_copy_key_numeric_prefix(wp
, key
);
709 case MODEKEYCOPY_RECTANGLETOGGLE
:
710 window_copy_rectangle_toggle(wp
);
716 data
->numprefix
= -1;
720 keys
= options_get_number(&wp
->window
->options
, "mode-keys");
721 if (keys
== MODEKEY_EMACS
)
722 mode_key_init(&data
->mdata
, &mode_key_tree_emacs_edit
);
724 mode_key_init(&data
->mdata
, &mode_key_tree_vi_edit
);
726 window_copy_redraw_lines(wp
, screen_size_y(s
) - 1, 1);
730 keys
= options_get_number(&wp
->window
->options
, "mode-keys");
731 if (keys
== MODEKEY_EMACS
)
732 mode_key_init(&data
->mdata
, &mode_key_tree_emacs_copy
);
734 mode_key_init(&data
->mdata
, &mode_key_tree_vi_copy
);
736 data
->inputtype
= WINDOW_COPY_OFF
;
737 data
->inputprompt
= NULL
;
739 window_copy_redraw_lines(wp
, screen_size_y(s
) - 1, 1);
743 window_copy_key_input(struct window_pane
*wp
, int key
)
745 struct window_copy_mode_data
*data
= wp
->modedata
;
746 struct screen
*s
= &data
->screen
;
750 switch (mode_key_lookup(&data
->mdata
, key
, NULL
)) {
751 case MODEKEYEDIT_CANCEL
:
752 data
->numprefix
= -1;
754 case MODEKEYEDIT_BACKSPACE
:
755 inputlen
= strlen(data
->inputstr
);
757 data
->inputstr
[inputlen
- 1] = '\0';
759 case MODEKEYEDIT_DELETELINE
:
760 *data
->inputstr
= '\0';
762 case MODEKEYEDIT_ENTER
:
763 np
= data
->numprefix
;
767 switch (data
->inputtype
) {
768 case WINDOW_COPY_OFF
:
769 case WINDOW_COPY_JUMPFORWARD
:
770 case WINDOW_COPY_JUMPBACK
:
771 case WINDOW_COPY_JUMPTOFORWARD
:
772 case WINDOW_COPY_JUMPTOBACK
:
773 case WINDOW_COPY_NUMERICPREFIX
:
775 case WINDOW_COPY_SEARCHUP
:
776 for (; np
!= 0; np
--)
777 window_copy_search_up(wp
, data
->inputstr
);
778 data
->searchtype
= data
->inputtype
;
779 data
->searchstr
= xstrdup(data
->inputstr
);
781 case WINDOW_COPY_SEARCHDOWN
:
782 for (; np
!= 0; np
--)
783 window_copy_search_down(wp
, data
->inputstr
);
784 data
->searchtype
= data
->inputtype
;
785 data
->searchstr
= xstrdup(data
->inputstr
);
787 case WINDOW_COPY_GOTOLINE
:
788 window_copy_goto_line(wp
, data
->inputstr
);
789 *data
->inputstr
= '\0';
792 data
->numprefix
= -1;
795 if (key
< 32 || key
> 126)
797 inputlen
= strlen(data
->inputstr
) + 2;
799 data
->inputstr
= xrealloc(data
->inputstr
, 1, inputlen
);
800 data
->inputstr
[inputlen
- 2] = key
;
801 data
->inputstr
[inputlen
- 1] = '\0';
807 window_copy_redraw_lines(wp
, screen_size_y(s
) - 1, 1);
812 window_copy_key_numeric_prefix(struct window_pane
*wp
, int key
)
814 struct window_copy_mode_data
*data
= wp
->modedata
;
815 struct screen
*s
= &data
->screen
;
817 key
&= KEYC_MASK_KEY
;
818 if (key
< '0' || key
> '9')
821 if (data
->numprefix
>= 100) /* no more than three digits */
823 data
->numprefix
= data
->numprefix
* 10 + key
- '0';
825 window_copy_redraw_lines(wp
, screen_size_y(s
) - 1, 1);
831 struct window_pane
*wp
, struct session
*sess
, struct mouse_event
*m
)
833 struct window_copy_mode_data
*data
= wp
->modedata
;
834 struct screen
*s
= &data
->screen
;
837 if (m
->x
>= screen_size_x(s
))
839 if (m
->y
>= screen_size_y(s
))
842 /* If mouse wheel (buttons 4 and 5), scroll. */
843 if (m
->event
== MOUSE_EVENT_WHEEL
) {
844 if (m
->wheel
== MOUSE_WHEEL_UP
) {
845 for (i
= 0; i
< 5; i
++)
846 window_copy_cursor_up(wp
, 1);
847 } else if (m
->wheel
== MOUSE_WHEEL_DOWN
) {
848 for (i
= 0; i
< 5; i
++)
849 window_copy_cursor_down(wp
, 1);
857 * If already reading motion, move the cursor while buttons are still
858 * pressed, or stop the selection on their release.
860 if (s
->mode
& MODE_MOUSE_BUTTON
) {
861 if (~m
->event
& MOUSE_EVENT_UP
) {
862 window_copy_update_cursor(wp
, m
->x
, m
->y
);
863 if (window_copy_update_selection(wp
))
864 window_copy_redraw_screen(wp
);
870 /* Otherwise if other buttons pressed, start selection and motion. */
871 if (~m
->event
& MOUSE_EVENT_UP
) {
872 s
->mode
&= ~MODE_MOUSE_STANDARD
;
873 s
->mode
|= MODE_MOUSE_BUTTON
;
875 window_copy_update_cursor(wp
, m
->x
, m
->y
);
876 window_copy_start_selection(wp
);
877 window_copy_redraw_screen(wp
);
883 s
->mode
&= ~MODE_MOUSE_BUTTON
;
884 s
->mode
|= MODE_MOUSE_STANDARD
;
886 window_copy_copy_selection(wp
, -1);
887 window_pane_reset_mode(wp
);
892 window_copy_scroll_to(struct window_pane
*wp
, u_int px
, u_int py
)
894 struct window_copy_mode_data
*data
= wp
->modedata
;
895 struct grid
*gd
= data
->backing
->grid
;
904 } else if (py
> gd
->hsize
+ gd
->sy
- gap
) {
906 data
->cy
= py
- gd
->hsize
;
908 offset
= py
+ gap
- gd
->sy
;
909 data
->cy
= py
- offset
;
911 data
->oy
= gd
->hsize
- offset
;
913 window_copy_update_selection(wp
);
914 window_copy_redraw_screen(wp
);
918 window_copy_search_compare(
919 struct grid
*gd
, u_int px
, u_int py
, struct grid
*sgd
, u_int spx
)
921 const struct grid_cell
*gc
, *sgc
;
922 struct utf8_data ud
, sud
;
924 gc
= grid_peek_cell(gd
, px
, py
);
925 grid_cell_get(gc
, &ud
);
926 sgc
= grid_peek_cell(sgd
, spx
, 0);
927 grid_cell_get(sgc
, &sud
);
929 if (ud
.size
!= sud
.size
|| ud
.width
!= sud
.width
)
931 return (memcmp(ud
.data
, sud
.data
, ud
.size
) == 0);
935 window_copy_search_lr(struct grid
*gd
,
936 struct grid
*sgd
, u_int
*ppx
, u_int py
, u_int first
, u_int last
)
940 for (ax
= first
; ax
< last
; ax
++) {
941 if (ax
+ sgd
->sx
>= gd
->sx
)
943 for (bx
= 0; bx
< sgd
->sx
; bx
++) {
945 if (!window_copy_search_compare(gd
, px
, py
, sgd
, bx
))
957 window_copy_search_rl(struct grid
*gd
,
958 struct grid
*sgd
, u_int
*ppx
, u_int py
, u_int first
, u_int last
)
962 for (ax
= last
+ 1; ax
> first
; ax
--) {
963 if (gd
->sx
- (ax
- 1) < sgd
->sx
)
965 for (bx
= 0; bx
< sgd
->sx
; bx
++) {
967 if (!window_copy_search_compare(gd
, px
, py
, sgd
, bx
))
979 window_copy_search_up(struct window_pane
*wp
, const char *searchstr
)
981 struct window_copy_mode_data
*data
= wp
->modedata
;
982 struct screen
*s
= data
->backing
, ss
;
983 struct screen_write_ctx ctx
;
984 struct grid
*gd
= s
->grid
, *sgd
;
987 u_int i
, last
, fx
, fy
, px
;
988 int utf8flag
, n
, wrapped
, wrapflag
;
990 if (*searchstr
== '\0')
992 utf8flag
= options_get_number(&wp
->window
->options
, "utf8");
993 wrapflag
= options_get_number(&wp
->window
->options
, "wrap-search");
994 searchlen
= screen_write_strlen(utf8flag
, "%s", searchstr
);
996 screen_init(&ss
, searchlen
, 1, 0);
997 screen_write_start(&ctx
, NULL
, &ss
);
998 memcpy(&gc
, &grid_default_cell
, sizeof gc
);
999 screen_write_nputs(&ctx
, -1, &gc
, utf8flag
, "%s", searchstr
);
1000 screen_write_stop(&ctx
);
1003 fy
= gd
->hsize
- data
->oy
+ data
->cy
;
1016 for (i
= fy
+ 1; i
> 0; i
--) {
1017 last
= screen_size_x(s
);
1020 n
= window_copy_search_rl(gd
, sgd
, &px
, i
- 1, 0, last
);
1022 window_copy_scroll_to(wp
, px
, i
- 1);
1026 if (wrapflag
&& !n
&& !wrapped
) {
1028 fy
= gd
->hsize
+ gd
->sy
- 1;
1037 window_copy_search_down(struct window_pane
*wp
, const char *searchstr
)
1039 struct window_copy_mode_data
*data
= wp
->modedata
;
1040 struct screen
*s
= data
->backing
, ss
;
1041 struct screen_write_ctx ctx
;
1042 struct grid
*gd
= s
->grid
, *sgd
;
1043 struct grid_cell gc
;
1045 u_int i
, first
, fx
, fy
, px
;
1046 int utf8flag
, n
, wrapped
, wrapflag
;
1048 if (*searchstr
== '\0')
1050 utf8flag
= options_get_number(&wp
->window
->options
, "utf8");
1051 wrapflag
= options_get_number(&wp
->window
->options
, "wrap-search");
1052 searchlen
= screen_write_strlen(utf8flag
, "%s", searchstr
);
1054 screen_init(&ss
, searchlen
, 1, 0);
1055 screen_write_start(&ctx
, NULL
, &ss
);
1056 memcpy(&gc
, &grid_default_cell
, sizeof gc
);
1057 screen_write_nputs(&ctx
, -1, &gc
, utf8flag
, "%s", searchstr
);
1058 screen_write_stop(&ctx
);
1061 fy
= gd
->hsize
- data
->oy
+ data
->cy
;
1063 if (fx
== gd
->sx
- 1) {
1064 if (fy
== gd
->hsize
+ gd
->sy
)
1074 for (i
= fy
+ 1; i
< gd
->hsize
+ gd
->sy
+ 1; i
++) {
1078 n
= window_copy_search_lr(gd
, sgd
, &px
, i
- 1, first
, gd
->sx
);
1080 window_copy_scroll_to(wp
, px
, i
- 1);
1084 if (wrapflag
&& !n
&& !wrapped
) {
1095 window_copy_goto_line(struct window_pane
*wp
, const char *linestr
)
1097 struct window_copy_mode_data
*data
= wp
->modedata
;
1101 lineno
= strtonum(linestr
, 0, screen_hsize(data
->backing
), &errstr
);
1106 window_copy_update_selection(wp
);
1107 window_copy_redraw_screen(wp
);
1111 window_copy_write_line(
1112 struct window_pane
*wp
, struct screen_write_ctx
*ctx
, u_int py
)
1114 struct window_copy_mode_data
*data
= wp
->modedata
;
1115 struct screen
*s
= &data
->screen
;
1116 struct options
*oo
= &wp
->window
->options
;
1117 struct grid_cell gc
;
1119 size_t last
, xoff
= 0, size
= 0;
1121 window_mode_attrs(&gc
, oo
);
1123 last
= screen_size_y(s
) - 1;
1125 size
= xsnprintf(hdr
, sizeof hdr
,
1126 "[%u/%u]", data
->oy
, screen_hsize(data
->backing
));
1127 if (size
> screen_size_x(s
))
1128 size
= screen_size_x(s
);
1129 screen_write_cursormove(ctx
, screen_size_x(s
) - size
, 0);
1130 screen_write_puts(ctx
, &gc
, "%s", hdr
);
1131 } else if (py
== last
&& data
->inputtype
!= WINDOW_COPY_OFF
) {
1132 if (data
->inputtype
== WINDOW_COPY_NUMERICPREFIX
) {
1133 xoff
= size
= xsnprintf(hdr
, sizeof hdr
,
1134 "Repeat: %u", data
->numprefix
);
1136 xoff
= size
= xsnprintf(hdr
, sizeof hdr
,
1137 "%s: %s", data
->inputprompt
, data
->inputstr
);
1139 screen_write_cursormove(ctx
, 0, last
);
1140 screen_write_puts(ctx
, &gc
, "%s", hdr
);
1144 screen_write_cursormove(ctx
, xoff
, py
);
1145 screen_write_copy(ctx
, data
->backing
, xoff
,
1146 (screen_hsize(data
->backing
) - data
->oy
) + py
,
1147 screen_size_x(s
) - size
, 1);
1149 if (py
== data
->cy
&& data
->cx
== screen_size_x(s
)) {
1150 memcpy(&gc
, &grid_default_cell
, sizeof gc
);
1151 screen_write_cursormove(ctx
, screen_size_x(s
) - 1, py
);
1152 screen_write_putc(ctx
, &gc
, '$');
1157 window_copy_write_lines(
1158 struct window_pane
*wp
, struct screen_write_ctx
*ctx
, u_int py
, u_int ny
)
1162 for (yy
= py
; yy
< py
+ ny
; yy
++)
1163 window_copy_write_line(wp
, ctx
, py
);
1167 window_copy_redraw_lines(struct window_pane
*wp
, u_int py
, u_int ny
)
1169 struct window_copy_mode_data
*data
= wp
->modedata
;
1170 struct screen_write_ctx ctx
;
1173 screen_write_start(&ctx
, wp
, NULL
);
1174 for (i
= py
; i
< py
+ ny
; i
++)
1175 window_copy_write_line(wp
, &ctx
, i
);
1176 screen_write_cursormove(&ctx
, data
->cx
, data
->cy
);
1177 screen_write_stop(&ctx
);
1181 window_copy_redraw_screen(struct window_pane
*wp
)
1183 struct window_copy_mode_data
*data
= wp
->modedata
;
1185 window_copy_redraw_lines(wp
, 0, screen_size_y(&data
->screen
));
1189 window_copy_update_cursor(struct window_pane
*wp
, u_int cx
, u_int cy
)
1191 struct window_copy_mode_data
*data
= wp
->modedata
;
1192 struct screen
*s
= &data
->screen
;
1193 struct screen_write_ctx ctx
;
1194 u_int old_cx
, old_cy
;
1196 old_cx
= data
->cx
; old_cy
= data
->cy
;
1197 data
->cx
= cx
; data
->cy
= cy
;
1198 if (old_cx
== screen_size_x(s
))
1199 window_copy_redraw_lines(wp
, old_cy
, 1);
1200 if (data
->cx
== screen_size_x(s
))
1201 window_copy_redraw_lines(wp
, data
->cy
, 1);
1203 screen_write_start(&ctx
, wp
, NULL
);
1204 screen_write_cursormove(&ctx
, data
->cx
, data
->cy
);
1205 screen_write_stop(&ctx
);
1210 window_copy_start_selection(struct window_pane
*wp
)
1212 struct window_copy_mode_data
*data
= wp
->modedata
;
1213 struct screen
*s
= &data
->screen
;
1215 data
->selx
= data
->cx
;
1216 data
->sely
= screen_hsize(data
->backing
) + data
->cy
- data
->oy
;
1219 window_copy_update_selection(wp
);
1223 window_copy_update_selection(struct window_pane
*wp
)
1225 struct window_copy_mode_data
*data
= wp
->modedata
;
1226 struct screen
*s
= &data
->screen
;
1227 struct options
*oo
= &wp
->window
->options
;
1228 struct grid_cell gc
;
1229 u_int sx
, sy
, ty
, cy
;
1235 window_mode_attrs(&gc
, oo
);
1237 /* Find top of screen. */
1238 ty
= screen_hsize(data
->backing
) - data
->oy
;
1240 /* Adjust the selection. */
1243 if (sy
< ty
) { /* above screen */
1244 if (!data
->rectflag
)
1247 } else if (sy
> ty
+ screen_size_y(s
) - 1) { /* below screen */
1248 if (!data
->rectflag
)
1249 sx
= screen_size_x(s
) - 1;
1250 sy
= screen_size_y(s
) - 1;
1253 sy
= screen_hsize(s
) + sy
;
1255 screen_set_selection(s
,
1256 sx
, sy
, data
->cx
, screen_hsize(s
) + data
->cy
, data
->rectflag
, &gc
);
1258 if (data
->rectflag
) {
1260 * Can't rely on the caller to redraw the right lines for
1261 * rectangle selection - find the highest line and the number
1262 * of lines, and redraw just past that in both directions
1266 window_copy_redraw_lines(wp
, sy
, cy
- sy
+ 1);
1268 window_copy_redraw_lines(wp
, cy
, sy
- cy
+ 1);
1275 window_copy_get_selection(struct window_pane
*wp
, size_t *len
)
1277 struct window_copy_mode_data
*data
= wp
->modedata
;
1278 struct screen
*s
= &data
->screen
;
1281 u_int i
, xx
, yy
, sx
, sy
, ex
, ey
;
1282 u_int firstsx
, lastex
, restex
, restsx
;
1294 * The selection extends from selx,sely to (adjusted) cx,cy on
1298 /* Find start and end. */
1300 yy
= screen_hsize(data
->backing
) + data
->cy
- data
->oy
;
1301 if (yy
< data
->sely
|| (yy
== data
->sely
&& xx
< data
->selx
)) {
1303 ex
= data
->selx
; ey
= data
->sely
;
1305 sx
= data
->selx
; sy
= data
->sely
;
1309 /* Trim ex to end of line. */
1310 xx
= window_copy_find_length(wp
, ey
);
1315 * Deal with rectangle-copy if necessary; four situations: start of
1316 * first line (firstsx), end of last line (lastex), start (restsx) and
1317 * end (restex) of all other lines.
1319 xx
= screen_size_x(s
);
1322 * Behave according to mode-keys. If it is emacs, copy like emacs,
1323 * keeping the top-left-most character, and dropping the
1324 * bottom-right-most, regardless of copy direction. If it is vi, also
1325 * keep bottom-right-most character.
1327 keys
= options_get_number(&wp
->window
->options
, "mode-keys");
1328 if (data
->rectflag
) {
1330 * Need to ignore the column with the cursor in it, which for
1331 * rectangular copy means knowing which side the cursor is on.
1333 if (data
->selx
< data
->cx
) {
1334 /* Selection start is on the left. */
1335 if (keys
== MODEKEY_EMACS
) {
1340 lastex
= data
->cx
+ 1;
1341 restex
= data
->cx
+ 1;
1343 firstsx
= data
->selx
;
1344 restsx
= data
->selx
;
1346 /* Cursor is on the left. */
1347 lastex
= data
->selx
+ 1;
1348 restex
= data
->selx
+ 1;
1353 if (keys
== MODEKEY_EMACS
)
1362 /* Copy the lines. */
1364 window_copy_copy_line(wp
, &buf
, &off
, sy
, firstsx
, lastex
);
1366 window_copy_copy_line(wp
, &buf
, &off
, sy
, firstsx
, restex
);
1368 for (i
= sy
+ 1; i
< ey
; i
++) {
1369 window_copy_copy_line(
1370 wp
, &buf
, &off
, i
, restsx
, restex
);
1373 window_copy_copy_line(wp
, &buf
, &off
, ey
, restsx
, lastex
);
1376 /* Don't bother if no data. */
1381 *len
= off
- 1; /* remove final \n */
1386 window_copy_copy_buffer(struct window_pane
*wp
, int idx
, void *buf
, size_t len
)
1389 struct screen_write_ctx ctx
;
1391 if (options_get_number(&global_options
, "set-clipboard")) {
1392 screen_write_start(&ctx
, wp
, NULL
);
1393 screen_write_setselection(&ctx
, buf
, len
);
1394 screen_write_stop(&ctx
);
1398 limit
= options_get_number(&global_options
, "buffer-limit");
1399 paste_add(&global_buffers
, buf
, len
, limit
);
1401 paste_replace(&global_buffers
, idx
, buf
, len
);
1405 window_copy_copy_pipe(
1406 struct window_pane
*wp
, struct session
*sess
, int idx
, const char *arg
)
1413 buf
= window_copy_get_selection(wp
, &len
);
1417 job
= job_run(arg
, sess
, NULL
, NULL
, NULL
);
1418 bufferevent_write(job
->event
, buf
, len
);
1420 window_copy_copy_buffer(wp
, idx
, buf
, len
);
1424 window_copy_copy_selection(struct window_pane
*wp
, int idx
)
1429 buf
= window_copy_get_selection(wp
, &len
);
1433 window_copy_copy_buffer(wp
, idx
, buf
, len
);
1437 window_copy_copy_line(struct window_pane
*wp
,
1438 char **buf
, size_t *off
, u_int sy
, u_int sx
, u_int ex
)
1440 struct window_copy_mode_data
*data
= wp
->modedata
;
1441 struct grid
*gd
= data
->backing
->grid
;
1442 const struct grid_cell
*gc
;
1443 struct grid_line
*gl
;
1444 struct utf8_data ud
;
1445 u_int i
, xx
, wrapped
= 0;
1451 * Work out if the line was wrapped at the screen edge and all of it is
1454 gl
= &gd
->linedata
[sy
];
1455 if (gl
->flags
& GRID_LINE_WRAPPED
&& gl
->cellsize
<= gd
->sx
)
1458 /* If the line was wrapped, don't strip spaces (use the full length). */
1462 xx
= window_copy_find_length(wp
, sy
);
1469 for (i
= sx
; i
< ex
; i
++) {
1470 gc
= grid_peek_cell(gd
, i
, sy
);
1471 if (gc
->flags
& GRID_FLAG_PADDING
)
1473 grid_cell_get(gc
, &ud
);
1475 *buf
= xrealloc(*buf
, 1, (*off
) + ud
.size
);
1476 memcpy(*buf
+ *off
, ud
.data
, ud
.size
);
1481 /* Only add a newline if the line wasn't wrapped. */
1482 if (!wrapped
|| ex
!= xx
) {
1483 *buf
= xrealloc(*buf
, 1, (*off
) + 1);
1484 (*buf
)[(*off
)++] = '\n';
1489 window_copy_clear_selection(struct window_pane
*wp
)
1491 struct window_copy_mode_data
*data
= wp
->modedata
;
1494 screen_clear_selection(&data
->screen
);
1496 py
= screen_hsize(data
->backing
) + data
->cy
- data
->oy
;
1497 px
= window_copy_find_length(wp
, py
);
1499 window_copy_update_cursor(wp
, px
, data
->cy
);
1503 window_copy_in_set(struct window_pane
*wp
, u_int px
, u_int py
, const char *set
)
1505 struct window_copy_mode_data
*data
= wp
->modedata
;
1506 const struct grid_cell
*gc
;
1507 struct utf8_data ud
;
1509 gc
= grid_peek_cell(data
->backing
->grid
, px
, py
);
1510 grid_cell_get(gc
, &ud
);
1511 if (ud
.size
!= 1 || gc
->flags
& GRID_FLAG_PADDING
)
1513 if (*ud
.data
== 0x00 || *ud
.data
== 0x7f)
1515 return (strchr(set
, *ud
.data
) != NULL
);
1519 window_copy_find_length(struct window_pane
*wp
, u_int py
)
1521 struct window_copy_mode_data
*data
= wp
->modedata
;
1522 struct screen
*s
= data
->backing
;
1523 const struct grid_cell
*gc
;
1524 struct utf8_data ud
;
1528 * If the pane has been resized, its grid can contain old overlong
1529 * lines. grid_peek_cell does not allow accessing cells beyond the
1530 * width of the grid, and screen_write_copy treats them as spaces, so
1531 * ignore them here too.
1533 px
= s
->grid
->linedata
[py
].cellsize
;
1534 if (px
> screen_size_x(s
))
1535 px
= screen_size_x(s
);
1537 gc
= grid_peek_cell(s
->grid
, px
- 1, py
);
1538 grid_cell_get(gc
, &ud
);
1539 if (ud
.size
!= 1 || *ud
.data
!= ' ')
1547 window_copy_cursor_start_of_line(struct window_pane
*wp
)
1549 struct window_copy_mode_data
*data
= wp
->modedata
;
1550 struct screen
*back_s
= data
->backing
;
1551 struct grid
*gd
= back_s
->grid
;
1554 if (data
->cx
== 0) {
1555 py
= screen_hsize(back_s
) + data
->cy
- data
->oy
;
1556 while (py
> 0 && gd
->linedata
[py
-1].flags
& GRID_LINE_WRAPPED
) {
1557 window_copy_cursor_up(wp
, 0);
1558 py
= screen_hsize(back_s
) + data
->cy
- data
->oy
;
1561 window_copy_update_cursor(wp
, 0, data
->cy
);
1562 if (window_copy_update_selection(wp
))
1563 window_copy_redraw_lines(wp
, data
->cy
, 1);
1567 window_copy_cursor_back_to_indentation(struct window_pane
*wp
)
1569 struct window_copy_mode_data
*data
= wp
->modedata
;
1571 const struct grid_cell
*gc
;
1572 struct utf8_data ud
;
1575 py
= screen_hsize(data
->backing
) + data
->cy
- data
->oy
;
1576 xx
= window_copy_find_length(wp
, py
);
1579 gc
= grid_peek_cell(data
->backing
->grid
, px
, py
);
1580 grid_cell_get(gc
, &ud
);
1581 if (ud
.size
!= 1 || *ud
.data
!= ' ')
1586 window_copy_update_cursor(wp
, px
, data
->cy
);
1587 if (window_copy_update_selection(wp
))
1588 window_copy_redraw_lines(wp
, data
->cy
, 1);
1592 window_copy_cursor_end_of_line(struct window_pane
*wp
)
1594 struct window_copy_mode_data
*data
= wp
->modedata
;
1595 struct screen
*back_s
= data
->backing
;
1596 struct grid
*gd
= back_s
->grid
;
1599 py
= screen_hsize(back_s
) + data
->cy
- data
->oy
;
1600 px
= window_copy_find_length(wp
, py
);
1602 if (data
->cx
== px
) {
1603 if (data
->screen
.sel
.flag
&& data
->rectflag
)
1604 px
= screen_size_x(back_s
);
1605 if (gd
->linedata
[py
].flags
& GRID_LINE_WRAPPED
) {
1606 while (py
< gd
->sy
+ gd
->hsize
&&
1607 gd
->linedata
[py
].flags
& GRID_LINE_WRAPPED
) {
1608 window_copy_cursor_down(wp
, 0);
1609 py
= screen_hsize(back_s
)
1610 + data
->cy
- data
->oy
;
1612 px
= window_copy_find_length(wp
, py
);
1615 window_copy_update_cursor(wp
, px
, data
->cy
);
1617 if (window_copy_update_selection(wp
))
1618 window_copy_redraw_lines(wp
, data
->cy
, 1);
1622 window_copy_cursor_left(struct window_pane
*wp
)
1624 struct window_copy_mode_data
*data
= wp
->modedata
;
1626 if (data
->cx
== 0) {
1627 window_copy_cursor_up(wp
, 0);
1628 window_copy_cursor_end_of_line(wp
);
1630 window_copy_update_cursor(wp
, data
->cx
- 1, data
->cy
);
1631 if (window_copy_update_selection(wp
))
1632 window_copy_redraw_lines(wp
, data
->cy
, 1);
1637 window_copy_cursor_right(struct window_pane
*wp
)
1639 struct window_copy_mode_data
*data
= wp
->modedata
;
1642 if (data
->screen
.sel
.flag
&& data
->rectflag
)
1643 px
= screen_size_x(&data
->screen
);
1645 py
= screen_hsize(data
->backing
) + data
->cy
- data
->oy
;
1646 px
= window_copy_find_length(wp
, py
);
1649 if (data
->cx
>= px
) {
1650 window_copy_cursor_start_of_line(wp
);
1651 window_copy_cursor_down(wp
, 0);
1653 window_copy_update_cursor(wp
, data
->cx
+ 1, data
->cy
);
1654 if (window_copy_update_selection(wp
))
1655 window_copy_redraw_lines(wp
, data
->cy
, 1);
1660 window_copy_cursor_up(struct window_pane
*wp
, int scroll_only
)
1662 struct window_copy_mode_data
*data
= wp
->modedata
;
1663 struct screen
*s
= &data
->screen
;
1664 u_int ox
, oy
, px
, py
;
1666 oy
= screen_hsize(data
->backing
) + data
->cy
- data
->oy
;
1667 ox
= window_copy_find_length(wp
, oy
);
1668 if (data
->cx
!= ox
) {
1669 data
->lastcx
= data
->cx
;
1673 data
->cx
= data
->lastcx
;
1674 if (scroll_only
|| data
->cy
== 0) {
1675 window_copy_scroll_down(wp
, 1);
1677 if (data
->cy
== screen_size_y(s
) - 1)
1678 window_copy_redraw_lines(wp
, data
->cy
, 1);
1680 window_copy_redraw_lines(wp
, data
->cy
, 2);
1683 window_copy_update_cursor(wp
, data
->cx
, data
->cy
- 1);
1684 if (window_copy_update_selection(wp
)) {
1685 if (data
->cy
== screen_size_y(s
) - 1)
1686 window_copy_redraw_lines(wp
, data
->cy
, 1);
1688 window_copy_redraw_lines(wp
, data
->cy
, 2);
1692 if (!data
->screen
.sel
.flag
|| !data
->rectflag
) {
1693 py
= screen_hsize(data
->backing
) + data
->cy
- data
->oy
;
1694 px
= window_copy_find_length(wp
, py
);
1695 if ((data
->cx
>= data
->lastsx
&& data
->cx
!= px
) ||
1697 window_copy_cursor_end_of_line(wp
);
1702 window_copy_cursor_down(struct window_pane
*wp
, int scroll_only
)
1704 struct window_copy_mode_data
*data
= wp
->modedata
;
1705 struct screen
*s
= &data
->screen
;
1706 u_int ox
, oy
, px
, py
;
1708 oy
= screen_hsize(data
->backing
) + data
->cy
- data
->oy
;
1709 ox
= window_copy_find_length(wp
, oy
);
1710 if (data
->cx
!= ox
) {
1711 data
->lastcx
= data
->cx
;
1715 data
->cx
= data
->lastcx
;
1716 if (scroll_only
|| data
->cy
== screen_size_y(s
) - 1) {
1717 window_copy_scroll_up(wp
, 1);
1718 if (scroll_only
&& data
->cy
> 0)
1719 window_copy_redraw_lines(wp
, data
->cy
- 1, 2);
1721 window_copy_update_cursor(wp
, data
->cx
, data
->cy
+ 1);
1722 if (window_copy_update_selection(wp
))
1723 window_copy_redraw_lines(wp
, data
->cy
- 1, 2);
1726 if (!data
->screen
.sel
.flag
|| !data
->rectflag
) {
1727 py
= screen_hsize(data
->backing
) + data
->cy
- data
->oy
;
1728 px
= window_copy_find_length(wp
, py
);
1729 if ((data
->cx
>= data
->lastsx
&& data
->cx
!= px
) ||
1731 window_copy_cursor_end_of_line(wp
);
1736 window_copy_cursor_jump(struct window_pane
*wp
)
1738 struct window_copy_mode_data
*data
= wp
->modedata
;
1739 struct screen
*back_s
= data
->backing
;
1740 const struct grid_cell
*gc
;
1741 struct utf8_data ud
;
1745 py
= screen_hsize(back_s
) + data
->cy
- data
->oy
;
1746 xx
= window_copy_find_length(wp
, py
);
1749 gc
= grid_peek_cell(back_s
->grid
, px
, py
);
1750 grid_cell_get(gc
, &ud
);
1751 if (!(gc
->flags
& GRID_FLAG_PADDING
) &&
1752 ud
.size
== 1 && *ud
.data
== data
->jumpchar
) {
1753 window_copy_update_cursor(wp
, px
, data
->cy
);
1754 if (window_copy_update_selection(wp
))
1755 window_copy_redraw_lines(wp
, data
->cy
, 1);
1763 window_copy_cursor_jump_back(struct window_pane
*wp
)
1765 struct window_copy_mode_data
*data
= wp
->modedata
;
1766 struct screen
*back_s
= data
->backing
;
1767 const struct grid_cell
*gc
;
1768 struct utf8_data ud
;
1772 py
= screen_hsize(back_s
) + data
->cy
- data
->oy
;
1778 gc
= grid_peek_cell(back_s
->grid
, px
, py
);
1779 grid_cell_get(gc
, &ud
);
1780 if (!(gc
->flags
& GRID_FLAG_PADDING
) &&
1781 ud
.size
== 1 && *ud
.data
== data
->jumpchar
) {
1782 window_copy_update_cursor(wp
, px
, data
->cy
);
1783 if (window_copy_update_selection(wp
))
1784 window_copy_redraw_lines(wp
, data
->cy
, 1);
1794 window_copy_cursor_jump_to(struct window_pane
*wp
)
1796 struct window_copy_mode_data
*data
= wp
->modedata
;
1797 struct screen
*back_s
= data
->backing
;
1798 const struct grid_cell
*gc
;
1799 struct utf8_data ud
;
1803 py
= screen_hsize(back_s
) + data
->cy
- data
->oy
;
1804 xx
= window_copy_find_length(wp
, py
);
1807 gc
= grid_peek_cell(back_s
->grid
, px
, py
);
1808 grid_cell_get(gc
, &ud
);
1809 if (!(gc
->flags
& GRID_FLAG_PADDING
) &&
1810 ud
.size
== 1 && *ud
.data
== data
->jumpchar
) {
1811 window_copy_update_cursor(wp
, px
- 1, data
->cy
);
1812 if (window_copy_update_selection(wp
))
1813 window_copy_redraw_lines(wp
, data
->cy
, 1);
1821 window_copy_cursor_jump_to_back(struct window_pane
*wp
)
1823 struct window_copy_mode_data
*data
= wp
->modedata
;
1824 struct screen
*back_s
= data
->backing
;
1825 const struct grid_cell
*gc
;
1826 struct utf8_data ud
;
1830 py
= screen_hsize(back_s
) + data
->cy
- data
->oy
;
1836 gc
= grid_peek_cell(back_s
->grid
, px
, py
);
1837 grid_cell_get(gc
, &ud
);
1838 if (!(gc
->flags
& GRID_FLAG_PADDING
) &&
1839 ud
.size
== 1 && *ud
.data
== data
->jumpchar
) {
1840 window_copy_update_cursor(wp
, px
+ 1, data
->cy
);
1841 if (window_copy_update_selection(wp
))
1842 window_copy_redraw_lines(wp
, data
->cy
, 1);
1852 window_copy_cursor_next_word(struct window_pane
*wp
, const char *separators
)
1854 struct window_copy_mode_data
*data
= wp
->modedata
;
1855 struct screen
*back_s
= data
->backing
;
1856 u_int px
, py
, xx
, yy
;
1860 py
= screen_hsize(back_s
) + data
->cy
- data
->oy
;
1861 xx
= window_copy_find_length(wp
, py
);
1862 yy
= screen_hsize(back_s
) + screen_size_y(back_s
) - 1;
1865 * First skip past any nonword characters and then any word characters.
1867 * expected is initially set to 0 for the former and then 1 for the
1872 window_copy_in_set(wp
, px
, py
, separators
) == expected
) {
1873 /* Move down if we're past the end of the line. */
1877 window_copy_cursor_down(wp
, 0);
1880 py
= screen_hsize(back_s
) + data
->cy
- data
->oy
;
1881 xx
= window_copy_find_length(wp
, py
);
1885 expected
= !expected
;
1886 } while (expected
== 1);
1888 window_copy_update_cursor(wp
, px
, data
->cy
);
1889 if (window_copy_update_selection(wp
))
1890 window_copy_redraw_lines(wp
, data
->cy
, 1);
1894 window_copy_cursor_next_word_end(struct window_pane
*wp
, const char *separators
)
1896 struct window_copy_mode_data
*data
= wp
->modedata
;
1897 struct options
*oo
= &wp
->window
->options
;
1898 struct screen
*back_s
= data
->backing
;
1899 u_int px
, py
, xx
, yy
;
1900 int keys
, expected
= 1;
1903 py
= screen_hsize(back_s
) + data
->cy
- data
->oy
;
1904 xx
= window_copy_find_length(wp
, py
);
1905 yy
= screen_hsize(back_s
) + screen_size_y(back_s
) - 1;
1907 keys
= options_get_number(oo
, "mode-keys");
1908 if (keys
== MODEKEY_VI
&& !window_copy_in_set(wp
, px
, py
, separators
))
1912 * First skip past any word characters, then any nonword characters.
1914 * expected is initially set to 1 for the former and then 0 for the
1919 window_copy_in_set(wp
, px
, py
, separators
) == expected
) {
1920 /* Move down if we're past the end of the line. */
1924 window_copy_cursor_down(wp
, 0);
1927 py
= screen_hsize(back_s
) + data
->cy
- data
->oy
;
1928 xx
= window_copy_find_length(wp
, py
);
1932 expected
= !expected
;
1933 } while (expected
== 0);
1935 if (keys
== MODEKEY_VI
&& px
!= 0)
1938 window_copy_update_cursor(wp
, px
, data
->cy
);
1939 if (window_copy_update_selection(wp
))
1940 window_copy_redraw_lines(wp
, data
->cy
, 1);
1943 /* Move to the previous place where a word begins. */
1945 window_copy_cursor_previous_word(struct window_pane
*wp
, const char *separators
)
1947 struct window_copy_mode_data
*data
= wp
->modedata
;
1951 py
= screen_hsize(data
->backing
) + data
->cy
- data
->oy
;
1953 /* Move back to the previous word character. */
1957 if (!window_copy_in_set(wp
, px
, py
, separators
))
1960 if (data
->cy
== 0 &&
1961 (screen_hsize(data
->backing
) == 0 ||
1962 data
->oy
>= screen_hsize(data
->backing
) - 1))
1964 window_copy_cursor_up(wp
, 0);
1966 py
= screen_hsize(data
->backing
) + data
->cy
- data
->oy
;
1967 px
= window_copy_find_length(wp
, py
);
1971 /* Move back to the beginning of this word. */
1972 while (px
> 0 && !window_copy_in_set(wp
, px
- 1, py
, separators
))
1976 window_copy_update_cursor(wp
, px
, data
->cy
);
1977 if (window_copy_update_selection(wp
))
1978 window_copy_redraw_lines(wp
, data
->cy
, 1);
1982 window_copy_scroll_up(struct window_pane
*wp
, u_int ny
)
1984 struct window_copy_mode_data
*data
= wp
->modedata
;
1985 struct screen
*s
= &data
->screen
;
1986 struct screen_write_ctx ctx
;
1994 screen_write_start(&ctx
, wp
, NULL
);
1995 screen_write_cursormove(&ctx
, 0, 0);
1996 screen_write_deleteline(&ctx
, ny
);
1997 window_copy_write_lines(wp
, &ctx
, screen_size_y(s
) - ny
, ny
);
1998 window_copy_write_line(wp
, &ctx
, 0);
1999 if (screen_size_y(s
) > 1)
2000 window_copy_write_line(wp
, &ctx
, 1);
2001 if (screen_size_y(s
) > 3)
2002 window_copy_write_line(wp
, &ctx
, screen_size_y(s
) - 2);
2003 if (s
->sel
.flag
&& screen_size_y(s
) > ny
) {
2004 window_copy_update_selection(wp
);
2005 window_copy_write_line(wp
, &ctx
, screen_size_y(s
) - ny
- 1);
2007 screen_write_cursormove(&ctx
, data
->cx
, data
->cy
);
2008 window_copy_update_selection(wp
);
2009 screen_write_stop(&ctx
);
2013 window_copy_scroll_down(struct window_pane
*wp
, u_int ny
)
2015 struct window_copy_mode_data
*data
= wp
->modedata
;
2016 struct screen
*s
= &data
->screen
;
2017 struct screen_write_ctx ctx
;
2019 if (ny
> screen_hsize(data
->backing
))
2022 if (data
->oy
> screen_hsize(data
->backing
) - ny
)
2023 ny
= screen_hsize(data
->backing
) - data
->oy
;
2028 screen_write_start(&ctx
, wp
, NULL
);
2029 screen_write_cursormove(&ctx
, 0, 0);
2030 screen_write_insertline(&ctx
, ny
);
2031 window_copy_write_lines(wp
, &ctx
, 0, ny
);
2032 if (s
->sel
.flag
&& screen_size_y(s
) > ny
) {
2033 window_copy_update_selection(wp
);
2034 window_copy_write_line(wp
, &ctx
, ny
);
2035 } else if (ny
== 1) /* nuke position */
2036 window_copy_write_line(wp
, &ctx
, 1);
2037 screen_write_cursormove(&ctx
, data
->cx
, data
->cy
);
2038 window_copy_update_selection(wp
);
2039 screen_write_stop(&ctx
);
2043 window_copy_rectangle_toggle(struct window_pane
*wp
)
2045 struct window_copy_mode_data
*data
= wp
->modedata
;
2048 data
->rectflag
= !data
->rectflag
;
2050 py
= screen_hsize(data
->backing
) + data
->cy
- data
->oy
;
2051 px
= window_copy_find_length(wp
, py
);
2053 window_copy_update_cursor(wp
, px
, data
->cy
);
2055 window_copy_update_selection(wp
);
2056 window_copy_redraw_screen(wp
);