Coverage Report

Created: 2026-10-17 06:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/window-switch.c
Line
Count
Source
1
/* $OpenBSD: window-switch.c,v 1.7 2026/10/16 17:28:19 nicm Exp $ */
2
3
/*
4
 * Copyright (c) 2026 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 <stdlib.h>
23
#include <string.h>
24
25
#include "tmux.h"
26
27
static struct screen  *window_switch_init(struct window_mode_entry *,
28
           struct cmdq_item *, struct cmd_find_state *,
29
           struct args *);
30
static void    window_switch_free(struct window_mode_entry *);
31
static void    window_switch_resize(struct window_mode_entry *, u_int,
32
           u_int);
33
static void    window_switch_key(struct window_mode_entry *,
34
           struct client *, struct session *,
35
           struct winlink *, key_code, struct mouse_event *);
36
static enum prompt_result window_switch_prompt_callback(void *, const char *,
37
           enum prompt_key_result);
38
39
0
#define WINDOW_SWITCH_DEFAULT_COMMAND "switch-client -Zt '%%'"
40
41
#define WINDOW_SWITCH_DEFAULT_FORMAT    \
42
0
  "#{?window_format," \
43
0
    "#{window_name} " \
44
0
    "#[dim]#{session_name}:#{window_index}#{window_flags}#[default] " \
45
0
    "#[dim]#{pane_current_command}#[default] " \
46
0
          "#[dim]#{?#{!=:#{pane_title},#{host_short}},#{pane_title},}#[default]" \
47
0
  "," \
48
0
    "#{session_name} " \
49
0
    "#[dim]#{session_windows} windows#[default] " \
50
0
    "#{?session_attached,attached,#[dim]detached#[default]} " \
51
0
    "#[dim]#{window_name}#[default]" \
52
0
  "}"
53
54
const struct window_mode window_switch_mode = {
55
  .name = "switch-mode",
56
  .default_format = WINDOW_SWITCH_DEFAULT_FORMAT,
57
58
  .init = window_switch_init,
59
  .free = window_switch_free,
60
  .resize = window_switch_resize,
61
  .key = window_switch_key,
62
};
63
64
enum window_switch_type {
65
  WINDOW_SWITCH_TYPE_SESSION,
66
  WINDOW_SWITCH_TYPE_WINDOW,
67
  WINDOW_SWITCH_TYPE_INPUT
68
};
69
70
struct window_switch_itemdata {
71
  enum window_switch_type  type;
72
  int      session;
73
  int      winlink;
74
75
  uint64_t     tag;
76
  char      *text;
77
  const char    *value;
78
  bitstr_t    *match;
79
80
  u_int      score;
81
  u_int      order;
82
};
83
84
struct window_switch_modedata {
85
  struct window_pane     *wp;
86
  struct screen       screen;
87
  int         zoomed;
88
89
  char         *format;
90
  char         *command;
91
92
  char        **lines;
93
  u_int         lines_size;
94
95
  enum window_switch_type     type;
96
  enum sort_order       order;
97
  int         reversed;
98
  char         *filter;
99
  struct prompt      *prompt;
100
  u_int         prompt_cx;
101
102
  struct window_switch_itemdata **item_list;
103
  u_int         item_size;
104
105
  struct window_switch_itemdata **matches;
106
  u_int         matches_size;
107
108
  u_int         current;
109
  u_int         offset;
110
};
111
112
static void
113
window_switch_free_item(struct window_switch_itemdata *item)
114
0
{
115
0
  free(item->match);
116
0
  free(item->text);
117
0
  free(item);
118
0
}
119
120
static struct window_switch_itemdata *
121
window_switch_add_item(struct window_switch_modedata *data)
122
0
{
123
0
  struct window_switch_itemdata *item;
124
125
0
  data->item_list = xreallocarray(data->item_list, data->item_size + 1,
126
0
      sizeof *data->item_list);
127
0
  item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
128
0
  return (item);
129
0
}
130
131
static void
132
window_switch_add_session(struct window_switch_modedata *data,
133
    struct session *s, u_int *order)
134
0
{
135
0
  struct window_switch_itemdata *item;
136
0
  struct format_tree    *ft;
137
138
0
  ft = format_create(NULL, NULL, FORMAT_NONE, 0);
139
0
  format_defaults(ft, NULL, s, NULL, NULL);
140
141
0
  item = window_switch_add_item(data);
142
0
  item->type = WINDOW_SWITCH_TYPE_SESSION;
143
0
  item->session = s->id;
144
0
  item->winlink = -1;
145
0
  item->tag = (uint64_t)s;
146
0
  item->order = (*order)++;
147
0
  item->text = format_expand(ft, data->format);
148
149
0
  format_free(ft);
150
0
}
151
152
static void
153
window_switch_add_window(struct window_switch_modedata *data,
154
    struct winlink *wl, u_int *order)
155
0
{
156
0
  struct window_switch_itemdata *item;
157
0
  struct format_tree    *ft;
158
159
0
  ft = format_create(NULL, NULL, FORMAT_NONE, 0);
160
0
  format_defaults(ft, NULL, wl->session, wl, NULL);
161
162
0
  item = window_switch_add_item(data);
163
0
  item->type = WINDOW_SWITCH_TYPE_WINDOW;
164
0
  item->session = wl->session->id;
165
0
  item->winlink = wl->idx;
166
0
  item->tag = (uint64_t)wl;
167
0
  item->order = (*order)++;
168
0
  item->text = format_expand(ft, data->format);
169
170
0
  format_free(ft);
171
0
}
172
173
static void
174
window_switch_add_input(struct window_switch_modedata *data, u_int idx,
175
    struct format_tree *ft)
176
0
{
177
0
  struct window_switch_itemdata *item;
178
179
0
  item = window_switch_add_item(data);
180
0
  item->type = WINDOW_SWITCH_TYPE_INPUT;
181
0
  item->value = data->lines[idx];
182
0
  item->order = data->reversed ? data->lines_size - idx - 1 : idx;
183
0
  format_add(ft, "switch_item", "%s", item->value);
184
0
  item->text = format_expand(ft, data->format);
185
0
}
186
187
static int
188
window_switch_compare(const void *a0, const void *b0)
189
0
{
190
0
  struct window_switch_itemdata *const *a = a0;
191
0
  struct window_switch_itemdata *const *b = b0;
192
193
0
  if ((*a)->score > (*b)->score)
194
0
    return (-1);
195
0
  if ((*a)->score < (*b)->score)
196
0
    return (1);
197
0
  if ((*a)->order < (*b)->order)
198
0
    return (-1);
199
0
  if ((*a)->order > (*b)->order)
200
0
    return (1);
201
0
  return (0);
202
0
}
203
204
static void
205
window_switch_build(struct window_switch_modedata *data)
206
0
{
207
0
  struct window_switch_itemdata  *item, **m = NULL;
208
0
  const char       *f = data->filter;
209
0
  u_int         ns, nw, i, n = 0, order = 0;
210
0
  u_int         sx = screen_size_x(&data->screen);
211
0
  struct session      **sl;
212
0
  struct winlink      **wl;
213
0
  struct sort_criteria      sort_crit;
214
0
  struct cmd_find_state     fs;
215
0
  struct format_tree     *ft;
216
217
0
  sort_crit.order = data->order;
218
0
  sort_crit.reversed = data->reversed;
219
0
  sort_crit.order_seq = NULL;
220
221
0
  for (i = 0; i < data->item_size; i++)
222
0
    window_switch_free_item(data->item_list[i]);
223
0
  free(data->item_list);
224
0
  data->item_list = NULL;
225
0
  data->item_size = 0;
226
227
0
  switch (data->type) {
228
0
  case WINDOW_SWITCH_TYPE_SESSION:
229
0
    sl = sort_get_sessions(&ns, &sort_crit);
230
0
    for (i = 0; i < ns; i++)
231
0
      window_switch_add_session(data, sl[i], &order);
232
0
    break;
233
0
  case WINDOW_SWITCH_TYPE_WINDOW:
234
0
    wl = sort_get_winlinks(&nw, &sort_crit);
235
0
    for (i = 0; i < nw; i++)
236
0
      window_switch_add_window(data, wl[i], &order);
237
0
    break;
238
0
  case WINDOW_SWITCH_TYPE_INPUT:
239
0
    cmd_find_from_pane(&fs, data->wp, 0);
240
0
    ft = format_create(NULL, NULL, FORMAT_NONE, 0);
241
0
    format_defaults(ft, NULL, fs.s, fs.wl, fs.wp);
242
0
    for (i = 0; i < data->lines_size; i++)
243
0
      window_switch_add_input(data, i, ft);
244
0
    format_free(ft);
245
0
    break;
246
0
  }
247
248
0
  for (i = 0; i < data->item_size; i++) {
249
0
    item = data->item_list[i];
250
0
    if (*f == '\0') {
251
0
      m = xreallocarray(m, n + 1, sizeof *m);
252
0
      m[n++] = item;
253
0
      continue;
254
0
    }
255
256
0
    item->match = fuzzy_match(f, item->text, sx, &item->score);
257
0
    if (item->match == NULL)
258
0
      continue;
259
0
    m = xreallocarray(m, n + 1, sizeof *m);
260
0
    m[n++] = item;
261
0
  }
262
0
  if (n > 1)
263
0
    qsort(m, n, sizeof *m, window_switch_compare);
264
265
0
  free(data->matches);
266
0
  data->matches = m;
267
0
  data->matches_size = n;
268
0
}
269
270
static u_int
271
window_switch_visible(struct window_switch_modedata *data)
272
0
{
273
0
  u_int sy = screen_size_y(&data->screen);
274
275
0
  if (sy <= 1)
276
0
    return (0);
277
0
  return (sy - 1);
278
0
}
279
280
static void
281
window_switch_set_current(struct window_switch_modedata *data, u_int current)
282
0
{
283
0
  u_int visible = window_switch_visible(data);
284
285
0
  if (data->matches_size == 0) {
286
0
    data->current = 0;
287
0
    data->offset = 0;
288
0
    return;
289
0
  }
290
291
0
  if (current > data->matches_size - 1)
292
0
    current = data->matches_size - 1;
293
0
  data->current = current;
294
295
0
  if (data->current < data->offset)
296
0
    data->offset = data->current;
297
0
  else if (visible != 0 && data->current >= data->offset + visible)
298
0
    data->offset = data->current - visible + 1;
299
0
}
300
301
static void
302
window_switch_draw_screen(struct window_mode_entry *wme)
303
0
{
304
0
  struct window_pane    *wp = wme->wp;
305
0
  struct window_switch_modedata *data = wme->data;
306
0
  struct options      *oo = wp->options;
307
0
  struct screen_write_ctx    ctx;
308
0
  struct screen     *s = &data->screen;
309
0
  u_int        sx = screen_size_x(s), i, j;
310
0
  u_int        sy = screen_size_y(s), visible, idx;
311
0
  struct window_switch_itemdata *item;
312
0
  struct grid_cell     mgc, sgc, gc;
313
0
  const struct grid_cell    *dgc = &grid_default_cell;
314
0
  struct prompt_draw_data    pdd;
315
0
  screen_write_start(&ctx, s);
316
0
  screen_write_clearscreen(&ctx, 8);
317
318
0
  if (sy <= 1) {
319
0
    screen_write_stop(&ctx);
320
0
    return;
321
0
  }
322
323
0
  style_apply(&mgc, oo, "switch-mode-match-style", NULL);
324
0
  style_apply(&sgc, oo, "mode-style", NULL);
325
326
0
  visible = window_switch_visible(data);
327
0
  for (i = 0; i < visible; i++) {
328
0
    idx = data->offset + i;
329
0
    if (idx >= data->matches_size)
330
0
      break;
331
0
    item = data->matches[idx];
332
333
0
    screen_write_cursormove(&ctx, 0, i, 0);
334
0
    if (idx != data->current)
335
0
      format_draw(&ctx, dgc, sx, item->text, NULL, 0);
336
0
    else {
337
0
      screen_write_clearendofline(&ctx, sgc.bg);
338
0
      format_draw(&ctx, &sgc, sx, item->text, NULL, 0);
339
0
    }
340
341
0
    if (item->match == NULL)
342
0
      continue;
343
0
    for (j = 0; j < sx; j++) {
344
0
      if (!bit_test(item->match, j))
345
0
        continue;
346
0
      grid_get_cell(s->grid, j, i, &gc);
347
0
      gc.attr = mgc.attr;
348
0
      gc.fg = mgc.fg;
349
0
      gc.bg = mgc.bg;
350
0
      screen_write_cursormove(&ctx, j, i, 0);
351
0
      screen_write_cell(&ctx, &gc);
352
0
    }
353
0
  }
354
355
0
  if (data->prompt != NULL) {
356
0
    pdd.ctx = &ctx;
357
0
    pdd.cursor_x = &data->prompt_cx;
358
0
    pdd.area_x = 0;
359
0
    pdd.area_width = sx;
360
0
    pdd.prompt_line = sy - 1;
361
0
    s->mode |= MODE_CURSOR;
362
0
    prompt_draw(data->prompt, &pdd);
363
0
    screen_write_cursormove(&ctx, data->prompt_cx, sy - 1, 0);
364
0
  }
365
0
  screen_write_stop(&ctx);
366
0
}
367
368
static struct screen *
369
window_switch_init(struct window_mode_entry *wme,
370
    __unused struct cmdq_item *item, struct cmd_find_state *fs,
371
    struct args *args)
372
0
{
373
0
  struct window_pane    *wp = wme->wp;
374
0
  struct window_switch_modedata *data;
375
0
  struct screen     *s;
376
0
  struct prompt_create_data  pd;
377
378
0
  wme->data = data = xcalloc(1, sizeof *data);
379
0
  data->wp = wp;
380
381
0
  data->order = SORT_NAME;
382
0
  data->reversed = 0;
383
0
  if (args != NULL) {
384
0
    if (args_has(args, 'O')) {
385
0
      data->order = sort_order_from_string(args_get(args, 'O'));
386
0
      if (data->order == SORT_END)
387
0
        data->order = SORT_NAME;
388
0
    }
389
0
    if (args_has(args, 'r'))
390
0
      data->reversed = 1;
391
0
  }
392
393
0
  if (args_has(args, 'w'))
394
0
    data->type = WINDOW_SWITCH_TYPE_WINDOW;
395
0
  else
396
0
    data->type = WINDOW_SWITCH_TYPE_SESSION;
397
0
  if (args_has(args, 'f'))
398
0
    data->type = WINDOW_SWITCH_TYPE_INPUT;
399
400
0
  data->filter = xstrdup("");
401
0
  if (args == NULL || !args_has(args, 'F')) {
402
0
    if (data->type == WINDOW_SWITCH_TYPE_INPUT)
403
0
      data->format = xstrdup("#{switch_item}");
404
0
    else
405
0
      data->format = xstrdup(WINDOW_SWITCH_DEFAULT_FORMAT);
406
0
  } else
407
0
    data->format = xstrdup(args_get(args, 'F'));
408
0
  if (args == NULL || args_count(args) == 0)
409
0
    data->command = xstrdup(WINDOW_SWITCH_DEFAULT_COMMAND);
410
0
  else
411
0
    data->command = xstrdup(args_string(args, 0));
412
413
0
  memset(&pd, 0, sizeof pd);
414
0
  prompt_set_options(&pd, fs->s);
415
0
  pd.fs = fs;
416
0
  pd.prompt = "(search) ";
417
0
  pd.input = "";
418
0
  pd.type = PROMPT_TYPE_SEARCH;
419
0
  pd.flags = PROMPT_INCREMENTAL|PROMPT_NOFORMAT|PROMPT_ISMODE|
420
0
      PROMPT_EDITARROWS;
421
0
  pd.inputcb = window_switch_prompt_callback;
422
0
  pd.data = data;
423
0
  data->prompt = prompt_create(&pd);
424
0
  prompt_update(data->prompt, "(search) ", data->filter);
425
426
0
  s = &data->screen;
427
0
  screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
428
429
0
  if (!args_has(args, 'Z'))
430
0
    data->zoomed = -1;
431
0
  else {
432
0
    data->zoomed = (wp->window->flags & WINDOW_ZOOMED);
433
0
    if (!data->zoomed && window_zoom(wp) == 0)
434
0
      server_redraw_window(wp->window);
435
0
  }
436
437
0
  window_switch_build(data);
438
0
  prompt_incremental_start(data->prompt);
439
0
  window_switch_draw_screen(wme);
440
441
0
  return (s);
442
0
}
443
444
void
445
window_switch_set_input(struct window_mode_entry *wme, struct evbuffer *buffer)
446
0
{
447
0
  struct window_switch_modedata *data = wme->data;
448
0
  char        *line;
449
0
  size_t         size;
450
451
0
  for (;;) {
452
0
    line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_LF);
453
0
    if (line == NULL) {
454
0
      size = EVBUFFER_LENGTH(buffer);
455
0
      if (size == 0)
456
0
        break;
457
0
      line = xmemdup(EVBUFFER_DATA(buffer), size);
458
0
      evbuffer_drain(buffer, size);
459
0
    }
460
0
    if (*line == '\0') {
461
0
      free(line);
462
0
      continue;
463
0
    }
464
0
    data->lines = xreallocarray(data->lines, data->lines_size + 1,
465
0
        sizeof *data->lines);
466
0
    data->lines[data->lines_size++] = line;
467
0
  }
468
0
  window_switch_build(data);
469
0
  window_switch_set_current(data, 0);
470
0
  window_switch_draw_screen(wme);
471
0
  wme->wp->flags |= PANE_REDRAW;
472
0
}
473
474
static void
475
window_switch_free(struct window_mode_entry *wme)
476
0
{
477
0
  struct window_switch_modedata *data = wme->data;
478
0
  u_int        i;
479
480
0
  if (data->zoomed == 0)
481
0
    server_unzoom_window(wme->wp->window);
482
483
0
  for (i = 0; i < data->item_size; i++)
484
0
    window_switch_free_item(data->item_list[i]);
485
0
  free(data->item_list);
486
0
  for (i = 0; i < data->lines_size; i++)
487
0
    free(data->lines[i]);
488
0
  free(data->lines);
489
490
0
  free(data->matches);
491
0
  free(data->filter);
492
0
  prompt_free(data->prompt);
493
0
  free(data->format);
494
0
  free(data->command);
495
0
  screen_free(&data->screen);
496
497
0
  free(data);
498
0
}
499
500
static void
501
window_switch_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
502
0
{
503
0
  struct window_switch_modedata *data = wme->data;
504
0
  struct screen     *s = &data->screen;
505
506
0
  screen_resize(s, sx, sy, 0);
507
0
  window_switch_build(data);
508
0
  window_switch_set_current(data, data->current);
509
0
  window_switch_draw_screen(wme);
510
0
}
511
512
static int
513
window_switch_run_command(struct window_switch_modedata *data, struct client *c)
514
0
{
515
0
  struct window_switch_itemdata *item;
516
0
  struct cmd_find_state    fs;
517
0
  struct session      *s;
518
0
  struct winlink      *wl;
519
0
  char        *target = NULL;
520
0
  struct cmdq_state   *state;
521
0
  char        *command, *error;
522
0
  enum cmd_parse_status    status;
523
524
0
  if (data->matches_size == 0)
525
0
    return (0);
526
0
  item = data->matches[data->current];
527
528
0
  cmd_find_clear_state(&fs, 0);
529
0
  switch (item->type) {
530
0
  case WINDOW_SWITCH_TYPE_SESSION:
531
0
    s = session_find_by_id(item->session);
532
0
    if (s != NULL) {
533
0
      xasprintf(&target, "$%u:", s->id);
534
0
      cmd_find_from_session(&fs, s, 0);
535
0
    }
536
0
    break;
537
0
  case WINDOW_SWITCH_TYPE_WINDOW:
538
0
    s = session_find_by_id(item->session);
539
0
    if (s != NULL) {
540
0
      wl = winlink_find_by_index(&s->windows, item->winlink);
541
0
      if (s != NULL && wl != NULL) {
542
0
        xasprintf(&target, "$%u:%u.", s->id, wl->idx);
543
0
        cmd_find_from_winlink(&fs, wl, 0);
544
0
      }
545
0
    }
546
0
    break;
547
0
  case WINDOW_SWITCH_TYPE_INPUT:
548
0
    target = xstrdup(item->value);
549
0
    cmd_find_from_pane(&fs, data->wp, 0);
550
0
    break;
551
0
  }
552
0
  if (target == NULL)
553
0
    return (0);
554
555
0
  command = cmd_template_replace(data->command, target, 1);
556
0
  if (command != NULL && *command != '\0') {
557
0
    state = cmdq_new_state(&fs, NULL, 0);
558
0
    if (item->type == WINDOW_SWITCH_TYPE_INPUT)
559
0
      cmdq_add_format(state, "switch_item", "%s", item->value);
560
0
    status = cmd_parse_and_append(command, NULL, c, state, &error);
561
0
    if (status == CMD_PARSE_ERROR) {
562
0
      if (c != NULL) {
563
0
        *error = toupper((u_char)*error);
564
0
        status_message_set(c, -1, 1, 0, 0, "%s", error);
565
0
      }
566
0
      free(error);
567
0
    }
568
0
    cmdq_free_state(state);
569
0
  }
570
0
  free(command);
571
0
  free(target);
572
0
  return (1);
573
0
}
574
575
static enum prompt_result
576
window_switch_prompt_callback(void *arg, const char *s,
577
    enum prompt_key_result key)
578
0
{
579
0
  struct window_switch_modedata *data = arg;
580
581
0
  if (key != PROMPT_KEY_HANDLED)
582
0
    return (PROMPT_CONTINUE);
583
584
0
  if (s == NULL)
585
0
    s = "";
586
0
  else if (*s != '\0')
587
0
    s++;
588
589
0
  free(data->filter);
590
0
  data->filter = xstrdup(s);
591
0
  window_switch_build(data);
592
0
  data->current = 0;
593
0
  data->offset = 0;
594
595
0
  return (PROMPT_CONTINUE);
596
0
}
597
598
static void
599
window_switch_key(struct window_mode_entry *wme, struct client *c,
600
    __unused struct session *s, __unused struct winlink *wl, key_code key,
601
    struct mouse_event *m)
602
0
{
603
0
  struct window_pane    *wp = wme->wp;
604
0
  struct window_switch_modedata *data = wme->data;
605
0
  u_int        visible, current = data->current;
606
0
  u_int        x, y, size = data->matches_size;
607
0
  enum prompt_key_result     result;
608
0
  int        redraw = 0;
609
610
0
  if (KEYC_IS_MOUSE(key)) {
611
0
    if (m == NULL || cmd_mouse_at(wp, m, &x, &y, 0) != 0)
612
0
      return;
613
0
    if (data->prompt != NULL && screen_size_y(&data->screen) != 0 &&
614
0
        y == screen_size_y(&data->screen) - 1 &&
615
0
        MOUSE_BUTTONS(m->b) == MOUSE_BUTTON_1 &&
616
0
        !MOUSE_DRAG(m->b) &&
617
0
        !MOUSE_RELEASE(m->b)) {
618
0
      result = prompt_mouse(data->prompt, x, 0,
619
0
          screen_size_x(&data->screen), &redraw);
620
0
      if (redraw || result == PROMPT_KEY_HANDLED) {
621
0
        window_switch_draw_screen(wme);
622
0
        wp->flags |= PANE_REDRAW;
623
0
      }
624
0
      return;
625
0
    }
626
0
    switch (key) {
627
0
    case KEYC_WHEELUP_PANE:
628
0
      if (size != 0 && current != 0)
629
0
        window_switch_set_current(data, current - 1);
630
0
      goto moved;
631
0
    case KEYC_WHEELDOWN_PANE:
632
0
      if (size != 0 && current != size - 1)
633
0
        window_switch_set_current(data, current + 1);
634
0
      goto moved;
635
0
    case KEYC_MOUSEDOWN1_PANE:
636
0
    case KEYC_DOUBLECLICK1_PANE:
637
0
      if (y >= window_switch_visible(data) ||
638
0
          data->offset + y >= size)
639
0
        return;
640
0
      window_switch_set_current(data, data->offset + y);
641
0
      if (key == KEYC_DOUBLECLICK1_PANE) {
642
0
        if (window_switch_run_command(data, c))
643
0
          window_pane_reset_mode(wp);
644
0
        return;
645
0
      }
646
0
      goto moved;
647
0
    }
648
0
    return;
649
0
  }
650
651
0
  switch (key) {
652
0
  case 'p'|KEYC_CTRL:
653
0
  case 'k'|KEYC_CTRL:
654
0
    key = KEYC_UP;
655
0
    break;
656
0
  case 'n'|KEYC_CTRL:
657
0
  case 'j'|KEYC_CTRL:
658
0
    key = KEYC_DOWN;
659
0
    break;
660
0
  }
661
662
0
  switch (key) {
663
0
  case '\r':
664
0
    if (window_switch_run_command(data, c))
665
0
      window_pane_reset_mode(wp);
666
0
    return;
667
0
  case '\033': /* Escape */
668
0
  case '['|KEYC_CTRL:
669
0
  case 'c'|KEYC_CTRL:
670
0
  case 'g'|KEYC_CTRL:
671
0
    window_pane_reset_mode(wp);
672
0
    return;
673
0
  }
674
675
0
  if (data->prompt != NULL) {
676
0
    result = prompt_key(data->prompt, key, &redraw);
677
0
    if (redraw) {
678
0
      window_switch_draw_screen(wme);
679
0
      wp->flags |= PANE_REDRAW;
680
0
    }
681
0
    if (result == PROMPT_KEY_HANDLED ||
682
0
        result == PROMPT_KEY_NOT_HANDLED)
683
0
      return;
684
0
    current = data->current;
685
0
    size = data->matches_size;
686
0
  }
687
688
0
  switch (key) {
689
0
  case KEYC_UP:
690
0
    if (size == 0)
691
0
      goto moved;
692
0
    if (current == 0)
693
0
      window_switch_set_current(data, size - 1);
694
0
    else
695
0
      window_switch_set_current(data, current - 1);
696
0
    goto moved;
697
0
  case KEYC_DOWN:
698
0
    if (size == 0)
699
0
      goto moved;
700
0
    if (current == size - 1)
701
0
      window_switch_set_current(data, 0);
702
0
    else
703
0
      window_switch_set_current(data, current + 1);
704
0
    goto moved;
705
0
  case KEYC_PPAGE:
706
0
    visible = window_switch_visible(data);
707
0
    if (current >= visible)
708
0
      window_switch_set_current(data, current - visible);
709
0
    else
710
0
      window_switch_set_current(data, 0);
711
0
    goto moved;
712
0
  case KEYC_NPAGE:
713
0
    visible = window_switch_visible(data);
714
0
    window_switch_set_current(data, current + visible);
715
0
    goto moved;
716
0
  case KEYC_HOME:
717
0
    window_switch_set_current(data, 0);
718
0
    goto moved;
719
0
  case KEYC_END:
720
0
    if (size > 0)
721
0
      window_switch_set_current(data, size - 1);
722
0
    goto moved;
723
0
  }
724
725
0
moved:
726
0
  window_switch_draw_screen(wme);
727
0
  wp->flags |= PANE_REDRAW;
728
0
}