1 | /* $OpenBSD$ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2007 Nicholas Marriott <[email protected]> |
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 | |
26 | #include "tmux.h" |
27 | |
28 | static const char *window_copy_key_table(struct window_mode_entry *); |
29 | static void window_copy_command(struct window_mode_entry *, struct client *, |
30 | struct session *, struct winlink *, struct args *, |
31 | struct mouse_event *); |
32 | static struct screen *window_copy_init(struct window_mode_entry *, |
33 | struct cmd_find_state *, struct args *); |
34 | static struct screen *window_copy_view_init(struct window_mode_entry *, |
35 | struct cmd_find_state *, struct args *); |
36 | static void window_copy_free(struct window_mode_entry *); |
37 | static void window_copy_resize(struct window_mode_entry *, u_int, u_int); |
38 | static void window_copy_formats(struct window_mode_entry *, |
39 | struct format_tree *); |
40 | static void window_copy_pageup1(struct window_mode_entry *, int); |
41 | static int window_copy_pagedown(struct window_mode_entry *, int, int); |
42 | static void window_copy_next_paragraph(struct window_mode_entry *); |
43 | static void window_copy_previous_paragraph(struct window_mode_entry *); |
44 | |
45 | static void window_copy_redraw_selection(struct window_mode_entry *, u_int); |
46 | static void window_copy_redraw_lines(struct window_mode_entry *, u_int, |
47 | u_int); |
48 | static void window_copy_redraw_screen(struct window_mode_entry *); |
49 | static void window_copy_write_line(struct window_mode_entry *, |
50 | struct screen_write_ctx *, u_int); |
51 | static void window_copy_write_lines(struct window_mode_entry *, |
52 | struct screen_write_ctx *, u_int, u_int); |
53 | |
54 | static void window_copy_scroll_to(struct window_mode_entry *, u_int, u_int); |
55 | static int window_copy_search_compare(struct grid *, u_int, u_int, |
56 | struct grid *, u_int, int); |
57 | static int window_copy_search_lr(struct grid *, struct grid *, u_int *, |
58 | u_int, u_int, u_int, int); |
59 | static int window_copy_search_rl(struct grid *, struct grid *, u_int *, |
60 | u_int, u_int, u_int, int); |
61 | static int window_copy_last_regex(struct grid *gd, u_int py, u_int first, |
62 | u_int last, u_int len, u_int *ppx, u_int *psx, |
63 | const char *buf, const regex_t *preg, int eflags); |
64 | static char *window_copy_stringify(struct grid *, u_int, u_int, u_int, |
65 | char *, u_int *); |
66 | static void window_copy_cstrtocellpos(struct grid *, u_int, u_int *, u_int *, |
67 | const char *str); |
68 | static int window_copy_search_marks(struct window_mode_entry *, |
69 | struct screen *, int); |
70 | static void window_copy_clear_marks(struct window_mode_entry *); |
71 | static void window_copy_move_left(struct screen *, u_int *, u_int *, int); |
72 | static void window_copy_move_right(struct screen *, u_int *, u_int *, int); |
73 | static int window_copy_is_lowercase(const char *); |
74 | static int window_copy_search_jump(struct window_mode_entry *, |
75 | struct grid *, struct grid *, u_int, u_int, u_int, int, int, |
76 | int, int); |
77 | static int window_copy_search(struct window_mode_entry *, int, int); |
78 | static int window_copy_search_up(struct window_mode_entry *, int); |
79 | static int window_copy_search_down(struct window_mode_entry *, int); |
80 | static void window_copy_goto_line(struct window_mode_entry *, const char *); |
81 | static void window_copy_update_cursor(struct window_mode_entry *, u_int, |
82 | u_int); |
83 | static void window_copy_start_selection(struct window_mode_entry *); |
84 | static int window_copy_adjust_selection(struct window_mode_entry *, |
85 | u_int *, u_int *); |
86 | static int window_copy_set_selection(struct window_mode_entry *, int, int); |
87 | static int window_copy_update_selection(struct window_mode_entry *, int, |
88 | int); |
89 | static void window_copy_synchronize_cursor(struct window_mode_entry *, int); |
90 | static void *window_copy_get_selection(struct window_mode_entry *, size_t *); |
91 | static void window_copy_copy_buffer(struct window_mode_entry *, |
92 | const char *, void *, size_t); |
93 | static void window_copy_copy_pipe(struct window_mode_entry *, |
94 | struct session *, const char *, const char *); |
95 | static void window_copy_copy_selection(struct window_mode_entry *, |
96 | const char *); |
97 | static void window_copy_append_selection(struct window_mode_entry *); |
98 | static void window_copy_clear_selection(struct window_mode_entry *); |
99 | static void window_copy_copy_line(struct window_mode_entry *, char **, |
100 | size_t *, u_int, u_int, u_int); |
101 | static int window_copy_in_set(struct window_mode_entry *, u_int, u_int, |
102 | const char *); |
103 | static u_int window_copy_find_length(struct window_mode_entry *, u_int); |
104 | static void window_copy_cursor_start_of_line(struct window_mode_entry *); |
105 | static void window_copy_cursor_back_to_indentation( |
106 | struct window_mode_entry *); |
107 | static void window_copy_cursor_end_of_line(struct window_mode_entry *); |
108 | static void window_copy_other_end(struct window_mode_entry *); |
109 | static void window_copy_cursor_left(struct window_mode_entry *); |
110 | static void window_copy_cursor_right(struct window_mode_entry *); |
111 | static void window_copy_cursor_up(struct window_mode_entry *, int); |
112 | static void window_copy_cursor_down(struct window_mode_entry *, int); |
113 | static void window_copy_cursor_jump(struct window_mode_entry *); |
114 | static void window_copy_cursor_jump_back(struct window_mode_entry *); |
115 | static void window_copy_cursor_jump_to(struct window_mode_entry *); |
116 | static void window_copy_cursor_jump_to_back(struct window_mode_entry *); |
117 | static void window_copy_cursor_next_word(struct window_mode_entry *, |
118 | const char *); |
119 | static void window_copy_cursor_next_word_end_pos(struct window_mode_entry *, |
120 | const char *, u_int *, u_int *); |
121 | static void window_copy_cursor_next_word_end(struct window_mode_entry *, |
122 | const char *, int); |
123 | static void window_copy_cursor_previous_word_pos(struct window_mode_entry *, |
124 | const char *, int, u_int *, u_int *); |
125 | static void window_copy_cursor_previous_word(struct window_mode_entry *, |
126 | const char *, int); |
127 | static void window_copy_scroll_up(struct window_mode_entry *, u_int); |
128 | static void window_copy_scroll_down(struct window_mode_entry *, u_int); |
129 | static void window_copy_rectangle_toggle(struct window_mode_entry *); |
130 | static void window_copy_move_mouse(struct mouse_event *); |
131 | static void window_copy_drag_update(struct client *, struct mouse_event *); |
132 | static void window_copy_drag_release(struct client *, struct mouse_event *); |
133 | |
134 | const struct window_mode window_copy_mode = { |
135 | .name = "copy-mode" , |
136 | |
137 | .init = window_copy_init, |
138 | .free = window_copy_free, |
139 | .resize = window_copy_resize, |
140 | .key_table = window_copy_key_table, |
141 | .command = window_copy_command, |
142 | .formats = window_copy_formats, |
143 | }; |
144 | |
145 | const struct window_mode window_view_mode = { |
146 | .name = "view-mode" , |
147 | |
148 | .init = window_copy_view_init, |
149 | .free = window_copy_free, |
150 | .resize = window_copy_resize, |
151 | .key_table = window_copy_key_table, |
152 | .command = window_copy_command, |
153 | .formats = window_copy_formats, |
154 | }; |
155 | |
156 | enum { |
157 | WINDOW_COPY_OFF, |
158 | WINDOW_COPY_SEARCHUP, |
159 | WINDOW_COPY_SEARCHDOWN, |
160 | WINDOW_COPY_JUMPFORWARD, |
161 | WINDOW_COPY_JUMPBACKWARD, |
162 | WINDOW_COPY_JUMPTOFORWARD, |
163 | WINDOW_COPY_JUMPTOBACKWARD, |
164 | }; |
165 | |
166 | enum { |
167 | WINDOW_COPY_REL_POS_ABOVE, |
168 | WINDOW_COPY_REL_POS_ON_SCREEN, |
169 | WINDOW_COPY_REL_POS_BELOW, |
170 | }; |
171 | |
172 | enum window_copy_cmd_action { |
173 | WINDOW_COPY_CMD_NOTHING, |
174 | WINDOW_COPY_CMD_REDRAW, |
175 | WINDOW_COPY_CMD_CANCEL, |
176 | }; |
177 | |
178 | struct window_copy_cmd_state { |
179 | struct window_mode_entry *wme; |
180 | struct args *args; |
181 | struct mouse_event *m; |
182 | |
183 | struct client *c; |
184 | struct session *s; |
185 | struct winlink *wl; |
186 | }; |
187 | |
188 | /* |
189 | * Copy mode's visible screen (the "screen" field) is filled from one of two |
190 | * sources: the original contents of the pane (used when we actually enter via |
191 | * the "copy-mode" command, to copy the contents of the current pane), or else |
192 | * a series of lines containing the output from an output-writing tmux command |
193 | * (such as any of the "show-*" or "list-*" commands). |
194 | * |
195 | * In either case, the full content of the copy-mode grid is pointed at by the |
196 | * "backing" field, and is copied into "screen" as needed (that is, when |
197 | * scrolling occurs). When copy-mode is backed by a pane, backing points |
198 | * directly at that pane's screen structure (&wp->base); when backed by a list |
199 | * of output-lines from a command, it points at a newly-allocated screen |
200 | * structure (which is deallocated when the mode ends). |
201 | */ |
202 | struct window_copy_mode_data { |
203 | struct screen screen; |
204 | |
205 | struct screen *backing; |
206 | int backing_written; /* backing display started */ |
207 | |
208 | u_int oy; /* number of lines scrolled up */ |
209 | |
210 | u_int selx; /* beginning of selection */ |
211 | u_int sely; |
212 | |
213 | u_int endselx; /* end of selection */ |
214 | u_int endsely; |
215 | |
216 | enum { |
217 | CURSORDRAG_NONE, /* selection is independent of cursor */ |
218 | CURSORDRAG_ENDSEL, /* end is synchronized with cursor */ |
219 | CURSORDRAG_SEL, /* start is synchronized with cursor */ |
220 | } cursordrag; |
221 | |
222 | int modekeys; |
223 | enum { |
224 | LINE_SEL_NONE, |
225 | LINE_SEL_LEFT_RIGHT, |
226 | LINE_SEL_RIGHT_LEFT, |
227 | } lineflag; /* line selection mode */ |
228 | int rectflag; /* in rectangle copy mode? */ |
229 | int scroll_exit; /* exit on scroll to end? */ |
230 | int hide_position; /* hide position marker */ |
231 | |
232 | enum { |
233 | SEL_CHAR, /* select one char at a time */ |
234 | SEL_WORD, /* select one word at a time */ |
235 | SEL_LINE, /* select one line at a time */ |
236 | } selflag; |
237 | |
238 | const char *ws; /* word separators */ |
239 | |
240 | u_int dx; /* drag start position */ |
241 | u_int dy; |
242 | |
243 | u_int selrx; /* selection reset positions */ |
244 | u_int selry; |
245 | u_int endselrx; |
246 | u_int endselry; |
247 | |
248 | u_int cx; |
249 | u_int cy; |
250 | |
251 | u_int lastcx; /* position in last line w/ content */ |
252 | u_int lastsx; /* size of last line w/ content */ |
253 | |
254 | int searchtype; |
255 | int searchregex; |
256 | char *searchstr; |
257 | bitstr_t *searchmark; |
258 | u_int searchcount; |
259 | int searchthis; |
260 | int searchx; |
261 | int searchy; |
262 | int searcho; |
263 | |
264 | int timeout; /* search has timed out */ |
265 | #define WINDOW_COPY_SEARCH_TIMEOUT 10 |
266 | |
267 | int jumptype; |
268 | char jumpchar; |
269 | |
270 | struct event dragtimer; |
271 | #define WINDOW_COPY_DRAG_REPEAT_TIME 50000 |
272 | }; |
273 | |
274 | static void |
275 | window_copy_scroll_timer(__unused int fd, __unused short events, void *arg) |
276 | { |
277 | struct window_mode_entry *wme = arg; |
278 | struct window_pane *wp = wme->wp; |
279 | struct window_copy_mode_data *data = wme->data; |
280 | struct timeval tv = { |
281 | .tv_usec = WINDOW_COPY_DRAG_REPEAT_TIME |
282 | }; |
283 | |
284 | evtimer_del(&data->dragtimer); |
285 | |
286 | if (TAILQ_FIRST(&wp->modes) != wme) |
287 | return; |
288 | |
289 | if (data->cy == 0) { |
290 | evtimer_add(&data->dragtimer, &tv); |
291 | window_copy_cursor_up(wme, 1); |
292 | } else if (data->cy == screen_size_y(&data->screen) - 1) { |
293 | evtimer_add(&data->dragtimer, &tv); |
294 | window_copy_cursor_down(wme, 1); |
295 | } |
296 | } |
297 | |
298 | static struct window_copy_mode_data * |
299 | window_copy_common_init(struct window_mode_entry *wme) |
300 | { |
301 | struct window_pane *wp = wme->wp; |
302 | struct window_copy_mode_data *data; |
303 | struct screen *base = &wp->base; |
304 | |
305 | wme->data = data = xcalloc(1, sizeof *data); |
306 | |
307 | data->cursordrag = CURSORDRAG_NONE; |
308 | data->lineflag = LINE_SEL_NONE; |
309 | data->selflag = SEL_CHAR; |
310 | |
311 | if (wp->searchstr != NULL) { |
312 | data->searchtype = WINDOW_COPY_SEARCHUP; |
313 | data->searchregex = wp->searchregex; |
314 | data->searchstr = xstrdup(wp->searchstr); |
315 | } else { |
316 | data->searchtype = WINDOW_COPY_OFF; |
317 | data->searchregex = 0; |
318 | data->searchstr = NULL; |
319 | } |
320 | data->searchmark = NULL; |
321 | data->searchx = data->searchy = data->searcho = -1; |
322 | data->timeout = 0; |
323 | |
324 | data->jumptype = WINDOW_COPY_OFF; |
325 | data->jumpchar = '\0'; |
326 | |
327 | screen_init(&data->screen, screen_size_x(base), screen_size_y(base), 0); |
328 | data->modekeys = options_get_number(wp->window->options, "mode-keys" ); |
329 | |
330 | evtimer_set(&data->dragtimer, window_copy_scroll_timer, wme); |
331 | |
332 | return (data); |
333 | } |
334 | |
335 | static struct screen * |
336 | window_copy_init(struct window_mode_entry *wme, |
337 | __unused struct cmd_find_state *fs, struct args *args) |
338 | { |
339 | struct window_pane *wp = wme->wp; |
340 | struct window_copy_mode_data *data; |
341 | struct screen_write_ctx ctx; |
342 | u_int i; |
343 | |
344 | data = window_copy_common_init(wme); |
345 | |
346 | if (wp->fd != -1 && wp->disabled++ == 0) |
347 | bufferevent_disable(wp->event, EV_READ|EV_WRITE); |
348 | |
349 | data->backing = &wp->base; |
350 | data->cx = data->backing->cx; |
351 | data->cy = data->backing->cy; |
352 | |
353 | data->scroll_exit = args_has(args, 'e'); |
354 | data->hide_position = args_has(args, 'H'); |
355 | |
356 | data->screen.cx = data->cx; |
357 | data->screen.cy = data->cy; |
358 | |
359 | screen_write_start(&ctx, NULL, &data->screen); |
360 | for (i = 0; i < screen_size_y(&data->screen); i++) |
361 | window_copy_write_line(wme, &ctx, i); |
362 | screen_write_cursormove(&ctx, data->cx, data->cy, 0); |
363 | screen_write_stop(&ctx); |
364 | |
365 | return (&data->screen); |
366 | } |
367 | |
368 | static struct screen * |
369 | window_copy_view_init(struct window_mode_entry *wme, |
370 | __unused struct cmd_find_state *fs, __unused struct args *args) |
371 | { |
372 | struct window_pane *wp = wme->wp; |
373 | struct window_copy_mode_data *data; |
374 | struct screen *base = &wp->base; |
375 | struct screen *s; |
376 | |
377 | data = window_copy_common_init(wme); |
378 | |
379 | data->backing = s = xmalloc(sizeof *data->backing); |
380 | screen_init(s, screen_size_x(base), screen_size_y(base), UINT_MAX); |
381 | |
382 | return (&data->screen); |
383 | } |
384 | |
385 | static void |
386 | window_copy_free(struct window_mode_entry *wme) |
387 | { |
388 | struct window_pane *wp = wme->wp; |
389 | struct window_copy_mode_data *data = wme->data; |
390 | |
391 | evtimer_del(&data->dragtimer); |
392 | |
393 | if (wp->fd != -1 && --wp->disabled == 0) |
394 | bufferevent_enable(wp->event, EV_READ|EV_WRITE); |
395 | |
396 | free(data->searchmark); |
397 | free(data->searchstr); |
398 | |
399 | if (data->backing != &wp->base) { |
400 | screen_free(data->backing); |
401 | free(data->backing); |
402 | } |
403 | screen_free(&data->screen); |
404 | |
405 | free(data); |
406 | } |
407 | |
408 | void |
409 | window_copy_add(struct window_pane *wp, const char *fmt, ...) |
410 | { |
411 | va_list ap; |
412 | |
413 | va_start(ap, fmt); |
414 | window_copy_vadd(wp, fmt, ap); |
415 | va_end(ap); |
416 | } |
417 | |
418 | void |
419 | window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap) |
420 | { |
421 | struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); |
422 | struct window_copy_mode_data *data = wme->data; |
423 | struct screen *backing = data->backing; |
424 | struct screen_write_ctx back_ctx, ctx; |
425 | struct grid_cell gc; |
426 | u_int old_hsize, old_cy; |
427 | |
428 | if (backing == &wp->base) |
429 | return; |
430 | |
431 | memcpy(&gc, &grid_default_cell, sizeof gc); |
432 | |
433 | old_hsize = screen_hsize(data->backing); |
434 | screen_write_start(&back_ctx, NULL, backing); |
435 | if (data->backing_written) { |
436 | /* |
437 | * On the second or later line, do a CRLF before writing |
438 | * (so it's on a new line). |
439 | */ |
440 | screen_write_carriagereturn(&back_ctx); |
441 | screen_write_linefeed(&back_ctx, 0, 8); |
442 | } else |
443 | data->backing_written = 1; |
444 | old_cy = backing->cy; |
445 | screen_write_vnputs(&back_ctx, 0, &gc, fmt, ap); |
446 | screen_write_stop(&back_ctx); |
447 | |
448 | data->oy += screen_hsize(data->backing) - old_hsize; |
449 | |
450 | screen_write_start(&ctx, wp, &data->screen); |
451 | |
452 | /* |
453 | * If the history has changed, draw the top line. |
454 | * (If there's any history at all, it has changed.) |
455 | */ |
456 | if (screen_hsize(data->backing)) |
457 | window_copy_redraw_lines(wme, 0, 1); |
458 | |
459 | /* Write the new lines. */ |
460 | window_copy_redraw_lines(wme, old_cy, backing->cy - old_cy + 1); |
461 | |
462 | screen_write_stop(&ctx); |
463 | } |
464 | |
465 | void |
466 | window_copy_pageup(struct window_pane *wp, int half_page) |
467 | { |
468 | window_copy_pageup1(TAILQ_FIRST(&wp->modes), half_page); |
469 | } |
470 | |
471 | static void |
472 | window_copy_pageup1(struct window_mode_entry *wme, int half_page) |
473 | { |
474 | struct window_copy_mode_data *data = wme->data; |
475 | struct screen *s = &data->screen; |
476 | u_int n, ox, oy, px, py; |
477 | |
478 | oy = screen_hsize(data->backing) + data->cy - data->oy; |
479 | ox = window_copy_find_length(wme, oy); |
480 | |
481 | if (data->cx != ox) { |
482 | data->lastcx = data->cx; |
483 | data->lastsx = ox; |
484 | } |
485 | data->cx = data->lastcx; |
486 | |
487 | n = 1; |
488 | if (screen_size_y(s) > 2) { |
489 | if (half_page) |
490 | n = screen_size_y(s) / 2; |
491 | else |
492 | n = screen_size_y(s) - 2; |
493 | } |
494 | |
495 | if (data->oy + n > screen_hsize(data->backing)) { |
496 | data->oy = screen_hsize(data->backing); |
497 | if (data->cy < n) |
498 | data->cy = 0; |
499 | else |
500 | data->cy -= n; |
501 | } else |
502 | data->oy += n; |
503 | |
504 | if (data->screen.sel == NULL || !data->rectflag) { |
505 | py = screen_hsize(data->backing) + data->cy - data->oy; |
506 | px = window_copy_find_length(wme, py); |
507 | if ((data->cx >= data->lastsx && data->cx != px) || |
508 | data->cx > px) |
509 | window_copy_cursor_end_of_line(wme); |
510 | } |
511 | |
512 | if (data->searchmark != NULL && !data->timeout) |
513 | window_copy_search_marks(wme, NULL, data->searchregex); |
514 | window_copy_update_selection(wme, 1, 0); |
515 | window_copy_redraw_screen(wme); |
516 | } |
517 | |
518 | static int |
519 | window_copy_pagedown(struct window_mode_entry *wme, int half_page, |
520 | int scroll_exit) |
521 | { |
522 | struct window_copy_mode_data *data = wme->data; |
523 | struct screen *s = &data->screen; |
524 | u_int n, ox, oy, px, py; |
525 | |
526 | oy = screen_hsize(data->backing) + data->cy - data->oy; |
527 | ox = window_copy_find_length(wme, oy); |
528 | |
529 | if (data->cx != ox) { |
530 | data->lastcx = data->cx; |
531 | data->lastsx = ox; |
532 | } |
533 | data->cx = data->lastcx; |
534 | |
535 | n = 1; |
536 | if (screen_size_y(s) > 2) { |
537 | if (half_page) |
538 | n = screen_size_y(s) / 2; |
539 | else |
540 | n = screen_size_y(s) - 2; |
541 | } |
542 | |
543 | if (data->oy < n) { |
544 | data->oy = 0; |
545 | if (data->cy + (n - data->oy) >= screen_size_y(data->backing)) |
546 | data->cy = screen_size_y(data->backing) - 1; |
547 | else |
548 | data->cy += n - data->oy; |
549 | } else |
550 | data->oy -= n; |
551 | |
552 | if (data->screen.sel == NULL || !data->rectflag) { |
553 | py = screen_hsize(data->backing) + data->cy - data->oy; |
554 | px = window_copy_find_length(wme, py); |
555 | if ((data->cx >= data->lastsx && data->cx != px) || |
556 | data->cx > px) |
557 | window_copy_cursor_end_of_line(wme); |
558 | } |
559 | |
560 | if (scroll_exit && data->oy == 0) |
561 | return (1); |
562 | if (data->searchmark != NULL && !data->timeout) |
563 | window_copy_search_marks(wme, NULL, data->searchregex); |
564 | window_copy_update_selection(wme, 1, 0); |
565 | window_copy_redraw_screen(wme); |
566 | return (0); |
567 | } |
568 | |
569 | static void |
570 | window_copy_previous_paragraph(struct window_mode_entry *wme) |
571 | { |
572 | struct window_copy_mode_data *data = wme->data; |
573 | u_int oy; |
574 | |
575 | oy = screen_hsize(data->backing) + data->cy - data->oy; |
576 | |
577 | while (oy > 0 && window_copy_find_length(wme, oy) == 0) |
578 | oy--; |
579 | |
580 | while (oy > 0 && window_copy_find_length(wme, oy) > 0) |
581 | oy--; |
582 | |
583 | window_copy_scroll_to(wme, 0, oy); |
584 | } |
585 | |
586 | static void |
587 | window_copy_next_paragraph(struct window_mode_entry *wme) |
588 | { |
589 | struct window_copy_mode_data *data = wme->data; |
590 | struct screen *s = &data->screen; |
591 | u_int maxy, ox, oy; |
592 | |
593 | oy = screen_hsize(data->backing) + data->cy - data->oy; |
594 | maxy = screen_hsize(data->backing) + screen_size_y(s) - 1; |
595 | |
596 | while (oy < maxy && window_copy_find_length(wme, oy) == 0) |
597 | oy++; |
598 | |
599 | while (oy < maxy && window_copy_find_length(wme, oy) > 0) |
600 | oy++; |
601 | |
602 | ox = window_copy_find_length(wme, oy); |
603 | window_copy_scroll_to(wme, ox, oy); |
604 | } |
605 | |
606 | char * |
607 | window_copy_get_word(struct window_pane *wp, u_int x, u_int y) |
608 | { |
609 | struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); |
610 | struct window_copy_mode_data *data = wme->data; |
611 | struct grid *gd = data->screen.grid; |
612 | |
613 | return (format_grid_word(gd, x, gd->hsize + y)); |
614 | } |
615 | |
616 | char * |
617 | window_copy_get_line(struct window_pane *wp, u_int y) |
618 | { |
619 | struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); |
620 | struct window_copy_mode_data *data = wme->data; |
621 | struct grid *gd = data->screen.grid; |
622 | |
623 | return (format_grid_line(gd, gd->hsize + y)); |
624 | } |
625 | |
626 | static void |
627 | window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft) |
628 | { |
629 | struct window_copy_mode_data *data = wme->data; |
630 | struct grid *gd = data->screen.grid; |
631 | char *s; |
632 | |
633 | format_add(ft, "scroll_position" , "%d" , data->oy); |
634 | format_add(ft, "rectangle_toggle" , "%d" , data->rectflag); |
635 | |
636 | format_add(ft, "copy_cursor_x" , "%d" , data->cx); |
637 | format_add(ft, "copy_cursor_y" , "%d" , data->cy); |
638 | |
639 | format_add(ft, "selection_present" , "%d" , data->screen.sel != NULL); |
640 | if (data->screen.sel != NULL) { |
641 | format_add(ft, "selection_start_x" , "%d" , data->selx); |
642 | format_add(ft, "selection_start_y" , "%d" , data->sely); |
643 | format_add(ft, "selection_end_x" , "%d" , data->endselx); |
644 | format_add(ft, "selection_end_y" , "%d" , data->endsely); |
645 | format_add(ft, "selection_active" , "%d" , |
646 | data->cursordrag != CURSORDRAG_NONE); |
647 | } else |
648 | format_add(ft, "selection_active" , "%d" , 0); |
649 | |
650 | s = format_grid_word(gd, data->cx, gd->hsize + data->cy); |
651 | if (s != NULL) { |
652 | format_add(ft, "copy_cursor_word" , "%s" , s); |
653 | free(s); |
654 | } |
655 | |
656 | s = format_grid_line(gd, gd->hsize + data->cy); |
657 | if (s != NULL) { |
658 | format_add(ft, "copy_cursor_line" , "%s" , s); |
659 | free(s); |
660 | } |
661 | } |
662 | |
663 | static void |
664 | window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy) |
665 | { |
666 | struct window_pane *wp = wme->wp; |
667 | struct window_copy_mode_data *data = wme->data; |
668 | struct screen *s = &data->screen; |
669 | struct screen_write_ctx ctx; |
670 | int search; |
671 | |
672 | screen_resize(s, sx, sy, 1); |
673 | if (data->backing != &wp->base) |
674 | screen_resize(data->backing, sx, sy, 1); |
675 | |
676 | if (data->cy > sy - 1) |
677 | data->cy = sy - 1; |
678 | if (data->cx > sx) |
679 | data->cx = sx; |
680 | if (data->oy > screen_hsize(data->backing)) |
681 | data->oy = screen_hsize(data->backing); |
682 | |
683 | search = (data->searchmark != NULL); |
684 | window_copy_clear_selection(wme); |
685 | window_copy_clear_marks(wme); |
686 | |
687 | screen_write_start(&ctx, NULL, s); |
688 | window_copy_write_lines(wme, &ctx, 0, screen_size_y(s) - 1); |
689 | screen_write_stop(&ctx); |
690 | |
691 | if (search && !data->timeout) |
692 | window_copy_search_marks(wme, NULL, data->searchregex); |
693 | data->searchx = data->cx; |
694 | data->searchy = data->cy; |
695 | data->searcho = data->oy; |
696 | |
697 | window_copy_redraw_screen(wme); |
698 | } |
699 | |
700 | static const char * |
701 | window_copy_key_table(struct window_mode_entry *wme) |
702 | { |
703 | struct window_pane *wp = wme->wp; |
704 | |
705 | if (options_get_number(wp->window->options, "mode-keys" ) == MODEKEY_VI) |
706 | return ("copy-mode-vi" ); |
707 | return ("copy-mode" ); |
708 | } |
709 | |
710 | static int |
711 | window_copy_expand_search_string(struct window_copy_cmd_state *cs) |
712 | { |
713 | struct window_mode_entry *wme = cs->wme; |
714 | struct window_copy_mode_data *data = wme->data; |
715 | const char *argument; |
716 | char *expanded; |
717 | |
718 | if (cs->args->argc == 2) { |
719 | argument = cs->args->argv[1]; |
720 | if (*argument != '\0') { |
721 | if (args_has(cs->args, 'F')) { |
722 | expanded = format_single(NULL, argument, NULL, |
723 | NULL, NULL, wme->wp); |
724 | if (*expanded == '\0') { |
725 | free(expanded); |
726 | return (0); |
727 | } |
728 | free(data->searchstr); |
729 | data->searchstr = expanded; |
730 | } else { |
731 | free(data->searchstr); |
732 | data->searchstr = xstrdup(argument); |
733 | } |
734 | } |
735 | } |
736 | return (1); |
737 | } |
738 | |
739 | static enum window_copy_cmd_action |
740 | window_copy_cmd_append_selection(struct window_copy_cmd_state *cs) |
741 | { |
742 | struct window_mode_entry *wme = cs->wme; |
743 | struct session *s = cs->s; |
744 | |
745 | if (s != NULL) |
746 | window_copy_append_selection(wme); |
747 | window_copy_clear_selection(wme); |
748 | return (WINDOW_COPY_CMD_REDRAW); |
749 | } |
750 | |
751 | static enum window_copy_cmd_action |
752 | window_copy_cmd_append_selection_and_cancel(struct window_copy_cmd_state *cs) |
753 | { |
754 | struct window_mode_entry *wme = cs->wme; |
755 | struct session *s = cs->s; |
756 | |
757 | if (s != NULL) |
758 | window_copy_append_selection(wme); |
759 | window_copy_clear_selection(wme); |
760 | return (WINDOW_COPY_CMD_CANCEL); |
761 | } |
762 | |
763 | static enum window_copy_cmd_action |
764 | window_copy_cmd_back_to_indentation(struct window_copy_cmd_state *cs) |
765 | { |
766 | struct window_mode_entry *wme = cs->wme; |
767 | |
768 | window_copy_cursor_back_to_indentation(wme); |
769 | return (WINDOW_COPY_CMD_NOTHING); |
770 | } |
771 | |
772 | static enum window_copy_cmd_action |
773 | window_copy_cmd_begin_selection(struct window_copy_cmd_state *cs) |
774 | { |
775 | struct window_mode_entry *wme = cs->wme; |
776 | struct client *c = cs->c; |
777 | struct mouse_event *m = cs->m; |
778 | struct window_copy_mode_data *data = wme->data; |
779 | |
780 | if (m != NULL) { |
781 | window_copy_start_drag(c, m); |
782 | return (WINDOW_COPY_CMD_NOTHING); |
783 | } |
784 | |
785 | data->lineflag = LINE_SEL_NONE; |
786 | data->selflag = SEL_CHAR; |
787 | window_copy_start_selection(wme); |
788 | return (WINDOW_COPY_CMD_REDRAW); |
789 | } |
790 | |
791 | static enum window_copy_cmd_action |
792 | window_copy_cmd_stop_selection(struct window_copy_cmd_state *cs) |
793 | { |
794 | struct window_mode_entry *wme = cs->wme; |
795 | struct window_copy_mode_data *data = wme->data; |
796 | |
797 | data->cursordrag = CURSORDRAG_NONE; |
798 | data->lineflag = LINE_SEL_NONE; |
799 | data->selflag = SEL_CHAR; |
800 | return (WINDOW_COPY_CMD_NOTHING); |
801 | } |
802 | |
803 | static enum window_copy_cmd_action |
804 | window_copy_cmd_bottom_line(struct window_copy_cmd_state *cs) |
805 | { |
806 | struct window_mode_entry *wme = cs->wme; |
807 | struct window_copy_mode_data *data = wme->data; |
808 | |
809 | data->cx = 0; |
810 | data->cy = screen_size_y(&data->screen) - 1; |
811 | |
812 | window_copy_update_selection(wme, 1, 0); |
813 | return (WINDOW_COPY_CMD_REDRAW); |
814 | } |
815 | |
816 | static enum window_copy_cmd_action |
817 | window_copy_cmd_cancel(__unused struct window_copy_cmd_state *cs) |
818 | { |
819 | return (WINDOW_COPY_CMD_CANCEL); |
820 | } |
821 | |
822 | static enum window_copy_cmd_action |
823 | window_copy_cmd_clear_selection(struct window_copy_cmd_state *cs) |
824 | { |
825 | struct window_mode_entry *wme = cs->wme; |
826 | |
827 | window_copy_clear_selection(wme); |
828 | return (WINDOW_COPY_CMD_REDRAW); |
829 | } |
830 | |
831 | static enum window_copy_cmd_action |
832 | window_copy_cmd_copy_end_of_line(struct window_copy_cmd_state *cs) |
833 | { |
834 | struct window_mode_entry *wme = cs->wme; |
835 | struct client *c = cs->c; |
836 | struct session *s = cs->s; |
837 | struct winlink *wl = cs->wl; |
838 | struct window_pane *wp = wme->wp; |
839 | u_int np = wme->prefix; |
840 | char *prefix = NULL; |
841 | |
842 | if (cs->args->argc == 2) |
843 | prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp); |
844 | |
845 | window_copy_start_selection(wme); |
846 | for (; np > 1; np--) |
847 | window_copy_cursor_down(wme, 0); |
848 | window_copy_cursor_end_of_line(wme); |
849 | |
850 | if (s != NULL) { |
851 | window_copy_copy_selection(wme, prefix); |
852 | |
853 | free(prefix); |
854 | return (WINDOW_COPY_CMD_CANCEL); |
855 | } |
856 | |
857 | free(prefix); |
858 | return (WINDOW_COPY_CMD_REDRAW); |
859 | } |
860 | |
861 | static enum window_copy_cmd_action |
862 | window_copy_cmd_copy_line(struct window_copy_cmd_state *cs) |
863 | { |
864 | struct window_mode_entry *wme = cs->wme; |
865 | struct client *c = cs->c; |
866 | struct session *s = cs->s; |
867 | struct winlink *wl = cs->wl; |
868 | struct window_pane *wp = wme->wp; |
869 | struct window_copy_mode_data *data = wme->data; |
870 | u_int np = wme->prefix; |
871 | char *prefix = NULL; |
872 | |
873 | if (cs->args->argc == 2) |
874 | prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp); |
875 | |
876 | data->selflag = SEL_CHAR; |
877 | window_copy_cursor_start_of_line(wme); |
878 | window_copy_start_selection(wme); |
879 | for (; np > 1; np--) |
880 | window_copy_cursor_down(wme, 0); |
881 | window_copy_cursor_end_of_line(wme); |
882 | |
883 | if (s != NULL) { |
884 | window_copy_copy_selection(wme, prefix); |
885 | |
886 | free(prefix); |
887 | return (WINDOW_COPY_CMD_CANCEL); |
888 | } |
889 | |
890 | free(prefix); |
891 | return (WINDOW_COPY_CMD_REDRAW); |
892 | } |
893 | |
894 | static enum window_copy_cmd_action |
895 | window_copy_cmd_copy_selection_no_clear(struct window_copy_cmd_state *cs) |
896 | { |
897 | struct window_mode_entry *wme = cs->wme; |
898 | struct client *c = cs->c; |
899 | struct session *s = cs->s; |
900 | struct winlink *wl = cs->wl; |
901 | struct window_pane *wp = wme->wp; |
902 | char *prefix = NULL; |
903 | |
904 | if (cs->args->argc == 2) |
905 | prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp); |
906 | |
907 | if (s != NULL) |
908 | window_copy_copy_selection(wme, prefix); |
909 | |
910 | free(prefix); |
911 | return (WINDOW_COPY_CMD_NOTHING); |
912 | } |
913 | |
914 | static enum window_copy_cmd_action |
915 | window_copy_cmd_copy_selection(struct window_copy_cmd_state *cs) |
916 | { |
917 | struct window_mode_entry *wme = cs->wme; |
918 | |
919 | window_copy_cmd_copy_selection_no_clear(cs); |
920 | window_copy_clear_selection(wme); |
921 | return (WINDOW_COPY_CMD_REDRAW); |
922 | } |
923 | |
924 | static enum window_copy_cmd_action |
925 | window_copy_cmd_copy_selection_and_cancel(struct window_copy_cmd_state *cs) |
926 | { |
927 | struct window_mode_entry *wme = cs->wme; |
928 | |
929 | window_copy_cmd_copy_selection_no_clear(cs); |
930 | window_copy_clear_selection(wme); |
931 | return (WINDOW_COPY_CMD_CANCEL); |
932 | } |
933 | |
934 | static enum window_copy_cmd_action |
935 | window_copy_cmd_cursor_down(struct window_copy_cmd_state *cs) |
936 | { |
937 | struct window_mode_entry *wme = cs->wme; |
938 | u_int np = wme->prefix; |
939 | |
940 | for (; np != 0; np--) |
941 | window_copy_cursor_down(wme, 0); |
942 | return (WINDOW_COPY_CMD_NOTHING); |
943 | } |
944 | |
945 | static enum window_copy_cmd_action |
946 | window_copy_cmd_cursor_down_and_cancel(struct window_copy_cmd_state *cs) |
947 | { |
948 | struct window_mode_entry *wme = cs->wme; |
949 | struct window_copy_mode_data *data = wme->data; |
950 | u_int np = wme->prefix, cy; |
951 | |
952 | cy = data->cy; |
953 | for (; np != 0; np--) |
954 | window_copy_cursor_down(wme, 0); |
955 | if (cy == data->cy && data->oy == 0) |
956 | return (WINDOW_COPY_CMD_CANCEL); |
957 | return (WINDOW_COPY_CMD_NOTHING); |
958 | } |
959 | |
960 | static enum window_copy_cmd_action |
961 | window_copy_cmd_cursor_left(struct window_copy_cmd_state *cs) |
962 | { |
963 | struct window_mode_entry *wme = cs->wme; |
964 | u_int np = wme->prefix; |
965 | |
966 | for (; np != 0; np--) |
967 | window_copy_cursor_left(wme); |
968 | return (WINDOW_COPY_CMD_NOTHING); |
969 | } |
970 | |
971 | static enum window_copy_cmd_action |
972 | window_copy_cmd_cursor_right(struct window_copy_cmd_state *cs) |
973 | { |
974 | struct window_mode_entry *wme = cs->wme; |
975 | u_int np = wme->prefix; |
976 | |
977 | for (; np != 0; np--) |
978 | window_copy_cursor_right(wme); |
979 | return (WINDOW_COPY_CMD_NOTHING); |
980 | } |
981 | |
982 | static enum window_copy_cmd_action |
983 | window_copy_cmd_cursor_up(struct window_copy_cmd_state *cs) |
984 | { |
985 | struct window_mode_entry *wme = cs->wme; |
986 | u_int np = wme->prefix; |
987 | |
988 | for (; np != 0; np--) |
989 | window_copy_cursor_up(wme, 0); |
990 | return (WINDOW_COPY_CMD_NOTHING); |
991 | } |
992 | |
993 | static enum window_copy_cmd_action |
994 | window_copy_cmd_end_of_line(struct window_copy_cmd_state *cs) |
995 | { |
996 | struct window_mode_entry *wme = cs->wme; |
997 | |
998 | window_copy_cursor_end_of_line(wme); |
999 | return (WINDOW_COPY_CMD_NOTHING); |
1000 | } |
1001 | |
1002 | static enum window_copy_cmd_action |
1003 | window_copy_cmd_halfpage_down(struct window_copy_cmd_state *cs) |
1004 | { |
1005 | struct window_mode_entry *wme = cs->wme; |
1006 | struct window_copy_mode_data *data = wme->data; |
1007 | u_int np = wme->prefix; |
1008 | |
1009 | for (; np != 0; np--) { |
1010 | if (window_copy_pagedown(wme, 1, data->scroll_exit)) |
1011 | return (WINDOW_COPY_CMD_CANCEL); |
1012 | } |
1013 | return (WINDOW_COPY_CMD_NOTHING); |
1014 | } |
1015 | |
1016 | static enum window_copy_cmd_action |
1017 | window_copy_cmd_halfpage_down_and_cancel(struct window_copy_cmd_state *cs) |
1018 | { |
1019 | |
1020 | struct window_mode_entry *wme = cs->wme; |
1021 | u_int np = wme->prefix; |
1022 | |
1023 | for (; np != 0; np--) { |
1024 | if (window_copy_pagedown(wme, 1, 1)) |
1025 | return (WINDOW_COPY_CMD_CANCEL); |
1026 | } |
1027 | return (WINDOW_COPY_CMD_NOTHING); |
1028 | } |
1029 | |
1030 | static enum window_copy_cmd_action |
1031 | window_copy_cmd_halfpage_up(struct window_copy_cmd_state *cs) |
1032 | { |
1033 | struct window_mode_entry *wme = cs->wme; |
1034 | u_int np = wme->prefix; |
1035 | |
1036 | for (; np != 0; np--) |
1037 | window_copy_pageup1(wme, 1); |
1038 | return (WINDOW_COPY_CMD_NOTHING); |
1039 | } |
1040 | |
1041 | static enum window_copy_cmd_action |
1042 | window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs) |
1043 | { |
1044 | struct window_mode_entry *wme = cs->wme; |
1045 | struct window_copy_mode_data *data = wme->data; |
1046 | u_int oy; |
1047 | |
1048 | oy = screen_hsize(data->backing) + data->cy - data->oy; |
1049 | if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely) |
1050 | window_copy_other_end(wme); |
1051 | |
1052 | data->cy = screen_size_y(&data->screen) - 1; |
1053 | data->cx = window_copy_find_length(wme, data->cy); |
1054 | data->oy = 0; |
1055 | |
1056 | if (data->searchmark != NULL && !data->timeout) |
1057 | window_copy_search_marks(wme, NULL, data->searchregex); |
1058 | window_copy_update_selection(wme, 1, 0); |
1059 | return (WINDOW_COPY_CMD_REDRAW); |
1060 | } |
1061 | |
1062 | static enum window_copy_cmd_action |
1063 | window_copy_cmd_history_top(struct window_copy_cmd_state *cs) |
1064 | { |
1065 | struct window_mode_entry *wme = cs->wme; |
1066 | struct window_copy_mode_data *data = wme->data; |
1067 | u_int oy; |
1068 | |
1069 | oy = screen_hsize(data->backing) + data->cy - data->oy; |
1070 | if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely) |
1071 | window_copy_other_end(wme); |
1072 | |
1073 | data->cy = 0; |
1074 | data->cx = 0; |
1075 | data->oy = screen_hsize(data->backing); |
1076 | |
1077 | if (data->searchmark != NULL && !data->timeout) |
1078 | window_copy_search_marks(wme, NULL, data->searchregex); |
1079 | window_copy_update_selection(wme, 1, 0); |
1080 | return (WINDOW_COPY_CMD_REDRAW); |
1081 | } |
1082 | |
1083 | static enum window_copy_cmd_action |
1084 | window_copy_cmd_jump_again(struct window_copy_cmd_state *cs) |
1085 | { |
1086 | struct window_mode_entry *wme = cs->wme; |
1087 | struct window_copy_mode_data *data = wme->data; |
1088 | u_int np = wme->prefix; |
1089 | |
1090 | switch (data->jumptype) { |
1091 | case WINDOW_COPY_JUMPFORWARD: |
1092 | for (; np != 0; np--) |
1093 | window_copy_cursor_jump(wme); |
1094 | break; |
1095 | case WINDOW_COPY_JUMPBACKWARD: |
1096 | for (; np != 0; np--) |
1097 | window_copy_cursor_jump_back(wme); |
1098 | break; |
1099 | case WINDOW_COPY_JUMPTOFORWARD: |
1100 | for (; np != 0; np--) |
1101 | window_copy_cursor_jump_to(wme); |
1102 | break; |
1103 | case WINDOW_COPY_JUMPTOBACKWARD: |
1104 | for (; np != 0; np--) |
1105 | window_copy_cursor_jump_to_back(wme); |
1106 | break; |
1107 | } |
1108 | return (WINDOW_COPY_CMD_NOTHING); |
1109 | } |
1110 | |
1111 | static enum window_copy_cmd_action |
1112 | window_copy_cmd_jump_reverse(struct window_copy_cmd_state *cs) |
1113 | { |
1114 | struct window_mode_entry *wme = cs->wme; |
1115 | struct window_copy_mode_data *data = wme->data; |
1116 | u_int np = wme->prefix; |
1117 | |
1118 | switch (data->jumptype) { |
1119 | case WINDOW_COPY_JUMPFORWARD: |
1120 | for (; np != 0; np--) |
1121 | window_copy_cursor_jump_back(wme); |
1122 | break; |
1123 | case WINDOW_COPY_JUMPBACKWARD: |
1124 | for (; np != 0; np--) |
1125 | window_copy_cursor_jump(wme); |
1126 | break; |
1127 | case WINDOW_COPY_JUMPTOFORWARD: |
1128 | for (; np != 0; np--) |
1129 | window_copy_cursor_jump_to_back(wme); |
1130 | break; |
1131 | case WINDOW_COPY_JUMPTOBACKWARD: |
1132 | for (; np != 0; np--) |
1133 | window_copy_cursor_jump_to(wme); |
1134 | break; |
1135 | } |
1136 | return (WINDOW_COPY_CMD_NOTHING); |
1137 | } |
1138 | |
1139 | static enum window_copy_cmd_action |
1140 | window_copy_cmd_middle_line(struct window_copy_cmd_state *cs) |
1141 | { |
1142 | struct window_mode_entry *wme = cs->wme; |
1143 | struct window_copy_mode_data *data = wme->data; |
1144 | |
1145 | data->cx = 0; |
1146 | data->cy = (screen_size_y(&data->screen) - 1) / 2; |
1147 | |
1148 | window_copy_update_selection(wme, 1, 0); |
1149 | return (WINDOW_COPY_CMD_REDRAW); |
1150 | } |
1151 | |
1152 | static enum window_copy_cmd_action |
1153 | window_copy_cmd_previous_matching_bracket(struct window_copy_cmd_state *cs) |
1154 | { |
1155 | struct window_mode_entry *wme = cs->wme; |
1156 | u_int np = wme->prefix; |
1157 | struct window_copy_mode_data *data = wme->data; |
1158 | struct screen *s = data->backing; |
1159 | char open[] = "{[(" , close[] = "}])" ; |
1160 | char tried, found, start, *cp; |
1161 | u_int px, py, xx, n; |
1162 | struct grid_cell gc; |
1163 | int failed; |
1164 | |
1165 | for (; np != 0; np--) { |
1166 | /* Get cursor position and line length. */ |
1167 | px = data->cx; |
1168 | py = screen_hsize(s) + data->cy - data->oy; |
1169 | xx = window_copy_find_length(wme, py); |
1170 | if (xx == 0) |
1171 | break; |
1172 | |
1173 | /* |
1174 | * Get the current character. If not on a bracket, try the |
1175 | * previous. If still not, then behave like previous-word. |
1176 | */ |
1177 | tried = 0; |
1178 | retry: |
1179 | grid_get_cell(s->grid, px, py, &gc); |
1180 | if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING)) |
1181 | cp = NULL; |
1182 | else { |
1183 | found = *gc.data.data; |
1184 | cp = strchr(close, found); |
1185 | } |
1186 | if (cp == NULL) { |
1187 | if (data->modekeys == MODEKEY_EMACS) { |
1188 | if (!tried && px > 0) { |
1189 | px--; |
1190 | tried = 1; |
1191 | goto retry; |
1192 | } |
1193 | window_copy_cursor_previous_word(wme, "}]) " , 1); |
1194 | } |
1195 | continue; |
1196 | } |
1197 | start = open[cp - close]; |
1198 | |
1199 | /* Walk backward until the matching bracket is reached. */ |
1200 | n = 1; |
1201 | failed = 0; |
1202 | do { |
1203 | if (px == 0) { |
1204 | if (py == 0) { |
1205 | failed = 1; |
1206 | break; |
1207 | } |
1208 | do { |
1209 | py--; |
1210 | xx = window_copy_find_length(wme, py); |
1211 | } while (xx == 0 && py > 0); |
1212 | if (xx == 0 && py == 0) { |
1213 | failed = 1; |
1214 | break; |
1215 | } |
1216 | px = xx - 1; |
1217 | } else |
1218 | px--; |
1219 | |
1220 | grid_get_cell(s->grid, px, py, &gc); |
1221 | if (gc.data.size == 1 && |
1222 | (~gc.flags & GRID_FLAG_PADDING)) { |
1223 | if (*gc.data.data == found) |
1224 | n++; |
1225 | else if (*gc.data.data == start) |
1226 | n--; |
1227 | } |
1228 | } while (n != 0); |
1229 | |
1230 | /* Move the cursor to the found location if any. */ |
1231 | if (!failed) |
1232 | window_copy_scroll_to(wme, px, py); |
1233 | } |
1234 | |
1235 | return (WINDOW_COPY_CMD_NOTHING); |
1236 | } |
1237 | |
1238 | static enum window_copy_cmd_action |
1239 | window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state *cs) |
1240 | { |
1241 | struct window_mode_entry *wme = cs->wme; |
1242 | u_int np = wme->prefix; |
1243 | struct window_copy_mode_data *data = wme->data; |
1244 | struct screen *s = data->backing; |
1245 | char open[] = "{[(" , close[] = "}])" ; |
1246 | char tried, found, end, *cp; |
1247 | u_int px, py, xx, yy, sx, sy, n; |
1248 | struct grid_cell gc; |
1249 | int failed; |
1250 | struct grid_line *gl; |
1251 | |
1252 | for (; np != 0; np--) { |
1253 | /* Get cursor position and line length. */ |
1254 | px = data->cx; |
1255 | py = screen_hsize(s) + data->cy - data->oy; |
1256 | xx = window_copy_find_length(wme, py); |
1257 | yy = screen_hsize(s) + screen_size_y(s) - 1; |
1258 | if (xx == 0) |
1259 | break; |
1260 | |
1261 | /* |
1262 | * Get the current character. If not on a bracket, try the |
1263 | * next. If still not, then behave like next-word. |
1264 | */ |
1265 | tried = 0; |
1266 | retry: |
1267 | grid_get_cell(s->grid, px, py, &gc); |
1268 | if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING)) |
1269 | cp = NULL; |
1270 | else { |
1271 | found = *gc.data.data; |
1272 | |
1273 | /* |
1274 | * In vi mode, attempt to move to previous bracket if a |
1275 | * closing bracket is found first. If this fails, |
1276 | * return to the original cursor position. |
1277 | */ |
1278 | cp = strchr(close, found); |
1279 | if (cp != NULL && data->modekeys == MODEKEY_VI) { |
1280 | sx = data->cx; |
1281 | sy = screen_hsize(s) + data->cy - data->oy; |
1282 | |
1283 | window_copy_scroll_to(wme, px, py); |
1284 | window_copy_cmd_previous_matching_bracket(cs); |
1285 | |
1286 | px = data->cx; |
1287 | py = screen_hsize(s) + data->cy - data->oy; |
1288 | grid_get_cell(s->grid, px, py, &gc); |
1289 | if (gc.data.size != 1 || |
1290 | (gc.flags & GRID_FLAG_PADDING) || |
1291 | strchr(close, *gc.data.data) == NULL) |
1292 | window_copy_scroll_to(wme, sx, sy); |
1293 | break; |
1294 | } |
1295 | |
1296 | cp = strchr(open, found); |
1297 | } |
1298 | if (cp == NULL) { |
1299 | if (data->modekeys == MODEKEY_EMACS) { |
1300 | if (!tried && px <= xx) { |
1301 | px++; |
1302 | tried = 1; |
1303 | goto retry; |
1304 | } |
1305 | window_copy_cursor_next_word_end(wme, "{[( " , |
1306 | 0); |
1307 | continue; |
1308 | } |
1309 | /* For vi, continue searching for bracket until EOL. */ |
1310 | if (px > xx) { |
1311 | if (py == yy) |
1312 | continue; |
1313 | gl = grid_get_line(s->grid, py); |
1314 | if (~gl->flags & GRID_LINE_WRAPPED) |
1315 | continue; |
1316 | if (gl->cellsize > s->grid->sx) |
1317 | continue; |
1318 | px = 0; |
1319 | py++; |
1320 | xx = window_copy_find_length(wme, py); |
1321 | } else |
1322 | px++; |
1323 | goto retry; |
1324 | } |
1325 | end = close[cp - open]; |
1326 | |
1327 | /* Walk forward until the matching bracket is reached. */ |
1328 | n = 1; |
1329 | failed = 0; |
1330 | do { |
1331 | if (px > xx) { |
1332 | if (py == yy) { |
1333 | failed = 1; |
1334 | break; |
1335 | } |
1336 | px = 0; |
1337 | py++; |
1338 | xx = window_copy_find_length(wme, py); |
1339 | } else |
1340 | px++; |
1341 | |
1342 | grid_get_cell(s->grid, px, py, &gc); |
1343 | if (gc.data.size == 1 && |
1344 | (~gc.flags & GRID_FLAG_PADDING)) { |
1345 | if (*gc.data.data == found) |
1346 | n++; |
1347 | else if (*gc.data.data == end) |
1348 | n--; |
1349 | } |
1350 | } while (n != 0); |
1351 | |
1352 | /* Move the cursor to the found location if any. */ |
1353 | if (!failed) |
1354 | window_copy_scroll_to(wme, px, py); |
1355 | } |
1356 | |
1357 | return (WINDOW_COPY_CMD_NOTHING); |
1358 | } |
1359 | |
1360 | static enum window_copy_cmd_action |
1361 | window_copy_cmd_next_paragraph(struct window_copy_cmd_state *cs) |
1362 | { |
1363 | struct window_mode_entry *wme = cs->wme; |
1364 | u_int np = wme->prefix; |
1365 | |
1366 | for (; np != 0; np--) |
1367 | window_copy_next_paragraph(wme); |
1368 | return (WINDOW_COPY_CMD_NOTHING); |
1369 | } |
1370 | |
1371 | static enum window_copy_cmd_action |
1372 | window_copy_cmd_next_space(struct window_copy_cmd_state *cs) |
1373 | { |
1374 | struct window_mode_entry *wme = cs->wme; |
1375 | u_int np = wme->prefix; |
1376 | |
1377 | for (; np != 0; np--) |
1378 | window_copy_cursor_next_word(wme, " " ); |
1379 | return (WINDOW_COPY_CMD_NOTHING); |
1380 | } |
1381 | |
1382 | static enum window_copy_cmd_action |
1383 | window_copy_cmd_next_space_end(struct window_copy_cmd_state *cs) |
1384 | { |
1385 | struct window_mode_entry *wme = cs->wme; |
1386 | u_int np = wme->prefix; |
1387 | |
1388 | for (; np != 0; np--) |
1389 | window_copy_cursor_next_word_end(wme, " " , 0); |
1390 | return (WINDOW_COPY_CMD_NOTHING); |
1391 | } |
1392 | |
1393 | static enum window_copy_cmd_action |
1394 | window_copy_cmd_next_word(struct window_copy_cmd_state *cs) |
1395 | { |
1396 | struct window_mode_entry *wme = cs->wme; |
1397 | struct session *s = cs->s; |
1398 | u_int np = wme->prefix; |
1399 | const char *ws; |
1400 | |
1401 | ws = options_get_string(s->options, "word-separators" ); |
1402 | for (; np != 0; np--) |
1403 | window_copy_cursor_next_word(wme, ws); |
1404 | return (WINDOW_COPY_CMD_NOTHING); |
1405 | } |
1406 | |
1407 | static enum window_copy_cmd_action |
1408 | window_copy_cmd_next_word_end(struct window_copy_cmd_state *cs) |
1409 | { |
1410 | struct window_mode_entry *wme = cs->wme; |
1411 | struct session *s = cs->s; |
1412 | u_int np = wme->prefix; |
1413 | const char *ws; |
1414 | |
1415 | ws = options_get_string(s->options, "word-separators" ); |
1416 | for (; np != 0; np--) |
1417 | window_copy_cursor_next_word_end(wme, ws, 0); |
1418 | return (WINDOW_COPY_CMD_NOTHING); |
1419 | } |
1420 | |
1421 | static enum window_copy_cmd_action |
1422 | window_copy_cmd_other_end(struct window_copy_cmd_state *cs) |
1423 | { |
1424 | struct window_mode_entry *wme = cs->wme; |
1425 | u_int np = wme->prefix; |
1426 | struct window_copy_mode_data *data = wme->data; |
1427 | |
1428 | data->selflag = SEL_CHAR; |
1429 | if ((np % 2) != 0) |
1430 | window_copy_other_end(wme); |
1431 | return (WINDOW_COPY_CMD_NOTHING); |
1432 | } |
1433 | |
1434 | static enum window_copy_cmd_action |
1435 | window_copy_cmd_page_down(struct window_copy_cmd_state *cs) |
1436 | { |
1437 | struct window_mode_entry *wme = cs->wme; |
1438 | struct window_copy_mode_data *data = wme->data; |
1439 | u_int np = wme->prefix; |
1440 | |
1441 | for (; np != 0; np--) { |
1442 | if (window_copy_pagedown(wme, 0, data->scroll_exit)) |
1443 | return (WINDOW_COPY_CMD_CANCEL); |
1444 | } |
1445 | return (WINDOW_COPY_CMD_NOTHING); |
1446 | } |
1447 | |
1448 | static enum window_copy_cmd_action |
1449 | window_copy_cmd_page_down_and_cancel(struct window_copy_cmd_state *cs) |
1450 | { |
1451 | struct window_mode_entry *wme = cs->wme; |
1452 | u_int np = wme->prefix; |
1453 | |
1454 | for (; np != 0; np--) { |
1455 | if (window_copy_pagedown(wme, 0, 1)) |
1456 | return (WINDOW_COPY_CMD_CANCEL); |
1457 | } |
1458 | return (WINDOW_COPY_CMD_NOTHING); |
1459 | } |
1460 | |
1461 | static enum window_copy_cmd_action |
1462 | window_copy_cmd_page_up(struct window_copy_cmd_state *cs) |
1463 | { |
1464 | struct window_mode_entry *wme = cs->wme; |
1465 | u_int np = wme->prefix; |
1466 | |
1467 | for (; np != 0; np--) |
1468 | window_copy_pageup1(wme, 0); |
1469 | return (WINDOW_COPY_CMD_NOTHING); |
1470 | } |
1471 | |
1472 | static enum window_copy_cmd_action |
1473 | window_copy_cmd_previous_paragraph(struct window_copy_cmd_state *cs) |
1474 | { |
1475 | struct window_mode_entry *wme = cs->wme; |
1476 | u_int np = wme->prefix; |
1477 | |
1478 | for (; np != 0; np--) |
1479 | window_copy_previous_paragraph(wme); |
1480 | return (WINDOW_COPY_CMD_NOTHING); |
1481 | } |
1482 | |
1483 | static enum window_copy_cmd_action |
1484 | window_copy_cmd_previous_space(struct window_copy_cmd_state *cs) |
1485 | { |
1486 | struct window_mode_entry *wme = cs->wme; |
1487 | u_int np = wme->prefix; |
1488 | |
1489 | for (; np != 0; np--) |
1490 | window_copy_cursor_previous_word(wme, " " , 1); |
1491 | return (WINDOW_COPY_CMD_NOTHING); |
1492 | } |
1493 | |
1494 | static enum window_copy_cmd_action |
1495 | window_copy_cmd_previous_word(struct window_copy_cmd_state *cs) |
1496 | { |
1497 | struct window_mode_entry *wme = cs->wme; |
1498 | struct session *s = cs->s; |
1499 | u_int np = wme->prefix; |
1500 | const char *ws; |
1501 | |
1502 | ws = options_get_string(s->options, "word-separators" ); |
1503 | for (; np != 0; np--) |
1504 | window_copy_cursor_previous_word(wme, ws, 1); |
1505 | return (WINDOW_COPY_CMD_NOTHING); |
1506 | } |
1507 | |
1508 | static enum window_copy_cmd_action |
1509 | window_copy_cmd_rectangle_toggle(struct window_copy_cmd_state *cs) |
1510 | { |
1511 | struct window_mode_entry *wme = cs->wme; |
1512 | struct window_copy_mode_data *data = wme->data; |
1513 | |
1514 | data->lineflag = LINE_SEL_NONE; |
1515 | window_copy_rectangle_toggle(wme); |
1516 | |
1517 | return (WINDOW_COPY_CMD_NOTHING); |
1518 | } |
1519 | |
1520 | static enum window_copy_cmd_action |
1521 | window_copy_cmd_scroll_down(struct window_copy_cmd_state *cs) |
1522 | { |
1523 | struct window_mode_entry *wme = cs->wme; |
1524 | struct window_copy_mode_data *data = wme->data; |
1525 | u_int np = wme->prefix; |
1526 | |
1527 | for (; np != 0; np--) |
1528 | window_copy_cursor_down(wme, 1); |
1529 | if (data->scroll_exit && data->oy == 0) |
1530 | return (WINDOW_COPY_CMD_CANCEL); |
1531 | return (WINDOW_COPY_CMD_NOTHING); |
1532 | } |
1533 | |
1534 | static enum window_copy_cmd_action |
1535 | window_copy_cmd_scroll_down_and_cancel(struct window_copy_cmd_state *cs) |
1536 | { |
1537 | struct window_mode_entry *wme = cs->wme; |
1538 | struct window_copy_mode_data *data = wme->data; |
1539 | u_int np = wme->prefix; |
1540 | |
1541 | for (; np != 0; np--) |
1542 | window_copy_cursor_down(wme, 1); |
1543 | if (data->oy == 0) |
1544 | return (WINDOW_COPY_CMD_CANCEL); |
1545 | return (WINDOW_COPY_CMD_NOTHING); |
1546 | } |
1547 | |
1548 | static enum window_copy_cmd_action |
1549 | window_copy_cmd_scroll_up(struct window_copy_cmd_state *cs) |
1550 | { |
1551 | struct window_mode_entry *wme = cs->wme; |
1552 | u_int np = wme->prefix; |
1553 | |
1554 | for (; np != 0; np--) |
1555 | window_copy_cursor_up(wme, 1); |
1556 | return (WINDOW_COPY_CMD_NOTHING); |
1557 | } |
1558 | |
1559 | static enum window_copy_cmd_action |
1560 | window_copy_cmd_search_again(struct window_copy_cmd_state *cs) |
1561 | { |
1562 | struct window_mode_entry *wme = cs->wme; |
1563 | struct window_copy_mode_data *data = wme->data; |
1564 | u_int np = wme->prefix; |
1565 | |
1566 | if (data->searchtype == WINDOW_COPY_SEARCHUP) { |
1567 | for (; np != 0; np--) |
1568 | window_copy_search_up(wme, data->searchregex); |
1569 | } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { |
1570 | for (; np != 0; np--) |
1571 | window_copy_search_down(wme, data->searchregex); |
1572 | } |
1573 | return (WINDOW_COPY_CMD_NOTHING); |
1574 | } |
1575 | |
1576 | static enum window_copy_cmd_action |
1577 | window_copy_cmd_search_reverse(struct window_copy_cmd_state *cs) |
1578 | { |
1579 | struct window_mode_entry *wme = cs->wme; |
1580 | struct window_copy_mode_data *data = wme->data; |
1581 | u_int np = wme->prefix; |
1582 | |
1583 | if (data->searchtype == WINDOW_COPY_SEARCHUP) { |
1584 | for (; np != 0; np--) |
1585 | window_copy_search_down(wme, data->searchregex); |
1586 | } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { |
1587 | for (; np != 0; np--) |
1588 | window_copy_search_up(wme, data->searchregex); |
1589 | } |
1590 | return (WINDOW_COPY_CMD_NOTHING); |
1591 | } |
1592 | |
1593 | static enum window_copy_cmd_action |
1594 | window_copy_cmd_select_line(struct window_copy_cmd_state *cs) |
1595 | { |
1596 | struct window_mode_entry *wme = cs->wme; |
1597 | struct window_copy_mode_data *data = wme->data; |
1598 | u_int np = wme->prefix; |
1599 | |
1600 | data->lineflag = LINE_SEL_LEFT_RIGHT; |
1601 | data->rectflag = 0; |
1602 | data->selflag = SEL_LINE; |
1603 | data->dx = data->cx; |
1604 | data->dy = screen_hsize(data->backing) + data->cy - data->oy; |
1605 | |
1606 | window_copy_cursor_start_of_line(wme); |
1607 | data->selrx = data->cx; |
1608 | data->selry = screen_hsize(data->backing) + data->cy - data->oy; |
1609 | data->endselrx = window_copy_find_length(wme, data->selry); |
1610 | data->endselry = data->selry; |
1611 | window_copy_start_selection(wme); |
1612 | for (; np > 1; np--) |
1613 | window_copy_cursor_down(wme, 0); |
1614 | window_copy_cursor_end_of_line(wme); |
1615 | |
1616 | return (WINDOW_COPY_CMD_REDRAW); |
1617 | } |
1618 | |
1619 | static enum window_copy_cmd_action |
1620 | window_copy_cmd_select_word(struct window_copy_cmd_state *cs) |
1621 | { |
1622 | struct window_mode_entry *wme = cs->wme; |
1623 | struct session *s = cs->s; |
1624 | struct window_copy_mode_data *data = wme->data; |
1625 | u_int px, py; |
1626 | |
1627 | data->lineflag = LINE_SEL_LEFT_RIGHT; |
1628 | data->rectflag = 0; |
1629 | data->selflag = SEL_WORD; |
1630 | data->dx = data->cx; |
1631 | data->dy = screen_hsize(data->backing) + data->cy - data->oy; |
1632 | |
1633 | data->ws = options_get_string(s->options, "word-separators" ); |
1634 | window_copy_cursor_previous_word(wme, data->ws, 0); |
1635 | px = data->cx; |
1636 | py = screen_hsize(data->backing) + data->cy - data->oy; |
1637 | data->selrx = px; |
1638 | data->selry = py; |
1639 | window_copy_start_selection(wme); |
1640 | |
1641 | if (px >= window_copy_find_length(wme, py) || |
1642 | !window_copy_in_set(wme, px + 1, py, data->ws)) |
1643 | window_copy_cursor_next_word_end(wme, data->ws, 1); |
1644 | else { |
1645 | window_copy_update_cursor(wme, px, data->cy); |
1646 | if (window_copy_update_selection(wme, 1, 1)) |
1647 | window_copy_redraw_lines(wme, data->cy, 1); |
1648 | } |
1649 | data->endselrx = data->cx; |
1650 | data->endselry = screen_hsize(data->backing) + data->cy - data->oy; |
1651 | if (data->dx > data->endselrx) |
1652 | data->dx = data->endselrx; |
1653 | |
1654 | return (WINDOW_COPY_CMD_REDRAW); |
1655 | } |
1656 | |
1657 | static enum window_copy_cmd_action |
1658 | window_copy_cmd_start_of_line(struct window_copy_cmd_state *cs) |
1659 | { |
1660 | struct window_mode_entry *wme = cs->wme; |
1661 | |
1662 | window_copy_cursor_start_of_line(wme); |
1663 | return (WINDOW_COPY_CMD_NOTHING); |
1664 | } |
1665 | |
1666 | static enum window_copy_cmd_action |
1667 | window_copy_cmd_top_line(struct window_copy_cmd_state *cs) |
1668 | { |
1669 | struct window_mode_entry *wme = cs->wme; |
1670 | struct window_copy_mode_data *data = wme->data; |
1671 | |
1672 | data->cx = 0; |
1673 | data->cy = 0; |
1674 | |
1675 | window_copy_update_selection(wme, 1, 0); |
1676 | return (WINDOW_COPY_CMD_REDRAW); |
1677 | } |
1678 | |
1679 | static enum window_copy_cmd_action |
1680 | window_copy_cmd_copy_pipe_no_clear(struct window_copy_cmd_state *cs) |
1681 | { |
1682 | struct window_mode_entry *wme = cs->wme; |
1683 | struct client *c = cs->c; |
1684 | struct session *s = cs->s; |
1685 | struct winlink *wl = cs->wl; |
1686 | struct window_pane *wp = wme->wp; |
1687 | char *command = NULL; |
1688 | char *prefix = NULL; |
1689 | |
1690 | if (cs->args->argc == 3) |
1691 | prefix = format_single(NULL, cs->args->argv[2], c, s, wl, wp); |
1692 | |
1693 | if (s != NULL && *cs->args->argv[1] != '\0') { |
1694 | command = format_single(NULL, cs->args->argv[1], c, s, wl, wp); |
1695 | window_copy_copy_pipe(wme, s, prefix, command); |
1696 | free(command); |
1697 | } |
1698 | |
1699 | free(prefix); |
1700 | return (WINDOW_COPY_CMD_NOTHING); |
1701 | } |
1702 | |
1703 | static enum window_copy_cmd_action |
1704 | window_copy_cmd_copy_pipe(struct window_copy_cmd_state *cs) |
1705 | { |
1706 | struct window_mode_entry *wme = cs->wme; |
1707 | |
1708 | window_copy_cmd_copy_pipe_no_clear(cs); |
1709 | window_copy_clear_selection(wme); |
1710 | return (WINDOW_COPY_CMD_REDRAW); |
1711 | } |
1712 | |
1713 | static enum window_copy_cmd_action |
1714 | window_copy_cmd_copy_pipe_and_cancel(struct window_copy_cmd_state *cs) |
1715 | { |
1716 | struct window_mode_entry *wme = cs->wme; |
1717 | |
1718 | window_copy_cmd_copy_pipe_no_clear(cs); |
1719 | window_copy_clear_selection(wme); |
1720 | return (WINDOW_COPY_CMD_CANCEL); |
1721 | } |
1722 | |
1723 | static enum window_copy_cmd_action |
1724 | window_copy_cmd_goto_line(struct window_copy_cmd_state *cs) |
1725 | { |
1726 | struct window_mode_entry *wme = cs->wme; |
1727 | const char *argument = cs->args->argv[1]; |
1728 | |
1729 | if (*argument != '\0') |
1730 | window_copy_goto_line(wme, argument); |
1731 | return (WINDOW_COPY_CMD_NOTHING); |
1732 | } |
1733 | |
1734 | static enum window_copy_cmd_action |
1735 | window_copy_cmd_jump_backward(struct window_copy_cmd_state *cs) |
1736 | { |
1737 | struct window_mode_entry *wme = cs->wme; |
1738 | struct window_copy_mode_data *data = wme->data; |
1739 | u_int np = wme->prefix; |
1740 | const char *argument = cs->args->argv[1]; |
1741 | |
1742 | if (*argument != '\0') { |
1743 | data->jumptype = WINDOW_COPY_JUMPBACKWARD; |
1744 | data->jumpchar = *argument; |
1745 | for (; np != 0; np--) |
1746 | window_copy_cursor_jump_back(wme); |
1747 | } |
1748 | return (WINDOW_COPY_CMD_NOTHING); |
1749 | } |
1750 | |
1751 | static enum window_copy_cmd_action |
1752 | window_copy_cmd_jump_forward(struct window_copy_cmd_state *cs) |
1753 | { |
1754 | struct window_mode_entry *wme = cs->wme; |
1755 | struct window_copy_mode_data *data = wme->data; |
1756 | u_int np = wme->prefix; |
1757 | const char *argument = cs->args->argv[1]; |
1758 | |
1759 | if (*argument != '\0') { |
1760 | data->jumptype = WINDOW_COPY_JUMPFORWARD; |
1761 | data->jumpchar = *argument; |
1762 | for (; np != 0; np--) |
1763 | window_copy_cursor_jump(wme); |
1764 | } |
1765 | return (WINDOW_COPY_CMD_NOTHING); |
1766 | } |
1767 | |
1768 | static enum window_copy_cmd_action |
1769 | window_copy_cmd_jump_to_backward(struct window_copy_cmd_state *cs) |
1770 | { |
1771 | struct window_mode_entry *wme = cs->wme; |
1772 | struct window_copy_mode_data *data = wme->data; |
1773 | u_int np = wme->prefix; |
1774 | const char *argument = cs->args->argv[1]; |
1775 | |
1776 | if (*argument != '\0') { |
1777 | data->jumptype = WINDOW_COPY_JUMPTOBACKWARD; |
1778 | data->jumpchar = *argument; |
1779 | for (; np != 0; np--) |
1780 | window_copy_cursor_jump_to_back(wme); |
1781 | } |
1782 | return (WINDOW_COPY_CMD_NOTHING); |
1783 | } |
1784 | |
1785 | static enum window_copy_cmd_action |
1786 | window_copy_cmd_jump_to_forward(struct window_copy_cmd_state *cs) |
1787 | { |
1788 | struct window_mode_entry *wme = cs->wme; |
1789 | struct window_copy_mode_data *data = wme->data; |
1790 | u_int np = wme->prefix; |
1791 | const char *argument = cs->args->argv[1]; |
1792 | |
1793 | if (*argument != '\0') { |
1794 | data->jumptype = WINDOW_COPY_JUMPTOFORWARD; |
1795 | data->jumpchar = *argument; |
1796 | for (; np != 0; np--) |
1797 | window_copy_cursor_jump_to(wme); |
1798 | } |
1799 | return (WINDOW_COPY_CMD_NOTHING); |
1800 | } |
1801 | |
1802 | static enum window_copy_cmd_action |
1803 | window_copy_cmd_search_backward(struct window_copy_cmd_state *cs) |
1804 | { |
1805 | struct window_mode_entry *wme = cs->wme; |
1806 | struct window_copy_mode_data *data = wme->data; |
1807 | u_int np = wme->prefix; |
1808 | |
1809 | if (!window_copy_expand_search_string(cs)) |
1810 | return (WINDOW_COPY_CMD_NOTHING); |
1811 | |
1812 | if (data->searchstr != NULL) { |
1813 | data->searchtype = WINDOW_COPY_SEARCHUP; |
1814 | data->searchregex = 1; |
1815 | data->timeout = 0; |
1816 | for (; np != 0; np--) |
1817 | window_copy_search_up(wme, 1); |
1818 | } |
1819 | return (WINDOW_COPY_CMD_NOTHING); |
1820 | } |
1821 | |
1822 | static enum window_copy_cmd_action |
1823 | window_copy_cmd_search_backward_text(struct window_copy_cmd_state *cs) |
1824 | { |
1825 | struct window_mode_entry *wme = cs->wme; |
1826 | struct window_copy_mode_data *data = wme->data; |
1827 | u_int np = wme->prefix; |
1828 | |
1829 | if (!window_copy_expand_search_string(cs)) |
1830 | return (WINDOW_COPY_CMD_NOTHING); |
1831 | |
1832 | if (data->searchstr != NULL) { |
1833 | data->searchtype = WINDOW_COPY_SEARCHUP; |
1834 | data->searchregex = 0; |
1835 | data->timeout = 0; |
1836 | for (; np != 0; np--) |
1837 | window_copy_search_up(wme, 0); |
1838 | } |
1839 | return (WINDOW_COPY_CMD_NOTHING); |
1840 | } |
1841 | |
1842 | static enum window_copy_cmd_action |
1843 | window_copy_cmd_search_forward(struct window_copy_cmd_state *cs) |
1844 | { |
1845 | struct window_mode_entry *wme = cs->wme; |
1846 | struct window_copy_mode_data *data = wme->data; |
1847 | u_int np = wme->prefix; |
1848 | |
1849 | if (!window_copy_expand_search_string(cs)) |
1850 | return (WINDOW_COPY_CMD_NOTHING); |
1851 | |
1852 | if (data->searchstr != NULL) { |
1853 | data->searchtype = WINDOW_COPY_SEARCHDOWN; |
1854 | data->searchregex = 1; |
1855 | data->timeout = 0; |
1856 | for (; np != 0; np--) |
1857 | window_copy_search_down(wme, 1); |
1858 | } |
1859 | return (WINDOW_COPY_CMD_NOTHING); |
1860 | } |
1861 | |
1862 | static enum window_copy_cmd_action |
1863 | window_copy_cmd_search_forward_text(struct window_copy_cmd_state *cs) |
1864 | { |
1865 | struct window_mode_entry *wme = cs->wme; |
1866 | struct window_copy_mode_data *data = wme->data; |
1867 | u_int np = wme->prefix; |
1868 | |
1869 | if (!window_copy_expand_search_string(cs)) |
1870 | return (WINDOW_COPY_CMD_NOTHING); |
1871 | |
1872 | if (data->searchstr != NULL) { |
1873 | data->searchtype = WINDOW_COPY_SEARCHDOWN; |
1874 | data->searchregex = 0; |
1875 | data->timeout = 0; |
1876 | for (; np != 0; np--) |
1877 | window_copy_search_down(wme, 0); |
1878 | } |
1879 | return (WINDOW_COPY_CMD_NOTHING); |
1880 | } |
1881 | |
1882 | static enum window_copy_cmd_action |
1883 | window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) |
1884 | { |
1885 | struct window_mode_entry *wme = cs->wme; |
1886 | struct window_copy_mode_data *data = wme->data; |
1887 | const char *argument = cs->args->argv[1]; |
1888 | const char *ss = data->searchstr; |
1889 | char prefix; |
1890 | enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING; |
1891 | |
1892 | data->timeout = 0; |
1893 | |
1894 | prefix = *argument++; |
1895 | if (data->searchx == -1 || data->searchy == -1) { |
1896 | data->searchx = data->cx; |
1897 | data->searchy = data->cy; |
1898 | data->searcho = data->oy; |
1899 | } else if (ss != NULL && strcmp(argument, ss) != 0) { |
1900 | data->cx = data->searchx; |
1901 | data->cy = data->searchy; |
1902 | data->oy = data->searcho; |
1903 | action = WINDOW_COPY_CMD_REDRAW; |
1904 | } |
1905 | if (*argument == '\0') { |
1906 | window_copy_clear_marks(wme); |
1907 | return (WINDOW_COPY_CMD_REDRAW); |
1908 | } |
1909 | switch (prefix) { |
1910 | case '=': |
1911 | case '-': |
1912 | data->searchtype = WINDOW_COPY_SEARCHUP; |
1913 | data->searchregex = 0; |
1914 | free(data->searchstr); |
1915 | data->searchstr = xstrdup(argument); |
1916 | if (!window_copy_search_up(wme, 0)) { |
1917 | window_copy_clear_marks(wme); |
1918 | return (WINDOW_COPY_CMD_REDRAW); |
1919 | } |
1920 | break; |
1921 | case '+': |
1922 | data->searchtype = WINDOW_COPY_SEARCHDOWN; |
1923 | data->searchregex = 0; |
1924 | free(data->searchstr); |
1925 | data->searchstr = xstrdup(argument); |
1926 | if (!window_copy_search_down(wme, 0)) { |
1927 | window_copy_clear_marks(wme); |
1928 | return (WINDOW_COPY_CMD_REDRAW); |
1929 | } |
1930 | break; |
1931 | } |
1932 | return (action); |
1933 | } |
1934 | |
1935 | static enum window_copy_cmd_action |
1936 | window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) |
1937 | { |
1938 | struct window_mode_entry *wme = cs->wme; |
1939 | struct window_copy_mode_data *data = wme->data; |
1940 | const char *argument = cs->args->argv[1]; |
1941 | const char *ss = data->searchstr; |
1942 | char prefix; |
1943 | enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING; |
1944 | |
1945 | data->timeout = 0; |
1946 | |
1947 | prefix = *argument++; |
1948 | if (data->searchx == -1 || data->searchy == -1) { |
1949 | data->searchx = data->cx; |
1950 | data->searchy = data->cy; |
1951 | data->searcho = data->oy; |
1952 | } else if (ss != NULL && strcmp(argument, ss) != 0) { |
1953 | data->cx = data->searchx; |
1954 | data->cy = data->searchy; |
1955 | data->oy = data->searcho; |
1956 | action = WINDOW_COPY_CMD_REDRAW; |
1957 | } |
1958 | if (*argument == '\0') { |
1959 | window_copy_clear_marks(wme); |
1960 | return (WINDOW_COPY_CMD_REDRAW); |
1961 | } |
1962 | switch (prefix) { |
1963 | case '=': |
1964 | case '+': |
1965 | data->searchtype = WINDOW_COPY_SEARCHDOWN; |
1966 | data->searchregex = 0; |
1967 | free(data->searchstr); |
1968 | data->searchstr = xstrdup(argument); |
1969 | if (!window_copy_search_down(wme, 0)) { |
1970 | window_copy_clear_marks(wme); |
1971 | return (WINDOW_COPY_CMD_REDRAW); |
1972 | } |
1973 | break; |
1974 | case '-': |
1975 | data->searchtype = WINDOW_COPY_SEARCHUP; |
1976 | data->searchregex = 0; |
1977 | free(data->searchstr); |
1978 | data->searchstr = xstrdup(argument); |
1979 | if (!window_copy_search_up(wme, 0)) { |
1980 | window_copy_clear_marks(wme); |
1981 | return (WINDOW_COPY_CMD_REDRAW); |
1982 | } |
1983 | } |
1984 | return (action); |
1985 | } |
1986 | |
1987 | static const struct { |
1988 | const char *command; |
1989 | int minargs; |
1990 | int maxargs; |
1991 | int ismotion; |
1992 | enum window_copy_cmd_action (*f)(struct window_copy_cmd_state *); |
1993 | } window_copy_cmd_table[] = { |
1994 | { "append-selection" , 0, 0, 0, |
1995 | window_copy_cmd_append_selection }, |
1996 | { "append-selection-and-cancel" , 0, 0, 0, |
1997 | window_copy_cmd_append_selection_and_cancel }, |
1998 | { "back-to-indentation" , 0, 0, 0, |
1999 | window_copy_cmd_back_to_indentation }, |
2000 | { "begin-selection" , 0, 0, 0, |
2001 | window_copy_cmd_begin_selection }, |
2002 | { "bottom-line" , 0, 0, 1, |
2003 | window_copy_cmd_bottom_line }, |
2004 | { "cancel" , 0, 0, 0, |
2005 | window_copy_cmd_cancel }, |
2006 | { "clear-selection" , 0, 0, 0, |
2007 | window_copy_cmd_clear_selection }, |
2008 | { "copy-end-of-line" , 0, 1, 0, |
2009 | window_copy_cmd_copy_end_of_line }, |
2010 | { "copy-line" , 0, 1, 0, |
2011 | window_copy_cmd_copy_line }, |
2012 | { "copy-pipe-no-clear" , 1, 2, 0, |
2013 | window_copy_cmd_copy_pipe_no_clear }, |
2014 | { "copy-pipe" , 1, 2, 0, |
2015 | window_copy_cmd_copy_pipe }, |
2016 | { "copy-pipe-and-cancel" , 1, 2, 0, |
2017 | window_copy_cmd_copy_pipe_and_cancel }, |
2018 | { "copy-selection-no-clear" , 0, 1, 0, |
2019 | window_copy_cmd_copy_selection_no_clear }, |
2020 | { "copy-selection" , 0, 1, 0, |
2021 | window_copy_cmd_copy_selection }, |
2022 | { "copy-selection-and-cancel" , 0, 1, 0, |
2023 | window_copy_cmd_copy_selection_and_cancel }, |
2024 | { "cursor-down" , 0, 0, 1, |
2025 | window_copy_cmd_cursor_down }, |
2026 | { "cursor-down-and-cancel" , 0, 0, 0, |
2027 | window_copy_cmd_cursor_down_and_cancel }, |
2028 | { "cursor-left" , 0, 0, 1, |
2029 | window_copy_cmd_cursor_left }, |
2030 | { "cursor-right" , 0, 0, 1, |
2031 | window_copy_cmd_cursor_right }, |
2032 | { "cursor-up" , 0, 0, 1, |
2033 | window_copy_cmd_cursor_up }, |
2034 | { "end-of-line" , 0, 0, 1, |
2035 | window_copy_cmd_end_of_line }, |
2036 | { "goto-line" , 1, 1, 1, |
2037 | window_copy_cmd_goto_line }, |
2038 | { "halfpage-down" , 0, 0, 1, |
2039 | window_copy_cmd_halfpage_down }, |
2040 | { "halfpage-down-and-cancel" , 0, 0, 0, |
2041 | window_copy_cmd_halfpage_down_and_cancel }, |
2042 | { "halfpage-up" , 0, 0, 1, |
2043 | window_copy_cmd_halfpage_up }, |
2044 | { "history-bottom" , 0, 0, 1, |
2045 | window_copy_cmd_history_bottom }, |
2046 | { "history-top" , 0, 0, 1, |
2047 | window_copy_cmd_history_top }, |
2048 | { "jump-again" , 0, 0, 1, |
2049 | window_copy_cmd_jump_again }, |
2050 | { "jump-backward" , 1, 1, 1, |
2051 | window_copy_cmd_jump_backward }, |
2052 | { "jump-forward" , 1, 1, 1, |
2053 | window_copy_cmd_jump_forward }, |
2054 | { "jump-reverse" , 0, 0, 1, |
2055 | window_copy_cmd_jump_reverse }, |
2056 | { "jump-to-backward" , 1, 1, 1, |
2057 | window_copy_cmd_jump_to_backward }, |
2058 | { "jump-to-forward" , 1, 1, 1, |
2059 | window_copy_cmd_jump_to_forward }, |
2060 | { "middle-line" , 0, 0, 1, |
2061 | window_copy_cmd_middle_line }, |
2062 | { "next-matching-bracket" , 0, 0, 0, |
2063 | window_copy_cmd_next_matching_bracket }, |
2064 | { "next-paragraph" , 0, 0, 1, |
2065 | window_copy_cmd_next_paragraph }, |
2066 | { "next-space" , 0, 0, 1, |
2067 | window_copy_cmd_next_space }, |
2068 | { "next-space-end" , 0, 0, 1, |
2069 | window_copy_cmd_next_space_end }, |
2070 | { "next-word" , 0, 0, 1, |
2071 | window_copy_cmd_next_word }, |
2072 | { "next-word-end" , 0, 0, 1, |
2073 | window_copy_cmd_next_word_end }, |
2074 | { "other-end" , 0, 0, 1, |
2075 | window_copy_cmd_other_end }, |
2076 | { "page-down" , 0, 0, 1, |
2077 | window_copy_cmd_page_down }, |
2078 | { "page-down-and-cancel" , 0, 0, 0, |
2079 | window_copy_cmd_page_down_and_cancel }, |
2080 | { "page-up" , 0, 0, 1, |
2081 | window_copy_cmd_page_up }, |
2082 | { "previous-matching-bracket" , 0, 0, 0, |
2083 | window_copy_cmd_previous_matching_bracket }, |
2084 | { "previous-paragraph" , 0, 0, 1, |
2085 | window_copy_cmd_previous_paragraph }, |
2086 | { "previous-space" , 0, 0, 1, |
2087 | window_copy_cmd_previous_space }, |
2088 | { "previous-word" , 0, 0, 1, |
2089 | window_copy_cmd_previous_word }, |
2090 | { "rectangle-toggle" , 0, 0, 0, |
2091 | window_copy_cmd_rectangle_toggle }, |
2092 | { "scroll-down" , 0, 0, 1, |
2093 | window_copy_cmd_scroll_down }, |
2094 | { "scroll-down-and-cancel" , 0, 0, 0, |
2095 | window_copy_cmd_scroll_down_and_cancel }, |
2096 | { "scroll-up" , 0, 0, 1, |
2097 | window_copy_cmd_scroll_up }, |
2098 | { "search-again" , 0, 0, 0, |
2099 | window_copy_cmd_search_again }, |
2100 | { "search-backward" , 0, 1, 0, |
2101 | window_copy_cmd_search_backward }, |
2102 | { "search-backward-text" , 0, 1, 0, |
2103 | window_copy_cmd_search_backward_text }, |
2104 | { "search-backward-incremental" , 1, 1, 0, |
2105 | window_copy_cmd_search_backward_incremental }, |
2106 | { "search-forward" , 0, 1, 0, |
2107 | window_copy_cmd_search_forward }, |
2108 | { "search-forward-text" , 0, 1, 0, |
2109 | window_copy_cmd_search_forward_text }, |
2110 | { "search-forward-incremental" , 1, 1, 0, |
2111 | window_copy_cmd_search_forward_incremental }, |
2112 | { "search-reverse" , 0, 0, 0, |
2113 | window_copy_cmd_search_reverse }, |
2114 | { "select-line" , 0, 0, 0, |
2115 | window_copy_cmd_select_line }, |
2116 | { "select-word" , 0, 0, 0, |
2117 | window_copy_cmd_select_word }, |
2118 | { "start-of-line" , 0, 0, 1, |
2119 | window_copy_cmd_start_of_line }, |
2120 | { "stop-selection" , 0, 0, 0, |
2121 | window_copy_cmd_stop_selection }, |
2122 | { "top-line" , 0, 0, 1, |
2123 | window_copy_cmd_top_line }, |
2124 | }; |
2125 | |
2126 | static void |
2127 | window_copy_command(struct window_mode_entry *wme, struct client *c, |
2128 | struct session *s, struct winlink *wl, struct args *args, |
2129 | struct mouse_event *m) |
2130 | { |
2131 | struct window_copy_mode_data *data = wme->data; |
2132 | struct window_copy_cmd_state cs; |
2133 | enum window_copy_cmd_action action; |
2134 | const char *command; |
2135 | u_int i; |
2136 | int ismotion = 0, keys; |
2137 | |
2138 | if (args->argc == 0) |
2139 | return; |
2140 | command = args->argv[0]; |
2141 | |
2142 | if (m != NULL && m->valid && !MOUSE_WHEEL(m->b)) |
2143 | window_copy_move_mouse(m); |
2144 | |
2145 | cs.wme = wme; |
2146 | cs.args = args; |
2147 | cs.m = m; |
2148 | |
2149 | cs.c = c; |
2150 | cs.s = s; |
2151 | cs.wl = wl; |
2152 | |
2153 | action = WINDOW_COPY_CMD_NOTHING; |
2154 | for (i = 0; i < nitems(window_copy_cmd_table); i++) { |
2155 | if (strcmp(window_copy_cmd_table[i].command, command) == 0) { |
2156 | if (args->argc - 1 < window_copy_cmd_table[i].minargs || |
2157 | args->argc - 1 > window_copy_cmd_table[i].maxargs) |
2158 | break; |
2159 | ismotion = window_copy_cmd_table[i].ismotion; |
2160 | action = window_copy_cmd_table[i].f (&cs); |
2161 | break; |
2162 | } |
2163 | } |
2164 | |
2165 | if (strncmp(command, "search-" , 7) != 0 && data->searchmark != NULL) { |
2166 | keys = options_get_number(wme->wp->window->options, "mode-keys" ); |
2167 | if (keys != MODEKEY_VI || !ismotion) { |
2168 | window_copy_clear_marks(wme); |
2169 | data->searchx = data->searchy = -1; |
2170 | } else if (data->searchthis != -1) { |
2171 | data->searchthis = -1; |
2172 | action = WINDOW_COPY_CMD_REDRAW; |
2173 | } |
2174 | if (action == WINDOW_COPY_CMD_NOTHING) |
2175 | action = WINDOW_COPY_CMD_REDRAW; |
2176 | } |
2177 | wme->prefix = 1; |
2178 | |
2179 | if (action == WINDOW_COPY_CMD_CANCEL) |
2180 | window_pane_reset_mode(wme->wp); |
2181 | else if (action == WINDOW_COPY_CMD_REDRAW) |
2182 | window_copy_redraw_screen(wme); |
2183 | } |
2184 | |
2185 | static void |
2186 | window_copy_scroll_to(struct window_mode_entry *wme, u_int px, u_int py) |
2187 | { |
2188 | struct window_copy_mode_data *data = wme->data; |
2189 | struct grid *gd = data->backing->grid; |
2190 | u_int offset, gap; |
2191 | |
2192 | data->cx = px; |
2193 | |
2194 | if (py >= gd->hsize - data->oy && py < gd->hsize - data->oy + gd->sy) |
2195 | data->cy = py - (gd->hsize - data->oy); |
2196 | else { |
2197 | gap = gd->sy / 4; |
2198 | if (py < gd->sy) { |
2199 | offset = 0; |
2200 | data->cy = py; |
2201 | } else if (py > gd->hsize + gd->sy - gap) { |
2202 | offset = gd->hsize; |
2203 | data->cy = py - gd->hsize; |
2204 | } else { |
2205 | offset = py + gap - gd->sy; |
2206 | data->cy = py - offset; |
2207 | } |
2208 | data->oy = gd->hsize - offset; |
2209 | } |
2210 | |
2211 | if (data->searchmark != NULL && !data->timeout) |
2212 | window_copy_search_marks(wme, NULL, data->searchregex); |
2213 | window_copy_update_selection(wme, 1, 0); |
2214 | window_copy_redraw_screen(wme); |
2215 | } |
2216 | |
2217 | static int |
2218 | window_copy_search_compare(struct grid *gd, u_int px, u_int py, |
2219 | struct grid *sgd, u_int spx, int cis) |
2220 | { |
2221 | struct grid_cell gc, sgc; |
2222 | const struct utf8_data *ud, *sud; |
2223 | |
2224 | grid_get_cell(gd, px, py, &gc); |
2225 | ud = &gc.data; |
2226 | grid_get_cell(sgd, spx, 0, &sgc); |
2227 | sud = &sgc.data; |
2228 | |
2229 | if (ud->size != sud->size || ud->width != sud->width) |
2230 | return (0); |
2231 | |
2232 | if (cis && ud->size == 1) |
2233 | return (tolower(ud->data[0]) == sud->data[0]); |
2234 | |
2235 | return (memcmp(ud->data, sud->data, ud->size) == 0); |
2236 | } |
2237 | |
2238 | static int |
2239 | window_copy_search_lr(struct grid *gd, |
2240 | struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis) |
2241 | { |
2242 | u_int ax, bx, px, pywrap, endline; |
2243 | int matched; |
2244 | struct grid_line *gl; |
2245 | |
2246 | endline = gd->hsize + gd->sy - 1; |
2247 | for (ax = first; ax < last; ax++) { |
2248 | for (bx = 0; bx < sgd->sx; bx++) { |
2249 | px = ax + bx; |
2250 | pywrap = py; |
2251 | /* Wrap line. */ |
2252 | while (px >= gd->sx && pywrap < endline) { |
2253 | gl = grid_get_line(gd, pywrap); |
2254 | if (~gl->flags & GRID_LINE_WRAPPED) |
2255 | break; |
2256 | px -= gd->sx; |
2257 | pywrap++; |
2258 | } |
2259 | /* We have run off the end of the grid. */ |
2260 | if (px >= gd->sx) |
2261 | break; |
2262 | matched = window_copy_search_compare(gd, px, pywrap, |
2263 | sgd, bx, cis); |
2264 | if (!matched) |
2265 | break; |
2266 | } |
2267 | if (bx == sgd->sx) { |
2268 | *ppx = ax; |
2269 | return (1); |
2270 | } |
2271 | } |
2272 | return (0); |
2273 | } |
2274 | |
2275 | static int |
2276 | window_copy_search_rl(struct grid *gd, |
2277 | struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis) |
2278 | { |
2279 | u_int ax, bx, px, pywrap, endline; |
2280 | int matched; |
2281 | struct grid_line *gl; |
2282 | |
2283 | endline = gd->hsize + gd->sy - 1; |
2284 | for (ax = last; ax > first; ax--) { |
2285 | for (bx = 0; bx < sgd->sx; bx++) { |
2286 | px = ax - 1 + bx; |
2287 | pywrap = py; |
2288 | /* Wrap line. */ |
2289 | while (px >= gd->sx && pywrap < endline) { |
2290 | gl = grid_get_line(gd, pywrap); |
2291 | if (~gl->flags & GRID_LINE_WRAPPED) |
2292 | break; |
2293 | px -= gd->sx; |
2294 | pywrap++; |
2295 | } |
2296 | /* We have run off the end of the grid. */ |
2297 | if (px >= gd->sx) |
2298 | break; |
2299 | matched = window_copy_search_compare(gd, px, pywrap, |
2300 | sgd, bx, cis); |
2301 | if (!matched) |
2302 | break; |
2303 | } |
2304 | if (bx == sgd->sx) { |
2305 | *ppx = ax - 1; |
2306 | return (1); |
2307 | } |
2308 | } |
2309 | return (0); |
2310 | } |
2311 | |
2312 | static int |
2313 | window_copy_search_lr_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py, |
2314 | u_int first, u_int last, regex_t *reg) |
2315 | { |
2316 | int eflags = 0; |
2317 | u_int endline, foundx, foundy, len, pywrap, size = 1; |
2318 | char *buf; |
2319 | regmatch_t regmatch; |
2320 | struct grid_line *gl; |
2321 | |
2322 | /* |
2323 | * This can happen during search if the last match was the last |
2324 | * character on a line. |
2325 | */ |
2326 | if (first >= last) |
2327 | return (0); |
2328 | |
2329 | /* Set flags for regex search. */ |
2330 | if (first != 0) |
2331 | eflags |= REG_NOTBOL; |
2332 | |
2333 | /* Need to look at the entire string. */ |
2334 | buf = xmalloc(size); |
2335 | buf[0] = '\0'; |
2336 | buf = window_copy_stringify(gd, py, first, gd->sx, buf, &size); |
2337 | len = gd->sx - first; |
2338 | endline = gd->hsize + gd->sy - 1; |
2339 | pywrap = py; |
2340 | while (buf != NULL && pywrap <= endline) { |
2341 | gl = grid_get_line(gd, pywrap); |
2342 | if (~gl->flags & GRID_LINE_WRAPPED) |
2343 | break; |
2344 | pywrap++; |
2345 | buf = window_copy_stringify(gd, pywrap, 0, gd->sx, buf, &size); |
2346 | len += gd->sx; |
2347 | } |
2348 | |
2349 | if (regexec(reg, buf, 1, ®match, eflags) == 0) { |
2350 | foundx = first; |
2351 | foundy = py; |
2352 | window_copy_cstrtocellpos(gd, len, &foundx, &foundy, |
2353 | buf + regmatch.rm_so); |
2354 | if (foundy == py && foundx < last) { |
2355 | *ppx = foundx; |
2356 | len -= foundx - first; |
2357 | window_copy_cstrtocellpos(gd, len, &foundx, &foundy, |
2358 | buf + regmatch.rm_eo); |
2359 | *psx = foundx; |
2360 | while (foundy > py) { |
2361 | *psx += gd->sx; |
2362 | foundy--; |
2363 | } |
2364 | *psx -= *ppx; |
2365 | free(buf); |
2366 | return (1); |
2367 | } |
2368 | } |
2369 | |
2370 | free(buf); |
2371 | *ppx = 0; |
2372 | *psx = 0; |
2373 | return (0); |
2374 | } |
2375 | |
2376 | static int |
2377 | window_copy_search_rl_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py, |
2378 | u_int first, u_int last, regex_t *reg) |
2379 | { |
2380 | int eflags = 0; |
2381 | u_int endline, len, pywrap, size = 1; |
2382 | char *buf; |
2383 | struct grid_line *gl; |
2384 | |
2385 | /* Set flags for regex search. */ |
2386 | if (first != 0) |
2387 | eflags |= REG_NOTBOL; |
2388 | |
2389 | /* Need to look at the entire string. */ |
2390 | buf = xmalloc(size); |
2391 | buf[0] = '\0'; |
2392 | buf = window_copy_stringify(gd, py, first, gd->sx, buf, &size); |
2393 | len = gd->sx - first; |
2394 | endline = gd->hsize + gd->sy - 1; |
2395 | pywrap = py; |
2396 | while (buf != NULL && (pywrap <= endline)) { |
2397 | gl = grid_get_line(gd, pywrap); |
2398 | if (~gl->flags & GRID_LINE_WRAPPED) |
2399 | break; |
2400 | pywrap++; |
2401 | buf = window_copy_stringify(gd, pywrap, 0, gd->sx, buf, &size); |
2402 | len += gd->sx; |
2403 | } |
2404 | |
2405 | if (window_copy_last_regex(gd, py, first, last, len, ppx, psx, buf, |
2406 | reg, eflags)) |
2407 | { |
2408 | free(buf); |
2409 | return (1); |
2410 | } |
2411 | |
2412 | free(buf); |
2413 | *ppx = 0; |
2414 | *psx = 0; |
2415 | return (0); |
2416 | } |
2417 | |
2418 | static const char * |
2419 | window_copy_cellstring(const struct grid_line *gl, u_int px, size_t *size) |
2420 | { |
2421 | struct grid_cell_entry *gce; |
2422 | |
2423 | if (px >= gl->cellsize) { |
2424 | *size = 1; |
2425 | return (" " ); |
2426 | } |
2427 | |
2428 | gce = &gl->celldata[px]; |
2429 | if (~gce->flags & GRID_FLAG_EXTENDED) { |
2430 | *size = 1; |
2431 | return (&gce->data.data); |
2432 | } |
2433 | |
2434 | *size = gl->extddata[gce->offset].data.size; |
2435 | return (gl->extddata[gce->offset].data.data); |
2436 | } |
2437 | |
2438 | /* Find last match in given range. */ |
2439 | static int |
2440 | window_copy_last_regex(struct grid *gd, u_int py, u_int first, u_int last, |
2441 | u_int len, u_int *ppx, u_int *psx, const char *buf, const regex_t *preg, |
2442 | int eflags) |
2443 | { |
2444 | u_int foundx, foundy, oldx, px = 0, savepx, savesx = 0; |
2445 | regmatch_t regmatch; |
2446 | |
2447 | foundx = first; |
2448 | foundy = py; |
2449 | oldx = first; |
2450 | while (regexec(preg, buf + px, 1, ®match, eflags) == 0) { |
2451 | window_copy_cstrtocellpos(gd, len, &foundx, &foundy, |
2452 | buf + px + regmatch.rm_so); |
2453 | if (foundy > py || foundx >= last) |
2454 | break; |
2455 | len -= foundx - oldx; |
2456 | savepx = foundx; |
2457 | window_copy_cstrtocellpos(gd, len, &foundx, &foundy, |
2458 | buf + px + regmatch.rm_eo); |
2459 | if (foundy > py || foundx >= last) { |
2460 | *ppx = savepx; |
2461 | *psx = foundx; |
2462 | while (foundy > py) { |
2463 | *psx += gd->sx; |
2464 | foundy--; |
2465 | } |
2466 | *psx -= *ppx; |
2467 | return (1); |
2468 | } else { |
2469 | savesx = foundx - savepx; |
2470 | len -= savesx; |
2471 | oldx = foundx; |
2472 | } |
2473 | px += regmatch.rm_eo; |
2474 | } |
2475 | |
2476 | if (savesx > 0) { |
2477 | *ppx = savepx; |
2478 | *psx = savesx; |
2479 | return (1); |
2480 | } else { |
2481 | *ppx = 0; |
2482 | *psx = 0; |
2483 | return (0); |
2484 | } |
2485 | } |
2486 | |
2487 | /* Stringify line and append to input buffer. Caller frees. */ |
2488 | static char * |
2489 | window_copy_stringify(struct grid *gd, u_int py, u_int first, u_int last, |
2490 | char *buf, u_int *size) |
2491 | { |
2492 | u_int ax, bx, newsize = *size; |
2493 | const struct grid_line *gl; |
2494 | const char *d; |
2495 | size_t bufsize = 1024, dlen; |
2496 | |
2497 | while (bufsize < newsize) |
2498 | bufsize *= 2; |
2499 | buf = xrealloc(buf, bufsize); |
2500 | |
2501 | gl = grid_peek_line(gd, py); |
2502 | bx = *size - 1; |
2503 | for (ax = first; ax < last; ax++) { |
2504 | d = window_copy_cellstring(gl, ax, &dlen); |
2505 | newsize += dlen; |
2506 | while (bufsize < newsize) { |
2507 | bufsize *= 2; |
2508 | buf = xrealloc(buf, bufsize); |
2509 | } |
2510 | if (dlen == 1) |
2511 | buf[bx++] = *d; |
2512 | else { |
2513 | memcpy(buf + bx, d, dlen); |
2514 | bx += dlen; |
2515 | } |
2516 | } |
2517 | buf[newsize - 1] = '\0'; |
2518 | |
2519 | *size = newsize; |
2520 | return (buf); |
2521 | } |
2522 | |
2523 | /* Map start of C string containing UTF-8 data to grid cell position. */ |
2524 | static void |
2525 | window_copy_cstrtocellpos(struct grid *gd, u_int ncells, u_int *ppx, u_int *ppy, |
2526 | const char *str) |
2527 | { |
2528 | u_int cell, ccell, px, pywrap, pos, len; |
2529 | int match; |
2530 | const struct grid_line *gl; |
2531 | const char *d; |
2532 | size_t dlen; |
2533 | struct { |
2534 | const char *d; |
2535 | size_t dlen; |
2536 | } *cells; |
2537 | |
2538 | /* Populate the array of cell data. */ |
2539 | cells = xreallocarray(NULL, ncells, sizeof cells[0]); |
2540 | cell = 0; |
2541 | px = *ppx; |
2542 | pywrap = *ppy; |
2543 | gl = grid_peek_line(gd, pywrap); |
2544 | while (cell < ncells) { |
2545 | cells[cell].d = window_copy_cellstring(gl, px, |
2546 | &cells[cell].dlen); |
2547 | cell++; |
2548 | px++; |
2549 | if (px == gd->sx) { |
2550 | px = 0; |
2551 | pywrap++; |
2552 | gl = grid_peek_line(gd, pywrap); |
2553 | } |
2554 | } |
2555 | |
2556 | /* Locate starting cell. */ |
2557 | cell = 0; |
2558 | len = strlen(str); |
2559 | while (cell < ncells) { |
2560 | ccell = cell; |
2561 | pos = 0; |
2562 | match = 1; |
2563 | while (ccell < ncells) { |
2564 | if (str[pos] == '\0') { |
2565 | match = 0; |
2566 | break; |
2567 | } |
2568 | d = cells[ccell].d; |
2569 | dlen = cells[ccell].dlen; |
2570 | if (dlen == 1) { |
2571 | if (str[pos] != *d) { |
2572 | match = 0; |
2573 | break; |
2574 | } |
2575 | pos++; |
2576 | } else { |
2577 | if (dlen > len - pos) |
2578 | dlen = len - pos; |
2579 | if (memcmp(str + pos, d, dlen) != 0) { |
2580 | match = 0; |
2581 | break; |
2582 | } |
2583 | pos += dlen; |
2584 | } |
2585 | ccell++; |
2586 | } |
2587 | if (match) |
2588 | break; |
2589 | cell++; |
2590 | } |
2591 | |
2592 | /* If not found this will be one past the end. */ |
2593 | px = *ppx + cell; |
2594 | pywrap = *ppy; |
2595 | while (px >= gd->sx) { |
2596 | px -= gd->sx; |
2597 | pywrap++; |
2598 | } |
2599 | |
2600 | *ppx = px; |
2601 | *ppy = pywrap; |
2602 | |
2603 | /* Free cell data. */ |
2604 | free(cells); |
2605 | } |
2606 | |
2607 | static void |
2608 | window_copy_move_left(struct screen *s, u_int *fx, u_int *fy, int wrapflag) |
2609 | { |
2610 | if (*fx == 0) { /* left */ |
2611 | if (*fy == 0) { /* top */ |
2612 | if (wrapflag) { |
2613 | *fx = screen_size_x(s) - 1; |
2614 | *fy = screen_hsize(s) + screen_size_y(s) - 1; |
2615 | } |
2616 | return; |
2617 | } |
2618 | *fx = screen_size_x(s) - 1; |
2619 | *fy = *fy - 1; |
2620 | } else |
2621 | *fx = *fx - 1; |
2622 | } |
2623 | |
2624 | static void |
2625 | window_copy_move_right(struct screen *s, u_int *fx, u_int *fy, int wrapflag) |
2626 | { |
2627 | if (*fx == screen_size_x(s) - 1) { /* right */ |
2628 | if (*fy == screen_hsize(s) + screen_size_y(s) - 1) { /* bottom */ |
2629 | if (wrapflag) { |
2630 | *fx = 0; |
2631 | *fy = 0; |
2632 | } |
2633 | return; |
2634 | } |
2635 | *fx = 0; |
2636 | *fy = *fy + 1; |
2637 | } else |
2638 | *fx = *fx + 1; |
2639 | } |
2640 | |
2641 | static int |
2642 | window_copy_is_lowercase(const char *ptr) |
2643 | { |
2644 | while (*ptr != '\0') { |
2645 | if (*ptr != tolower((u_char)*ptr)) |
2646 | return (0); |
2647 | ++ptr; |
2648 | } |
2649 | return (1); |
2650 | } |
2651 | |
2652 | /* |
2653 | * Search for text stored in sgd starting from position fx,fy up to endline. If |
2654 | * found, jump to it. If cis then ignore case. The direction is 0 for searching |
2655 | * up, down otherwise. If wrap then go to begin/end of grid and try again if |
2656 | * not found. |
2657 | */ |
2658 | static int |
2659 | window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, |
2660 | struct grid *sgd, u_int fx, u_int fy, u_int endline, int cis, int wrap, |
2661 | int direction, int regex) |
2662 | { |
2663 | u_int i, px, sx, ssize = 1; |
2664 | int found = 0, cflags = REG_EXTENDED; |
2665 | char *sbuf; |
2666 | regex_t reg; |
2667 | |
2668 | if (regex) { |
2669 | sbuf = xmalloc(ssize); |
2670 | sbuf[0] = '\0'; |
2671 | sbuf = window_copy_stringify(sgd, 0, 0, sgd->sx, sbuf, &ssize); |
2672 | if (cis) |
2673 | cflags |= REG_ICASE; |
2674 | if (regcomp(®, sbuf, cflags) != 0) { |
2675 | free(sbuf); |
2676 | return (0); |
2677 | } |
2678 | } |
2679 | |
2680 | if (direction) { |
2681 | for (i = fy; i <= endline; i++) { |
2682 | if (regex) { |
2683 | found = window_copy_search_lr_regex(gd, |
2684 | &px, &sx, i, fx, gd->sx, ®); |
2685 | } else { |
2686 | found = window_copy_search_lr(gd, sgd, |
2687 | &px, i, fx, gd->sx, cis); |
2688 | } |
2689 | if (found) |
2690 | break; |
2691 | fx = 0; |
2692 | } |
2693 | } else { |
2694 | for (i = fy + 1; endline < i; i--) { |
2695 | if (regex) { |
2696 | found = window_copy_search_rl_regex(gd, |
2697 | &px, &sx, i - 1, 0, fx + 1, ®); |
2698 | } else { |
2699 | found = window_copy_search_rl(gd, sgd, |
2700 | &px, i - 1, 0, fx + 1, cis); |
2701 | } |
2702 | if (found) { |
2703 | i--; |
2704 | break; |
2705 | } |
2706 | fx = gd->sx - 1; |
2707 | } |
2708 | } |
2709 | if (regex) { |
2710 | free(sbuf); |
2711 | regfree(®); |
2712 | } |
2713 | |
2714 | if (found) { |
2715 | window_copy_scroll_to(wme, px, i); |
2716 | return (1); |
2717 | } |
2718 | if (wrap) { |
2719 | return (window_copy_search_jump(wme, gd, sgd, |
2720 | direction ? 0 : gd->sx - 1, |
2721 | direction ? 0 : gd->hsize + gd->sy - 1, fy, cis, 0, |
2722 | direction, regex)); |
2723 | } |
2724 | return (0); |
2725 | } |
2726 | |
2727 | /* |
2728 | * Search in for text searchstr. If direction is 0 then search up, otherwise |
2729 | * down. |
2730 | */ |
2731 | static int |
2732 | window_copy_search(struct window_mode_entry *wme, int direction, int regex) |
2733 | { |
2734 | struct window_pane *wp = wme->wp; |
2735 | struct window_copy_mode_data *data = wme->data; |
2736 | struct screen *s = data->backing, ss; |
2737 | struct screen_write_ctx ctx; |
2738 | struct grid *gd = s->grid; |
2739 | const char *str = data->searchstr; |
2740 | u_int fx, fy, endline; |
2741 | int wrapflag, cis, found; |
2742 | |
2743 | if (regex && str[strcspn(str, "^$*+()?[].\\" )] == '\0') |
2744 | regex = 0; |
2745 | |
2746 | if (data->timeout) |
2747 | return (0); |
2748 | |
2749 | free(wp->searchstr); |
2750 | wp->searchstr = xstrdup(str); |
2751 | wp->searchregex = regex; |
2752 | |
2753 | fx = data->cx; |
2754 | fy = screen_hsize(data->backing) - data->oy + data->cy; |
2755 | |
2756 | screen_init(&ss, screen_write_strlen("%s" , str), 1, 0); |
2757 | screen_write_start(&ctx, NULL, &ss); |
2758 | screen_write_nputs(&ctx, -1, &grid_default_cell, "%s" , str); |
2759 | screen_write_stop(&ctx); |
2760 | |
2761 | wrapflag = options_get_number(wp->window->options, "wrap-search" ); |
2762 | cis = window_copy_is_lowercase(str); |
2763 | |
2764 | if (direction) { |
2765 | window_copy_move_right(s, &fx, &fy, wrapflag); |
2766 | endline = gd->hsize + gd->sy - 1; |
2767 | } else { |
2768 | window_copy_move_left(s, &fx, &fy, wrapflag); |
2769 | endline = 0; |
2770 | } |
2771 | |
2772 | found = window_copy_search_jump(wme, gd, ss.grid, fx, fy, endline, cis, |
2773 | wrapflag, direction, regex); |
2774 | |
2775 | if (window_copy_search_marks(wme, &ss, regex)) |
2776 | window_copy_redraw_screen(wme); |
2777 | |
2778 | screen_free(&ss); |
2779 | return (found); |
2780 | } |
2781 | |
2782 | static int |
2783 | window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, |
2784 | int regex) |
2785 | { |
2786 | struct window_copy_mode_data *data = wme->data; |
2787 | struct screen *s = data->backing, ss; |
2788 | struct screen_write_ctx ctx; |
2789 | struct grid *gd = s->grid; |
2790 | const struct grid_line *gl; |
2791 | int found, cis, which = -1; |
2792 | int cflags = REG_EXTENDED; |
2793 | u_int px, py, b, nfound = 0, width; |
2794 | u_int ssize = 1; |
2795 | char *sbuf; |
2796 | regex_t reg; |
2797 | time_t tstart, t; |
2798 | |
2799 | if (ssp == NULL) { |
2800 | width = screen_write_strlen("%s" , data->searchstr); |
2801 | screen_init(&ss, width, 1, 0); |
2802 | screen_write_start(&ctx, NULL, &ss); |
2803 | screen_write_nputs(&ctx, -1, &grid_default_cell, "%s" , |
2804 | data->searchstr); |
2805 | screen_write_stop(&ctx); |
2806 | ssp = &ss; |
2807 | } else |
2808 | width = screen_size_x(ssp); |
2809 | |
2810 | cis = window_copy_is_lowercase(data->searchstr); |
2811 | |
2812 | free(data->searchmark); |
2813 | data->searchmark = bit_alloc((gd->hsize + gd->sy) * gd->sx); |
2814 | |
2815 | if (regex) { |
2816 | sbuf = xmalloc(ssize); |
2817 | sbuf[0] = '\0'; |
2818 | sbuf = window_copy_stringify(ssp->grid, 0, 0, ssp->grid->sx, |
2819 | sbuf, &ssize); |
2820 | if (cis) |
2821 | cflags |= REG_ICASE; |
2822 | if (regcomp(®, sbuf, cflags) != 0) { |
2823 | free(sbuf); |
2824 | return (0); |
2825 | } |
2826 | } |
2827 | time(&tstart); |
2828 | for (py = gd->hsize - data->oy; py > 0; py--) { |
2829 | gl = grid_peek_line(gd, py - 1); |
2830 | if (~gl->flags & GRID_LINE_WRAPPED) |
2831 | break; |
2832 | } |
2833 | for (; py < gd->hsize - data->oy + gd->sy; py++) { |
2834 | px = 0; |
2835 | for (;;) { |
2836 | if (regex) { |
2837 | found = window_copy_search_lr_regex(gd, |
2838 | &px, &width, py, px, gd->sx, ®); |
2839 | if (!found) |
2840 | break; |
2841 | } else { |
2842 | found = window_copy_search_lr(gd, ssp->grid, |
2843 | &px, py, px, gd->sx, cis); |
2844 | if (!found) |
2845 | break; |
2846 | } |
2847 | |
2848 | nfound++; |
2849 | if (px == data->cx && |
2850 | py == gd->hsize + data->cy - data->oy) |
2851 | which = nfound; |
2852 | |
2853 | b = (py * gd->sx) + px; |
2854 | bit_nset(data->searchmark, b, b + width - 1); |
2855 | |
2856 | px++; |
2857 | } |
2858 | |
2859 | time(&t); |
2860 | if (t - tstart > WINDOW_COPY_SEARCH_TIMEOUT) { |
2861 | data->timeout = 1; |
2862 | break; |
2863 | } |
2864 | } |
2865 | if (regex) { |
2866 | free(sbuf); |
2867 | regfree(®); |
2868 | } |
2869 | if (data->timeout) { |
2870 | window_copy_clear_marks(wme); |
2871 | return (1); |
2872 | } |
2873 | |
2874 | if (which != -1) |
2875 | data->searchthis = 1 + nfound - which; |
2876 | else |
2877 | data->searchthis = -1; |
2878 | data->searchcount = nfound; |
2879 | |
2880 | if (ssp == &ss) |
2881 | screen_free(&ss); |
2882 | return (1); |
2883 | } |
2884 | |
2885 | static void |
2886 | window_copy_clear_marks(struct window_mode_entry *wme) |
2887 | { |
2888 | struct window_copy_mode_data *data = wme->data; |
2889 | |
2890 | free(data->searchmark); |
2891 | data->searchmark = NULL; |
2892 | } |
2893 | |
2894 | static int |
2895 | window_copy_search_up(struct window_mode_entry *wme, int regex) |
2896 | { |
2897 | return (window_copy_search(wme, 0, regex)); |
2898 | } |
2899 | |
2900 | static int |
2901 | window_copy_search_down(struct window_mode_entry *wme, int regex) |
2902 | { |
2903 | return (window_copy_search(wme, 1, regex)); |
2904 | } |
2905 | |
2906 | static void |
2907 | window_copy_goto_line(struct window_mode_entry *wme, const char *linestr) |
2908 | { |
2909 | struct window_copy_mode_data *data = wme->data; |
2910 | const char *errstr; |
2911 | int lineno; |
2912 | |
2913 | lineno = strtonum(linestr, -1, INT_MAX, &errstr); |
2914 | if (errstr != NULL) |
2915 | return; |
2916 | if (lineno < 0 || (u_int)lineno > screen_hsize(data->backing)) |
2917 | lineno = screen_hsize(data->backing); |
2918 | |
2919 | data->oy = lineno; |
2920 | window_copy_update_selection(wme, 1, 0); |
2921 | window_copy_redraw_screen(wme); |
2922 | } |
2923 | |
2924 | static void |
2925 | window_copy_write_line(struct window_mode_entry *wme, |
2926 | struct screen_write_ctx *ctx, u_int py) |
2927 | { |
2928 | struct window_pane *wp = wme->wp; |
2929 | struct window_copy_mode_data *data = wme->data; |
2930 | struct screen *s = &data->screen; |
2931 | struct options *oo = wp->window->options; |
2932 | struct grid_cell gc; |
2933 | char hdr[512]; |
2934 | size_t size = 0; |
2935 | |
2936 | style_apply(&gc, oo, "mode-style" ); |
2937 | gc.flags |= GRID_FLAG_NOPALETTE; |
2938 | |
2939 | if (py == 0 && s->rupper < s->rlower && !data->hide_position) { |
2940 | if (data->searchmark == NULL) { |
2941 | if (data->timeout) { |
2942 | size = xsnprintf(hdr, sizeof hdr, |
2943 | "(timed out) [%u/%u]" , data->oy, |
2944 | screen_hsize(data->backing)); |
2945 | } else { |
2946 | size = xsnprintf(hdr, sizeof hdr, |
2947 | "[%u/%u]" , data->oy, |
2948 | screen_hsize(data->backing)); |
2949 | } |
2950 | } else { |
2951 | if (data->searchthis == -1) { |
2952 | size = xsnprintf(hdr, sizeof hdr, |
2953 | "(%u results) [%d/%u]" , data->searchcount, |
2954 | data->oy, screen_hsize(data->backing)); |
2955 | } else { |
2956 | size = xsnprintf(hdr, sizeof hdr, |
2957 | "(%u/%u results) [%d/%u]" , data->searchthis, |
2958 | data->searchcount, data->oy, |
2959 | screen_hsize(data->backing)); |
2960 | } |
2961 | } |
2962 | if (size > screen_size_x(s)) |
2963 | size = screen_size_x(s); |
2964 | screen_write_cursormove(ctx, screen_size_x(s) - size, 0, 0); |
2965 | screen_write_puts(ctx, &gc, "%s" , hdr); |
2966 | } else |
2967 | size = 0; |
2968 | |
2969 | if (size < screen_size_x(s)) { |
2970 | screen_write_cursormove(ctx, 0, py, 0); |
2971 | screen_write_copy(ctx, data->backing, 0, |
2972 | (screen_hsize(data->backing) - data->oy) + py, |
2973 | screen_size_x(s) - size, 1, data->searchmark, &gc); |
2974 | } |
2975 | |
2976 | if (py == data->cy && data->cx == screen_size_x(s)) { |
2977 | memcpy(&gc, &grid_default_cell, sizeof gc); |
2978 | screen_write_cursormove(ctx, screen_size_x(s) - 1, py, 0); |
2979 | screen_write_putc(ctx, &gc, '$'); |
2980 | } |
2981 | } |
2982 | |
2983 | static void |
2984 | window_copy_write_lines(struct window_mode_entry *wme, |
2985 | struct screen_write_ctx *ctx, u_int py, u_int ny) |
2986 | { |
2987 | u_int yy; |
2988 | |
2989 | for (yy = py; yy < py + ny; yy++) |
2990 | window_copy_write_line(wme, ctx, py); |
2991 | } |
2992 | |
2993 | static void |
2994 | window_copy_redraw_selection(struct window_mode_entry *wme, u_int old_y) |
2995 | { |
2996 | struct window_copy_mode_data *data = wme->data; |
2997 | struct grid *gd = data->backing->grid; |
2998 | u_int new_y, start, end; |
2999 | |
3000 | new_y = data->cy; |
3001 | if (old_y <= new_y) { |
3002 | start = old_y; |
3003 | end = new_y; |
3004 | } else { |
3005 | start = new_y; |
3006 | end = old_y; |
3007 | } |
3008 | |
3009 | /* |
3010 | * In word selection mode the first word on the line below the cursor |
3011 | * might be selected, so add this line to the redraw area. |
3012 | */ |
3013 | if (data->selflag == SEL_WORD) { |
3014 | /* Last grid line in data coordinates. */ |
3015 | if (end < gd->sy + data->oy - 1) |
3016 | end++; |
3017 | } |
3018 | window_copy_redraw_lines(wme, start, end - start + 1); |
3019 | } |
3020 | |
3021 | static void |
3022 | window_copy_redraw_lines(struct window_mode_entry *wme, u_int py, u_int ny) |
3023 | { |
3024 | struct window_pane *wp = wme->wp; |
3025 | struct window_copy_mode_data *data = wme->data; |
3026 | struct screen_write_ctx ctx; |
3027 | u_int i; |
3028 | |
3029 | screen_write_start(&ctx, wp, NULL); |
3030 | for (i = py; i < py + ny; i++) |
3031 | window_copy_write_line(wme, &ctx, i); |
3032 | screen_write_cursormove(&ctx, data->cx, data->cy, 0); |
3033 | screen_write_stop(&ctx); |
3034 | } |
3035 | |
3036 | static void |
3037 | window_copy_redraw_screen(struct window_mode_entry *wme) |
3038 | { |
3039 | struct window_copy_mode_data *data = wme->data; |
3040 | |
3041 | window_copy_redraw_lines(wme, 0, screen_size_y(&data->screen)); |
3042 | } |
3043 | |
3044 | static void |
3045 | window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin, |
3046 | int no_reset) |
3047 | { |
3048 | struct window_copy_mode_data *data = wme->data; |
3049 | u_int xx, yy; |
3050 | |
3051 | yy = screen_hsize(data->backing) + data->cy - data->oy; |
3052 | switch (data->selflag) { |
3053 | case SEL_WORD: |
3054 | xx = data->cx; |
3055 | if (no_reset) |
3056 | break; |
3057 | begin = 0; |
3058 | if (data->dy > yy || (data->dy == yy && data->dx > xx)) { |
3059 | /* Right to left selection. */ |
3060 | window_copy_cursor_previous_word_pos(wme, data->ws, 0, |
3061 | &xx, &yy); |
3062 | begin = 1; |
3063 | |
3064 | /* Reset the end. */ |
3065 | data->endselx = data->endselrx; |
3066 | data->endsely = data->endselry; |
3067 | } else { |
3068 | /* Left to right selection. */ |
3069 | if (xx >= window_copy_find_length(wme, yy) || |
3070 | !window_copy_in_set(wme, xx + 1, yy, data->ws)) |
3071 | window_copy_cursor_next_word_end_pos(wme, |
3072 | data->ws, &xx, &yy); |
3073 | |
3074 | /* Reset the start. */ |
3075 | data->selx = data->selrx; |
3076 | data->sely = data->selry; |
3077 | } |
3078 | break; |
3079 | case SEL_LINE: |
3080 | if (no_reset) { |
3081 | xx = data->cx; |
3082 | break; |
3083 | } |
3084 | begin = 0; |
3085 | if (data->dy > yy) { |
3086 | /* Right to left selection. */ |
3087 | xx = 0; |
3088 | begin = 1; |
3089 | |
3090 | /* Reset the end. */ |
3091 | data->endselx = data->endselrx; |
3092 | data->endsely = data->endselry; |
3093 | } else { |
3094 | /* Left to right selection. */ |
3095 | xx = window_copy_find_length(wme, yy); |
3096 | |
3097 | /* Reset the start. */ |
3098 | data->selx = data->selrx; |
3099 | data->sely = data->selry; |
3100 | } |
3101 | break; |
3102 | case SEL_CHAR: |
3103 | xx = data->cx; |
3104 | break; |
3105 | } |
3106 | if (begin) { |
3107 | data->selx = xx; |
3108 | data->sely = yy; |
3109 | } else { |
3110 | data->endselx = xx; |
3111 | data->endsely = yy; |
3112 | } |
3113 | } |
3114 | |
3115 | static void |
3116 | window_copy_synchronize_cursor(struct window_mode_entry *wme, int no_reset) |
3117 | { |
3118 | struct window_copy_mode_data *data = wme->data; |
3119 | |
3120 | switch (data->cursordrag) { |
3121 | case CURSORDRAG_ENDSEL: |
3122 | window_copy_synchronize_cursor_end(wme, 0, no_reset); |
3123 | break; |
3124 | case CURSORDRAG_SEL: |
3125 | window_copy_synchronize_cursor_end(wme, 1, no_reset); |
3126 | break; |
3127 | case CURSORDRAG_NONE: |
3128 | break; |
3129 | } |
3130 | } |
3131 | |
3132 | static void |
3133 | window_copy_update_cursor(struct window_mode_entry *wme, u_int cx, u_int cy) |
3134 | { |
3135 | struct window_pane *wp = wme->wp; |
3136 | struct window_copy_mode_data *data = wme->data; |
3137 | struct screen *s = &data->screen; |
3138 | struct screen_write_ctx ctx; |
3139 | u_int old_cx, old_cy; |
3140 | |
3141 | old_cx = data->cx; old_cy = data->cy; |
3142 | data->cx = cx; data->cy = cy; |
3143 | if (old_cx == screen_size_x(s)) |
3144 | window_copy_redraw_lines(wme, old_cy, 1); |
3145 | if (data->cx == screen_size_x(s)) |
3146 | window_copy_redraw_lines(wme, data->cy, 1); |
3147 | else { |
3148 | screen_write_start(&ctx, wp, NULL); |
3149 | screen_write_cursormove(&ctx, data->cx, data->cy, 0); |
3150 | screen_write_stop(&ctx); |
3151 | } |
3152 | } |
3153 | |
3154 | static void |
3155 | window_copy_start_selection(struct window_mode_entry *wme) |
3156 | { |
3157 | struct window_copy_mode_data *data = wme->data; |
3158 | |
3159 | data->selx = data->cx; |
3160 | data->sely = screen_hsize(data->backing) + data->cy - data->oy; |
3161 | |
3162 | data->endselx = data->selx; |
3163 | data->endsely = data->sely; |
3164 | |
3165 | data->cursordrag = CURSORDRAG_ENDSEL; |
3166 | |
3167 | window_copy_set_selection(wme, 1, 0); |
3168 | } |
3169 | |
3170 | static int |
3171 | window_copy_adjust_selection(struct window_mode_entry *wme, u_int *selx, |
3172 | u_int *sely) |
3173 | { |
3174 | struct window_copy_mode_data *data = wme->data; |
3175 | struct screen *s = &data->screen; |
3176 | u_int sx, sy, ty; |
3177 | int relpos; |
3178 | |
3179 | sx = *selx; |
3180 | sy = *sely; |
3181 | |
3182 | ty = screen_hsize(data->backing) - data->oy; |
3183 | if (sy < ty) { |
3184 | relpos = WINDOW_COPY_REL_POS_ABOVE; |
3185 | if (!data->rectflag) |
3186 | sx = 0; |
3187 | sy = 0; |
3188 | } else if (sy > ty + screen_size_y(s) - 1) { |
3189 | relpos = WINDOW_COPY_REL_POS_BELOW; |
3190 | if (!data->rectflag) |
3191 | sx = screen_size_x(s) - 1; |
3192 | sy = screen_size_y(s) - 1; |
3193 | } else { |
3194 | relpos = WINDOW_COPY_REL_POS_ON_SCREEN; |
3195 | sy -= ty; |
3196 | } |
3197 | |
3198 | *selx = sx; |
3199 | *sely = sy; |
3200 | return (relpos); |
3201 | } |
3202 | |
3203 | static int |
3204 | window_copy_update_selection(struct window_mode_entry *wme, int may_redraw, |
3205 | int no_reset) |
3206 | { |
3207 | struct window_copy_mode_data *data = wme->data; |
3208 | struct screen *s = &data->screen; |
3209 | |
3210 | if (s->sel == NULL && data->lineflag == LINE_SEL_NONE) |
3211 | return (0); |
3212 | return (window_copy_set_selection(wme, may_redraw, no_reset)); |
3213 | } |
3214 | |
3215 | static int |
3216 | window_copy_set_selection(struct window_mode_entry *wme, int may_redraw, |
3217 | int no_reset) |
3218 | { |
3219 | struct window_pane *wp = wme->wp; |
3220 | struct window_copy_mode_data *data = wme->data; |
3221 | struct screen *s = &data->screen; |
3222 | struct options *oo = wp->window->options; |
3223 | struct grid_cell gc; |
3224 | u_int sx, sy, cy, endsx, endsy; |
3225 | int startrelpos, endrelpos; |
3226 | |
3227 | window_copy_synchronize_cursor(wme, no_reset); |
3228 | |
3229 | /* Adjust the selection. */ |
3230 | sx = data->selx; |
3231 | sy = data->sely; |
3232 | startrelpos = window_copy_adjust_selection(wme, &sx, &sy); |
3233 | |
3234 | /* Adjust the end of selection. */ |
3235 | endsx = data->endselx; |
3236 | endsy = data->endsely; |
3237 | endrelpos = window_copy_adjust_selection(wme, &endsx, &endsy); |
3238 | |
3239 | /* Selection is outside of the current screen */ |
3240 | if (startrelpos == endrelpos && |
3241 | startrelpos != WINDOW_COPY_REL_POS_ON_SCREEN) { |
3242 | screen_hide_selection(s); |
3243 | return (0); |
3244 | } |
3245 | |
3246 | /* Set colours and selection. */ |
3247 | style_apply(&gc, oo, "mode-style" ); |
3248 | gc.flags |= GRID_FLAG_NOPALETTE; |
3249 | screen_set_selection(s, sx, sy, endsx, endsy, data->rectflag, |
3250 | data->modekeys, &gc); |
3251 | |
3252 | if (data->rectflag && may_redraw) { |
3253 | /* |
3254 | * Can't rely on the caller to redraw the right lines for |
3255 | * rectangle selection - find the highest line and the number |
3256 | * of lines, and redraw just past that in both directions |
3257 | */ |
3258 | cy = data->cy; |
3259 | if (data->cursordrag == CURSORDRAG_ENDSEL) { |
3260 | if (sy < cy) |
3261 | window_copy_redraw_lines(wme, sy, cy - sy + 1); |
3262 | else |
3263 | window_copy_redraw_lines(wme, cy, sy - cy + 1); |
3264 | } else { |
3265 | if (endsy < cy) { |
3266 | window_copy_redraw_lines(wme, endsy, |
3267 | cy - endsy + 1); |
3268 | } else { |
3269 | window_copy_redraw_lines(wme, cy, |
3270 | endsy - cy + 1); |
3271 | } |
3272 | } |
3273 | } |
3274 | |
3275 | return (1); |
3276 | } |
3277 | |
3278 | static void * |
3279 | window_copy_get_selection(struct window_mode_entry *wme, size_t *len) |
3280 | { |
3281 | struct window_pane *wp = wme->wp; |
3282 | struct window_copy_mode_data *data = wme->data; |
3283 | struct screen *s = &data->screen; |
3284 | char *buf; |
3285 | size_t off; |
3286 | u_int i, xx, yy, sx, sy, ex, ey, ey_last; |
3287 | u_int firstsx, lastex, restex, restsx, selx; |
3288 | int keys; |
3289 | |
3290 | if (data->screen.sel == NULL && data->lineflag == LINE_SEL_NONE) |
3291 | return (NULL); |
3292 | |
3293 | buf = xmalloc(1); |
3294 | off = 0; |
3295 | |
3296 | *buf = '\0'; |
3297 | |
3298 | /* |
3299 | * The selection extends from selx,sely to (adjusted) cx,cy on |
3300 | * the base screen. |
3301 | */ |
3302 | |
3303 | /* Find start and end. */ |
3304 | xx = data->endselx; |
3305 | yy = data->endsely; |
3306 | if (yy < data->sely || (yy == data->sely && xx < data->selx)) { |
3307 | sx = xx; sy = yy; |
3308 | ex = data->selx; ey = data->sely; |
3309 | } else { |
3310 | sx = data->selx; sy = data->sely; |
3311 | ex = xx; ey = yy; |
3312 | } |
3313 | |
3314 | /* Trim ex to end of line. */ |
3315 | ey_last = window_copy_find_length(wme, ey); |
3316 | if (ex > ey_last) |
3317 | ex = ey_last; |
3318 | |
3319 | /* |
3320 | * Deal with rectangle-copy if necessary; four situations: start of |
3321 | * first line (firstsx), end of last line (lastex), start (restsx) and |
3322 | * end (restex) of all other lines. |
3323 | */ |
3324 | xx = screen_size_x(s); |
3325 | |
3326 | /* |
3327 | * Behave according to mode-keys. If it is emacs, copy like emacs, |
3328 | * keeping the top-left-most character, and dropping the |
3329 | * bottom-right-most, regardless of copy direction. If it is vi, also |
3330 | * keep bottom-right-most character. |
3331 | */ |
3332 | keys = options_get_number(wp->window->options, "mode-keys" ); |
3333 | if (data->rectflag) { |
3334 | /* |
3335 | * Need to ignore the column with the cursor in it, which for |
3336 | * rectangular copy means knowing which side the cursor is on. |
3337 | */ |
3338 | if (data->cursordrag == CURSORDRAG_ENDSEL) |
3339 | selx = data->selx; |
3340 | else |
3341 | selx = data->endselx; |
3342 | if (selx < data->cx) { |
3343 | /* Selection start is on the left. */ |
3344 | if (keys == MODEKEY_EMACS) { |
3345 | lastex = data->cx; |
3346 | restex = data->cx; |
3347 | } |
3348 | else { |
3349 | lastex = data->cx + 1; |
3350 | restex = data->cx + 1; |
3351 | } |
3352 | firstsx = selx; |
3353 | restsx = selx; |
3354 | } else { |
3355 | /* Cursor is on the left. */ |
3356 | lastex = selx + 1; |
3357 | restex = selx + 1; |
3358 | firstsx = data->cx; |
3359 | restsx = data->cx; |
3360 | } |
3361 | } else { |
3362 | if (keys == MODEKEY_EMACS) |
3363 | lastex = ex; |
3364 | else |
3365 | lastex = ex + 1; |
3366 | restex = xx; |
3367 | firstsx = sx; |
3368 | restsx = 0; |
3369 | } |
3370 | |
3371 | /* Copy the lines. */ |
3372 | for (i = sy; i <= ey; i++) { |
3373 | window_copy_copy_line(wme, &buf, &off, i, |
3374 | (i == sy ? firstsx : restsx), |
3375 | (i == ey ? lastex : restex)); |
3376 | } |
3377 | |
3378 | /* Don't bother if no data. */ |
3379 | if (off == 0) { |
3380 | free(buf); |
3381 | return (NULL); |
3382 | } |
3383 | if (keys == MODEKEY_EMACS || lastex <= ey_last) |
3384 | off -= 1; /* remove final \n (unless at end in vi mode) */ |
3385 | *len = off; |
3386 | return (buf); |
3387 | } |
3388 | |
3389 | static void |
3390 | window_copy_copy_buffer(struct window_mode_entry *wme, const char *prefix, |
3391 | void *buf, size_t len) |
3392 | { |
3393 | struct window_pane *wp = wme->wp; |
3394 | struct screen_write_ctx ctx; |
3395 | |
3396 | if (options_get_number(global_options, "set-clipboard" ) != 0) { |
3397 | screen_write_start(&ctx, wp, NULL); |
3398 | screen_write_setselection(&ctx, buf, len); |
3399 | screen_write_stop(&ctx); |
3400 | notify_pane("pane-set-clipboard" , wp); |
3401 | } |
3402 | |
3403 | paste_add(prefix, buf, len); |
3404 | } |
3405 | |
3406 | static void |
3407 | window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s, |
3408 | const char *prefix, const char *cmd) |
3409 | { |
3410 | void *buf; |
3411 | size_t len; |
3412 | struct job *job; |
3413 | |
3414 | buf = window_copy_get_selection(wme, &len); |
3415 | if (buf == NULL) |
3416 | return; |
3417 | |
3418 | job = job_run(cmd, s, NULL, NULL, NULL, NULL, NULL, JOB_NOWAIT); |
3419 | bufferevent_write(job_get_event(job), buf, len); |
3420 | window_copy_copy_buffer(wme, prefix, buf, len); |
3421 | } |
3422 | |
3423 | static void |
3424 | window_copy_copy_selection(struct window_mode_entry *wme, const char *prefix) |
3425 | { |
3426 | char *buf; |
3427 | size_t len; |
3428 | |
3429 | buf = window_copy_get_selection(wme, &len); |
3430 | if (buf != NULL) |
3431 | window_copy_copy_buffer(wme, prefix, buf, len); |
3432 | } |
3433 | |
3434 | static void |
3435 | window_copy_append_selection(struct window_mode_entry *wme) |
3436 | { |
3437 | struct window_pane *wp = wme->wp; |
3438 | char *buf; |
3439 | struct paste_buffer *pb; |
3440 | const char *bufdata, *bufname = NULL; |
3441 | size_t len, bufsize; |
3442 | struct screen_write_ctx ctx; |
3443 | |
3444 | buf = window_copy_get_selection(wme, &len); |
3445 | if (buf == NULL) |
3446 | return; |
3447 | |
3448 | if (options_get_number(global_options, "set-clipboard" ) != 0) { |
3449 | screen_write_start(&ctx, wp, NULL); |
3450 | screen_write_setselection(&ctx, buf, len); |
3451 | screen_write_stop(&ctx); |
3452 | notify_pane("pane-set-clipboard" , wp); |
3453 | } |
3454 | |
3455 | pb = paste_get_top(&bufname); |
3456 | if (pb != NULL) { |
3457 | bufdata = paste_buffer_data(pb, &bufsize); |
3458 | buf = xrealloc(buf, len + bufsize); |
3459 | memmove(buf + bufsize, buf, len); |
3460 | memcpy(buf, bufdata, bufsize); |
3461 | len += bufsize; |
3462 | } |
3463 | if (paste_set(buf, len, bufname, NULL) != 0) |
3464 | free(buf); |
3465 | } |
3466 | |
3467 | static void |
3468 | window_copy_copy_line(struct window_mode_entry *wme, char **buf, size_t *off, |
3469 | u_int sy, u_int sx, u_int ex) |
3470 | { |
3471 | struct window_copy_mode_data *data = wme->data; |
3472 | struct grid *gd = data->backing->grid; |
3473 | struct grid_cell gc; |
3474 | struct grid_line *gl; |
3475 | struct utf8_data ud; |
3476 | u_int i, xx, wrapped = 0; |
3477 | const char *s; |
3478 | |
3479 | if (sx > ex) |
3480 | return; |
3481 | |
3482 | /* |
3483 | * Work out if the line was wrapped at the screen edge and all of it is |
3484 | * on screen. |
3485 | */ |
3486 | gl = grid_get_line(gd, sy); |
3487 | if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx) |
3488 | wrapped = 1; |
3489 | |
3490 | /* If the line was wrapped, don't strip spaces (use the full length). */ |
3491 | if (wrapped) |
3492 | xx = gl->cellsize; |
3493 | else |
3494 | xx = window_copy_find_length(wme, sy); |
3495 | if (ex > xx) |
3496 | ex = xx; |
3497 | if (sx > xx) |
3498 | sx = xx; |
3499 | |
3500 | if (sx < ex) { |
3501 | for (i = sx; i < ex; i++) { |
3502 | grid_get_cell(gd, i, sy, &gc); |
3503 | if (gc.flags & GRID_FLAG_PADDING) |
3504 | continue; |
3505 | utf8_copy(&ud, &gc.data); |
3506 | if (ud.size == 1 && (gc.attr & GRID_ATTR_CHARSET)) { |
3507 | s = tty_acs_get(NULL, ud.data[0]); |
3508 | if (s != NULL && strlen(s) <= sizeof ud.data) { |
3509 | ud.size = strlen(s); |
3510 | memcpy(ud.data, s, ud.size); |
3511 | } |
3512 | } |
3513 | |
3514 | *buf = xrealloc(*buf, (*off) + ud.size); |
3515 | memcpy(*buf + *off, ud.data, ud.size); |
3516 | *off += ud.size; |
3517 | } |
3518 | } |
3519 | |
3520 | /* Only add a newline if the line wasn't wrapped. */ |
3521 | if (!wrapped || ex != xx) { |
3522 | *buf = xrealloc(*buf, (*off) + 1); |
3523 | (*buf)[(*off)++] = '\n'; |
3524 | } |
3525 | } |
3526 | |
3527 | static void |
3528 | window_copy_clear_selection(struct window_mode_entry *wme) |
3529 | { |
3530 | struct window_copy_mode_data *data = wme->data; |
3531 | u_int px, py; |
3532 | |
3533 | screen_clear_selection(&data->screen); |
3534 | |
3535 | data->cursordrag = CURSORDRAG_NONE; |
3536 | data->lineflag = LINE_SEL_NONE; |
3537 | data->selflag = SEL_CHAR; |
3538 | |
3539 | py = screen_hsize(data->backing) + data->cy - data->oy; |
3540 | px = window_copy_find_length(wme, py); |
3541 | if (data->cx > px) |
3542 | window_copy_update_cursor(wme, px, data->cy); |
3543 | } |
3544 | |
3545 | static int |
3546 | window_copy_in_set(struct window_mode_entry *wme, u_int px, u_int py, |
3547 | const char *set) |
3548 | { |
3549 | struct window_copy_mode_data *data = wme->data; |
3550 | struct grid_cell gc; |
3551 | |
3552 | grid_get_cell(data->backing->grid, px, py, &gc); |
3553 | if (gc.flags & GRID_FLAG_PADDING) |
3554 | return (0); |
3555 | return (utf8_cstrhas(set, &gc.data)); |
3556 | } |
3557 | |
3558 | static u_int |
3559 | window_copy_find_length(struct window_mode_entry *wme, u_int py) |
3560 | { |
3561 | struct window_copy_mode_data *data = wme->data; |
3562 | |
3563 | return (grid_line_length(data->backing->grid, py)); |
3564 | } |
3565 | |
3566 | static void |
3567 | window_copy_cursor_start_of_line(struct window_mode_entry *wme) |
3568 | { |
3569 | struct window_copy_mode_data *data = wme->data; |
3570 | struct screen *back_s = data->backing; |
3571 | struct grid *gd = back_s->grid; |
3572 | u_int py; |
3573 | |
3574 | if (data->cx == 0 && data->lineflag == LINE_SEL_NONE) { |
3575 | py = screen_hsize(back_s) + data->cy - data->oy; |
3576 | while (py > 0 && |
3577 | grid_get_line(gd, py - 1)->flags & GRID_LINE_WRAPPED) { |
3578 | window_copy_cursor_up(wme, 0); |
3579 | py = screen_hsize(back_s) + data->cy - data->oy; |
3580 | } |
3581 | } |
3582 | window_copy_update_cursor(wme, 0, data->cy); |
3583 | if (window_copy_update_selection(wme, 1, 0)) |
3584 | window_copy_redraw_lines(wme, data->cy, 1); |
3585 | } |
3586 | |
3587 | static void |
3588 | window_copy_cursor_back_to_indentation(struct window_mode_entry *wme) |
3589 | { |
3590 | struct window_copy_mode_data *data = wme->data; |
3591 | u_int px, py, xx; |
3592 | struct grid_cell gc; |
3593 | |
3594 | px = 0; |
3595 | py = screen_hsize(data->backing) + data->cy - data->oy; |
3596 | xx = window_copy_find_length(wme, py); |
3597 | |
3598 | while (px < xx) { |
3599 | grid_get_cell(data->backing->grid, px, py, &gc); |
3600 | if (gc.data.size != 1 || *gc.data.data != ' ') |
3601 | break; |
3602 | px++; |
3603 | } |
3604 | |
3605 | window_copy_update_cursor(wme, px, data->cy); |
3606 | if (window_copy_update_selection(wme, 1, 0)) |
3607 | window_copy_redraw_lines(wme, data->cy, 1); |
3608 | } |
3609 | |
3610 | static void |
3611 | window_copy_cursor_end_of_line(struct window_mode_entry *wme) |
3612 | { |
3613 | struct window_copy_mode_data *data = wme->data; |
3614 | struct screen *back_s = data->backing; |
3615 | struct grid *gd = back_s->grid; |
3616 | struct grid_line *gl; |
3617 | u_int px, py; |
3618 | |
3619 | py = screen_hsize(back_s) + data->cy - data->oy; |
3620 | px = window_copy_find_length(wme, py); |
3621 | |
3622 | if (data->cx == px && data->lineflag == LINE_SEL_NONE) { |
3623 | if (data->screen.sel != NULL && data->rectflag) |
3624 | px = screen_size_x(back_s); |
3625 | gl = grid_get_line(gd, py); |
3626 | if (gl->flags & GRID_LINE_WRAPPED) { |
3627 | while (py < gd->sy + gd->hsize) { |
3628 | gl = grid_get_line(gd, py); |
3629 | if (~gl->flags & GRID_LINE_WRAPPED) |
3630 | break; |
3631 | window_copy_cursor_down(wme, 0); |
3632 | py = screen_hsize(back_s) + data->cy - data->oy; |
3633 | } |
3634 | px = window_copy_find_length(wme, py); |
3635 | } |
3636 | } |
3637 | window_copy_update_cursor(wme, px, data->cy); |
3638 | |
3639 | if (window_copy_update_selection(wme, 1, 0)) |
3640 | window_copy_redraw_lines(wme, data->cy, 1); |
3641 | } |
3642 | |
3643 | static void |
3644 | window_copy_other_end(struct window_mode_entry *wme) |
3645 | { |
3646 | struct window_copy_mode_data *data = wme->data; |
3647 | struct screen *s = &data->screen; |
3648 | u_int selx, sely, cy, yy, hsize; |
3649 | |
3650 | if (s->sel == NULL && data->lineflag == LINE_SEL_NONE) |
3651 | return; |
3652 | |
3653 | if (data->lineflag == LINE_SEL_LEFT_RIGHT) |
3654 | data->lineflag = LINE_SEL_RIGHT_LEFT; |
3655 | else if (data->lineflag == LINE_SEL_RIGHT_LEFT) |
3656 | data->lineflag = LINE_SEL_LEFT_RIGHT; |
3657 | |
3658 | switch (data->cursordrag) { |
3659 | case CURSORDRAG_NONE: |
3660 | case CURSORDRAG_SEL: |
3661 | data->cursordrag = CURSORDRAG_ENDSEL; |
3662 | break; |
3663 | case CURSORDRAG_ENDSEL: |
3664 | data->cursordrag = CURSORDRAG_SEL; |
3665 | break; |
3666 | } |
3667 | |
3668 | selx = data->endselx; |
3669 | sely = data->endsely; |
3670 | if (data->cursordrag == CURSORDRAG_SEL) { |
3671 | selx = data->selx; |
3672 | sely = data->sely; |
3673 | } |
3674 | |
3675 | cy = data->cy; |
3676 | yy = screen_hsize(data->backing) + data->cy - data->oy; |
3677 | |
3678 | data->cx = selx; |
3679 | |
3680 | hsize = screen_hsize(data->backing); |
3681 | if (sely < hsize - data->oy) { /* above */ |
3682 | data->oy = hsize - sely; |
3683 | data->cy = 0; |
3684 | } else if (sely > hsize - data->oy + screen_size_y(s)) { /* below */ |
3685 | data->oy = hsize - sely + screen_size_y(s) - 1; |
3686 | data->cy = screen_size_y(s) - 1; |
3687 | } else |
3688 | data->cy = cy + sely - yy; |
3689 | |
3690 | window_copy_update_selection(wme, 1, 1); |
3691 | window_copy_redraw_screen(wme); |
3692 | } |
3693 | |
3694 | static void |
3695 | window_copy_cursor_left(struct window_mode_entry *wme) |
3696 | { |
3697 | struct window_copy_mode_data *data = wme->data; |
3698 | u_int py, cx; |
3699 | struct grid_cell gc; |
3700 | |
3701 | py = screen_hsize(data->backing) + data->cy - data->oy; |
3702 | cx = data->cx; |
3703 | while (cx > 0) { |
3704 | grid_get_cell(data->backing->grid, cx, py, &gc); |
3705 | if (~gc.flags & GRID_FLAG_PADDING) |
3706 | break; |
3707 | cx--; |
3708 | } |
3709 | if (cx == 0 && py > 0) { |
3710 | window_copy_cursor_up(wme, 0); |
3711 | window_copy_cursor_end_of_line(wme); |
3712 | } else if (cx > 0) { |
3713 | window_copy_update_cursor(wme, cx - 1, data->cy); |
3714 | if (window_copy_update_selection(wme, 1, 0)) |
3715 | window_copy_redraw_lines(wme, data->cy, 1); |
3716 | } |
3717 | } |
3718 | |
3719 | static void |
3720 | window_copy_cursor_right(struct window_mode_entry *wme) |
3721 | { |
3722 | struct window_copy_mode_data *data = wme->data; |
3723 | u_int px, py, yy, cx, cy; |
3724 | struct grid_cell gc; |
3725 | |
3726 | py = screen_hsize(data->backing) + data->cy - data->oy; |
3727 | yy = screen_hsize(data->backing) + screen_size_y(data->backing) - 1; |
3728 | if (data->screen.sel != NULL && data->rectflag) |
3729 | px = screen_size_x(&data->screen); |
3730 | else |
3731 | px = window_copy_find_length(wme, py); |
3732 | |
3733 | if (data->cx >= px && py < yy) { |
3734 | window_copy_cursor_start_of_line(wme); |
3735 | window_copy_cursor_down(wme, 0); |
3736 | } else if (data->cx < px) { |
3737 | cx = data->cx + 1; |
3738 | cy = screen_hsize(data->backing) + data->cy - data->oy; |
3739 | while (cx < px) { |
3740 | grid_get_cell(data->backing->grid, cx, cy, &gc); |
3741 | if (~gc.flags & GRID_FLAG_PADDING) |
3742 | break; |
3743 | cx++; |
3744 | } |
3745 | window_copy_update_cursor(wme, cx, data->cy); |
3746 | if (window_copy_update_selection(wme, 1, 0)) |
3747 | window_copy_redraw_lines(wme, data->cy, 1); |
3748 | } |
3749 | } |
3750 | |
3751 | static void |
3752 | window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only) |
3753 | { |
3754 | struct window_copy_mode_data *data = wme->data; |
3755 | struct screen *s = &data->screen; |
3756 | u_int ox, oy, px, py; |
3757 | |
3758 | oy = screen_hsize(data->backing) + data->cy - data->oy; |
3759 | ox = window_copy_find_length(wme, oy); |
3760 | if (data->cx != ox) { |
3761 | data->lastcx = data->cx; |
3762 | data->lastsx = ox; |
3763 | } |
3764 | |
3765 | if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely) |
3766 | window_copy_other_end(wme); |
3767 | |
3768 | if (scroll_only || data->cy == 0) { |
3769 | data->cx = data->lastcx; |
3770 | window_copy_scroll_down(wme, 1); |
3771 | if (scroll_only) { |
3772 | if (data->cy == screen_size_y(s) - 1) |
3773 | window_copy_redraw_lines(wme, data->cy, 1); |
3774 | else |
3775 | window_copy_redraw_lines(wme, data->cy, 2); |
3776 | } |
3777 | } else { |
3778 | window_copy_update_cursor(wme, data->lastcx, data->cy - 1); |
3779 | if (window_copy_update_selection(wme, 1, 0)) { |
3780 | if (data->cy == screen_size_y(s) - 1) |
3781 | window_copy_redraw_lines(wme, data->cy, 1); |
3782 | else |
3783 | window_copy_redraw_lines(wme, data->cy, 2); |
3784 | } |
3785 | } |
3786 | |
3787 | if (data->screen.sel == NULL || !data->rectflag) { |
3788 | py = screen_hsize(data->backing) + data->cy - data->oy; |
3789 | px = window_copy_find_length(wme, py); |
3790 | if ((data->cx >= data->lastsx && data->cx != px) || |
3791 | data->cx > px) |
3792 | window_copy_cursor_end_of_line(wme); |
3793 | } |
3794 | |
3795 | if (data->lineflag == LINE_SEL_LEFT_RIGHT) |
3796 | window_copy_cursor_end_of_line(wme); |
3797 | else if (data->lineflag == LINE_SEL_RIGHT_LEFT) |
3798 | window_copy_cursor_start_of_line(wme); |
3799 | } |
3800 | |
3801 | static void |
3802 | window_copy_cursor_down(struct window_mode_entry *wme, int scroll_only) |
3803 | { |
3804 | struct window_copy_mode_data *data = wme->data; |
3805 | struct screen *s = &data->screen; |
3806 | u_int ox, oy, px, py; |
3807 | |
3808 | oy = screen_hsize(data->backing) + data->cy - data->oy; |
3809 | ox = window_copy_find_length(wme, oy); |
3810 | if (data->cx != ox) { |
3811 | data->lastcx = data->cx; |
3812 | data->lastsx = ox; |
3813 | } |
3814 | |
3815 | if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely) |
3816 | window_copy_other_end(wme); |
3817 | |
3818 | if (scroll_only || data->cy == screen_size_y(s) - 1) { |
3819 | data->cx = data->lastcx; |
3820 | window_copy_scroll_up(wme, 1); |
3821 | if (scroll_only && data->cy > 0) |
3822 | window_copy_redraw_lines(wme, data->cy - 1, 2); |
3823 | } else { |
3824 | window_copy_update_cursor(wme, data->lastcx, data->cy + 1); |
3825 | if (window_copy_update_selection(wme, 1, 0)) |
3826 | window_copy_redraw_lines(wme, data->cy - 1, 2); |
3827 | } |
3828 | |
3829 | if (data->screen.sel == NULL || !data->rectflag) { |
3830 | py = screen_hsize(data->backing) + data->cy - data->oy; |
3831 | px = window_copy_find_length(wme, py); |
3832 | if ((data->cx >= data->lastsx && data->cx != px) || |
3833 | data->cx > px) |
3834 | window_copy_cursor_end_of_line(wme); |
3835 | } |
3836 | |
3837 | if (data->lineflag == LINE_SEL_LEFT_RIGHT) |
3838 | window_copy_cursor_end_of_line(wme); |
3839 | else if (data->lineflag == LINE_SEL_RIGHT_LEFT) |
3840 | window_copy_cursor_start_of_line(wme); |
3841 | } |
3842 | |
3843 | static void |
3844 | window_copy_cursor_jump(struct window_mode_entry *wme) |
3845 | { |
3846 | struct window_copy_mode_data *data = wme->data; |
3847 | struct screen *back_s = data->backing; |
3848 | struct grid_cell gc; |
3849 | u_int px, py, xx; |
3850 | |
3851 | px = data->cx + 1; |
3852 | py = screen_hsize(back_s) + data->cy - data->oy; |
3853 | xx = window_copy_find_length(wme, py); |
3854 | |
3855 | while (px < xx) { |
3856 | grid_get_cell(back_s->grid, px, py, &gc); |
3857 | if (!(gc.flags & GRID_FLAG_PADDING) && |
3858 | gc.data.size == 1 && *gc.data.data == data->jumpchar) { |
3859 | window_copy_update_cursor(wme, px, data->cy); |
3860 | if (window_copy_update_selection(wme, 1, 0)) |
3861 | window_copy_redraw_lines(wme, data->cy, 1); |
3862 | return; |
3863 | } |
3864 | px++; |
3865 | } |
3866 | } |
3867 | |
3868 | static void |
3869 | window_copy_cursor_jump_back(struct window_mode_entry *wme) |
3870 | { |
3871 | struct window_copy_mode_data *data = wme->data; |
3872 | struct screen *back_s = data->backing; |
3873 | struct grid_cell gc; |
3874 | u_int px, py; |
3875 | |
3876 | px = data->cx; |
3877 | py = screen_hsize(back_s) + data->cy - data->oy; |
3878 | |
3879 | if (px > 0) |
3880 | px--; |
3881 | |
3882 | for (;;) { |
3883 | grid_get_cell(back_s->grid, px, py, &gc); |
3884 | if (!(gc.flags & GRID_FLAG_PADDING) && |
3885 | gc.data.size == 1 && *gc.data.data == data->jumpchar) { |
3886 | window_copy_update_cursor(wme, px, data->cy); |
3887 | if (window_copy_update_selection(wme, 1, 0)) |
3888 | window_copy_redraw_lines(wme, data->cy, 1); |
3889 | return; |
3890 | } |
3891 | if (px == 0) |
3892 | break; |
3893 | px--; |
3894 | } |
3895 | } |
3896 | |
3897 | static void |
3898 | window_copy_cursor_jump_to(struct window_mode_entry *wme) |
3899 | { |
3900 | struct window_copy_mode_data *data = wme->data; |
3901 | struct screen *back_s = data->backing; |
3902 | struct grid_cell gc; |
3903 | u_int px, py, xx; |
3904 | |
3905 | px = data->cx + 2; |
3906 | py = screen_hsize(back_s) + data->cy - data->oy; |
3907 | xx = window_copy_find_length(wme, py); |
3908 | |
3909 | while (px < xx) { |
3910 | grid_get_cell(back_s->grid, px, py, &gc); |
3911 | if (!(gc.flags & GRID_FLAG_PADDING) && |
3912 | gc.data.size == 1 && *gc.data.data == data->jumpchar) { |
3913 | window_copy_update_cursor(wme, px - 1, data->cy); |
3914 | if (window_copy_update_selection(wme, 1, 0)) |
3915 | window_copy_redraw_lines(wme, data->cy, 1); |
3916 | return; |
3917 | } |
3918 | px++; |
3919 | } |
3920 | } |
3921 | |
3922 | static void |
3923 | window_copy_cursor_jump_to_back(struct window_mode_entry *wme) |
3924 | { |
3925 | struct window_copy_mode_data *data = wme->data; |
3926 | struct screen *back_s = data->backing; |
3927 | struct grid_cell gc; |
3928 | u_int px, py; |
3929 | |
3930 | px = data->cx; |
3931 | py = screen_hsize(back_s) + data->cy - data->oy; |
3932 | |
3933 | if (px > 0) |
3934 | px--; |
3935 | |
3936 | if (px > 0) |
3937 | px--; |
3938 | |
3939 | for (;;) { |
3940 | grid_get_cell(back_s->grid, px, py, &gc); |
3941 | if (!(gc.flags & GRID_FLAG_PADDING) && |
3942 | gc.data.size == 1 && *gc.data.data == data->jumpchar) { |
3943 | window_copy_update_cursor(wme, px + 1, data->cy); |
3944 | if (window_copy_update_selection(wme, 1, 0)) |
3945 | window_copy_redraw_lines(wme, data->cy, 1); |
3946 | return; |
3947 | } |
3948 | if (px == 0) |
3949 | break; |
3950 | px--; |
3951 | } |
3952 | } |
3953 | |
3954 | static void |
3955 | window_copy_cursor_next_word(struct window_mode_entry *wme, |
3956 | const char *separators) |
3957 | { |
3958 | struct window_copy_mode_data *data = wme->data; |
3959 | struct screen *back_s = data->backing; |
3960 | u_int px, py, xx, yy; |
3961 | int expected = 0; |
3962 | |
3963 | px = data->cx; |
3964 | py = screen_hsize(back_s) + data->cy - data->oy; |
3965 | xx = window_copy_find_length(wme, py); |
3966 | yy = screen_hsize(back_s) + screen_size_y(back_s) - 1; |
3967 | |
3968 | /* |
3969 | * First skip past any nonword characters and then any word characters. |
3970 | * |
3971 | * expected is initially set to 0 for the former and then 1 for the |
3972 | * latter. |
3973 | */ |
3974 | do { |
3975 | while (px > xx || |
3976 | window_copy_in_set(wme, px, py, separators) == expected) { |
3977 | /* Move down if we're past the end of the line. */ |
3978 | if (px > xx) { |
3979 | if (py == yy) |
3980 | return; |
3981 | window_copy_cursor_down(wme, 0); |
3982 | px = 0; |
3983 | |
3984 | py = screen_hsize(back_s) + data->cy - data->oy; |
3985 | xx = window_copy_find_length(wme, py); |
3986 | } else |
3987 | px++; |
3988 | } |
3989 | expected = !expected; |
3990 | } while (expected == 1); |
3991 | |
3992 | window_copy_update_cursor(wme, px, data->cy); |
3993 | if (window_copy_update_selection(wme, 1, 0)) |
3994 | window_copy_redraw_lines(wme, data->cy, 1); |
3995 | } |
3996 | |
3997 | static void |
3998 | window_copy_cursor_next_word_end_pos(struct window_mode_entry *wme, |
3999 | const char *separators, u_int *ppx, u_int *ppy) |
4000 | { |
4001 | struct window_pane *wp = wme->wp; |
4002 | struct window_copy_mode_data *data = wme->data; |
4003 | struct options *oo = wp->window->options; |
4004 | struct screen *back_s = data->backing; |
4005 | u_int px, py, xx, yy; |
4006 | int keys, expected = 1; |
4007 | |
4008 | px = data->cx; |
4009 | py = screen_hsize(back_s) + data->cy - data->oy; |
4010 | xx = window_copy_find_length(wme, py); |
4011 | yy = screen_hsize(back_s) + screen_size_y(back_s) - 1; |
4012 | |
4013 | keys = options_get_number(oo, "mode-keys" ); |
4014 | if (keys == MODEKEY_VI && !window_copy_in_set(wme, px, py, separators)) |
4015 | px++; |
4016 | |
4017 | /* |
4018 | * First skip past any word characters, then any non-word characters. |
4019 | * |
4020 | * expected is initially set to 1 for the former and then 0 for the |
4021 | * latter. |
4022 | */ |
4023 | do { |
4024 | while (px > xx || |
4025 | window_copy_in_set(wme, px, py, separators) == expected) { |
4026 | /* Move down if we're past the end of the line. */ |
4027 | if (px > xx) { |
4028 | if (py == yy) |
4029 | return; |
4030 | py++; |
4031 | px = 0; |
4032 | xx = window_copy_find_length(wme, py); |
4033 | } else |
4034 | px++; |
4035 | } |
4036 | expected = !expected; |
4037 | } while (expected == 0); |
4038 | |
4039 | if (keys == MODEKEY_VI && px != 0) |
4040 | px--; |
4041 | |
4042 | *ppx = px; |
4043 | *ppy = py; |
4044 | } |
4045 | |
4046 | static void |
4047 | window_copy_cursor_next_word_end(struct window_mode_entry *wme, |
4048 | const char *separators, int no_reset) |
4049 | { |
4050 | struct window_pane *wp = wme->wp; |
4051 | struct window_copy_mode_data *data = wme->data; |
4052 | struct options *oo = wp->window->options; |
4053 | struct screen *back_s = data->backing; |
4054 | u_int px, py, xx, yy; |
4055 | int keys, expected = 1; |
4056 | |
4057 | px = data->cx; |
4058 | py = screen_hsize(back_s) + data->cy - data->oy; |
4059 | xx = window_copy_find_length(wme, py); |
4060 | yy = screen_hsize(back_s) + screen_size_y(back_s) - 1; |
4061 | |
4062 | keys = options_get_number(oo, "mode-keys" ); |
4063 | if (keys == MODEKEY_VI && !window_copy_in_set(wme, px, py, separators)) |
4064 | px++; |
4065 | |
4066 | /* |
4067 | * First skip past any word characters, then any nonword characters. |
4068 | * |
4069 | * expected is initially set to 1 for the former and then 0 for the |
4070 | * latter. |
4071 | */ |
4072 | do { |
4073 | while (px > xx || |
4074 | window_copy_in_set(wme, px, py, separators) == expected) { |
4075 | /* Move down if we're past the end of the line. */ |
4076 | if (px > xx) { |
4077 | if (py == yy) |
4078 | return; |
4079 | window_copy_cursor_down(wme, 0); |
4080 | px = 0; |
4081 | |
4082 | py = screen_hsize(back_s) + data->cy - data->oy; |
4083 | xx = window_copy_find_length(wme, py); |
4084 | } else |
4085 | px++; |
4086 | } |
4087 | expected = !expected; |
4088 | } while (expected == 0); |
4089 | |
4090 | if (keys == MODEKEY_VI && px != 0) |
4091 | px--; |
4092 | |
4093 | window_copy_update_cursor(wme, px, data->cy); |
4094 | if (window_copy_update_selection(wme, 1, no_reset)) |
4095 | window_copy_redraw_lines(wme, data->cy, 1); |
4096 | } |
4097 | |
4098 | /* Compute the previous place where a word begins. */ |
4099 | static void |
4100 | window_copy_cursor_previous_word_pos(struct window_mode_entry *wme, |
4101 | const char *separators, int already, u_int *ppx, u_int *ppy) |
4102 | { |
4103 | struct window_copy_mode_data *data = wme->data; |
4104 | u_int px, py; |
4105 | |
4106 | px = data->cx; |
4107 | py = screen_hsize(data->backing) + data->cy - data->oy; |
4108 | |
4109 | /* Move back to the previous word character. */ |
4110 | if (already || window_copy_in_set(wme, px, py, separators)) { |
4111 | for (;;) { |
4112 | if (px > 0) { |
4113 | px--; |
4114 | if (!window_copy_in_set(wme, px, py, |
4115 | separators)) |
4116 | break; |
4117 | } else { |
4118 | if (data->cy == 0 && |
4119 | (screen_hsize(data->backing) == 0 || |
4120 | data->oy >= |
4121 | screen_hsize(data->backing) - 1)) |
4122 | goto out; |
4123 | py--; |
4124 | |
4125 | py = screen_hsize(data->backing) + data->cy - |
4126 | data->oy; |
4127 | px = window_copy_find_length(wme, py); |
4128 | |
4129 | /* Stop if separator at EOL. */ |
4130 | if (px > 0 && window_copy_in_set(wme, px - 1, |
4131 | py, separators)) |
4132 | break; |
4133 | } |
4134 | } |
4135 | } |
4136 | |
4137 | /* Move back to the beginning of this word. */ |
4138 | while (px > 0 && !window_copy_in_set(wme, px - 1, py, separators)) |
4139 | px--; |
4140 | |
4141 | out: |
4142 | *ppx = px; |
4143 | *ppy = py; |
4144 | } |
4145 | |
4146 | /* Move to the previous place where a word begins. */ |
4147 | static void |
4148 | window_copy_cursor_previous_word(struct window_mode_entry *wme, |
4149 | const char *separators, int already) |
4150 | { |
4151 | struct window_copy_mode_data *data = wme->data; |
4152 | u_int px, py; |
4153 | |
4154 | px = data->cx; |
4155 | py = screen_hsize(data->backing) + data->cy - data->oy; |
4156 | |
4157 | /* Move back to the previous word character. */ |
4158 | if (already || window_copy_in_set(wme, px, py, separators)) { |
4159 | for (;;) { |
4160 | if (px > 0) { |
4161 | px--; |
4162 | if (!window_copy_in_set(wme, px, py, |
4163 | separators)) |
4164 | break; |
4165 | } else { |
4166 | if (data->cy == 0 && |
4167 | (screen_hsize(data->backing) == 0 || |
4168 | data->oy >= |
4169 | screen_hsize(data->backing) - 1)) |
4170 | goto out; |
4171 | window_copy_cursor_up(wme, 0); |
4172 | |
4173 | py = screen_hsize(data->backing) + data->cy - |
4174 | data->oy; |
4175 | px = window_copy_find_length(wme, py); |
4176 | |
4177 | /* Stop if separator at EOL. */ |
4178 | if (px > 0 && window_copy_in_set(wme, px - 1, |
4179 | py, separators)) |
4180 | break; |
4181 | } |
4182 | } |
4183 | } |
4184 | |
4185 | /* Move back to the beginning of this word. */ |
4186 | while (px > 0 && !window_copy_in_set(wme, px - 1, py, separators)) |
4187 | px--; |
4188 | |
4189 | out: |
4190 | window_copy_update_cursor(wme, px, data->cy); |
4191 | if (window_copy_update_selection(wme, 1, 0)) |
4192 | window_copy_redraw_lines(wme, data->cy, 1); |
4193 | } |
4194 | |
4195 | static void |
4196 | window_copy_scroll_up(struct window_mode_entry *wme, u_int ny) |
4197 | { |
4198 | struct window_pane *wp = wme->wp; |
4199 | struct window_copy_mode_data *data = wme->data; |
4200 | struct screen *s = &data->screen; |
4201 | struct screen_write_ctx ctx; |
4202 | |
4203 | if (data->oy < ny) |
4204 | ny = data->oy; |
4205 | if (ny == 0) |
4206 | return; |
4207 | data->oy -= ny; |
4208 | |
4209 | if (data->searchmark != NULL && !data->timeout) |
4210 | window_copy_search_marks(wme, NULL, data->searchregex); |
4211 | window_copy_update_selection(wme, 0, 0); |
4212 | |
4213 | screen_write_start(&ctx, wp, NULL); |
4214 | screen_write_cursormove(&ctx, 0, 0, 0); |
4215 | screen_write_deleteline(&ctx, ny, 8); |
4216 | window_copy_write_lines(wme, &ctx, screen_size_y(s) - ny, ny); |
4217 | window_copy_write_line(wme, &ctx, 0); |
4218 | if (screen_size_y(s) > 1) |
4219 | window_copy_write_line(wme, &ctx, 1); |
4220 | if (screen_size_y(s) > 3) |
4221 | window_copy_write_line(wme, &ctx, screen_size_y(s) - 2); |
4222 | if (s->sel != NULL && screen_size_y(s) > ny) |
4223 | window_copy_write_line(wme, &ctx, screen_size_y(s) - ny - 1); |
4224 | screen_write_cursormove(&ctx, data->cx, data->cy, 0); |
4225 | screen_write_stop(&ctx); |
4226 | } |
4227 | |
4228 | static void |
4229 | window_copy_scroll_down(struct window_mode_entry *wme, u_int ny) |
4230 | { |
4231 | struct window_pane *wp = wme->wp; |
4232 | struct window_copy_mode_data *data = wme->data; |
4233 | struct screen *s = &data->screen; |
4234 | struct screen_write_ctx ctx; |
4235 | |
4236 | if (ny > screen_hsize(data->backing)) |
4237 | return; |
4238 | |
4239 | if (data->oy > screen_hsize(data->backing) - ny) |
4240 | ny = screen_hsize(data->backing) - data->oy; |
4241 | if (ny == 0) |
4242 | return; |
4243 | data->oy += ny; |
4244 | |
4245 | if (data->searchmark != NULL && !data->timeout) |
4246 | window_copy_search_marks(wme, NULL, data->searchregex); |
4247 | window_copy_update_selection(wme, 0, 0); |
4248 | |
4249 | screen_write_start(&ctx, wp, NULL); |
4250 | screen_write_cursormove(&ctx, 0, 0, 0); |
4251 | screen_write_insertline(&ctx, ny, 8); |
4252 | window_copy_write_lines(wme, &ctx, 0, ny); |
4253 | if (s->sel != NULL && screen_size_y(s) > ny) |
4254 | window_copy_write_line(wme, &ctx, ny); |
4255 | else if (ny == 1) /* nuke position */ |
4256 | window_copy_write_line(wme, &ctx, 1); |
4257 | screen_write_cursormove(&ctx, data->cx, data->cy, 0); |
4258 | screen_write_stop(&ctx); |
4259 | } |
4260 | |
4261 | static void |
4262 | window_copy_rectangle_toggle(struct window_mode_entry *wme) |
4263 | { |
4264 | struct window_copy_mode_data *data = wme->data; |
4265 | u_int px, py; |
4266 | |
4267 | data->rectflag = !data->rectflag; |
4268 | |
4269 | py = screen_hsize(data->backing) + data->cy - data->oy; |
4270 | px = window_copy_find_length(wme, py); |
4271 | if (data->cx > px) |
4272 | window_copy_update_cursor(wme, px, data->cy); |
4273 | |
4274 | window_copy_update_selection(wme, 1, 0); |
4275 | window_copy_redraw_screen(wme); |
4276 | } |
4277 | |
4278 | static void |
4279 | window_copy_move_mouse(struct mouse_event *m) |
4280 | { |
4281 | struct window_pane *wp; |
4282 | struct window_mode_entry *wme; |
4283 | u_int x, y; |
4284 | |
4285 | wp = cmd_mouse_pane(m, NULL, NULL); |
4286 | if (wp == NULL) |
4287 | return; |
4288 | wme = TAILQ_FIRST(&wp->modes); |
4289 | if (wme == NULL) |
4290 | return; |
4291 | if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode) |
4292 | return; |
4293 | |
4294 | if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) |
4295 | return; |
4296 | |
4297 | window_copy_update_cursor(wme, x, y); |
4298 | } |
4299 | |
4300 | void |
4301 | window_copy_start_drag(struct client *c, struct mouse_event *m) |
4302 | { |
4303 | struct window_pane *wp; |
4304 | struct window_mode_entry *wme; |
4305 | struct window_copy_mode_data *data; |
4306 | u_int x, y, yg; |
4307 | |
4308 | if (c == NULL) |
4309 | return; |
4310 | |
4311 | wp = cmd_mouse_pane(m, NULL, NULL); |
4312 | if (wp == NULL) |
4313 | return; |
4314 | wme = TAILQ_FIRST(&wp->modes); |
4315 | if (wme == NULL) |
4316 | return; |
4317 | if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode) |
4318 | return; |
4319 | |
4320 | if (cmd_mouse_at(wp, m, &x, &y, 1) != 0) |
4321 | return; |
4322 | |
4323 | c->tty.mouse_drag_update = window_copy_drag_update; |
4324 | c->tty.mouse_drag_release = window_copy_drag_release; |
4325 | |
4326 | data = wme->data; |
4327 | yg = screen_hsize(data->backing) + y - data->oy; |
4328 | if (x < data->selrx || x > data->endselrx || yg != data->selry) |
4329 | data->selflag = SEL_CHAR; |
4330 | switch (data->selflag) { |
4331 | case SEL_WORD: |
4332 | if (data->ws) { |
4333 | window_copy_update_cursor(wme, x, y); |
4334 | window_copy_cursor_previous_word_pos(wme, |
4335 | data->ws, 0, &x, &y); |
4336 | y -= screen_hsize(data->backing) - data->oy; |
4337 | } |
4338 | window_copy_update_cursor(wme, x, y); |
4339 | break; |
4340 | case SEL_LINE: |
4341 | window_copy_update_cursor(wme, 0, y); |
4342 | break; |
4343 | case SEL_CHAR: |
4344 | window_copy_update_cursor(wme, x, y); |
4345 | window_copy_start_selection(wme); |
4346 | break; |
4347 | } |
4348 | |
4349 | window_copy_redraw_screen(wme); |
4350 | window_copy_drag_update(c, m); |
4351 | } |
4352 | |
4353 | static void |
4354 | window_copy_drag_update(struct client *c, struct mouse_event *m) |
4355 | { |
4356 | struct window_pane *wp; |
4357 | struct window_mode_entry *wme; |
4358 | struct window_copy_mode_data *data; |
4359 | u_int x, y, old_cx, old_cy; |
4360 | struct timeval tv = { |
4361 | .tv_usec = WINDOW_COPY_DRAG_REPEAT_TIME |
4362 | }; |
4363 | |
4364 | if (c == NULL) |
4365 | return; |
4366 | |
4367 | wp = cmd_mouse_pane(m, NULL, NULL); |
4368 | if (wp == NULL) |
4369 | return; |
4370 | wme = TAILQ_FIRST(&wp->modes); |
4371 | if (wme == NULL) |
4372 | return; |
4373 | if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode) |
4374 | return; |
4375 | |
4376 | data = wme->data; |
4377 | evtimer_del(&data->dragtimer); |
4378 | |
4379 | if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) |
4380 | return; |
4381 | old_cx = data->cx; |
4382 | old_cy = data->cy; |
4383 | |
4384 | window_copy_update_cursor(wme, x, y); |
4385 | if (window_copy_update_selection(wme, 1, 0)) |
4386 | window_copy_redraw_selection(wme, old_cy); |
4387 | if (old_cy != data->cy || old_cx == data->cx) { |
4388 | if (y == 0) { |
4389 | evtimer_add(&data->dragtimer, &tv); |
4390 | window_copy_cursor_up(wme, 1); |
4391 | } else if (y == screen_size_y(&data->screen) - 1) { |
4392 | evtimer_add(&data->dragtimer, &tv); |
4393 | window_copy_cursor_down(wme, 1); |
4394 | } |
4395 | } |
4396 | } |
4397 | |
4398 | static void |
4399 | window_copy_drag_release(struct client *c, struct mouse_event *m) |
4400 | { |
4401 | struct window_pane *wp; |
4402 | struct window_mode_entry *wme; |
4403 | struct window_copy_mode_data *data; |
4404 | |
4405 | if (c == NULL) |
4406 | return; |
4407 | |
4408 | wp = cmd_mouse_pane(m, NULL, NULL); |
4409 | if (wp == NULL) |
4410 | return; |
4411 | wme = TAILQ_FIRST(&wp->modes); |
4412 | if (wme == NULL) |
4413 | return; |
4414 | if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode) |
4415 | return; |
4416 | |
4417 | data = wme->data; |
4418 | evtimer_del(&data->dragtimer); |
4419 | } |
4420 | |