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 client
*, int);
30 int window_copy_key_input(struct window_pane
*, int);
31 void window_copy_mouse(
32 struct window_pane
*, struct client
*, struct mouse_event
*);
34 void window_copy_redraw_lines(struct window_pane
*, u_int
, u_int
);
35 void window_copy_redraw_screen(struct window_pane
*);
36 void window_copy_write_line(
37 struct window_pane
*, struct screen_write_ctx
*, u_int
);
38 void window_copy_write_lines(
39 struct window_pane
*, struct screen_write_ctx
*, u_int
, u_int
);
41 void window_copy_scroll_to(struct window_pane
*, u_int
, u_int
);
42 int window_copy_search_compare(
43 struct grid
*, u_int
, u_int
, struct grid
*, u_int
);
44 int window_copy_search_lr(
45 struct grid
*, struct grid
*, u_int
*, u_int
, u_int
, u_int
);
46 int window_copy_search_rl(
47 struct grid
*, struct grid
*, u_int
*, u_int
, u_int
, u_int
);
48 void window_copy_search_up(struct window_pane
*, const char *);
49 void window_copy_search_down(struct window_pane
*, const char *);
50 void window_copy_goto_line(struct window_pane
*, const char *);
51 void window_copy_update_cursor(struct window_pane
*, u_int
, u_int
);
52 void window_copy_start_selection(struct window_pane
*);
53 int window_copy_update_selection(struct window_pane
*);
54 void window_copy_copy_selection(struct window_pane
*, struct client
*);
55 void window_copy_copy_line(
56 struct window_pane
*, char **, size_t *, u_int
, u_int
, u_int
);
57 int window_copy_is_space(struct window_pane
*, u_int
, u_int
);
58 u_int
window_copy_find_length(struct window_pane
*, u_int
);
59 void window_copy_cursor_start_of_line(struct window_pane
*);
60 void window_copy_cursor_back_to_indentation(struct window_pane
*);
61 void window_copy_cursor_end_of_line(struct window_pane
*);
62 void window_copy_cursor_left(struct window_pane
*);
63 void window_copy_cursor_right(struct window_pane
*);
64 void window_copy_cursor_up(struct window_pane
*, int);
65 void window_copy_cursor_down(struct window_pane
*, int);
66 void window_copy_cursor_next_word(struct window_pane
*);
67 void window_copy_cursor_previous_word(struct window_pane
*);
68 void window_copy_scroll_up(struct window_pane
*, u_int
);
69 void window_copy_scroll_down(struct window_pane
*, u_int
);
71 const struct window_mode window_copy_mode
= {
80 enum window_copy_input_type
{
83 WINDOW_COPY_SEARCHDOWN
,
87 struct window_copy_mode_data
{
90 struct mode_key_data mdata
;
100 u_int lastcx
; /* position in last line with content */
101 u_int lastsx
; /* size of last line with content */
103 enum window_copy_input_type inputtype
;
104 const char *inputprompt
;
107 enum window_copy_input_type searchtype
;
112 window_copy_init(struct window_pane
*wp
)
114 struct window_copy_mode_data
*data
;
116 struct screen_write_ctx ctx
;
120 wp
->modedata
= data
= xmalloc(sizeof *data
);
122 data
->cx
= wp
->base
.cx
;
123 data
->cy
= wp
->base
.cy
;
128 data
->inputtype
= WINDOW_COPY_OFF
;
129 data
->inputprompt
= NULL
;
130 data
->inputstr
= xstrdup("");
132 data
->searchtype
= WINDOW_COPY_OFF
;
133 data
->searchstr
= NULL
;
136 screen_init(s
, screen_size_x(&wp
->base
), screen_size_y(&wp
->base
), 0);
137 if (options_get_number(&wp
->window
->options
, "mode-mouse"))
138 s
->mode
|= MODE_MOUSE
;
140 keys
= options_get_number(&wp
->window
->options
, "mode-keys");
141 if (keys
== MODEKEY_EMACS
)
142 mode_key_init(&data
->mdata
, &mode_key_tree_emacs_copy
);
144 mode_key_init(&data
->mdata
, &mode_key_tree_vi_copy
);
149 screen_write_start(&ctx
, NULL
, s
);
150 for (i
= 0; i
< screen_size_y(s
); i
++)
151 window_copy_write_line(wp
, &ctx
, i
);
152 screen_write_cursormove(&ctx
, data
->cx
, data
->cy
);
153 screen_write_stop(&ctx
);
159 window_copy_free(struct window_pane
*wp
)
161 struct window_copy_mode_data
*data
= wp
->modedata
;
163 if (data
->searchstr
!= NULL
)
164 xfree(data
->searchstr
);
165 xfree(data
->inputstr
);
167 screen_free(&data
->screen
);
173 window_copy_pageup(struct window_pane
*wp
)
175 struct window_copy_mode_data
*data
= wp
->modedata
;
176 struct screen
*s
= &data
->screen
;
180 if (screen_size_y(s
) > 2)
181 n
= screen_size_y(s
) - 2;
182 if (data
->oy
+ n
> screen_hsize(&wp
->base
))
183 data
->oy
= screen_hsize(&wp
->base
);
186 window_copy_update_selection(wp
);
187 window_copy_redraw_screen(wp
);
191 window_copy_resize(struct window_pane
*wp
, u_int sx
, u_int sy
)
193 struct window_copy_mode_data
*data
= wp
->modedata
;
194 struct screen
*s
= &data
->screen
;
195 struct screen_write_ctx ctx
;
197 screen_resize(s
, sx
, sy
);
199 if (data
->cy
> sy
- 1)
204 screen_clear_selection(&data
->screen
);
206 screen_write_start(&ctx
, NULL
, s
);
207 window_copy_write_lines(wp
, &ctx
, 0, screen_size_y(s
) - 1);
208 screen_write_stop(&ctx
);
210 window_copy_redraw_screen(wp
);
214 window_copy_key(struct window_pane
*wp
, struct client
*c
, int key
)
216 struct window_copy_mode_data
*data
= wp
->modedata
;
217 struct screen
*s
= &data
->screen
;
221 if (data
->inputtype
!= WINDOW_COPY_OFF
) {
222 if (window_copy_key_input(wp
, key
) != 0)
227 switch (mode_key_lookup(&data
->mdata
, key
)) {
228 case MODEKEYCOPY_CANCEL
:
229 window_pane_reset_mode(wp
);
231 case MODEKEYCOPY_LEFT
:
232 window_copy_cursor_left(wp
);
234 case MODEKEYCOPY_RIGHT
:
235 window_copy_cursor_right(wp
);
238 window_copy_cursor_up(wp
, 0);
240 case MODEKEYCOPY_DOWN
:
241 window_copy_cursor_down(wp
, 0);
243 case MODEKEYCOPY_SCROLLUP
:
244 window_copy_cursor_up(wp
, 1);
246 case MODEKEYCOPY_SCROLLDOWN
:
247 window_copy_cursor_down(wp
, 1);
249 case MODEKEYCOPY_PREVIOUSPAGE
:
250 window_copy_pageup(wp
);
252 case MODEKEYCOPY_NEXTPAGE
:
254 if (screen_size_y(s
) > 2)
255 n
= screen_size_y(s
) - 2;
260 window_copy_update_selection(wp
);
261 window_copy_redraw_screen(wp
);
263 case MODEKEYCOPY_HALFPAGEUP
:
264 n
= screen_size_y(s
) / 2;
265 if (data
->oy
+ n
> screen_hsize(&wp
->base
))
266 data
->oy
= screen_hsize(&wp
->base
);
269 window_copy_update_selection(wp
);
270 window_copy_redraw_screen(wp
);
272 case MODEKEYCOPY_HALFPAGEDOWN
:
273 n
= screen_size_y(s
) / 2;
278 window_copy_update_selection(wp
);
279 window_copy_redraw_screen(wp
);
281 case MODEKEYCOPY_TOPLINE
:
284 window_copy_update_selection(wp
);
285 window_copy_redraw_screen(wp
);
287 case MODEKEYCOPY_MIDDLELINE
:
289 data
->cy
= (screen_size_y(s
) - 1) / 2;
290 window_copy_update_selection(wp
);
291 window_copy_redraw_screen(wp
);
293 case MODEKEYCOPY_BOTTOMLINE
:
295 data
->cy
= screen_size_y(s
) - 1;
296 window_copy_update_selection(wp
);
297 window_copy_redraw_screen(wp
);
299 case MODEKEYCOPY_STARTSELECTION
:
300 window_copy_start_selection(wp
);
301 window_copy_redraw_screen(wp
);
303 case MODEKEYCOPY_CLEARSELECTION
:
304 screen_clear_selection(&data
->screen
);
305 window_copy_redraw_screen(wp
);
307 case MODEKEYCOPY_COPYSELECTION
:
308 if (c
!= NULL
&& c
->session
!= NULL
) {
309 window_copy_copy_selection(wp
, c
);
310 window_pane_reset_mode(wp
);
313 case MODEKEYCOPY_STARTOFLINE
:
314 window_copy_cursor_start_of_line(wp
);
316 case MODEKEYCOPY_BACKTOINDENTATION
:
317 window_copy_cursor_back_to_indentation(wp
);
319 case MODEKEYCOPY_ENDOFLINE
:
320 window_copy_cursor_end_of_line(wp
);
322 case MODEKEYCOPY_NEXTWORD
:
323 window_copy_cursor_next_word(wp
);
325 case MODEKEYCOPY_PREVIOUSWORD
:
326 window_copy_cursor_previous_word(wp
);
328 case MODEKEYCOPY_SEARCHUP
:
329 data
->inputtype
= WINDOW_COPY_SEARCHUP
;
330 data
->inputprompt
= "Search Up";
332 case MODEKEYCOPY_SEARCHDOWN
:
333 data
->inputtype
= WINDOW_COPY_SEARCHDOWN
;
334 data
->inputprompt
= "Search Down";
336 case MODEKEYCOPY_SEARCHAGAIN
:
337 switch (data
->searchtype
) {
338 case WINDOW_COPY_OFF
:
339 case WINDOW_COPY_GOTOLINE
:
341 case WINDOW_COPY_SEARCHUP
:
342 window_copy_search_up(wp
, data
->searchstr
);
344 case WINDOW_COPY_SEARCHDOWN
:
345 window_copy_search_down(wp
, data
->searchstr
);
349 case MODEKEYCOPY_GOTOLINE
:
350 data
->inputtype
= WINDOW_COPY_GOTOLINE
;
351 data
->inputprompt
= "Goto Line";
352 *data
->inputstr
= '\0';
361 keys
= options_get_number(&wp
->window
->options
, "mode-keys");
362 if (keys
== MODEKEY_EMACS
)
363 mode_key_init(&data
->mdata
, &mode_key_tree_emacs_edit
);
365 mode_key_init(&data
->mdata
, &mode_key_tree_vi_edit
);
367 window_copy_redraw_lines(wp
, screen_size_y(s
) - 1, 1);
371 keys
= options_get_number(&wp
->window
->options
, "mode-keys");
372 if (keys
== MODEKEY_EMACS
)
373 mode_key_init(&data
->mdata
, &mode_key_tree_emacs_copy
);
375 mode_key_init(&data
->mdata
, &mode_key_tree_vi_copy
);
377 data
->inputtype
= WINDOW_COPY_OFF
;
378 data
->inputprompt
= NULL
;
380 window_copy_redraw_lines(wp
, screen_size_y(s
) - 1, 1);
384 window_copy_key_input(struct window_pane
*wp
, int key
)
386 struct window_copy_mode_data
*data
= wp
->modedata
;
387 struct screen
*s
= &data
->screen
;
390 switch (mode_key_lookup(&data
->mdata
, key
)) {
391 case MODEKEYEDIT_CANCEL
:
393 case MODEKEYEDIT_BACKSPACE
:
394 inputlen
= strlen(data
->inputstr
);
396 data
->inputstr
[inputlen
- 1] = '\0';
398 case MODEKEYEDIT_DELETELINE
:
399 *data
->inputstr
= '\0';
401 case MODEKEYEDIT_ENTER
:
402 switch (data
->inputtype
) {
403 case WINDOW_COPY_OFF
:
405 case WINDOW_COPY_SEARCHUP
:
406 window_copy_search_up(wp
, data
->inputstr
);
407 data
->searchtype
= data
->inputtype
;
408 data
->searchstr
= xstrdup(data
->inputstr
);
410 case WINDOW_COPY_SEARCHDOWN
:
411 window_copy_search_down(wp
, data
->inputstr
);
412 data
->searchtype
= data
->inputtype
;
413 data
->searchstr
= xstrdup(data
->inputstr
);
415 case WINDOW_COPY_GOTOLINE
:
416 window_copy_goto_line(wp
, data
->inputstr
);
417 *data
->inputstr
= '\0';
422 if (key
< 32 || key
> 126)
424 inputlen
= strlen(data
->inputstr
) + 2;
426 data
->inputstr
= xrealloc(data
->inputstr
, 1, inputlen
);
427 data
->inputstr
[inputlen
- 2] = key
;
428 data
->inputstr
[inputlen
- 1] = '\0';
434 window_copy_redraw_lines(wp
, screen_size_y(s
) - 1, 1);
441 struct window_pane
*wp
, unused
struct client
*c
, struct mouse_event
*m
)
443 struct window_copy_mode_data
*data
= wp
->modedata
;
444 struct screen
*s
= &data
->screen
;
448 if (m
->x
>= screen_size_x(s
))
450 if (m
->y
>= screen_size_y(s
))
453 window_copy_update_cursor(wp
, m
->x
, m
->y
);
454 if (window_copy_update_selection(wp
))
455 window_copy_redraw_screen(wp
);
459 window_copy_scroll_to(struct window_pane
*wp
, u_int px
, u_int py
)
461 struct window_copy_mode_data
*data
= wp
->modedata
;
462 struct screen
*s
= &wp
->base
;
463 struct grid
*gd
= s
->grid
;
472 } else if (py
> gd
->hsize
+ gd
->sy
- gap
) {
474 data
->cy
= py
- gd
->hsize
;
476 offset
= py
+ gap
- gd
->sy
;
477 data
->cy
= py
- offset
;
479 data
->oy
= gd
->hsize
- offset
;
481 window_copy_redraw_screen(wp
);
485 window_copy_search_compare(
486 struct grid
*gd
, u_int px
, u_int py
, struct grid
*sgd
, u_int spx
)
488 const struct grid_cell
*gc
, *sgc
;
489 const struct grid_utf8
*gu
, *sgu
;
491 gc
= grid_peek_cell(gd
, px
, py
);
492 sgc
= grid_peek_cell(sgd
, spx
, 0);
494 if ((gc
->flags
& GRID_FLAG_UTF8
) != (sgc
->flags
& GRID_FLAG_UTF8
))
497 if (gc
->flags
& GRID_FLAG_UTF8
) {
498 gu
= grid_peek_utf8(gd
, px
, py
);
499 sgu
= grid_peek_utf8(sgd
, spx
, 0);
500 if (grid_utf8_compare(gu
, sgu
))
503 if (gc
->data
== sgc
->data
)
510 window_copy_search_lr(struct grid
*gd
,
511 struct grid
*sgd
, u_int
*ppx
, u_int py
, u_int first
, u_int last
)
515 for (ax
= first
; ax
< last
; ax
++) {
516 if (ax
+ sgd
->sx
>= gd
->sx
)
518 for (bx
= 0; bx
< sgd
->sx
; bx
++) {
520 if (!window_copy_search_compare(gd
, px
, py
, sgd
, bx
))
532 window_copy_search_rl(struct grid
*gd
,
533 struct grid
*sgd
, u_int
*ppx
, u_int py
, u_int first
, u_int last
)
537 for (ax
= last
+ 1; ax
> first
; ax
--) {
538 if (gd
->sx
- (ax
- 1) < sgd
->sx
)
540 for (bx
= 0; bx
< sgd
->sx
; bx
++) {
542 if (!window_copy_search_compare(gd
, px
, py
, sgd
, bx
))
554 window_copy_search_up(struct window_pane
*wp
, const char *searchstr
)
556 struct window_copy_mode_data
*data
= wp
->modedata
;
557 struct screen
*s
= &wp
->base
, ss
;
558 struct screen_write_ctx ctx
;
559 struct grid
*gd
= s
->grid
, *sgd
;
562 u_int i
, last
, fx
, fy
, px
;
563 int utf8flag
, n
, wrapped
;
565 if (*searchstr
== '\0')
567 utf8flag
= options_get_number(&wp
->window
->options
, "utf8");
568 searchlen
= screen_write_strlen(utf8flag
, "%s", searchstr
);
570 screen_init(&ss
, searchlen
, 1, 0);
571 screen_write_start(&ctx
, NULL
, &ss
);
572 memcpy(&gc
, &grid_default_cell
, sizeof gc
);
573 screen_write_nputs(&ctx
, -1, &gc
, utf8flag
, "%s", searchstr
);
574 screen_write_stop(&ctx
);
577 fy
= gd
->hsize
- data
->oy
+ data
->cy
;
590 for (i
= fy
+ 1; i
> 0; i
--) {
591 last
= screen_size_x(s
);
594 n
= window_copy_search_rl(gd
, sgd
, &px
, i
- 1, 0, last
);
596 window_copy_scroll_to(wp
, px
, i
- 1);
600 if (!n
&& !wrapped
) {
602 fy
= gd
->hsize
+ gd
->sy
- 1;
611 window_copy_search_down(struct window_pane
*wp
, const char *searchstr
)
613 struct window_copy_mode_data
*data
= wp
->modedata
;
614 struct screen
*s
= &wp
->base
, ss
;
615 struct screen_write_ctx ctx
;
616 struct grid
*gd
= s
->grid
, *sgd
;
619 u_int i
, first
, fx
, fy
, px
;
620 int utf8flag
, n
, wrapped
;
622 if (*searchstr
== '\0')
624 utf8flag
= options_get_number(&wp
->window
->options
, "utf8");
625 searchlen
= screen_write_strlen(utf8flag
, "%s", searchstr
);
627 screen_init(&ss
, searchlen
, 1, 0);
628 screen_write_start(&ctx
, NULL
, &ss
);
629 memcpy(&gc
, &grid_default_cell
, sizeof gc
);
630 screen_write_nputs(&ctx
, -1, &gc
, utf8flag
, "%s", searchstr
);
631 screen_write_stop(&ctx
);
634 fy
= gd
->hsize
- data
->oy
+ data
->cy
;
636 if (fx
== gd
->sx
- 1) {
637 if (fy
== gd
->hsize
+ gd
->sy
)
647 for (i
= fy
+ 1; i
< gd
->hsize
+ gd
->sy
; i
++) {
651 n
= window_copy_search_lr(gd
, sgd
, &px
, i
- 1, first
, gd
->sx
);
653 window_copy_scroll_to(wp
, px
, i
- 1);
657 if (!n
&& !wrapped
) {
668 window_copy_goto_line(struct window_pane
*wp
, const char *linestr
)
670 struct window_copy_mode_data
*data
= wp
->modedata
;
674 lineno
= strtonum(linestr
, 0, screen_hsize(&wp
->base
), &errstr
);
679 window_copy_redraw_screen(wp
);
683 window_copy_write_line(
684 struct window_pane
*wp
, struct screen_write_ctx
*ctx
, u_int py
)
686 struct window_copy_mode_data
*data
= wp
->modedata
;
687 struct screen
*s
= &data
->screen
;
688 struct options
*oo
= &wp
->window
->options
;
691 size_t last
, xoff
= 0, size
= 0;
693 memcpy(&gc
, &grid_default_cell
, sizeof gc
);
694 colour_set_fg(&gc
, options_get_number(oo
, "mode-fg"));
695 colour_set_bg(&gc
, options_get_number(oo
, "mode-bg"));
696 gc
.attr
|= options_get_number(oo
, "mode-attr");
698 last
= screen_size_y(s
) - 1;
700 size
= xsnprintf(hdr
, sizeof hdr
,
701 "[%u/%u]", data
->oy
, screen_hsize(&wp
->base
));
702 screen_write_cursormove(ctx
, screen_size_x(s
) - size
, 0);
703 screen_write_puts(ctx
, &gc
, "%s", hdr
);
704 } else if (py
== last
&& data
->inputtype
!= WINDOW_COPY_OFF
) {
705 xoff
= size
= xsnprintf(hdr
, sizeof hdr
,
706 "%s: %s", data
->inputprompt
, data
->inputstr
);
707 screen_write_cursormove(ctx
, 0, last
);
708 screen_write_puts(ctx
, &gc
, "%s", hdr
);
712 screen_write_cursormove(ctx
, xoff
, py
);
713 screen_write_copy(ctx
, &wp
->base
, xoff
, (screen_hsize(&wp
->base
) -
714 data
->oy
) + py
, screen_size_x(s
) - size
, 1);
716 if (py
== data
->cy
&& data
->cx
== screen_size_x(s
)) {
717 memcpy(&gc
, &grid_default_cell
, sizeof gc
);
718 screen_write_cursormove(ctx
, screen_size_x(s
) - 1, py
);
719 screen_write_putc(ctx
, &gc
, '$');
724 window_copy_write_lines(
725 struct window_pane
*wp
, struct screen_write_ctx
*ctx
, u_int py
, u_int ny
)
729 for (yy
= py
; yy
< py
+ ny
; yy
++)
730 window_copy_write_line(wp
, ctx
, py
);
734 window_copy_redraw_lines(struct window_pane
*wp
, u_int py
, u_int ny
)
736 struct window_copy_mode_data
*data
= wp
->modedata
;
737 struct screen_write_ctx ctx
;
740 screen_write_start(&ctx
, wp
, NULL
);
741 for (i
= py
; i
< py
+ ny
; i
++)
742 window_copy_write_line(wp
, &ctx
, i
);
743 screen_write_cursormove(&ctx
, data
->cx
, data
->cy
);
744 screen_write_stop(&ctx
);
748 window_copy_redraw_screen(struct window_pane
*wp
)
750 struct window_copy_mode_data
*data
= wp
->modedata
;
752 window_copy_redraw_lines(wp
, 0, screen_size_y(&data
->screen
));
756 window_copy_update_cursor(struct window_pane
*wp
, u_int cx
, u_int cy
)
758 struct window_copy_mode_data
*data
= wp
->modedata
;
759 struct screen
*s
= &data
->screen
;
760 struct screen_write_ctx ctx
;
761 u_int old_cx
, old_cy
;
763 old_cx
= data
->cx
; old_cy
= data
->cy
;
764 data
->cx
= cx
; data
->cy
= cy
;
765 if (old_cx
== screen_size_x(s
))
766 window_copy_redraw_lines(wp
, old_cy
, 1);
767 if (data
->cx
== screen_size_x(s
))
768 window_copy_redraw_lines(wp
, data
->cy
, 1);
770 screen_write_start(&ctx
, wp
, NULL
);
771 screen_write_cursormove(&ctx
, data
->cx
, data
->cy
);
772 screen_write_stop(&ctx
);
777 window_copy_start_selection(struct window_pane
*wp
)
779 struct window_copy_mode_data
*data
= wp
->modedata
;
780 struct screen
*s
= &data
->screen
;
782 data
->selx
= data
->cx
;
783 data
->sely
= screen_hsize(&wp
->base
) + data
->cy
- data
->oy
;
786 window_copy_update_selection(wp
);
790 window_copy_update_selection(struct window_pane
*wp
)
792 struct window_copy_mode_data
*data
= wp
->modedata
;
793 struct screen
*s
= &data
->screen
;
794 struct options
*oo
= &wp
->window
->options
;
802 memcpy(&gc
, &grid_default_cell
, sizeof gc
);
803 colour_set_fg(&gc
, options_get_number(oo
, "mode-fg"));
804 colour_set_bg(&gc
, options_get_number(oo
, "mode-bg"));
805 gc
.attr
|= options_get_number(oo
, "mode-attr");
807 /* Find top of screen. */
808 ty
= screen_hsize(&wp
->base
) - data
->oy
;
810 /* Adjust the selection. */
813 if (sy
< ty
) { /* above screen */
816 } else if (sy
> ty
+ screen_size_y(s
) - 1) { /* below screen */
817 sx
= screen_size_x(s
) - 1;
818 sy
= screen_size_y(s
) - 1;
821 sy
= screen_hsize(s
) + sy
;
823 screen_set_selection(
824 s
, sx
, sy
, data
->cx
, screen_hsize(s
) + data
->cy
, &gc
);
829 window_copy_copy_selection(struct window_pane
*wp
, struct client
*c
)
831 struct window_copy_mode_data
*data
= wp
->modedata
;
832 struct screen
*s
= &data
->screen
;
835 u_int i
, xx
, yy
, sx
, sy
, ex
, ey
, limit
;
846 * The selection extends from selx,sely to (adjusted) cx,cy on
850 /* Find start and end. */
852 yy
= screen_hsize(&wp
->base
) + data
->cy
- data
->oy
;
853 if (yy
< data
->sely
|| (yy
== data
->sely
&& xx
< data
->selx
)) {
855 ex
= data
->selx
; ey
= data
->sely
;
857 sx
= data
->selx
; sy
= data
->sely
;
861 /* Trim ex to end of line. */
862 xx
= window_copy_find_length(wp
, ey
);
866 /* Copy the lines. */
868 window_copy_copy_line(wp
, &buf
, &off
, sy
, sx
, ex
);
870 xx
= screen_size_x(s
);
871 window_copy_copy_line(wp
, &buf
, &off
, sy
, sx
, xx
);
873 for (i
= sy
+ 1; i
< ey
; i
++)
874 window_copy_copy_line(wp
, &buf
, &off
, i
, 0, xx
);
876 window_copy_copy_line(wp
, &buf
, &off
, ey
, 0, ex
);
879 /* Don't bother if no data. */
884 off
--; /* remove final \n */
886 /* Add the buffer to the stack. */
887 limit
= options_get_number(&c
->session
->options
, "buffer-limit");
888 paste_add(&c
->session
->buffers
, buf
, off
, limit
);
892 window_copy_copy_line(struct window_pane
*wp
,
893 char **buf
, size_t *off
, u_int sy
, u_int sx
, u_int ex
)
895 struct grid
*gd
= wp
->base
.grid
;
896 const struct grid_cell
*gc
;
897 const struct grid_utf8
*gu
;
898 struct grid_line
*gl
;
899 u_int i
, xx
, wrapped
= 0;
906 * Work out if the line was wrapped at the screen edge and all of it is
909 gl
= &gd
->linedata
[sy
];
910 if (gl
->flags
& GRID_LINE_WRAPPED
&& gl
->cellsize
<= gd
->sx
)
913 /* If the line was wrapped, don't strip spaces (use the full length). */
917 xx
= window_copy_find_length(wp
, sy
);
924 for (i
= sx
; i
< ex
; i
++) {
925 gc
= grid_peek_cell(gd
, i
, sy
);
926 if (gc
->flags
& GRID_FLAG_PADDING
)
928 if (!(gc
->flags
& GRID_FLAG_UTF8
)) {
929 *buf
= xrealloc(*buf
, 1, (*off
) + 1);
930 (*buf
)[(*off
)++] = gc
->data
;
932 gu
= grid_peek_utf8(gd
, i
, sy
);
933 size
= grid_utf8_size(gu
);
934 *buf
= xrealloc(*buf
, 1, (*off
) + size
);
935 *off
+= grid_utf8_copy(gu
, *buf
+ *off
, size
);
940 /* Only add a newline if the line wasn't wrapped. */
942 *buf
= xrealloc(*buf
, 1, (*off
) + 1);
943 (*buf
)[(*off
)++] = '\n';
948 window_copy_is_space(struct window_pane
*wp
, u_int px
, u_int py
)
950 const struct grid_cell
*gc
;
951 const char *spaces
= " -_@";
953 gc
= grid_peek_cell(wp
->base
.grid
, px
, py
);
954 if (gc
->flags
& (GRID_FLAG_PADDING
|GRID_FLAG_UTF8
))
956 if (gc
->data
== 0x00 || gc
->data
== 0x7f)
958 return (strchr(spaces
, gc
->data
) != NULL
);
962 window_copy_find_length(struct window_pane
*wp
, u_int py
)
964 const struct grid_cell
*gc
;
968 * If the pane has been resized, its grid can contain old overlong
969 * lines. grid_peek_cell does not allow accessing cells beyond the
970 * width of the grid, and screen_write_copy treats them as spaces, so
971 * ignore them here too.
973 px
= wp
->base
.grid
->linedata
[py
].cellsize
;
974 if (px
> screen_size_x(&wp
->base
))
975 px
= screen_size_x(&wp
->base
);
977 gc
= grid_peek_cell(wp
->base
.grid
, px
- 1, py
);
978 if (gc
->flags
& GRID_FLAG_UTF8
)
988 window_copy_cursor_start_of_line(struct window_pane
*wp
)
990 struct window_copy_mode_data
*data
= wp
->modedata
;
992 window_copy_update_cursor(wp
, 0, data
->cy
);
993 if (window_copy_update_selection(wp
))
994 window_copy_redraw_lines(wp
, data
->cy
, 1);
998 window_copy_cursor_back_to_indentation(struct window_pane
*wp
)
1000 struct window_copy_mode_data
*data
= wp
->modedata
;
1002 const struct grid_cell
*gc
;
1005 py
= screen_hsize(&wp
->base
) + data
->cy
- data
->oy
;
1006 xx
= window_copy_find_length(wp
, py
);
1009 * Don't use window_copy_is_space because that treats some word
1010 * delimiters as spaces.
1013 gc
= grid_peek_cell(wp
->base
.grid
, px
, py
);
1014 if (gc
->flags
& GRID_FLAG_UTF8
)
1016 if (gc
->data
!= ' ')
1021 window_copy_update_cursor(wp
, px
, data
->cy
);
1022 if (window_copy_update_selection(wp
))
1023 window_copy_redraw_lines(wp
, data
->cy
, 1);
1027 window_copy_cursor_end_of_line(struct window_pane
*wp
)
1029 struct window_copy_mode_data
*data
= wp
->modedata
;
1032 py
= screen_hsize(&wp
->base
) + data
->cy
- data
->oy
;
1033 px
= window_copy_find_length(wp
, py
);
1035 window_copy_update_cursor(wp
, px
, data
->cy
);
1036 if (window_copy_update_selection(wp
))
1037 window_copy_redraw_lines(wp
, data
->cy
, 1);
1041 window_copy_cursor_left(struct window_pane
*wp
)
1043 struct window_copy_mode_data
*data
= wp
->modedata
;
1045 if (data
->cx
== 0) {
1046 window_copy_cursor_up(wp
, 0);
1047 window_copy_cursor_end_of_line(wp
);
1049 window_copy_update_cursor(wp
, data
->cx
- 1, data
->cy
);
1050 if (window_copy_update_selection(wp
))
1051 window_copy_redraw_lines(wp
, data
->cy
, 1);
1056 window_copy_cursor_right(struct window_pane
*wp
)
1058 struct window_copy_mode_data
*data
= wp
->modedata
;
1061 py
= screen_hsize(&wp
->base
) + data
->cy
- data
->oy
;
1062 px
= window_copy_find_length(wp
, py
);
1064 if (data
->cx
>= px
) {
1065 window_copy_cursor_start_of_line(wp
);
1066 window_copy_cursor_down(wp
, 0);
1068 window_copy_update_cursor(wp
, data
->cx
+ 1, data
->cy
);
1069 if (window_copy_update_selection(wp
))
1070 window_copy_redraw_lines(wp
, data
->cy
, 1);
1075 window_copy_cursor_up(struct window_pane
*wp
, int scroll_only
)
1077 struct window_copy_mode_data
*data
= wp
->modedata
;
1078 u_int ox
, oy
, px
, py
;
1080 oy
= screen_hsize(&wp
->base
) + data
->cy
- data
->oy
;
1081 ox
= window_copy_find_length(wp
, oy
);
1083 data
->lastcx
= data
->cx
;
1087 data
->cx
= data
->lastcx
;
1088 if (scroll_only
|| data
->cy
== 0) {
1089 window_copy_scroll_down(wp
, 1);
1091 window_copy_redraw_lines(wp
, data
->cy
, 2);
1093 window_copy_update_cursor(wp
, data
->cx
, data
->cy
- 1);
1094 if (window_copy_update_selection(wp
))
1095 window_copy_redraw_lines(wp
, data
->cy
, 2);
1098 py
= screen_hsize(&wp
->base
) + data
->cy
- data
->oy
;
1099 px
= window_copy_find_length(wp
, py
);
1100 if (data
->cx
>= data
->lastsx
|| data
->cx
> px
)
1101 window_copy_cursor_end_of_line(wp
);
1105 window_copy_cursor_down(struct window_pane
*wp
, int scroll_only
)
1107 struct window_copy_mode_data
*data
= wp
->modedata
;
1108 struct screen
*s
= &data
->screen
;
1109 u_int ox
, oy
, px
, py
;
1111 oy
= screen_hsize(&wp
->base
) + data
->cy
- data
->oy
;
1112 ox
= window_copy_find_length(wp
, oy
);
1114 data
->lastcx
= data
->cx
;
1118 data
->cx
= data
->lastcx
;
1119 if (scroll_only
|| data
->cy
== screen_size_y(s
) - 1) {
1120 window_copy_scroll_up(wp
, 1);
1121 if (scroll_only
&& data
->cy
> 0)
1122 window_copy_redraw_lines(wp
, data
->cy
- 1, 2);
1124 window_copy_update_cursor(wp
, data
->cx
, data
->cy
+ 1);
1125 if (window_copy_update_selection(wp
))
1126 window_copy_redraw_lines(wp
, data
->cy
- 1, 2);
1129 py
= screen_hsize(&wp
->base
) + data
->cy
- data
->oy
;
1130 px
= window_copy_find_length(wp
, py
);
1131 if (data
->cx
>= data
->lastsx
|| data
->cx
> px
)
1132 window_copy_cursor_end_of_line(wp
);
1136 window_copy_cursor_next_word(struct window_pane
*wp
)
1138 struct window_copy_mode_data
*data
= wp
->modedata
;
1139 struct screen
*s
= &data
->screen
;
1140 u_int px
, py
, xx
, skip
;
1143 py
= screen_hsize(&wp
->base
) + data
->cy
- data
->oy
;
1144 xx
= window_copy_find_length(wp
, py
);
1148 /* If currently on a space, skip space. */
1149 if (window_copy_is_space(wp
, px
, py
))
1161 if (data
->cy
== screen_size_y(s
) - 1) {
1167 window_copy_cursor_down(wp
, 0);
1170 &wp
->base
) + data
->cy
- data
->oy
;
1171 xx
= window_copy_find_length(wp
, py
);
1176 /* Currently skipping non-space (until space). */
1177 if (window_copy_is_space(wp
, px
, py
))
1180 /* Currently skipping space (until non-space). */
1181 if (!window_copy_is_space(wp
, px
, py
))
1189 window_copy_update_cursor(wp
, px
, data
->cy
);
1190 if (window_copy_update_selection(wp
))
1191 window_copy_redraw_lines(wp
, data
->cy
, 1);
1194 /* Move to the previous place where a word begins. */
1196 window_copy_cursor_previous_word(struct window_pane
*wp
)
1198 struct window_copy_mode_data
*data
= wp
->modedata
;
1202 py
= screen_hsize(&wp
->base
) + data
->cy
- data
->oy
;
1204 /* Move back to the previous word character. */
1208 if (!window_copy_is_space(wp
, px
, py
))
1211 if (data
->cy
== 0 &&
1212 (screen_hsize(&wp
->base
) == 0 ||
1213 data
->oy
>= screen_hsize(&wp
->base
) - 1))
1215 window_copy_cursor_up(wp
, 0);
1218 &wp
->base
) + data
->cy
- data
->oy
;
1219 px
= window_copy_find_length(wp
, py
);
1223 /* Move back to the beginning of this word. */
1224 while (px
> 0 && !window_copy_is_space(wp
, px
- 1, py
))
1228 window_copy_update_cursor(wp
, px
, data
->cy
);
1229 if (window_copy_update_selection(wp
))
1230 window_copy_redraw_lines(wp
, data
->cy
, 1);
1234 window_copy_scroll_up(struct window_pane
*wp
, u_int ny
)
1236 struct window_copy_mode_data
*data
= wp
->modedata
;
1237 struct screen
*s
= &data
->screen
;
1238 struct screen_write_ctx ctx
;
1245 window_copy_update_selection(wp
);
1247 screen_write_start(&ctx
, wp
, NULL
);
1248 screen_write_cursormove(&ctx
, 0, 0);
1249 screen_write_deleteline(&ctx
, ny
);
1250 window_copy_write_lines(wp
, &ctx
, screen_size_y(s
) - ny
, ny
);
1251 window_copy_write_line(wp
, &ctx
, 0);
1252 if (screen_size_y(s
) > 1)
1253 window_copy_write_line(wp
, &ctx
, 1);
1254 if (screen_size_y(s
) > 3)
1255 window_copy_write_line(wp
, &ctx
, screen_size_y(s
) - 2);
1256 if (s
->sel
.flag
&& screen_size_y(s
) > ny
)
1257 window_copy_write_line(wp
, &ctx
, screen_size_y(s
) - ny
- 1);
1258 screen_write_cursormove(&ctx
, data
->cx
, data
->cy
);
1259 screen_write_stop(&ctx
);
1263 window_copy_scroll_down(struct window_pane
*wp
, u_int ny
)
1265 struct window_copy_mode_data
*data
= wp
->modedata
;
1266 struct screen
*s
= &data
->screen
;
1267 struct screen_write_ctx ctx
;
1269 if (ny
> screen_hsize(&wp
->base
))
1272 if (data
->oy
> screen_hsize(&wp
->base
) - ny
)
1273 ny
= screen_hsize(&wp
->base
) - data
->oy
;
1277 window_copy_update_selection(wp
);
1279 screen_write_start(&ctx
, wp
, NULL
);
1280 screen_write_cursormove(&ctx
, 0, 0);
1281 screen_write_insertline(&ctx
, ny
);
1282 window_copy_write_lines(wp
, &ctx
, 0, ny
);
1283 if (s
->sel
.flag
&& screen_size_y(s
) > ny
)
1284 window_copy_write_line(wp
, &ctx
, ny
);
1285 else if (ny
== 1) /* nuke position */
1286 window_copy_write_line(wp
, &ctx
, 1);
1287 screen_write_cursormove(&ctx
, data
->cx
, data
->cy
);
1288 screen_write_stop(&ctx
);