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