Coverage Report

Created: 2026-07-16 06:53

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