1*0460263cSnicm /* $OpenBSD: status.c,v 1.250 2024/10/28 08:11:59 nicm Exp $ */ 2311827fbSnicm 3311827fbSnicm /* 498ca8272Snicm * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> 5311827fbSnicm * 6311827fbSnicm * Permission to use, copy, modify, and distribute this software for any 7311827fbSnicm * purpose with or without fee is hereby granted, provided that the above 8311827fbSnicm * copyright notice and this permission notice appear in all copies. 9311827fbSnicm * 10311827fbSnicm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11311827fbSnicm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12311827fbSnicm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13311827fbSnicm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14311827fbSnicm * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15311827fbSnicm * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16311827fbSnicm * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17311827fbSnicm */ 18311827fbSnicm 19311827fbSnicm #include <sys/types.h> 20311827fbSnicm #include <sys/time.h> 21311827fbSnicm 22311827fbSnicm #include <errno.h> 23311827fbSnicm #include <limits.h> 24311827fbSnicm #include <stdarg.h> 25311827fbSnicm #include <stdlib.h> 26311827fbSnicm #include <string.h> 27311827fbSnicm #include <time.h> 28311827fbSnicm #include <unistd.h> 29311827fbSnicm 30311827fbSnicm #include "tmux.h" 31311827fbSnicm 329883b791Snicm static void status_message_callback(int, short, void *); 339883b791Snicm static void status_timer_callback(int, short, void *); 34311827fbSnicm 359883b791Snicm static char *status_prompt_find_history_file(void); 36bc5a8fc2Snicm static const char *status_prompt_up_history(u_int *, u_int); 37bc5a8fc2Snicm static const char *status_prompt_down_history(u_int *, u_int); 38bc5a8fc2Snicm static void status_prompt_add_history(const char *, u_int); 39276a572eSnicm 40d6317402Snicm static char *status_prompt_complete(struct client *, const char *, u_int); 4137a3aee2Snicm static char *status_prompt_complete_window_menu(struct client *, 42e484fb00Snicm struct session *, const char *, u_int, char); 43d6317402Snicm 44d6317402Snicm struct status_prompt_menu { 45d6317402Snicm struct client *c; 46d6317402Snicm u_int start; 47d6317402Snicm u_int size; 48d6317402Snicm char **list; 49d6317402Snicm char flag; 50d6317402Snicm }; 51179ef399Snicm 52bc5a8fc2Snicm static const char *prompt_type_strings[] = { 53bc5a8fc2Snicm "command", 54bc5a8fc2Snicm "search", 55bc5a8fc2Snicm "target", 56bc5a8fc2Snicm "window-target" 57bc5a8fc2Snicm }; 58bc5a8fc2Snicm 594ee77f13Snicm /* Status prompt history. */ 60bc5a8fc2Snicm char **status_prompt_hlist[PROMPT_NTYPES]; 61bc5a8fc2Snicm u_int status_prompt_hsize[PROMPT_NTYPES]; 624ee77f13Snicm 63179ef399Snicm /* Find the history file to load/save from/to. */ 649883b791Snicm static char * 65179ef399Snicm status_prompt_find_history_file(void) 66179ef399Snicm { 67179ef399Snicm const char *home, *history_file; 68179ef399Snicm char *path; 69179ef399Snicm 70d89252e5Snicm history_file = options_get_string(global_options, "history-file"); 71179ef399Snicm if (*history_file == '\0') 72179ef399Snicm return (NULL); 73179ef399Snicm if (*history_file == '/') 74179ef399Snicm return (xstrdup(history_file)); 75179ef399Snicm 76179ef399Snicm if (history_file[0] != '~' || history_file[1] != '/') 77179ef399Snicm return (NULL); 78179ef399Snicm if ((home = find_home()) == NULL) 79179ef399Snicm return (NULL); 80179ef399Snicm xasprintf(&path, "%s%s", home, history_file + 1); 81179ef399Snicm return (path); 82179ef399Snicm } 83179ef399Snicm 84bc5a8fc2Snicm /* Add loaded history item to the appropriate list. */ 85bc5a8fc2Snicm static void 86bc5a8fc2Snicm status_prompt_add_typed_history(char *line) 87bc5a8fc2Snicm { 88bc5a8fc2Snicm char *typestr; 89bc5a8fc2Snicm enum prompt_type type = PROMPT_TYPE_INVALID; 90bc5a8fc2Snicm 91bc5a8fc2Snicm typestr = strsep(&line, ":"); 92bc5a8fc2Snicm if (line != NULL) 93bc5a8fc2Snicm type = status_prompt_type(typestr); 94bc5a8fc2Snicm if (type == PROMPT_TYPE_INVALID) { 95bc5a8fc2Snicm /* 96bc5a8fc2Snicm * Invalid types are not expected, but this provides backward 97bc5a8fc2Snicm * compatibility with old history files. 98bc5a8fc2Snicm */ 99bc5a8fc2Snicm if (line != NULL) 100bc5a8fc2Snicm *(--line) = ':'; 101bc5a8fc2Snicm status_prompt_add_history(typestr, PROMPT_TYPE_COMMAND); 102bc5a8fc2Snicm } else 103bc5a8fc2Snicm status_prompt_add_history(line, type); 104bc5a8fc2Snicm } 105bc5a8fc2Snicm 106179ef399Snicm /* Load status prompt history from file. */ 107179ef399Snicm void 108179ef399Snicm status_prompt_load_history(void) 109179ef399Snicm { 110179ef399Snicm FILE *f; 111179ef399Snicm char *history_file, *line, *tmp; 112179ef399Snicm size_t length; 113179ef399Snicm 114179ef399Snicm if ((history_file = status_prompt_find_history_file()) == NULL) 115179ef399Snicm return; 116179ef399Snicm log_debug("loading history from %s", history_file); 117179ef399Snicm 118179ef399Snicm f = fopen(history_file, "r"); 119179ef399Snicm if (f == NULL) { 120179ef399Snicm log_debug("%s: %s", history_file, strerror(errno)); 121179ef399Snicm free(history_file); 122179ef399Snicm return; 123179ef399Snicm } 124179ef399Snicm free(history_file); 125179ef399Snicm 126179ef399Snicm for (;;) { 127179ef399Snicm if ((line = fgetln(f, &length)) == NULL) 128179ef399Snicm break; 129179ef399Snicm 130179ef399Snicm if (length > 0) { 131179ef399Snicm if (line[length - 1] == '\n') { 132179ef399Snicm line[length - 1] = '\0'; 133bc5a8fc2Snicm status_prompt_add_typed_history(line); 134179ef399Snicm } else { 135179ef399Snicm tmp = xmalloc(length + 1); 136179ef399Snicm memcpy(tmp, line, length); 137179ef399Snicm tmp[length] = '\0'; 138bc5a8fc2Snicm status_prompt_add_typed_history(tmp); 139179ef399Snicm free(tmp); 140179ef399Snicm } 141179ef399Snicm } 142179ef399Snicm } 143179ef399Snicm fclose(f); 144179ef399Snicm } 145179ef399Snicm 146179ef399Snicm /* Save status prompt history to file. */ 147179ef399Snicm void 148179ef399Snicm status_prompt_save_history(void) 149179ef399Snicm { 150179ef399Snicm FILE *f; 151bc5a8fc2Snicm u_int i, type; 152179ef399Snicm char *history_file; 153179ef399Snicm 154179ef399Snicm if ((history_file = status_prompt_find_history_file()) == NULL) 155179ef399Snicm return; 156179ef399Snicm log_debug("saving history to %s", history_file); 157179ef399Snicm 158179ef399Snicm f = fopen(history_file, "w"); 159179ef399Snicm if (f == NULL) { 160179ef399Snicm log_debug("%s: %s", history_file, strerror(errno)); 161179ef399Snicm free(history_file); 162179ef399Snicm return; 163179ef399Snicm } 164179ef399Snicm free(history_file); 165179ef399Snicm 166bc5a8fc2Snicm for (type = 0; type < PROMPT_NTYPES; type++) { 167bc5a8fc2Snicm for (i = 0; i < status_prompt_hsize[type]; i++) { 168bc5a8fc2Snicm fputs(prompt_type_strings[type], f); 169bc5a8fc2Snicm fputc(':', f); 170bc5a8fc2Snicm fputs(status_prompt_hlist[type][i], f); 171179ef399Snicm fputc('\n', f); 172179ef399Snicm } 173bc5a8fc2Snicm } 174179ef399Snicm fclose(f); 175179ef399Snicm 176179ef399Snicm } 177179ef399Snicm 178e8f6715cSnicm /* Status timer callback. */ 1799883b791Snicm static void 180d0e2e7f1Snicm status_timer_callback(__unused int fd, __unused short events, void *arg) 181e8f6715cSnicm { 182e8f6715cSnicm struct client *c = arg; 183e8f6715cSnicm struct session *s = c->session; 184e8f6715cSnicm struct timeval tv; 185e8f6715cSnicm 186a4c723edSnicm evtimer_del(&c->status.timer); 187e8f6715cSnicm 188e8f6715cSnicm if (s == NULL) 189e8f6715cSnicm return; 190e8f6715cSnicm 191e8f6715cSnicm if (c->message_string == NULL && c->prompt_string == NULL) 192e7808201Snicm c->flags |= CLIENT_REDRAWSTATUS; 193e8f6715cSnicm 194e8f6715cSnicm timerclear(&tv); 195d89252e5Snicm tv.tv_sec = options_get_number(s->options, "status-interval"); 196e8f6715cSnicm 197e8f6715cSnicm if (tv.tv_sec != 0) 198a4c723edSnicm evtimer_add(&c->status.timer, &tv); 1997ad4d2ccSnicm log_debug("client %p, status interval %d", c, (int)tv.tv_sec); 200e8f6715cSnicm } 201e8f6715cSnicm 202e8f6715cSnicm /* Start status timer for client. */ 203e8f6715cSnicm void 204e8f6715cSnicm status_timer_start(struct client *c) 205e8f6715cSnicm { 206e8f6715cSnicm struct session *s = c->session; 207e8f6715cSnicm 208a4c723edSnicm if (event_initialized(&c->status.timer)) 209a4c723edSnicm evtimer_del(&c->status.timer); 210e8f6715cSnicm else 211a4c723edSnicm evtimer_set(&c->status.timer, status_timer_callback, c); 212e8f6715cSnicm 213d89252e5Snicm if (s != NULL && options_get_number(s->options, "status")) 214e8f6715cSnicm status_timer_callback(-1, 0, c); 215e8f6715cSnicm } 216e8f6715cSnicm 217e8f6715cSnicm /* Start status timer for all clients. */ 218e8f6715cSnicm void 219e8f6715cSnicm status_timer_start_all(void) 220e8f6715cSnicm { 221e8f6715cSnicm struct client *c; 222e8f6715cSnicm 223e8f6715cSnicm TAILQ_FOREACH(c, &clients, entry) 224e8f6715cSnicm status_timer_start(c); 225e8f6715cSnicm } 226e8f6715cSnicm 227e62b76d6Snicm /* Update status cache. */ 228e62b76d6Snicm void 229b2140406Snicm status_update_cache(struct session *s) 230e62b76d6Snicm { 2314ffcb1c8Snicm s->statuslines = options_get_number(s->options, "status"); 2324ffcb1c8Snicm if (s->statuslines == 0) 233e62b76d6Snicm s->statusat = -1; 234e62b76d6Snicm else if (options_get_number(s->options, "status-position") == 0) 235e62b76d6Snicm s->statusat = 0; 236e62b76d6Snicm else 237e62b76d6Snicm s->statusat = 1; 238e62b76d6Snicm } 239e62b76d6Snicm 240be5b7d79Snicm /* Get screen line of status line. -1 means off. */ 241be5b7d79Snicm int 242be5b7d79Snicm status_at_line(struct client *c) 243be5b7d79Snicm { 244be5b7d79Snicm struct session *s = c->session; 245be5b7d79Snicm 246f415a97bSnicm if (c->flags & (CLIENT_STATUSOFF|CLIENT_CONTROL)) 247d75a8bcfSnicm return (-1); 248e62b76d6Snicm if (s->statusat != 1) 249e62b76d6Snicm return (s->statusat); 2507b470e93Snicm return (c->tty.sy - status_line_size(c)); 251d75a8bcfSnicm } 252d75a8bcfSnicm 2537b470e93Snicm /* Get size of status line for client's session. 0 means off. */ 254d75a8bcfSnicm u_int 2557b470e93Snicm status_line_size(struct client *c) 256d75a8bcfSnicm { 2577b470e93Snicm struct session *s = c->session; 2587b470e93Snicm 259f415a97bSnicm if (c->flags & (CLIENT_STATUSOFF|CLIENT_CONTROL)) 2607b470e93Snicm return (0); 2612077c06bSnicm if (s == NULL) 2622077c06bSnicm return (options_get_number(global_s_options, "status")); 2634ffcb1c8Snicm return (s->statuslines); 2640f8aceb2Snicm } 2650f8aceb2Snicm 266c755e00aSnicm /* Get the prompt line number for client's session. 1 means at the bottom. */ 267c755e00aSnicm static u_int 268c755e00aSnicm status_prompt_line_at(struct client *c) 269c755e00aSnicm { 270c755e00aSnicm struct session *s = c->session; 271c755e00aSnicm 272c755e00aSnicm if (c->flags & (CLIENT_STATUSOFF|CLIENT_CONTROL)) 273c755e00aSnicm return (1); 2740cf631b5Snicm return (options_get_number(s->options, "message-line")); 275c755e00aSnicm } 276c755e00aSnicm 277e048bb79Snicm /* Get window at window list position. */ 2784ffcb1c8Snicm struct style_range * 2794ffcb1c8Snicm status_get_range(struct client *c, u_int x, u_int y) 280da26b0f5Snicm { 2814ffcb1c8Snicm struct status_line *sl = &c->status; 2824ffcb1c8Snicm struct style_range *sr; 283da26b0f5Snicm 2844ffcb1c8Snicm if (y >= nitems(sl->entries)) 2854ffcb1c8Snicm return (NULL); 2864ffcb1c8Snicm TAILQ_FOREACH(sr, &sl->entries[y].ranges, entry) { 2874ffcb1c8Snicm if (x >= sr->start && x < sr->end) 2884ffcb1c8Snicm return (sr); 289da26b0f5Snicm } 290e048bb79Snicm return (NULL); 291da26b0f5Snicm } 292da26b0f5Snicm 2934ffcb1c8Snicm /* Free all ranges. */ 2944ffcb1c8Snicm static void 2954ffcb1c8Snicm status_free_ranges(struct style_ranges *srs) 2964ffcb1c8Snicm { 2974ffcb1c8Snicm struct style_range *sr, *sr1; 2984ffcb1c8Snicm 2994ffcb1c8Snicm TAILQ_FOREACH_SAFE(sr, srs, entry, sr1) { 3004ffcb1c8Snicm TAILQ_REMOVE(srs, sr, entry); 3014ffcb1c8Snicm free(sr); 3024ffcb1c8Snicm } 3034ffcb1c8Snicm } 3044ffcb1c8Snicm 3058e497f9eSnicm /* Save old status line. */ 3068e497f9eSnicm static void 3078e497f9eSnicm status_push_screen(struct client *c) 3088e497f9eSnicm { 3098e497f9eSnicm struct status_line *sl = &c->status; 3108e497f9eSnicm 3118e497f9eSnicm if (sl->active == &sl->screen) { 3128e497f9eSnicm sl->active = xmalloc(sizeof *sl->active); 3138e497f9eSnicm screen_init(sl->active, c->tty.sx, status_line_size(c), 0); 3148e497f9eSnicm } 3158e497f9eSnicm sl->references++; 3168e497f9eSnicm } 3178e497f9eSnicm 3188e497f9eSnicm /* Restore old status line. */ 3198e497f9eSnicm static void 3208e497f9eSnicm status_pop_screen(struct client *c) 3218e497f9eSnicm { 3228e497f9eSnicm struct status_line *sl = &c->status; 3238e497f9eSnicm 3248e497f9eSnicm if (--sl->references == 0) { 3258e497f9eSnicm screen_free(sl->active); 3268e497f9eSnicm free(sl->active); 3278e497f9eSnicm sl->active = &sl->screen; 3288e497f9eSnicm } 3298e497f9eSnicm } 3308e497f9eSnicm 331b2140406Snicm /* Initialize status line. */ 332b2140406Snicm void 333b2140406Snicm status_init(struct client *c) 334b2140406Snicm { 335b2140406Snicm struct status_line *sl = &c->status; 3364ffcb1c8Snicm u_int i; 3374ffcb1c8Snicm 3384ffcb1c8Snicm for (i = 0; i < nitems(sl->entries); i++) 3394ffcb1c8Snicm TAILQ_INIT(&sl->entries[i].ranges); 340b2140406Snicm 341b2140406Snicm screen_init(&sl->screen, c->tty.sx, 1, 0); 3428e497f9eSnicm sl->active = &sl->screen; 343b2140406Snicm } 344b2140406Snicm 34594912b4eSnicm /* Free status line. */ 34694912b4eSnicm void 34794912b4eSnicm status_free(struct client *c) 34894912b4eSnicm { 34994912b4eSnicm struct status_line *sl = &c->status; 3504ffcb1c8Snicm u_int i; 3514ffcb1c8Snicm 3524ffcb1c8Snicm for (i = 0; i < nitems(sl->entries); i++) { 3534ffcb1c8Snicm status_free_ranges(&sl->entries[i].ranges); 3544ffcb1c8Snicm free((void *)sl->entries[i].expanded); 3554ffcb1c8Snicm } 35694912b4eSnicm 35794912b4eSnicm if (event_initialized(&sl->timer)) 35894912b4eSnicm evtimer_del(&sl->timer); 35994912b4eSnicm 3608e497f9eSnicm if (sl->active != &sl->screen) { 3618e497f9eSnicm screen_free(sl->active); 3628e497f9eSnicm free(sl->active); 3638e497f9eSnicm } 364b2140406Snicm screen_free(&sl->screen); 36594912b4eSnicm } 36694912b4eSnicm 36794912b4eSnicm /* Draw status line for client. */ 368311827fbSnicm int 369311827fbSnicm status_redraw(struct client *c) 370311827fbSnicm { 371b2140406Snicm struct status_line *sl = &c->status; 3724ffcb1c8Snicm struct status_line_entry *sle; 373311827fbSnicm struct session *s = c->session; 3744ffcb1c8Snicm struct screen_write_ctx ctx; 3754ffcb1c8Snicm struct grid_cell gc; 376d90eabadSnicm u_int lines, i, n, width = c->tty.sx; 37701c0c428Snicm int flags, force = 0, changed = 0, fg, bg; 3784ffcb1c8Snicm struct options_entry *o; 37984306383Snicm union options_value *ov; 3804ffcb1c8Snicm struct format_tree *ft; 3814ffcb1c8Snicm char *expanded; 3824ffcb1c8Snicm 3834ffcb1c8Snicm log_debug("%s enter", __func__); 384311827fbSnicm 3858e497f9eSnicm /* Shouldn't get here if not the active screen. */ 3868e497f9eSnicm if (sl->active != &sl->screen) 3878e497f9eSnicm fatalx("not the active screen"); 388a058cf74Snicm 38979fc95a8Snicm /* No status line? */ 3907b470e93Snicm lines = status_line_size(c); 391d75a8bcfSnicm if (c->tty.sy == 0 || lines == 0) 39279fc95a8Snicm return (1); 39379fc95a8Snicm 394caa5d2c6Snicm /* Create format tree. */ 395caa5d2c6Snicm flags = FORMAT_STATUS; 396caa5d2c6Snicm if (c->flags & CLIENT_STATUSFORCE) 397caa5d2c6Snicm flags |= FORMAT_FORCE; 398caa5d2c6Snicm ft = format_create(c, NULL, FORMAT_NONE, flags); 399caa5d2c6Snicm format_defaults(ft, c, NULL, NULL, NULL); 400caa5d2c6Snicm 4010f8aceb2Snicm /* Set up default colour. */ 402caa5d2c6Snicm style_apply(&gc, s->options, "status-style", ft); 40301c0c428Snicm fg = options_get_number(s->options, "status-fg"); 404d5510c2eSnicm if (!COLOUR_DEFAULT(fg)) 40501c0c428Snicm gc.fg = fg; 40601c0c428Snicm bg = options_get_number(s->options, "status-bg"); 407d5510c2eSnicm if (!COLOUR_DEFAULT(bg)) 40801c0c428Snicm gc.bg = bg; 4094ffcb1c8Snicm if (!grid_cells_equal(&gc, &sl->style)) { 4104ffcb1c8Snicm force = 1; 4114ffcb1c8Snicm memcpy(&sl->style, &gc, sizeof sl->style); 4120f8aceb2Snicm } 4130f8aceb2Snicm 4144ffcb1c8Snicm /* Resize the target screen. */ 4154ffcb1c8Snicm if (screen_size_x(&sl->screen) != width || 4164ffcb1c8Snicm screen_size_y(&sl->screen) != lines) { 4174ffcb1c8Snicm screen_resize(&sl->screen, width, lines, 0); 41834b35fabSnicm changed = force = 1; 4190f8aceb2Snicm } 42083e83a91Snicm screen_write_start(&ctx, &sl->screen); 4210f8aceb2Snicm 4224ffcb1c8Snicm /* Write the status lines. */ 4234ffcb1c8Snicm o = options_get(s->options, "status-format"); 424d90eabadSnicm if (o == NULL) { 425d90eabadSnicm for (n = 0; n < width * lines; n++) 426d90eabadSnicm screen_write_putc(&ctx, &gc, ' '); 427d90eabadSnicm } else { 4284ffcb1c8Snicm for (i = 0; i < lines; i++) { 4294ffcb1c8Snicm screen_write_cursormove(&ctx, 0, i, 0); 4304ffcb1c8Snicm 43184306383Snicm ov = options_array_get(o, i); 43284306383Snicm if (ov == NULL) { 433d90eabadSnicm for (n = 0; n < width; n++) 434d90eabadSnicm screen_write_putc(&ctx, &gc, ' '); 4354ffcb1c8Snicm continue; 4364ffcb1c8Snicm } 4374ffcb1c8Snicm sle = &sl->entries[i]; 438afe199e1Snicm 43984306383Snicm expanded = format_expand_time(ft, ov->string); 4404ffcb1c8Snicm if (!force && 4414ffcb1c8Snicm sle->expanded != NULL && 4424ffcb1c8Snicm strcmp(expanded, sle->expanded) == 0) { 4434ffcb1c8Snicm free(expanded); 4444ffcb1c8Snicm continue; 4454ffcb1c8Snicm } 4464ffcb1c8Snicm changed = 1; 447afe199e1Snicm 448d90eabadSnicm for (n = 0; n < width; n++) 449d90eabadSnicm screen_write_putc(&ctx, &gc, ' '); 450d90eabadSnicm screen_write_cursormove(&ctx, 0, i, 0); 451d90eabadSnicm 4524ffcb1c8Snicm status_free_ranges(&sle->ranges); 453173e8225Snicm format_draw(&ctx, &gc, width, expanded, &sle->ranges, 454173e8225Snicm 0); 4554ffcb1c8Snicm 4564ffcb1c8Snicm free(sle->expanded); 4574ffcb1c8Snicm sle->expanded = expanded; 4584ffcb1c8Snicm } 4594ffcb1c8Snicm } 4604ffcb1c8Snicm screen_write_stop(&ctx); 4614ffcb1c8Snicm 4624ffcb1c8Snicm /* Free the format tree. */ 463ba28090cSnicm format_free(ft); 464311827fbSnicm 4654ffcb1c8Snicm /* Return if the status line has changed. */ 4664ffcb1c8Snicm log_debug("%s exit: force=%d, changed=%d", __func__, force, changed); 4674ffcb1c8Snicm return (force || changed); 468311827fbSnicm } 469311827fbSnicm 4701ae39a95Snicm /* Set a status line message. */ 47174d4b937Snicm void 472247fdabfSnicm status_message_set(struct client *c, int delay, int ignore_styles, 473e7e79d0aSnicm int ignore_keys, const char *fmt, ...) 474311827fbSnicm { 475311827fbSnicm struct timeval tv; 47622864d78Snicm va_list ap; 47731e1eab0Snicm char *s; 47831e1eab0Snicm 47931e1eab0Snicm va_start(ap, fmt); 48031e1eab0Snicm xvasprintf(&s, fmt, ap); 48131e1eab0Snicm va_end(ap); 48231e1eab0Snicm 48331e1eab0Snicm log_debug("%s: %s", __func__, s); 48431e1eab0Snicm 48531e1eab0Snicm if (c == NULL) { 48631e1eab0Snicm server_add_message("message: %s", s); 48731e1eab0Snicm free(s); 48831e1eab0Snicm return; 48931e1eab0Snicm } 490311827fbSnicm 491e468f8d3Snicm status_message_clear(c); 4928e497f9eSnicm status_push_screen(c); 49331e1eab0Snicm c->message_string = s; 49431e1eab0Snicm server_add_message("%s message: %s", c->name, s); 495dca899eaSnicm 496247fdabfSnicm /* 497247fdabfSnicm * With delay -1, the display-time option is used; zero means wait for 498247fdabfSnicm * key press; more than zero is the actual delay time in milliseconds. 499247fdabfSnicm */ 500247fdabfSnicm if (delay == -1) 501d89252e5Snicm delay = options_get_number(c->session->options, "display-time"); 502b62d8cfbStim if (delay > 0) { 503311827fbSnicm tv.tv_sec = delay / 1000; 504311827fbSnicm tv.tv_usec = (delay % 1000) * 1000L; 505311827fbSnicm 506346357b7Snicm if (event_initialized(&c->message_timer)) 507c5e332b7Snicm evtimer_del(&c->message_timer); 508c5e332b7Snicm evtimer_set(&c->message_timer, status_message_callback, c); 509247fdabfSnicm 510c5e332b7Snicm evtimer_add(&c->message_timer, &tv); 511b62d8cfbStim } 512311827fbSnicm 513e7e79d0aSnicm if (delay != 0) 514e7e79d0aSnicm c->message_ignore_keys = ignore_keys; 515e7e79d0aSnicm c->message_ignore_styles = ignore_styles; 516e7e79d0aSnicm 517311827fbSnicm c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE); 518e7808201Snicm c->flags |= CLIENT_REDRAWSTATUS; 519311827fbSnicm } 520311827fbSnicm 5211ae39a95Snicm /* Clear status line message. */ 522311827fbSnicm void 523311827fbSnicm status_message_clear(struct client *c) 524311827fbSnicm { 525311827fbSnicm if (c->message_string == NULL) 526311827fbSnicm return; 527311827fbSnicm 5287d053cf9Snicm free(c->message_string); 529311827fbSnicm c->message_string = NULL; 530311827fbSnicm 53194837f72Snicm if (c->prompt_string == NULL) 532311827fbSnicm c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE); 533e7808201Snicm c->flags |= CLIENT_ALLREDRAWFLAGS; /* was frozen and may have changed */ 53479fc95a8Snicm 5358e497f9eSnicm status_pop_screen(c); 536311827fbSnicm } 537311827fbSnicm 5381ae39a95Snicm /* Clear status line message after timer expires. */ 5399883b791Snicm static void 540d0e2e7f1Snicm status_message_callback(__unused int fd, __unused short event, void *data) 541c5e332b7Snicm { 542c5e332b7Snicm struct client *c = data; 543c5e332b7Snicm 544c5e332b7Snicm status_message_clear(c); 545c5e332b7Snicm } 546c5e332b7Snicm 547311827fbSnicm /* Draw client message on status line of present else on last line. */ 548311827fbSnicm int 549311827fbSnicm status_message_redraw(struct client *c) 550311827fbSnicm { 5518e497f9eSnicm struct status_line *sl = &c->status; 552311827fbSnicm struct screen_write_ctx ctx; 553311827fbSnicm struct session *s = c->session; 5548e497f9eSnicm struct screen old_screen; 555311827fbSnicm size_t len; 556c755e00aSnicm u_int lines, offset, messageline; 5578e497f9eSnicm struct grid_cell gc; 558caa5d2c6Snicm struct format_tree *ft; 559311827fbSnicm 560311827fbSnicm if (c->tty.sx == 0 || c->tty.sy == 0) 561311827fbSnicm return (0); 5628e497f9eSnicm memcpy(&old_screen, sl->active, sizeof old_screen); 563d75a8bcfSnicm 5647b470e93Snicm lines = status_line_size(c); 5658e497f9eSnicm if (lines <= 1) 5668ff11a1cSnicm lines = 1; 567483c5500Snicm screen_init(sl->active, c->tty.sx, lines, 0); 568311827fbSnicm 569c755e00aSnicm messageline = status_prompt_line_at(c); 570c755e00aSnicm if (messageline > lines - 1) 571c755e00aSnicm messageline = lines - 1; 572c755e00aSnicm 573f650d6adSnicm len = screen_write_strlen("%s", c->message_string); 574311827fbSnicm if (len > c->tty.sx) 575311827fbSnicm len = c->tty.sx; 576311827fbSnicm 577caa5d2c6Snicm ft = format_create_defaults(NULL, c, NULL, NULL, NULL); 578caa5d2c6Snicm style_apply(&gc, s->options, "message-style", ft); 579caa5d2c6Snicm format_free(ft); 580311827fbSnicm 58183e83a91Snicm screen_write_start(&ctx, sl->active); 582c755e00aSnicm screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines); 583c755e00aSnicm screen_write_cursormove(&ctx, 0, messageline, 0); 5844ffcb1c8Snicm for (offset = 0; offset < c->tty.sx; offset++) 58534f5d6a0Snicm screen_write_putc(&ctx, &gc, ' '); 586c755e00aSnicm screen_write_cursormove(&ctx, 0, messageline, 0); 5874f4307f9Snicm if (c->message_ignore_styles) 588f650d6adSnicm screen_write_nputs(&ctx, len, &gc, "%s", c->message_string); 5894f4307f9Snicm else 590173e8225Snicm format_draw(&ctx, &gc, c->tty.sx, c->message_string, NULL, 0); 591311827fbSnicm screen_write_stop(&ctx); 592311827fbSnicm 5938e497f9eSnicm if (grid_compare(sl->active->grid, old_screen.grid) == 0) { 5948e497f9eSnicm screen_free(&old_screen); 595311827fbSnicm return (0); 596311827fbSnicm } 5978e497f9eSnicm screen_free(&old_screen); 598311827fbSnicm return (1); 599311827fbSnicm } 600311827fbSnicm 601af11b61dSnicm /* Accept prompt immediately. */ 602af11b61dSnicm static enum cmd_retval 603af11b61dSnicm status_prompt_accept(__unused struct cmdq_item *item, void *data) 604af11b61dSnicm { 605af11b61dSnicm struct client *c = data; 606af11b61dSnicm 607af11b61dSnicm if (c->prompt_string != NULL) { 608af11b61dSnicm c->prompt_inputcb(c, c->prompt_data, "y", 1); 609af11b61dSnicm status_prompt_clear(c); 610af11b61dSnicm } 611af11b61dSnicm return (CMD_RETURN_NORMAL); 612af11b61dSnicm } 613af11b61dSnicm 6141ae39a95Snicm /* Enable status line prompt. */ 615311827fbSnicm void 61694adf770Snicm status_prompt_set(struct client *c, struct cmd_find_state *fs, 61794adf770Snicm const char *msg, const char *input, prompt_input_cb inputcb, 618bc5a8fc2Snicm prompt_free_cb freecb, void *data, int flags, enum prompt_type prompt_type) 619311827fbSnicm { 620fb0b819bSnicm struct format_tree *ft; 6217e7432ecSnicm char *tmp; 622fb0b819bSnicm 6233979f694Snicm server_client_clear_overlay(c); 6243979f694Snicm 62594adf770Snicm if (fs != NULL) 62694adf770Snicm ft = format_create_from_state(NULL, c, fs); 62794adf770Snicm else 62894adf770Snicm ft = format_create_defaults(NULL, c, NULL, NULL, NULL); 62901948674Snicm 63001948674Snicm if (input == NULL) 63101948674Snicm input = ""; 63201948674Snicm if (flags & PROMPT_NOFORMAT) 63301948674Snicm tmp = xstrdup(input); 63401948674Snicm else 635a7d9196cSnicm tmp = format_expand_time(ft, input); 636c538f0fcSnicm 637e468f8d3Snicm status_message_clear(c); 638e468f8d3Snicm status_prompt_clear(c); 6398e497f9eSnicm status_push_screen(c); 640a058cf74Snicm 641a7d9196cSnicm c->prompt_string = format_expand_time(ft, msg); 642311827fbSnicm 6437e7432ecSnicm if (flags & PROMPT_INCREMENTAL) { 6447e7432ecSnicm c->prompt_last = xstrdup(tmp); 6457e7432ecSnicm c->prompt_buffer = utf8_fromcstr(""); 6467e7432ecSnicm } else { 6477e7432ecSnicm c->prompt_last = NULL; 648746b61e4Snicm c->prompt_buffer = utf8_fromcstr(tmp); 6497e7432ecSnicm } 650746b61e4Snicm c->prompt_index = utf8_strlen(c->prompt_buffer); 651311827fbSnicm 6523bf5ffecSnicm c->prompt_inputcb = inputcb; 6533bf5ffecSnicm c->prompt_freecb = freecb; 654311827fbSnicm c->prompt_data = data; 655311827fbSnicm 656bc5a8fc2Snicm memset(c->prompt_hindex, 0, sizeof c->prompt_hindex); 657311827fbSnicm 658311827fbSnicm c->prompt_flags = flags; 659bc5a8fc2Snicm c->prompt_type = prompt_type; 660ce3f476aSnicm c->prompt_mode = PROMPT_ENTRY; 661311827fbSnicm 662edd3b079Snicm if (~flags & PROMPT_INCREMENTAL) 66332099560Snicm c->tty.flags |= TTY_FREEZE; 664e7808201Snicm c->flags |= CLIENT_REDRAWSTATUS; 665fb0b819bSnicm 6667e7432ecSnicm if (flags & PROMPT_INCREMENTAL) 6677e7432ecSnicm c->prompt_inputcb(c, c->prompt_data, "=", 0); 668332346e2Snicm 669746b61e4Snicm free(tmp); 670fb0b819bSnicm format_free(ft); 671af11b61dSnicm 672af11b61dSnicm if ((flags & PROMPT_SINGLE) && (flags & PROMPT_ACCEPT)) 673af11b61dSnicm cmdq_append(c, cmdq_get_callback(status_prompt_accept, c)); 674311827fbSnicm } 675311827fbSnicm 6761ae39a95Snicm /* Remove status line prompt. */ 677311827fbSnicm void 678311827fbSnicm status_prompt_clear(struct client *c) 679311827fbSnicm { 680311827fbSnicm if (c->prompt_string == NULL) 681311827fbSnicm return; 682311827fbSnicm 6833bf5ffecSnicm if (c->prompt_freecb != NULL && c->prompt_data != NULL) 6843bf5ffecSnicm c->prompt_freecb(c->prompt_data); 685e468f8d3Snicm 6867e7432ecSnicm free(c->prompt_last); 6877e7432ecSnicm c->prompt_last = NULL; 6887e7432ecSnicm 6897d053cf9Snicm free(c->prompt_string); 690311827fbSnicm c->prompt_string = NULL; 691311827fbSnicm 6927d053cf9Snicm free(c->prompt_buffer); 693311827fbSnicm c->prompt_buffer = NULL; 694311827fbSnicm 69543a68934Snicm free(c->prompt_saved); 69643a68934Snicm c->prompt_saved = NULL; 69743a68934Snicm 698311827fbSnicm c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE); 699e7808201Snicm c->flags |= CLIENT_ALLREDRAWFLAGS; /* was frozen and may have changed */ 70079fc95a8Snicm 7018e497f9eSnicm status_pop_screen(c); 702311827fbSnicm } 703311827fbSnicm 7041ae39a95Snicm /* Update status line prompt with a new prompt string. */ 7053a89195fSnicm void 706b3a5ab26Snicm status_prompt_update(struct client *c, const char *msg, const char *input) 7073a89195fSnicm { 708fb0b819bSnicm struct format_tree *ft; 709746b61e4Snicm char *tmp; 710fb0b819bSnicm 711d559dd45Snicm ft = format_create(c, NULL, FORMAT_NONE, 0); 712fb0b819bSnicm format_defaults(ft, c, NULL, NULL, NULL); 713746b61e4Snicm 714a7d9196cSnicm tmp = format_expand_time(ft, input); 715fb0b819bSnicm 7167d053cf9Snicm free(c->prompt_string); 717a7d9196cSnicm c->prompt_string = format_expand_time(ft, msg); 7183a89195fSnicm 7197d053cf9Snicm free(c->prompt_buffer); 720746b61e4Snicm c->prompt_buffer = utf8_fromcstr(tmp); 721746b61e4Snicm c->prompt_index = utf8_strlen(c->prompt_buffer); 7223a89195fSnicm 723bc5a8fc2Snicm memset(c->prompt_hindex, 0, sizeof c->prompt_hindex); 7243a89195fSnicm 725e7808201Snicm c->flags |= CLIENT_REDRAWSTATUS; 726fb0b819bSnicm 727746b61e4Snicm free(tmp); 728fb0b819bSnicm format_free(ft); 7293a89195fSnicm } 7303a89195fSnicm 731*0460263cSnicm /* Redraw character. Return 1 if can continue redrawing, 0 otherwise. */ 732*0460263cSnicm static int 733*0460263cSnicm status_prompt_redraw_character(struct screen_write_ctx *ctx, u_int offset, 734*0460263cSnicm u_int pwidth, u_int *width, struct grid_cell *gc, 735*0460263cSnicm const struct utf8_data *ud) 736*0460263cSnicm { 737*0460263cSnicm u_char ch; 738*0460263cSnicm 739*0460263cSnicm if (*width < offset) { 740*0460263cSnicm *width += ud->width; 741*0460263cSnicm return (1); 742*0460263cSnicm } 743*0460263cSnicm if (*width >= offset + pwidth) 744*0460263cSnicm return (0); 745*0460263cSnicm *width += ud->width; 746*0460263cSnicm if (*width > offset + pwidth) 747*0460263cSnicm return (0); 748*0460263cSnicm 749*0460263cSnicm ch = *ud->data; 750*0460263cSnicm if (ud->size == 1 && (ch <= 0x1f || ch == 0x7f)) { 751*0460263cSnicm gc->data.data[0] = '^'; 752*0460263cSnicm gc->data.data[1] = (ch == 0x7f) ? '?' : ch|0x40; 753*0460263cSnicm gc->data.size = gc->data.have = 2; 754*0460263cSnicm gc->data.width = 2; 755*0460263cSnicm } else 756*0460263cSnicm utf8_copy(&gc->data, ud); 757*0460263cSnicm screen_write_cell(ctx, gc); 758*0460263cSnicm return (1); 759*0460263cSnicm } 760*0460263cSnicm 761*0460263cSnicm /* 762*0460263cSnicm * Redraw quote indicator '^' if necessary. Return 1 if can continue redrawing, 763*0460263cSnicm * 0 otherwise. 764*0460263cSnicm */ 765*0460263cSnicm static int 766*0460263cSnicm status_prompt_redraw_quote(const struct client *c, u_int pcursor, 767*0460263cSnicm struct screen_write_ctx *ctx, u_int offset, u_int pwidth, u_int *width, 768*0460263cSnicm struct grid_cell *gc) 769*0460263cSnicm { 770*0460263cSnicm struct utf8_data ud; 771*0460263cSnicm 772*0460263cSnicm if (c->prompt_flags & PROMPT_QUOTENEXT && ctx->s->cx == pcursor + 1) { 773*0460263cSnicm utf8_set(&ud, '^'); 774*0460263cSnicm return (status_prompt_redraw_character(ctx, offset, pwidth, 775*0460263cSnicm width, gc, &ud)); 776*0460263cSnicm } 777*0460263cSnicm return (1); 778*0460263cSnicm } 779*0460263cSnicm 780311827fbSnicm /* Draw client prompt on status line of present else on last line. */ 781311827fbSnicm int 782311827fbSnicm status_prompt_redraw(struct client *c) 783311827fbSnicm { 7848e497f9eSnicm struct status_line *sl = &c->status; 785311827fbSnicm struct screen_write_ctx ctx; 786311827fbSnicm struct session *s = c->session; 7878e497f9eSnicm struct screen old_screen; 7882b436e6eSnicm u_int i, lines, offset, left, start, width, n; 789c755e00aSnicm u_int pcursor, pwidth, promptline; 79032099560Snicm struct grid_cell gc; 791caa5d2c6Snicm struct format_tree *ft; 792311827fbSnicm 793311827fbSnicm if (c->tty.sx == 0 || c->tty.sy == 0) 794311827fbSnicm return (0); 7958e497f9eSnicm memcpy(&old_screen, sl->active, sizeof old_screen); 796d75a8bcfSnicm 7977b470e93Snicm lines = status_line_size(c); 7988e497f9eSnicm if (lines <= 1) 7998ff11a1cSnicm lines = 1; 8008e497f9eSnicm screen_init(sl->active, c->tty.sx, lines, 0); 801d75a8bcfSnicm 8022b436e6eSnicm n = options_get_number(s->options, "prompt-cursor-colour"); 8032b436e6eSnicm sl->active->default_ccolour = n; 8042b436e6eSnicm n = options_get_number(s->options, "prompt-cursor-style"); 8052b436e6eSnicm screen_set_cursor_style(n, &sl->active->default_cstyle, 8062b436e6eSnicm &sl->active->default_mode); 8072b436e6eSnicm 808c755e00aSnicm promptline = status_prompt_line_at(c); 809c755e00aSnicm if (promptline > lines - 1) 810c755e00aSnicm promptline = lines - 1; 811c755e00aSnicm 812caa5d2c6Snicm ft = format_create_defaults(NULL, c, NULL, NULL, NULL); 813ce3f476aSnicm if (c->prompt_mode == PROMPT_COMMAND) 814caa5d2c6Snicm style_apply(&gc, s->options, "message-command-style", ft); 815a8c9f839Snicm else 816caa5d2c6Snicm style_apply(&gc, s->options, "message-style", ft); 817caa5d2c6Snicm format_free(ft); 818311827fbSnicm 819b492da73Snicm start = format_width(c->prompt_string); 820746b61e4Snicm if (start > c->tty.sx) 821746b61e4Snicm start = c->tty.sx; 822746b61e4Snicm 82383e83a91Snicm screen_write_start(&ctx, sl->active); 824c755e00aSnicm screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines); 825c755e00aSnicm screen_write_cursormove(&ctx, 0, promptline, 0); 8264ffcb1c8Snicm for (offset = 0; offset < c->tty.sx; offset++) 82734f5d6a0Snicm screen_write_putc(&ctx, &gc, ' '); 828c755e00aSnicm screen_write_cursormove(&ctx, 0, promptline, 0); 829b492da73Snicm format_draw(&ctx, &gc, start, c->prompt_string, NULL, 0); 830c755e00aSnicm screen_write_cursormove(&ctx, start, promptline, 0); 831746b61e4Snicm 832746b61e4Snicm left = c->tty.sx - start; 833746b61e4Snicm if (left == 0) 834746b61e4Snicm goto finished; 835746b61e4Snicm 836746b61e4Snicm pcursor = utf8_strwidth(c->prompt_buffer, c->prompt_index); 837746b61e4Snicm pwidth = utf8_strwidth(c->prompt_buffer, -1); 838*0460263cSnicm if (c->prompt_flags & PROMPT_QUOTENEXT) 839*0460263cSnicm pwidth++; 840746b61e4Snicm if (pcursor >= left) { 841746b61e4Snicm /* 842746b61e4Snicm * The cursor would be outside the screen so start drawing 843746b61e4Snicm * with it on the right. 844746b61e4Snicm */ 845746b61e4Snicm offset = (pcursor - left) + 1; 846746b61e4Snicm pwidth = left; 847746b61e4Snicm } else 848746b61e4Snicm offset = 0; 849746b61e4Snicm if (pwidth > left) 850746b61e4Snicm pwidth = left; 851*0460263cSnicm c->prompt_cursor = start + pcursor - offset; 852746b61e4Snicm 853746b61e4Snicm width = 0; 854746b61e4Snicm for (i = 0; c->prompt_buffer[i].size != 0; i++) { 855*0460263cSnicm if (!status_prompt_redraw_quote(c, pcursor, &ctx, offset, 856*0460263cSnicm pwidth, &width, &gc)) 857746b61e4Snicm break; 858*0460263cSnicm if (!status_prompt_redraw_character(&ctx, offset, pwidth, 859*0460263cSnicm &width, &gc, &c->prompt_buffer[i])) 860746b61e4Snicm break; 861746b61e4Snicm } 862*0460263cSnicm status_prompt_redraw_quote(c, pcursor, &ctx, offset, pwidth, &width, 863*0460263cSnicm &gc); 864746b61e4Snicm 865746b61e4Snicm finished: 866311827fbSnicm screen_write_stop(&ctx); 867311827fbSnicm 8688e497f9eSnicm if (grid_compare(sl->active->grid, old_screen.grid) == 0) { 8698e497f9eSnicm screen_free(&old_screen); 870311827fbSnicm return (0); 871311827fbSnicm } 8728e497f9eSnicm screen_free(&old_screen); 873311827fbSnicm return (1); 874311827fbSnicm } 875311827fbSnicm 876746b61e4Snicm /* Is this a separator? */ 877746b61e4Snicm static int 878746b61e4Snicm status_prompt_in_list(const char *ws, const struct utf8_data *ud) 879746b61e4Snicm { 880746b61e4Snicm if (ud->size != 1 || ud->width != 1) 881746b61e4Snicm return (0); 882746b61e4Snicm return (strchr(ws, *ud->data) != NULL); 883746b61e4Snicm } 884746b61e4Snicm 885746b61e4Snicm /* Is this a space? */ 886746b61e4Snicm static int 887746b61e4Snicm status_prompt_space(const struct utf8_data *ud) 888746b61e4Snicm { 889746b61e4Snicm if (ud->size != 1 || ud->width != 1) 890746b61e4Snicm return (0); 891746b61e4Snicm return (*ud->data == ' '); 892746b61e4Snicm } 893746b61e4Snicm 894ce3f476aSnicm /* 895bc5a8fc2Snicm * Translate key from vi to emacs. Return 0 to drop key, 1 to process the key 896ce3f476aSnicm * as an emacs key; return 2 to append to the buffer. 897ce3f476aSnicm */ 898ce3f476aSnicm static int 899ce3f476aSnicm status_prompt_translate_key(struct client *c, key_code key, key_code *new_key) 900ce3f476aSnicm { 901ce3f476aSnicm if (c->prompt_mode == PROMPT_ENTRY) { 902ce3f476aSnicm switch (key) { 903719f5715Snicm case 'a'|KEYC_CTRL: 904719f5715Snicm case 'c'|KEYC_CTRL: 905719f5715Snicm case 'e'|KEYC_CTRL: 906719f5715Snicm case 'g'|KEYC_CTRL: 907719f5715Snicm case 'h'|KEYC_CTRL: 908ce3f476aSnicm case '\011': /* Tab */ 909719f5715Snicm case 'k'|KEYC_CTRL: 910719f5715Snicm case 'n'|KEYC_CTRL: 911719f5715Snicm case 'p'|KEYC_CTRL: 912719f5715Snicm case 't'|KEYC_CTRL: 913719f5715Snicm case 'u'|KEYC_CTRL: 914e505ea96Snicm case 'v'|KEYC_CTRL: 915719f5715Snicm case 'w'|KEYC_CTRL: 916719f5715Snicm case 'y'|KEYC_CTRL: 917ce3f476aSnicm case '\n': 918ce3f476aSnicm case '\r': 919e2dae3e1Snicm case KEYC_LEFT|KEYC_CTRL: 920e2dae3e1Snicm case KEYC_RIGHT|KEYC_CTRL: 921ce3f476aSnicm case KEYC_BSPACE: 922ce3f476aSnicm case KEYC_DC: 923ce3f476aSnicm case KEYC_DOWN: 924ce3f476aSnicm case KEYC_END: 925ce3f476aSnicm case KEYC_HOME: 926ce3f476aSnicm case KEYC_LEFT: 927ce3f476aSnicm case KEYC_RIGHT: 928ce3f476aSnicm case KEYC_UP: 929ce3f476aSnicm *new_key = key; 930ce3f476aSnicm return (1); 931ce3f476aSnicm case '\033': /* Escape */ 932ce3f476aSnicm c->prompt_mode = PROMPT_COMMAND; 933e7808201Snicm c->flags |= CLIENT_REDRAWSTATUS; 934ce3f476aSnicm return (0); 935ce3f476aSnicm } 936ce3f476aSnicm *new_key = key; 937ce3f476aSnicm return (2); 938ce3f476aSnicm } 939ce3f476aSnicm 940ce3f476aSnicm switch (key) { 941e2dae3e1Snicm case KEYC_BSPACE: 942e2dae3e1Snicm *new_key = KEYC_LEFT; 943e2dae3e1Snicm return (1); 944ce3f476aSnicm case 'A': 945ce3f476aSnicm case 'I': 946ce3f476aSnicm case 'C': 947ce3f476aSnicm case 's': 948ce3f476aSnicm case 'a': 949ce3f476aSnicm c->prompt_mode = PROMPT_ENTRY; 950e7808201Snicm c->flags |= CLIENT_REDRAWSTATUS; 951ce3f476aSnicm break; /* switch mode and... */ 952ce3f476aSnicm case 'S': 953ce3f476aSnicm c->prompt_mode = PROMPT_ENTRY; 954e7808201Snicm c->flags |= CLIENT_REDRAWSTATUS; 955719f5715Snicm *new_key = 'u'|KEYC_CTRL; 956ce3f476aSnicm return (1); 957ce3f476aSnicm case 'i': 958ce3f476aSnicm case '\033': /* Escape */ 959ce3f476aSnicm c->prompt_mode = PROMPT_ENTRY; 960e7808201Snicm c->flags |= CLIENT_REDRAWSTATUS; 961ce3f476aSnicm return (0); 962ce3f476aSnicm } 963ce3f476aSnicm 964ce3f476aSnicm switch (key) { 965ce3f476aSnicm case 'A': 966ce3f476aSnicm case '$': 967ce3f476aSnicm *new_key = KEYC_END; 968ce3f476aSnicm return (1); 969ce3f476aSnicm case 'I': 970ce3f476aSnicm case '0': 971ce3f476aSnicm case '^': 972ce3f476aSnicm *new_key = KEYC_HOME; 973ce3f476aSnicm return (1); 974ce3f476aSnicm case 'C': 975ce3f476aSnicm case 'D': 976719f5715Snicm *new_key = 'k'|KEYC_CTRL; 977ce3f476aSnicm return (1); 978ce3f476aSnicm case KEYC_BSPACE: 979ce3f476aSnicm case 'X': 980ce3f476aSnicm *new_key = KEYC_BSPACE; 981ce3f476aSnicm return (1); 982ce3f476aSnicm case 'b': 9836a385b80Snicm *new_key = 'b'|KEYC_META; 984ce3f476aSnicm return (1); 9858f36458cSnicm case 'B': 9868f36458cSnicm *new_key = 'B'|KEYC_VI; 9878f36458cSnicm return (1); 988ce3f476aSnicm case 'd': 989719f5715Snicm *new_key = 'u'|KEYC_CTRL; 990ce3f476aSnicm return (1); 991ce3f476aSnicm case 'e': 9928f36458cSnicm *new_key = 'e'|KEYC_VI; 9938f36458cSnicm return (1); 994ce3f476aSnicm case 'E': 9958f36458cSnicm *new_key = 'E'|KEYC_VI; 9968f36458cSnicm return (1); 997ce3f476aSnicm case 'w': 9988f36458cSnicm *new_key = 'w'|KEYC_VI; 9998f36458cSnicm return (1); 1000ce3f476aSnicm case 'W': 10018f36458cSnicm *new_key = 'W'|KEYC_VI; 1002ce3f476aSnicm return (1); 1003ce3f476aSnicm case 'p': 1004719f5715Snicm *new_key = 'y'|KEYC_CTRL; 1005ce3f476aSnicm return (1); 10062e9f6870Snicm case 'q': 1007719f5715Snicm *new_key = 'c'|KEYC_CTRL; 10082e9f6870Snicm return (1); 1009ce3f476aSnicm case 's': 1010ce3f476aSnicm case KEYC_DC: 1011ce3f476aSnicm case 'x': 1012ce3f476aSnicm *new_key = KEYC_DC; 1013ce3f476aSnicm return (1); 1014ce3f476aSnicm case KEYC_DOWN: 1015ce3f476aSnicm case 'j': 1016ce3f476aSnicm *new_key = KEYC_DOWN; 1017ce3f476aSnicm return (1); 1018ce3f476aSnicm case KEYC_LEFT: 1019ce3f476aSnicm case 'h': 1020ce3f476aSnicm *new_key = KEYC_LEFT; 1021ce3f476aSnicm return (1); 1022ce3f476aSnicm case 'a': 1023ce3f476aSnicm case KEYC_RIGHT: 1024ce3f476aSnicm case 'l': 1025ce3f476aSnicm *new_key = KEYC_RIGHT; 1026ce3f476aSnicm return (1); 1027ce3f476aSnicm case KEYC_UP: 1028ce3f476aSnicm case 'k': 1029ce3f476aSnicm *new_key = KEYC_UP; 1030ce3f476aSnicm return (1); 1031719f5715Snicm case 'h'|KEYC_CTRL: 1032719f5715Snicm case 'c'|KEYC_CTRL: 1033ce3f476aSnicm case '\n': 1034ce3f476aSnicm case '\r': 1035ce3f476aSnicm return (1); 1036ce3f476aSnicm } 1037ce3f476aSnicm return (0); 1038ce3f476aSnicm } 1039ce3f476aSnicm 1040cfd48a7fSnicm /* Paste into prompt. */ 1041cfd48a7fSnicm static int 1042cfd48a7fSnicm status_prompt_paste(struct client *c) 1043cfd48a7fSnicm { 1044cfd48a7fSnicm struct paste_buffer *pb; 1045cfd48a7fSnicm const char *bufdata; 1046cfd48a7fSnicm size_t size, n, bufsize; 1047cfd48a7fSnicm u_int i; 1048cfd48a7fSnicm struct utf8_data *ud, *udp; 1049cfd48a7fSnicm enum utf8_state more; 1050cfd48a7fSnicm 1051cfd48a7fSnicm size = utf8_strlen(c->prompt_buffer); 1052cfd48a7fSnicm if (c->prompt_saved != NULL) { 1053cfd48a7fSnicm ud = c->prompt_saved; 1054cfd48a7fSnicm n = utf8_strlen(c->prompt_saved); 1055cfd48a7fSnicm } else { 1056cfd48a7fSnicm if ((pb = paste_get_top(NULL)) == NULL) 1057cfd48a7fSnicm return (0); 1058cfd48a7fSnicm bufdata = paste_buffer_data(pb, &bufsize); 1059095d8c2eSnicm ud = udp = xreallocarray(NULL, bufsize + 1, sizeof *ud); 1060cfd48a7fSnicm for (i = 0; i != bufsize; /* nothing */) { 1061cfd48a7fSnicm more = utf8_open(udp, bufdata[i]); 1062cfd48a7fSnicm if (more == UTF8_MORE) { 1063cfd48a7fSnicm while (++i != bufsize && more == UTF8_MORE) 1064cfd48a7fSnicm more = utf8_append(udp, bufdata[i]); 1065cfd48a7fSnicm if (more == UTF8_DONE) { 1066cfd48a7fSnicm udp++; 1067cfd48a7fSnicm continue; 1068cfd48a7fSnicm } 1069cfd48a7fSnicm i -= udp->have; 1070cfd48a7fSnicm } 1071cfd48a7fSnicm if (bufdata[i] <= 31 || bufdata[i] >= 127) 1072cfd48a7fSnicm break; 1073cfd48a7fSnicm utf8_set(udp, bufdata[i]); 1074cfd48a7fSnicm udp++; 1075cfd48a7fSnicm i++; 1076cfd48a7fSnicm } 1077cfd48a7fSnicm udp->size = 0; 1078cfd48a7fSnicm n = udp - ud; 1079cfd48a7fSnicm } 1080095d8c2eSnicm if (n != 0) { 1081cfd48a7fSnicm c->prompt_buffer = xreallocarray(c->prompt_buffer, size + n + 1, 1082cfd48a7fSnicm sizeof *c->prompt_buffer); 1083cfd48a7fSnicm if (c->prompt_index == size) { 1084cfd48a7fSnicm memcpy(c->prompt_buffer + c->prompt_index, ud, 1085cfd48a7fSnicm n * sizeof *c->prompt_buffer); 1086cfd48a7fSnicm c->prompt_index += n; 1087cfd48a7fSnicm c->prompt_buffer[c->prompt_index].size = 0; 1088cfd48a7fSnicm } else { 1089cfd48a7fSnicm memmove(c->prompt_buffer + c->prompt_index + n, 1090cfd48a7fSnicm c->prompt_buffer + c->prompt_index, 1091095d8c2eSnicm (size + 1 - c->prompt_index) * 1092095d8c2eSnicm sizeof *c->prompt_buffer); 1093cfd48a7fSnicm memcpy(c->prompt_buffer + c->prompt_index, ud, 1094cfd48a7fSnicm n * sizeof *c->prompt_buffer); 1095cfd48a7fSnicm c->prompt_index += n; 1096cfd48a7fSnicm } 1097095d8c2eSnicm } 1098cfd48a7fSnicm if (ud != c->prompt_saved) 1099cfd48a7fSnicm free(ud); 1100cfd48a7fSnicm return (1); 1101cfd48a7fSnicm } 1102cfd48a7fSnicm 1103d6317402Snicm /* Finish completion. */ 1104d6317402Snicm static int 1105d6317402Snicm status_prompt_replace_complete(struct client *c, const char *s) 1106d6317402Snicm { 1107d6317402Snicm char word[64], *allocated = NULL; 1108d6317402Snicm size_t size, n, off, idx, used; 1109d6317402Snicm struct utf8_data *first, *last, *ud; 1110d6317402Snicm 111137a3aee2Snicm /* Work out where the cursor currently is. */ 1112d6317402Snicm idx = c->prompt_index; 1113d6317402Snicm if (idx != 0) 1114d6317402Snicm idx--; 111537a3aee2Snicm size = utf8_strlen(c->prompt_buffer); 1116d6317402Snicm 1117d6317402Snicm /* Find the word we are in. */ 1118d6317402Snicm first = &c->prompt_buffer[idx]; 1119d6317402Snicm while (first > c->prompt_buffer && !status_prompt_space(first)) 1120d6317402Snicm first--; 1121d6317402Snicm while (first->size != 0 && status_prompt_space(first)) 1122d6317402Snicm first++; 1123d6317402Snicm last = &c->prompt_buffer[idx]; 1124d6317402Snicm while (last->size != 0 && !status_prompt_space(last)) 1125d6317402Snicm last++; 1126d6317402Snicm while (last > c->prompt_buffer && status_prompt_space(last)) 1127d6317402Snicm last--; 1128d6317402Snicm if (last->size != 0) 1129d6317402Snicm last++; 113037a3aee2Snicm if (last < first) 1131d6317402Snicm return (0); 1132d6317402Snicm if (s == NULL) { 1133d6317402Snicm used = 0; 1134d6317402Snicm for (ud = first; ud < last; ud++) { 1135d6317402Snicm if (used + ud->size >= sizeof word) 1136d6317402Snicm break; 1137d6317402Snicm memcpy(word + used, ud->data, ud->size); 1138d6317402Snicm used += ud->size; 1139d6317402Snicm } 1140d6317402Snicm if (ud != last) 1141d6317402Snicm return (0); 1142d6317402Snicm word[used] = '\0'; 1143d6317402Snicm } 1144d6317402Snicm 1145d6317402Snicm /* Try to complete it. */ 1146d6317402Snicm if (s == NULL) { 1147d6317402Snicm allocated = status_prompt_complete(c, word, 1148d6317402Snicm first - c->prompt_buffer); 1149d6317402Snicm if (allocated == NULL) 1150d6317402Snicm return (0); 1151d6317402Snicm s = allocated; 1152d6317402Snicm } 1153d6317402Snicm 1154d6317402Snicm /* Trim out word. */ 1155d6317402Snicm n = size - (last - c->prompt_buffer) + 1; /* with \0 */ 1156d6317402Snicm memmove(first, last, n * sizeof *c->prompt_buffer); 1157d6317402Snicm size -= last - first; 1158d6317402Snicm 1159d6317402Snicm /* Insert the new word. */ 1160d6317402Snicm size += strlen(s); 1161d6317402Snicm off = first - c->prompt_buffer; 1162d6317402Snicm c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 1, 1163d6317402Snicm sizeof *c->prompt_buffer); 1164d6317402Snicm first = c->prompt_buffer + off; 1165d6317402Snicm memmove(first + strlen(s), first, n * sizeof *c->prompt_buffer); 1166d6317402Snicm for (idx = 0; idx < strlen(s); idx++) 1167d6317402Snicm utf8_set(&first[idx], s[idx]); 1168d6317402Snicm c->prompt_index = (first - c->prompt_buffer) + strlen(s); 1169d6317402Snicm 1170d6317402Snicm free(allocated); 1171d6317402Snicm return (1); 1172d6317402Snicm } 1173d6317402Snicm 11748f36458cSnicm /* Prompt forward to the next beginning of a word. */ 11758f36458cSnicm static void 11768f36458cSnicm status_prompt_forward_word(struct client *c, size_t size, int vi, 11778f36458cSnicm const char *separators) 11788f36458cSnicm { 11798f36458cSnicm size_t idx = c->prompt_index; 11808f36458cSnicm int word_is_separators; 11818f36458cSnicm 11828f36458cSnicm /* In emacs mode, skip until the first non-whitespace character. */ 11838f36458cSnicm if (!vi) 11848f36458cSnicm while (idx != size && 11858f36458cSnicm status_prompt_space(&c->prompt_buffer[idx])) 11868f36458cSnicm idx++; 11878f36458cSnicm 11888f36458cSnicm /* Can't move forward if we're already at the end. */ 11898f36458cSnicm if (idx == size) { 11908f36458cSnicm c->prompt_index = idx; 11918f36458cSnicm return; 11928f36458cSnicm } 11938f36458cSnicm 11948f36458cSnicm /* Determine the current character class (separators or not). */ 11958f36458cSnicm word_is_separators = status_prompt_in_list(separators, 11968f36458cSnicm &c->prompt_buffer[idx]) && 11978f36458cSnicm !status_prompt_space(&c->prompt_buffer[idx]); 11988f36458cSnicm 11998f36458cSnicm /* Skip ahead until the first space or opposite character class. */ 12008f36458cSnicm do { 12018f36458cSnicm idx++; 12028f36458cSnicm if (status_prompt_space(&c->prompt_buffer[idx])) { 12038f36458cSnicm /* In vi mode, go to the start of the next word. */ 12048f36458cSnicm if (vi) 12058f36458cSnicm while (idx != size && 12068f36458cSnicm status_prompt_space(&c->prompt_buffer[idx])) 12078f36458cSnicm idx++; 12088f36458cSnicm break; 12098f36458cSnicm } 12108f36458cSnicm } while (idx != size && word_is_separators == status_prompt_in_list( 12118f36458cSnicm separators, &c->prompt_buffer[idx])); 12128f36458cSnicm 12138f36458cSnicm c->prompt_index = idx; 12148f36458cSnicm } 12158f36458cSnicm 12168f36458cSnicm /* Prompt forward to the next end of a word. */ 12178f36458cSnicm static void 12188f36458cSnicm status_prompt_end_word(struct client *c, size_t size, const char *separators) 12198f36458cSnicm { 12208f36458cSnicm size_t idx = c->prompt_index; 12218f36458cSnicm int word_is_separators; 12228f36458cSnicm 12238f36458cSnicm /* Can't move forward if we're already at the end. */ 12248f36458cSnicm if (idx == size) 12258f36458cSnicm return; 12268f36458cSnicm 12278f36458cSnicm /* Find the next word. */ 12288f36458cSnicm do { 12298f36458cSnicm idx++; 12308f36458cSnicm if (idx == size) { 12318f36458cSnicm c->prompt_index = idx; 12328f36458cSnicm return; 12338f36458cSnicm } 12348f36458cSnicm } while (status_prompt_space(&c->prompt_buffer[idx])); 12358f36458cSnicm 12368f36458cSnicm /* Determine the character class (separators or not). */ 12378f36458cSnicm word_is_separators = status_prompt_in_list(separators, 12388f36458cSnicm &c->prompt_buffer[idx]); 12398f36458cSnicm 12408f36458cSnicm /* Skip ahead until the next space or opposite character class. */ 12418f36458cSnicm do { 12428f36458cSnicm idx++; 12438f36458cSnicm if (idx == size) 12448f36458cSnicm break; 12458f36458cSnicm } while (!status_prompt_space(&c->prompt_buffer[idx]) && 12468f36458cSnicm word_is_separators == status_prompt_in_list(separators, 12478f36458cSnicm &c->prompt_buffer[idx])); 12488f36458cSnicm 12498f36458cSnicm /* Back up to the previous character to stop at the end of the word. */ 12508f36458cSnicm c->prompt_index = idx - 1; 12518f36458cSnicm } 12528f36458cSnicm 12538f36458cSnicm /* Prompt backward to the previous beginning of a word. */ 12548f36458cSnicm static void 12558f36458cSnicm status_prompt_backward_word(struct client *c, const char *separators) 12568f36458cSnicm { 12578f36458cSnicm size_t idx = c->prompt_index; 12588f36458cSnicm int word_is_separators; 12598f36458cSnicm 12608f36458cSnicm /* Find non-whitespace. */ 12618f36458cSnicm while (idx != 0) { 12628f36458cSnicm --idx; 12638f36458cSnicm if (!status_prompt_space(&c->prompt_buffer[idx])) 12648f36458cSnicm break; 12658f36458cSnicm } 12668f36458cSnicm word_is_separators = status_prompt_in_list(separators, 12678f36458cSnicm &c->prompt_buffer[idx]); 12688f36458cSnicm 12698f36458cSnicm /* Find the character before the beginning of the word. */ 12708f36458cSnicm while (idx != 0) { 12718f36458cSnicm --idx; 12728f36458cSnicm if (status_prompt_space(&c->prompt_buffer[idx]) || 12738f36458cSnicm word_is_separators != status_prompt_in_list(separators, 12748f36458cSnicm &c->prompt_buffer[idx])) { 12758f36458cSnicm /* Go back to the word. */ 12768f36458cSnicm idx++; 12778f36458cSnicm break; 12788f36458cSnicm } 12798f36458cSnicm } 12808f36458cSnicm c->prompt_index = idx; 12818f36458cSnicm } 12828f36458cSnicm 1283311827fbSnicm /* Handle keys in prompt. */ 1284a3806a61Snicm int 1285885a4698Snicm status_prompt_key(struct client *c, key_code key) 1286311827fbSnicm { 1287746b61e4Snicm struct options *oo = c->session->options; 1288d6317402Snicm char *s, *cp, prefix = '='; 12898f36458cSnicm const char *histstr, *separators = NULL, *keystring; 1290d6317402Snicm size_t size, idx; 1291d6317402Snicm struct utf8_data tmp; 12928f36458cSnicm int keys, word_is_separators; 1293311827fbSnicm 1294c8877404Snicm if (c->prompt_flags & PROMPT_KEY) { 12955416581eSnicm keystring = key_string_lookup_key(key, 0); 1296c8877404Snicm c->prompt_inputcb(c, c->prompt_data, keystring, 1); 1297c8877404Snicm status_prompt_clear(c); 1298c8877404Snicm return (0); 1299c8877404Snicm } 1300746b61e4Snicm size = utf8_strlen(c->prompt_buffer); 1301a3806a61Snicm 1302a3806a61Snicm if (c->prompt_flags & PROMPT_NUMERIC) { 1303a3806a61Snicm if (key >= '0' && key <= '9') 1304a3806a61Snicm goto append_key; 1305a3806a61Snicm s = utf8_tocstr(c->prompt_buffer); 13063bf5ffecSnicm c->prompt_inputcb(c, c->prompt_data, s, 1); 1307a3806a61Snicm status_prompt_clear(c); 1308a3806a61Snicm free(s); 1309a3806a61Snicm return (1); 1310a3806a61Snicm } 13115416581eSnicm key &= ~KEYC_MASK_FLAGS; 1312a3806a61Snicm 1313e505ea96Snicm if (c->prompt_flags & (PROMPT_SINGLE|PROMPT_QUOTENEXT)) { 1314e505ea96Snicm if ((key & KEYC_MASK_KEY) == KEYC_BSPACE) 1315e505ea96Snicm key = 0x7f; 1316e505ea96Snicm else if ((key & KEYC_MASK_KEY) > 0x7f) { 1317e505ea96Snicm if (!KEYC_IS_UNICODE(key)) 1318e505ea96Snicm return (0); 1319e505ea96Snicm key &= KEYC_MASK_KEY; 1320e505ea96Snicm } else 1321e505ea96Snicm key &= (key & KEYC_CTRL) ? 0x1f : KEYC_MASK_KEY; 1322*0460263cSnicm c->prompt_flags &= ~PROMPT_QUOTENEXT; 1323e505ea96Snicm goto append_key; 1324e505ea96Snicm } 1325e505ea96Snicm 1326ce3f476aSnicm keys = options_get_number(c->session->options, "status-keys"); 1327ce3f476aSnicm if (keys == MODEKEY_VI) { 1328ce3f476aSnicm switch (status_prompt_translate_key(c, key, &key)) { 1329ce3f476aSnicm case 1: 1330ce3f476aSnicm goto process_key; 1331ce3f476aSnicm case 2: 1332ce3f476aSnicm goto append_key; 1333ce3f476aSnicm default: 1334ce3f476aSnicm return (0); 1335ce3f476aSnicm } 1336ce3f476aSnicm } 1337ce3f476aSnicm 1338ce3f476aSnicm process_key: 1339ce3f476aSnicm switch (key) { 1340ce3f476aSnicm case KEYC_LEFT: 1341719f5715Snicm case 'b'|KEYC_CTRL: 1342311827fbSnicm if (c->prompt_index > 0) { 1343311827fbSnicm c->prompt_index--; 1344edd3b079Snicm break; 1345311827fbSnicm } 1346311827fbSnicm break; 1347ce3f476aSnicm case KEYC_RIGHT: 1348719f5715Snicm case 'f'|KEYC_CTRL: 1349311827fbSnicm if (c->prompt_index < size) { 1350311827fbSnicm c->prompt_index++; 1351edd3b079Snicm break; 1352311827fbSnicm } 1353311827fbSnicm break; 1354ce3f476aSnicm case KEYC_HOME: 1355719f5715Snicm case 'a'|KEYC_CTRL: 1356311827fbSnicm if (c->prompt_index != 0) { 1357311827fbSnicm c->prompt_index = 0; 1358edd3b079Snicm break; 1359311827fbSnicm } 1360311827fbSnicm break; 1361ce3f476aSnicm case KEYC_END: 1362719f5715Snicm case 'e'|KEYC_CTRL: 1363311827fbSnicm if (c->prompt_index != size) { 1364311827fbSnicm c->prompt_index = size; 1365edd3b079Snicm break; 1366311827fbSnicm } 1367311827fbSnicm break; 1368ce3f476aSnicm case '\011': /* Tab */ 1369e484fb00Snicm if (status_prompt_replace_complete(c, NULL)) 1370edd3b079Snicm goto changed; 1371d6317402Snicm break; 1372ce3f476aSnicm case KEYC_BSPACE: 1373719f5715Snicm case 'h'|KEYC_CTRL: 1374311827fbSnicm if (c->prompt_index != 0) { 1375311827fbSnicm if (c->prompt_index == size) 1376746b61e4Snicm c->prompt_buffer[--c->prompt_index].size = 0; 1377311827fbSnicm else { 1378311827fbSnicm memmove(c->prompt_buffer + c->prompt_index - 1, 1379311827fbSnicm c->prompt_buffer + c->prompt_index, 1380746b61e4Snicm (size + 1 - c->prompt_index) * 1381746b61e4Snicm sizeof *c->prompt_buffer); 1382311827fbSnicm c->prompt_index--; 1383311827fbSnicm } 1384edd3b079Snicm goto changed; 1385311827fbSnicm } 1386311827fbSnicm break; 1387ce3f476aSnicm case KEYC_DC: 1388719f5715Snicm case 'd'|KEYC_CTRL: 1389311827fbSnicm if (c->prompt_index != size) { 1390311827fbSnicm memmove(c->prompt_buffer + c->prompt_index, 1391311827fbSnicm c->prompt_buffer + c->prompt_index + 1, 1392746b61e4Snicm (size + 1 - c->prompt_index) * 1393746b61e4Snicm sizeof *c->prompt_buffer); 1394edd3b079Snicm goto changed; 1395311827fbSnicm } 1396311827fbSnicm break; 1397719f5715Snicm case 'u'|KEYC_CTRL: 1398746b61e4Snicm c->prompt_buffer[0].size = 0; 1399b6c0765cSnicm c->prompt_index = 0; 1400edd3b079Snicm goto changed; 1401719f5715Snicm case 'k'|KEYC_CTRL: 1402c2b39c4eSnicm if (c->prompt_index < size) { 1403746b61e4Snicm c->prompt_buffer[c->prompt_index].size = 0; 1404edd3b079Snicm goto changed; 1405c2b39c4eSnicm } 1406c2b39c4eSnicm break; 1407719f5715Snicm case 'w'|KEYC_CTRL: 14088f36458cSnicm separators = options_get_string(oo, "word-separators"); 1409fcf68e62Snicm idx = c->prompt_index; 1410fcf68e62Snicm 14118f36458cSnicm /* Find non-whitespace. */ 1412fcf68e62Snicm while (idx != 0) { 1413fcf68e62Snicm idx--; 14148f36458cSnicm if (!status_prompt_space(&c->prompt_buffer[idx])) 1415fcf68e62Snicm break; 1416fcf68e62Snicm } 14178f36458cSnicm word_is_separators = status_prompt_in_list(separators, 14188f36458cSnicm &c->prompt_buffer[idx]); 1419fcf68e62Snicm 14208f36458cSnicm /* Find the character before the beginning of the word. */ 1421fcf68e62Snicm while (idx != 0) { 1422fcf68e62Snicm idx--; 14238f36458cSnicm if (status_prompt_space(&c->prompt_buffer[idx]) || 14248f36458cSnicm word_is_separators != status_prompt_in_list( 14258f36458cSnicm separators, &c->prompt_buffer[idx])) { 1426fcf68e62Snicm /* Go back to the word. */ 1427fcf68e62Snicm idx++; 1428fcf68e62Snicm break; 1429fcf68e62Snicm } 1430fcf68e62Snicm } 1431fcf68e62Snicm 143243a68934Snicm free(c->prompt_saved); 143343a68934Snicm c->prompt_saved = xcalloc(sizeof *c->prompt_buffer, 143443a68934Snicm (c->prompt_index - idx) + 1); 143543a68934Snicm memcpy(c->prompt_saved, c->prompt_buffer + idx, 143643a68934Snicm (c->prompt_index - idx) * sizeof *c->prompt_buffer); 143743a68934Snicm 1438fcf68e62Snicm memmove(c->prompt_buffer + idx, 1439fcf68e62Snicm c->prompt_buffer + c->prompt_index, 1440746b61e4Snicm (size + 1 - c->prompt_index) * 1441746b61e4Snicm sizeof *c->prompt_buffer); 1442fcf68e62Snicm memset(c->prompt_buffer + size - (c->prompt_index - idx), 1443746b61e4Snicm '\0', (c->prompt_index - idx) * sizeof *c->prompt_buffer); 1444fcf68e62Snicm c->prompt_index = idx; 1445746b61e4Snicm 1446edd3b079Snicm goto changed; 144731ded1eaSnicm case KEYC_RIGHT|KEYC_CTRL: 14488f36458cSnicm case 'f'|KEYC_META: 14498f36458cSnicm separators = options_get_string(oo, "word-separators"); 14508f36458cSnicm status_prompt_forward_word(c, size, 0, separators); 1451edd3b079Snicm goto changed; 14528f36458cSnicm case 'E'|KEYC_VI: 14538f36458cSnicm status_prompt_end_word(c, size, ""); 14548f36458cSnicm goto changed; 14558f36458cSnicm case 'e'|KEYC_VI: 14568f36458cSnicm separators = options_get_string(oo, "word-separators"); 14578f36458cSnicm status_prompt_end_word(c, size, separators); 14588f36458cSnicm goto changed; 14598f36458cSnicm case 'W'|KEYC_VI: 14608f36458cSnicm status_prompt_forward_word(c, size, 1, ""); 14618f36458cSnicm goto changed; 14628f36458cSnicm case 'w'|KEYC_VI: 14638f36458cSnicm separators = options_get_string(oo, "word-separators"); 14648f36458cSnicm status_prompt_forward_word(c, size, 1, separators); 14658f36458cSnicm goto changed; 14668f36458cSnicm case 'B'|KEYC_VI: 14678f36458cSnicm status_prompt_backward_word(c, ""); 14688f36458cSnicm goto changed; 146931ded1eaSnicm case KEYC_LEFT|KEYC_CTRL: 14708f36458cSnicm case 'b'|KEYC_META: 14718f36458cSnicm separators = options_get_string(oo, "word-separators"); 14728f36458cSnicm status_prompt_backward_word(c, separators); 1473edd3b079Snicm goto changed; 1474ce3f476aSnicm case KEYC_UP: 1475719f5715Snicm case 'p'|KEYC_CTRL: 1476bc5a8fc2Snicm histstr = status_prompt_up_history(c->prompt_hindex, 1477bc5a8fc2Snicm c->prompt_type); 14783c9a94faSnicm if (histstr == NULL) 1479311827fbSnicm break; 14807d053cf9Snicm free(c->prompt_buffer); 1481746b61e4Snicm c->prompt_buffer = utf8_fromcstr(histstr); 1482746b61e4Snicm c->prompt_index = utf8_strlen(c->prompt_buffer); 1483edd3b079Snicm goto changed; 1484ce3f476aSnicm case KEYC_DOWN: 1485719f5715Snicm case 'n'|KEYC_CTRL: 1486bc5a8fc2Snicm histstr = status_prompt_down_history(c->prompt_hindex, 1487bc5a8fc2Snicm c->prompt_type); 14883c9a94faSnicm if (histstr == NULL) 14894ee77f13Snicm break; 14907d053cf9Snicm free(c->prompt_buffer); 1491746b61e4Snicm c->prompt_buffer = utf8_fromcstr(histstr); 1492746b61e4Snicm c->prompt_index = utf8_strlen(c->prompt_buffer); 1493edd3b079Snicm goto changed; 1494719f5715Snicm case 'y'|KEYC_CTRL: 1495cfd48a7fSnicm if (status_prompt_paste(c)) 1496edd3b079Snicm goto changed; 1497cfd48a7fSnicm break; 1498719f5715Snicm case 't'|KEYC_CTRL: 1499cdefdc92Snicm idx = c->prompt_index; 1500cdefdc92Snicm if (idx < size) 1501cdefdc92Snicm idx++; 1502cdefdc92Snicm if (idx >= 2) { 1503746b61e4Snicm utf8_copy(&tmp, &c->prompt_buffer[idx - 2]); 1504746b61e4Snicm utf8_copy(&c->prompt_buffer[idx - 2], 1505746b61e4Snicm &c->prompt_buffer[idx - 1]); 1506746b61e4Snicm utf8_copy(&c->prompt_buffer[idx - 1], &tmp); 1507cdefdc92Snicm c->prompt_index = idx; 1508edd3b079Snicm goto changed; 1509cdefdc92Snicm } 1510cdefdc92Snicm break; 1511ce3f476aSnicm case '\r': 1512ce3f476aSnicm case '\n': 1513746b61e4Snicm s = utf8_tocstr(c->prompt_buffer); 1514746b61e4Snicm if (*s != '\0') 1515bc5a8fc2Snicm status_prompt_add_history(s, c->prompt_type); 15163bf5ffecSnicm if (c->prompt_inputcb(c, c->prompt_data, s, 1) == 0) 1517311827fbSnicm status_prompt_clear(c); 1518746b61e4Snicm free(s); 1519311827fbSnicm break; 1520ce3f476aSnicm case '\033': /* Escape */ 1521719f5715Snicm case 'c'|KEYC_CTRL: 1522719f5715Snicm case 'g'|KEYC_CTRL: 15233bf5ffecSnicm if (c->prompt_inputcb(c, c->prompt_data, NULL, 1) == 0) 1524311827fbSnicm status_prompt_clear(c); 1525311827fbSnicm break; 1526719f5715Snicm case 'r'|KEYC_CTRL: 15277e7432ecSnicm if (~c->prompt_flags & PROMPT_INCREMENTAL) 15287e7432ecSnicm break; 15297e7432ecSnicm if (c->prompt_buffer[0].size == 0) { 15307e7432ecSnicm prefix = '='; 15317e7432ecSnicm free(c->prompt_buffer); 15327e7432ecSnicm c->prompt_buffer = utf8_fromcstr(c->prompt_last); 15337e7432ecSnicm c->prompt_index = utf8_strlen(c->prompt_buffer); 15347e7432ecSnicm } else 1535edd3b079Snicm prefix = '-'; 1536edd3b079Snicm goto changed; 1537719f5715Snicm case 's'|KEYC_CTRL: 15387e7432ecSnicm if (~c->prompt_flags & PROMPT_INCREMENTAL) 15397e7432ecSnicm break; 15407e7432ecSnicm if (c->prompt_buffer[0].size == 0) { 15417e7432ecSnicm prefix = '='; 15427e7432ecSnicm free(c->prompt_buffer); 15437e7432ecSnicm c->prompt_buffer = utf8_fromcstr(c->prompt_last); 15447e7432ecSnicm c->prompt_index = utf8_strlen(c->prompt_buffer); 15457e7432ecSnicm } else 1546edd3b079Snicm prefix = '+'; 1547edd3b079Snicm goto changed; 1548e505ea96Snicm case 'v'|KEYC_CTRL: 1549e505ea96Snicm c->prompt_flags |= PROMPT_QUOTENEXT; 1550e505ea96Snicm break; 1551edd3b079Snicm default: 1552edd3b079Snicm goto append_key; 1553edd3b079Snicm } 1554edd3b079Snicm 1555e7808201Snicm c->flags |= CLIENT_REDRAWSTATUS; 1556edd3b079Snicm return (0); 1557a3806a61Snicm 1558a3806a61Snicm append_key: 1559e505ea96Snicm if (key <= 0x7f) { 156034779269Snicm utf8_set(&tmp, key); 1561e505ea96Snicm if (key <= 0x1f || key == 0x7f) 1562e505ea96Snicm tmp.width = 2; 1563e505ea96Snicm } else if (KEYC_IS_UNICODE(key)) 15646852c63bSnicm utf8_to_data(key, &tmp); 15658f36458cSnicm else 15668f36458cSnicm return (0); 1567746b61e4Snicm 1568746b61e4Snicm c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 2, 1569746b61e4Snicm sizeof *c->prompt_buffer); 1570311827fbSnicm 1571311827fbSnicm if (c->prompt_index == size) { 1572746b61e4Snicm utf8_copy(&c->prompt_buffer[c->prompt_index], &tmp); 1573746b61e4Snicm c->prompt_index++; 1574746b61e4Snicm c->prompt_buffer[c->prompt_index].size = 0; 1575311827fbSnicm } else { 1576311827fbSnicm memmove(c->prompt_buffer + c->prompt_index + 1, 1577311827fbSnicm c->prompt_buffer + c->prompt_index, 1578746b61e4Snicm (size + 1 - c->prompt_index) * 1579746b61e4Snicm sizeof *c->prompt_buffer); 1580746b61e4Snicm utf8_copy(&c->prompt_buffer[c->prompt_index], &tmp); 1581746b61e4Snicm c->prompt_index++; 1582311827fbSnicm } 1583311827fbSnicm 1584311827fbSnicm if (c->prompt_flags & PROMPT_SINGLE) { 1585d70f1befSnicm if (utf8_strlen(c->prompt_buffer) != 1) 1586311827fbSnicm status_prompt_clear(c); 1587d70f1befSnicm else { 1588d70f1befSnicm s = utf8_tocstr(c->prompt_buffer); 1589d70f1befSnicm if (c->prompt_inputcb(c, c->prompt_data, s, 1) == 0) 1590746b61e4Snicm status_prompt_clear(c); 1591746b61e4Snicm free(s); 1592311827fbSnicm } 1593d70f1befSnicm } 1594311827fbSnicm 1595edd3b079Snicm changed: 1596e7808201Snicm c->flags |= CLIENT_REDRAWSTATUS; 1597edd3b079Snicm if (c->prompt_flags & PROMPT_INCREMENTAL) { 1598edd3b079Snicm s = utf8_tocstr(c->prompt_buffer); 1599edd3b079Snicm xasprintf(&cp, "%c%s", prefix, s); 16003bf5ffecSnicm c->prompt_inputcb(c, c->prompt_data, cp, 0); 1601edd3b079Snicm free(cp); 1602edd3b079Snicm free(s); 1603edd3b079Snicm } 1604a3806a61Snicm return (0); 1605311827fbSnicm } 1606311827fbSnicm 16074ee77f13Snicm /* Get previous line from the history. */ 16089883b791Snicm static const char * 1609bc5a8fc2Snicm status_prompt_up_history(u_int *idx, u_int type) 1610311827fbSnicm { 16114ee77f13Snicm /* 1612276a572eSnicm * History runs from 0 to size - 1. Index is from 0 to size. Zero is 1613276a572eSnicm * empty. 16144ee77f13Snicm */ 16154ee77f13Snicm 1616bc5a8fc2Snicm if (status_prompt_hsize[type] == 0 || 1617bc5a8fc2Snicm idx[type] == status_prompt_hsize[type]) 16184ee77f13Snicm return (NULL); 1619bc5a8fc2Snicm idx[type]++; 1620bc5a8fc2Snicm return (status_prompt_hlist[type][status_prompt_hsize[type] - idx[type]]); 1621311827fbSnicm } 1622311827fbSnicm 16234ee77f13Snicm /* Get next line from the history. */ 16249883b791Snicm static const char * 1625bc5a8fc2Snicm status_prompt_down_history(u_int *idx, u_int type) 16264ee77f13Snicm { 1627bc5a8fc2Snicm if (status_prompt_hsize[type] == 0 || idx[type] == 0) 16284ee77f13Snicm return (""); 1629bc5a8fc2Snicm idx[type]--; 1630bc5a8fc2Snicm if (idx[type] == 0) 16314ee77f13Snicm return (""); 1632bc5a8fc2Snicm return (status_prompt_hlist[type][status_prompt_hsize[type] - idx[type]]); 16334ee77f13Snicm } 16344ee77f13Snicm 16354ee77f13Snicm /* Add line to the history. */ 16369883b791Snicm static void 1637bc5a8fc2Snicm status_prompt_add_history(const char *line, u_int type) 16384ee77f13Snicm { 1639bc5a8fc2Snicm u_int i, oldsize, newsize, freecount, hlimit, new = 1; 1640bc5a8fc2Snicm size_t movesize; 16414ee77f13Snicm 1642bc5a8fc2Snicm oldsize = status_prompt_hsize[type]; 1643bc5a8fc2Snicm if (oldsize > 0 && 1644bc5a8fc2Snicm strcmp(status_prompt_hlist[type][oldsize - 1], line) == 0) 1645bc5a8fc2Snicm new = 0; 1646bc5a8fc2Snicm 1647bc5a8fc2Snicm hlimit = options_get_number(global_options, "prompt-history-limit"); 1648bc5a8fc2Snicm if (hlimit > oldsize) { 1649bc5a8fc2Snicm if (new == 0) 16504ee77f13Snicm return; 1651bc5a8fc2Snicm newsize = oldsize + new; 1652bc5a8fc2Snicm } else { 1653bc5a8fc2Snicm newsize = hlimit; 1654bc5a8fc2Snicm freecount = oldsize + new - newsize; 1655bc5a8fc2Snicm if (freecount > oldsize) 1656bc5a8fc2Snicm freecount = oldsize; 1657bc5a8fc2Snicm if (freecount == 0) 1658276a572eSnicm return; 1659bc5a8fc2Snicm for (i = 0; i < freecount; i++) 1660bc5a8fc2Snicm free(status_prompt_hlist[type][i]); 1661bc5a8fc2Snicm movesize = (oldsize - freecount) * 1662bc5a8fc2Snicm sizeof *status_prompt_hlist[type]; 1663bc5a8fc2Snicm if (movesize > 0) { 1664bc5a8fc2Snicm memmove(&status_prompt_hlist[type][0], 1665bc5a8fc2Snicm &status_prompt_hlist[type][freecount], movesize); 1666bc5a8fc2Snicm } 16674ee77f13Snicm } 16684ee77f13Snicm 1669bc5a8fc2Snicm if (newsize == 0) { 1670bc5a8fc2Snicm free(status_prompt_hlist[type]); 1671bc5a8fc2Snicm status_prompt_hlist[type] = NULL; 1672bc5a8fc2Snicm } else if (newsize != oldsize) { 1673bc5a8fc2Snicm status_prompt_hlist[type] = 1674bc5a8fc2Snicm xreallocarray(status_prompt_hlist[type], newsize, 1675bc5a8fc2Snicm sizeof *status_prompt_hlist[type]); 1676bc5a8fc2Snicm } 1677bc5a8fc2Snicm 1678bc5a8fc2Snicm if (new == 1 && newsize > 0) 1679bc5a8fc2Snicm status_prompt_hlist[type][newsize - 1] = xstrdup(line); 1680bc5a8fc2Snicm status_prompt_hsize[type] = newsize; 1681276a572eSnicm } 1682276a572eSnicm 168348d4b375Snicm /* Add to completion list. */ 168448d4b375Snicm static void 168548d4b375Snicm status_prompt_add_list(char ***list, u_int *size, const char *s) 168648d4b375Snicm { 168748d4b375Snicm u_int i; 168848d4b375Snicm 168948d4b375Snicm for (i = 0; i < *size; i++) { 169048d4b375Snicm if (strcmp((*list)[i], s) == 0) 169148d4b375Snicm return; 169248d4b375Snicm } 169348d4b375Snicm *list = xreallocarray(*list, (*size) + 1, sizeof **list); 169448d4b375Snicm (*list)[(*size)++] = xstrdup(s); 169548d4b375Snicm } 169648d4b375Snicm 1697276a572eSnicm /* Build completion list. */ 1698d6317402Snicm static char ** 1699d6317402Snicm status_prompt_complete_list(u_int *size, const char *s, int at_start) 1700276a572eSnicm { 170148d4b375Snicm char **list = NULL, *tmp; 1702de8eb587Snicm const char **layout, *value, *cp; 1703276a572eSnicm const struct cmd_entry **cmdent; 1704276a572eSnicm const struct options_table_entry *oe; 1705de8eb587Snicm size_t slen = strlen(s), valuelen; 1706de8eb587Snicm struct options_entry *o; 170739052edfSnicm struct options_array_item *a; 1708276a572eSnicm const char *layouts[] = { 170914aabaa7Snicm "even-horizontal", "even-vertical", 171014aabaa7Snicm "main-horizontal", "main-horizontal-mirrored", 171114aabaa7Snicm "main-vertical", "main-vertical-mirrored", "tiled", NULL 1712276a572eSnicm }; 1713276a572eSnicm 1714276a572eSnicm *size = 0; 1715276a572eSnicm for (cmdent = cmd_table; *cmdent != NULL; cmdent++) { 171648d4b375Snicm if (strncmp((*cmdent)->name, s, slen) == 0) 171748d4b375Snicm status_prompt_add_list(&list, size, (*cmdent)->name); 171837a3aee2Snicm if ((*cmdent)->alias != NULL && 171948d4b375Snicm strncmp((*cmdent)->alias, s, slen) == 0) 172048d4b375Snicm status_prompt_add_list(&list, size, (*cmdent)->alias); 1721276a572eSnicm } 1722de8eb587Snicm o = options_get_only(global_options, "command-alias"); 172339052edfSnicm if (o != NULL) { 172439052edfSnicm a = options_array_first(o); 172539052edfSnicm while (a != NULL) { 1726dbd67d4eSnicm value = options_array_item_value(a)->string; 172761b6b4dcSnicm if ((cp = strchr(value, '=')) == NULL) 172861b6b4dcSnicm goto next; 1729de8eb587Snicm valuelen = cp - value; 1730de8eb587Snicm if (slen > valuelen || strncmp(value, s, slen) != 0) 173139052edfSnicm goto next; 173239052edfSnicm 173348d4b375Snicm xasprintf(&tmp, "%.*s", (int)valuelen, value); 173448d4b375Snicm status_prompt_add_list(&list, size, tmp); 173548d4b375Snicm free(tmp); 173639052edfSnicm 173739052edfSnicm next: 173839052edfSnicm a = options_array_next(a); 1739de8eb587Snicm } 1740de8eb587Snicm } 1741d6317402Snicm if (at_start) 1742d6317402Snicm return (list); 1743d6317402Snicm for (oe = options_table; oe->name != NULL; oe++) { 174448d4b375Snicm if (strncmp(oe->name, s, slen) == 0) 174548d4b375Snicm status_prompt_add_list(&list, size, oe->name); 1746d6317402Snicm } 1747d6317402Snicm for (layout = layouts; *layout != NULL; layout++) { 174848d4b375Snicm if (strncmp(*layout, s, slen) == 0) 174948d4b375Snicm status_prompt_add_list(&list, size, *layout); 1750d6317402Snicm } 1751276a572eSnicm return (list); 1752276a572eSnicm } 1753276a572eSnicm 1754276a572eSnicm /* Find longest prefix. */ 17559883b791Snicm static char * 1756de8eb587Snicm status_prompt_complete_prefix(char **list, u_int size) 1757276a572eSnicm { 1758276a572eSnicm char *out; 1759276a572eSnicm u_int i; 1760276a572eSnicm size_t j; 1761276a572eSnicm 1762552c5a37Snicm if (list == NULL || size == 0) 1763552c5a37Snicm return (NULL); 1764276a572eSnicm out = xstrdup(list[0]); 1765276a572eSnicm for (i = 1; i < size; i++) { 1766276a572eSnicm j = strlen(list[i]); 1767276a572eSnicm if (j > strlen(out)) 1768276a572eSnicm j = strlen(out); 1769276a572eSnicm for (; j > 0; j--) { 1770276a572eSnicm if (out[j - 1] != list[i][j - 1]) 1771276a572eSnicm out[j - 1] = '\0'; 1772276a572eSnicm } 1773276a572eSnicm } 1774276a572eSnicm return (out); 1775311827fbSnicm } 1776311827fbSnicm 1777d6317402Snicm /* Complete word menu callback. */ 1778d6317402Snicm static void 1779d6317402Snicm status_prompt_menu_callback(__unused struct menu *menu, u_int idx, key_code key, 1780d6317402Snicm void *data) 1781d6317402Snicm { 1782d6317402Snicm struct status_prompt_menu *spm = data; 1783d6317402Snicm struct client *c = spm->c; 1784d6317402Snicm u_int i; 1785d6317402Snicm char *s; 1786d6317402Snicm 1787d6317402Snicm if (key != KEYC_NONE) { 1788d6317402Snicm idx += spm->start; 1789d6317402Snicm if (spm->flag == '\0') 1790d6317402Snicm s = xstrdup(spm->list[idx]); 1791d6317402Snicm else 1792d6317402Snicm xasprintf(&s, "-%c%s", spm->flag, spm->list[idx]); 1793bc5a8fc2Snicm if (c->prompt_type == PROMPT_TYPE_WINDOW_TARGET) { 179437a3aee2Snicm free(c->prompt_buffer); 179537a3aee2Snicm c->prompt_buffer = utf8_fromcstr(s); 179637a3aee2Snicm c->prompt_index = utf8_strlen(c->prompt_buffer); 179737a3aee2Snicm c->flags |= CLIENT_REDRAWSTATUS; 179837a3aee2Snicm } else if (status_prompt_replace_complete(c, s)) 1799d6317402Snicm c->flags |= CLIENT_REDRAWSTATUS; 1800d6317402Snicm free(s); 1801d6317402Snicm } 1802d6317402Snicm 1803d6317402Snicm for (i = 0; i < spm->size; i++) 1804d6317402Snicm free(spm->list[i]); 1805d6317402Snicm free(spm->list); 1806d6317402Snicm } 1807d6317402Snicm 1808d6317402Snicm /* Show complete word menu. */ 1809d6317402Snicm static int 1810d6317402Snicm status_prompt_complete_list_menu(struct client *c, char **list, u_int size, 1811d6317402Snicm u_int offset, char flag) 1812d6317402Snicm { 1813d6317402Snicm struct menu *menu; 1814d6317402Snicm struct menu_item item; 1815d6317402Snicm struct status_prompt_menu *spm; 1816d6317402Snicm u_int lines = status_line_size(c), height, i; 1817d6317402Snicm u_int py; 1818d6317402Snicm 1819d6317402Snicm if (size <= 1) 1820d6317402Snicm return (0); 1821d6317402Snicm if (c->tty.sy - lines < 3) 1822d6317402Snicm return (0); 1823d6317402Snicm 1824d6317402Snicm spm = xmalloc(sizeof *spm); 1825d6317402Snicm spm->c = c; 1826d6317402Snicm spm->size = size; 1827d6317402Snicm spm->list = list; 1828d6317402Snicm spm->flag = flag; 1829d6317402Snicm 1830d6317402Snicm height = c->tty.sy - lines - 2; 1831d6317402Snicm if (height > 10) 1832d6317402Snicm height = 10; 1833d6317402Snicm if (height > size) 1834d6317402Snicm height = size; 1835d6317402Snicm spm->start = size - height; 1836d6317402Snicm 1837d6317402Snicm menu = menu_create(""); 1838d6317402Snicm for (i = spm->start; i < size; i++) { 1839d6317402Snicm item.name = list[i]; 1840d6317402Snicm item.key = '0' + (i - spm->start); 1841d6317402Snicm item.command = NULL; 1842e8ab8068Snicm menu_add_item(menu, &item, NULL, c, NULL); 1843d6317402Snicm } 1844d6317402Snicm 1845d6317402Snicm if (options_get_number(c->session->options, "status-position") == 0) 1846d6317402Snicm py = lines; 1847d6317402Snicm else 1848d6317402Snicm py = c->tty.sy - 3 - height; 1849d6317402Snicm offset += utf8_cstrwidth(c->prompt_string); 1850d6317402Snicm if (offset > 2) 1851d6317402Snicm offset -= 2; 1852d6317402Snicm else 1853d6317402Snicm offset = 0; 1854d6317402Snicm 185517d7ce67Snicm if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, 0, NULL, offset, py, c, 185619c94b00Snicm BOX_LINES_DEFAULT, NULL, NULL, NULL, NULL, 185719c94b00Snicm status_prompt_menu_callback, spm) != 0) { 1858d6317402Snicm menu_free(menu); 1859d6317402Snicm free(spm); 1860d6317402Snicm return (0); 1861d6317402Snicm } 1862d6317402Snicm return (1); 1863d6317402Snicm } 1864d6317402Snicm 1865d6317402Snicm /* Show complete word menu. */ 1866d6317402Snicm static char * 1867d6317402Snicm status_prompt_complete_window_menu(struct client *c, struct session *s, 1868e484fb00Snicm const char *word, u_int offset, char flag) 1869d6317402Snicm { 1870d6317402Snicm struct menu *menu; 1871d6317402Snicm struct menu_item item; 1872d6317402Snicm struct status_prompt_menu *spm; 1873d6317402Snicm struct winlink *wl; 1874d6317402Snicm char **list = NULL, *tmp; 1875d6317402Snicm u_int lines = status_line_size(c), height; 1876d6317402Snicm u_int py, size = 0; 1877d6317402Snicm 1878d6317402Snicm if (c->tty.sy - lines < 3) 1879d6317402Snicm return (NULL); 1880d6317402Snicm 1881d6317402Snicm spm = xmalloc(sizeof *spm); 1882d6317402Snicm spm->c = c; 1883d6317402Snicm spm->flag = flag; 1884d6317402Snicm 1885d6317402Snicm height = c->tty.sy - lines - 2; 1886d6317402Snicm if (height > 10) 1887d6317402Snicm height = 10; 1888d6317402Snicm spm->start = 0; 1889d6317402Snicm 1890d6317402Snicm menu = menu_create(""); 1891d6317402Snicm RB_FOREACH(wl, winlinks, &s->windows) { 1892e484fb00Snicm if (word != NULL && *word != '\0') { 1893e484fb00Snicm xasprintf(&tmp, "%d", wl->idx); 1894e484fb00Snicm if (strncmp(tmp, word, strlen(word)) != 0) { 1895e484fb00Snicm free(tmp); 1896e484fb00Snicm continue; 1897e484fb00Snicm } 1898e484fb00Snicm free(tmp); 1899e484fb00Snicm } 1900e484fb00Snicm 1901d6317402Snicm list = xreallocarray(list, size + 1, sizeof *list); 1902bc5a8fc2Snicm if (c->prompt_type == PROMPT_TYPE_WINDOW_TARGET) { 190337a3aee2Snicm xasprintf(&tmp, "%d (%s)", wl->idx, wl->window->name); 190437a3aee2Snicm xasprintf(&list[size++], "%d", wl->idx); 190537a3aee2Snicm } else { 1906d6317402Snicm xasprintf(&tmp, "%s:%d (%s)", s->name, wl->idx, 1907d6317402Snicm wl->window->name); 190837a3aee2Snicm xasprintf(&list[size++], "%s:%d", s->name, wl->idx); 190937a3aee2Snicm } 1910d6317402Snicm item.name = tmp; 1911d6317402Snicm item.key = '0' + size - 1; 1912d6317402Snicm item.command = NULL; 19130f4e471eSnicm menu_add_item(menu, &item, NULL, c, NULL); 1914d6317402Snicm free(tmp); 1915d6317402Snicm 1916d6317402Snicm if (size == height) 1917d6317402Snicm break; 1918d6317402Snicm } 1919e484fb00Snicm if (size == 0) { 1920e484fb00Snicm menu_free(menu); 1921095d8c2eSnicm free(spm); 1922e484fb00Snicm return (NULL); 1923e484fb00Snicm } 1924d6317402Snicm if (size == 1) { 1925d6317402Snicm menu_free(menu); 192637a3aee2Snicm if (flag != '\0') { 1927d6317402Snicm xasprintf(&tmp, "-%c%s", flag, list[0]); 1928d6317402Snicm free(list[0]); 192937a3aee2Snicm } else 193037a3aee2Snicm tmp = list[0]; 1931d6317402Snicm free(list); 1932095d8c2eSnicm free(spm); 1933d6317402Snicm return (tmp); 1934d6317402Snicm } 1935d6317402Snicm if (height > size) 1936d6317402Snicm height = size; 1937d6317402Snicm 1938d6317402Snicm spm->size = size; 1939d6317402Snicm spm->list = list; 1940d6317402Snicm 1941d6317402Snicm if (options_get_number(c->session->options, "status-position") == 0) 1942d6317402Snicm py = lines; 1943d6317402Snicm else 1944d6317402Snicm py = c->tty.sy - 3 - height; 1945d6317402Snicm offset += utf8_cstrwidth(c->prompt_string); 1946d6317402Snicm if (offset > 2) 1947d6317402Snicm offset -= 2; 1948d6317402Snicm else 1949d6317402Snicm offset = 0; 1950d6317402Snicm 195117d7ce67Snicm if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, 0, NULL, offset, py, c, 195219c94b00Snicm BOX_LINES_DEFAULT, NULL, NULL, NULL, NULL, 195319c94b00Snicm status_prompt_menu_callback, spm) != 0) { 1954d6317402Snicm menu_free(menu); 1955d6317402Snicm free(spm); 1956d6317402Snicm return (NULL); 1957d6317402Snicm } 1958d6317402Snicm return (NULL); 1959d6317402Snicm } 1960d6317402Snicm 1961d6317402Snicm /* Sort complete list. */ 1962d6317402Snicm static int 1963d6317402Snicm status_prompt_complete_sort(const void *a, const void *b) 1964d6317402Snicm { 1965d6317402Snicm const char **aa = (const char **)a, **bb = (const char **)b; 1966d6317402Snicm 1967d6317402Snicm return (strcmp(*aa, *bb)); 1968d6317402Snicm } 1969d6317402Snicm 197037a3aee2Snicm /* Complete a session. */ 197137a3aee2Snicm static char * 197237a3aee2Snicm status_prompt_complete_session(char ***list, u_int *size, const char *s, 197337a3aee2Snicm char flag) 197437a3aee2Snicm { 197537a3aee2Snicm struct session *loop; 1976552c5a37Snicm char *out, *tmp, n[11]; 197737a3aee2Snicm 197837a3aee2Snicm RB_FOREACH(loop, sessions, &sessions) { 1979552c5a37Snicm if (*s == '\0' || strncmp(loop->name, s, strlen(s)) == 0) { 1980552c5a37Snicm *list = xreallocarray(*list, (*size) + 2, 1981552c5a37Snicm sizeof **list); 198237a3aee2Snicm xasprintf(&(*list)[(*size)++], "%s:", loop->name); 1983552c5a37Snicm } else if (*s == '$') { 1984552c5a37Snicm xsnprintf(n, sizeof n, "%u", loop->id); 1985552c5a37Snicm if (s[1] == '\0' || 1986552c5a37Snicm strncmp(n, s + 1, strlen(s) - 1) == 0) { 1987552c5a37Snicm *list = xreallocarray(*list, (*size) + 2, 1988552c5a37Snicm sizeof **list); 1989552c5a37Snicm xasprintf(&(*list)[(*size)++], "$%s:", n); 1990552c5a37Snicm } 1991552c5a37Snicm } 199237a3aee2Snicm } 199337a3aee2Snicm out = status_prompt_complete_prefix(*list, *size); 199437a3aee2Snicm if (out != NULL && flag != '\0') { 199537a3aee2Snicm xasprintf(&tmp, "-%c%s", flag, out); 199637a3aee2Snicm free(out); 199737a3aee2Snicm out = tmp; 199837a3aee2Snicm } 199937a3aee2Snicm return (out); 200037a3aee2Snicm } 200137a3aee2Snicm 2002311827fbSnicm /* Complete word. */ 20039883b791Snicm static char * 2004d6317402Snicm status_prompt_complete(struct client *c, const char *word, u_int offset) 2005311827fbSnicm { 200637a3aee2Snicm struct session *session; 2007d6317402Snicm const char *s, *colon; 200837a3aee2Snicm char **list = NULL, *copy = NULL, *out = NULL; 2009d6317402Snicm char flag = '\0'; 2010276a572eSnicm u_int size = 0, i; 2011311827fbSnicm 2012e484fb00Snicm if (*word == '\0' && 2013bc5a8fc2Snicm c->prompt_type != PROMPT_TYPE_TARGET && 2014bc5a8fc2Snicm c->prompt_type != PROMPT_TYPE_WINDOW_TARGET) 2015311827fbSnicm return (NULL); 2016311827fbSnicm 2017bc5a8fc2Snicm if (c->prompt_type != PROMPT_TYPE_TARGET && 2018bc5a8fc2Snicm c->prompt_type != PROMPT_TYPE_WINDOW_TARGET && 201937a3aee2Snicm strncmp(word, "-t", 2) != 0 && 202037a3aee2Snicm strncmp(word, "-s", 2) != 0) { 2021d6317402Snicm list = status_prompt_complete_list(&size, word, offset == 0); 2022276a572eSnicm if (size == 0) 2023276a572eSnicm out = NULL; 2024276a572eSnicm else if (size == 1) 2025276a572eSnicm xasprintf(&out, "%s ", list[0]); 2026276a572eSnicm else 2027276a572eSnicm out = status_prompt_complete_prefix(list, size); 2028276a572eSnicm goto found; 2029311827fbSnicm } 2030311827fbSnicm 2031bc5a8fc2Snicm if (c->prompt_type == PROMPT_TYPE_TARGET || 2032bc5a8fc2Snicm c->prompt_type == PROMPT_TYPE_WINDOW_TARGET) { 203337a3aee2Snicm s = word; 203437a3aee2Snicm flag = '\0'; 203537a3aee2Snicm } else { 2036d6317402Snicm s = word + 2; 2037d6317402Snicm flag = word[1]; 2038d6317402Snicm offset += 2; 203937a3aee2Snicm } 2040e484fb00Snicm 2041e484fb00Snicm /* If this is a window completion, open the window menu. */ 2042bc5a8fc2Snicm if (c->prompt_type == PROMPT_TYPE_WINDOW_TARGET) { 2043e484fb00Snicm out = status_prompt_complete_window_menu(c, c->session, s, 2044e484fb00Snicm offset, '\0'); 2045e484fb00Snicm goto found; 2046e484fb00Snicm } 2047d6317402Snicm colon = strchr(s, ':'); 2048311827fbSnicm 2049d6317402Snicm /* If there is no colon, complete as a session. */ 2050d6317402Snicm if (colon == NULL) { 205137a3aee2Snicm out = status_prompt_complete_session(&list, &size, s, flag); 2052d6317402Snicm goto found; 2053d6317402Snicm } 2054311827fbSnicm 2055d6317402Snicm /* If there is a colon but no period, find session and show a menu. */ 2056d6317402Snicm if (strchr(colon + 1, '.') == NULL) { 2057d6317402Snicm if (*s == ':') 2058d6317402Snicm session = c->session; 2059d6317402Snicm else { 2060d6317402Snicm copy = xstrdup(s); 2061d6317402Snicm *strchr(copy, ':') = '\0'; 2062d6317402Snicm session = session_find(copy); 2063d6317402Snicm free(copy); 2064d6317402Snicm if (session == NULL) 2065d6317402Snicm goto found; 2066d6317402Snicm } 2067e484fb00Snicm out = status_prompt_complete_window_menu(c, session, colon + 1, 2068e484fb00Snicm offset, flag); 2069d6317402Snicm if (out == NULL) 2070d6317402Snicm return (NULL); 2071d6317402Snicm } 2072276a572eSnicm 2073276a572eSnicm found: 2074d6317402Snicm if (size != 0) { 2075d6317402Snicm qsort(list, size, sizeof *list, status_prompt_complete_sort); 2076d6317402Snicm for (i = 0; i < size; i++) 2077d6317402Snicm log_debug("complete %u: %s", i, list[i]); 2078d6317402Snicm } 2079d6317402Snicm 2080d6317402Snicm if (out != NULL && strcmp(word, out) == 0) { 2081d6317402Snicm free(out); 2082d6317402Snicm out = NULL; 2083d6317402Snicm } 2084d6317402Snicm if (out != NULL || 2085d6317402Snicm !status_prompt_complete_list_menu(c, list, size, offset, flag)) { 2086d6317402Snicm for (i = 0; i < size; i++) 2087d6317402Snicm free(list[i]); 2088276a572eSnicm free(list); 2089d6317402Snicm } 2090276a572eSnicm return (out); 2091311827fbSnicm } 2092bc5a8fc2Snicm 2093bc5a8fc2Snicm /* Return the type of the prompt as an enum. */ 2094bc5a8fc2Snicm enum prompt_type 2095bc5a8fc2Snicm status_prompt_type(const char *type) 2096bc5a8fc2Snicm { 2097bc5a8fc2Snicm u_int i; 2098bc5a8fc2Snicm 2099bc5a8fc2Snicm for (i = 0; i < PROMPT_NTYPES; i++) { 2100bc5a8fc2Snicm if (strcmp(type, status_prompt_type_string(i)) == 0) 2101bc5a8fc2Snicm return (i); 2102bc5a8fc2Snicm } 2103bc5a8fc2Snicm return (PROMPT_TYPE_INVALID); 2104bc5a8fc2Snicm } 2105bc5a8fc2Snicm 2106bc5a8fc2Snicm /* Accessor for prompt_type_strings. */ 2107bc5a8fc2Snicm const char * 2108bc5a8fc2Snicm status_prompt_type_string(u_int type) 2109bc5a8fc2Snicm { 2110bc5a8fc2Snicm if (type >= PROMPT_NTYPES) 2111bc5a8fc2Snicm return ("invalid"); 2112bc5a8fc2Snicm return (prompt_type_strings[type]); 2113bc5a8fc2Snicm } 2114