16483eba0Schristos /* $OpenBSD$ */ 26483eba0Schristos 36483eba0Schristos /* 46483eba0Schristos * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com> 56483eba0Schristos * 66483eba0Schristos * Permission to use, copy, modify, and distribute this software for any 76483eba0Schristos * purpose with or without fee is hereby granted, provided that the above 86483eba0Schristos * copyright notice and this permission notice appear in all copies. 96483eba0Schristos * 106483eba0Schristos * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 116483eba0Schristos * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 126483eba0Schristos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 136483eba0Schristos * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 146483eba0Schristos * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 156483eba0Schristos * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 166483eba0Schristos * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 176483eba0Schristos */ 186483eba0Schristos 196483eba0Schristos #include <sys/types.h> 206483eba0Schristos 216483eba0Schristos #include <stdlib.h> 226483eba0Schristos #include <string.h> 236483eba0Schristos 246483eba0Schristos #include "tmux.h" 256483eba0Schristos 266483eba0Schristos struct menu_data { 276483eba0Schristos struct cmdq_item *item; 286483eba0Schristos int flags; 296483eba0Schristos 30f844e94eSwiz struct grid_cell style; 31f844e94eSwiz struct grid_cell border_style; 32f844e94eSwiz struct grid_cell selected_style; 33f844e94eSwiz enum box_lines border_lines; 34f844e94eSwiz 356483eba0Schristos struct cmd_find_state fs; 366483eba0Schristos struct screen s; 376483eba0Schristos 386483eba0Schristos u_int px; 396483eba0Schristos u_int py; 406483eba0Schristos 416483eba0Schristos struct menu *menu; 426483eba0Schristos int choice; 436483eba0Schristos 446483eba0Schristos menu_choice_cb cb; 456483eba0Schristos void *data; 466483eba0Schristos }; 476483eba0Schristos 486483eba0Schristos void 496483eba0Schristos menu_add_items(struct menu *menu, const struct menu_item *items, 506483eba0Schristos struct cmdq_item *qitem, struct client *c, struct cmd_find_state *fs) 516483eba0Schristos { 526483eba0Schristos const struct menu_item *loop; 536483eba0Schristos 546483eba0Schristos for (loop = items; loop->name != NULL; loop++) 556483eba0Schristos menu_add_item(menu, loop, qitem, c, fs); 566483eba0Schristos } 576483eba0Schristos 586483eba0Schristos void 596483eba0Schristos menu_add_item(struct menu *menu, const struct menu_item *item, 606483eba0Schristos struct cmdq_item *qitem, struct client *c, struct cmd_find_state *fs) 616483eba0Schristos { 626483eba0Schristos struct menu_item *new_item; 6346548964Swiz const char *key = NULL, *cmd, *suffix = ""; 6446548964Swiz char *s, *trimmed, *name; 6546548964Swiz u_int width, max_width; 666483eba0Schristos int line; 6746548964Swiz size_t keylen, slen; 686483eba0Schristos 696483eba0Schristos line = (item == NULL || item->name == NULL || *item->name == '\0'); 706483eba0Schristos if (line && menu->count == 0) 716483eba0Schristos return; 72f844e94eSwiz if (line && menu->items[menu->count - 1].name == NULL) 73f844e94eSwiz return; 746483eba0Schristos 756483eba0Schristos menu->items = xreallocarray(menu->items, menu->count + 1, 766483eba0Schristos sizeof *menu->items); 776483eba0Schristos new_item = &menu->items[menu->count++]; 786483eba0Schristos memset(new_item, 0, sizeof *new_item); 796483eba0Schristos 806483eba0Schristos if (line) 816483eba0Schristos return; 826483eba0Schristos 836483eba0Schristos if (fs != NULL) 84e271dbb8Schristos s = format_single_from_state(qitem, item->name, c, fs); 856483eba0Schristos else 866483eba0Schristos s = format_single(qitem, item->name, c, NULL, NULL, NULL); 876483eba0Schristos if (*s == '\0') { /* no item if empty after format expanded */ 886483eba0Schristos menu->count--; 896483eba0Schristos return; 906483eba0Schristos } 9146548964Swiz max_width = c->tty.sx - 4; 9246548964Swiz 9346548964Swiz slen = strlen(s); 946483eba0Schristos if (*s != '-' && item->key != KEYC_UNKNOWN && item->key != KEYC_NONE) { 95e271dbb8Schristos key = key_string_lookup_key(item->key, 0); 9646548964Swiz keylen = strlen(key) + 3; /* 3 = space and two brackets */ 9746548964Swiz 9846548964Swiz /* 9946548964Swiz * Add the key if it is shorter than a quarter of the available 10046548964Swiz * space or there is space for the entire item text and the 10146548964Swiz * key. 10246548964Swiz */ 10346548964Swiz if (keylen <= max_width / 4) 10446548964Swiz max_width -= keylen; 10546548964Swiz else if (keylen >= max_width || slen >= max_width - keylen) 10646548964Swiz key = NULL; 10746548964Swiz } 10846548964Swiz 10946548964Swiz if (slen > max_width) { 11046548964Swiz max_width--; 11146548964Swiz suffix = ">"; 11246548964Swiz } 11346548964Swiz trimmed = format_trim_right(s, max_width); 11446548964Swiz if (key != NULL) { 11546548964Swiz xasprintf(&name, "%s%s#[default] #[align=right](%s)", 11646548964Swiz trimmed, suffix, key); 1176483eba0Schristos } else 11846548964Swiz xasprintf(&name, "%s%s", trimmed, suffix); 11946548964Swiz free(trimmed); 12046548964Swiz 1216483eba0Schristos new_item->name = name; 1226483eba0Schristos free(s); 1236483eba0Schristos 1246483eba0Schristos cmd = item->command; 1256483eba0Schristos if (cmd != NULL) { 1266483eba0Schristos if (fs != NULL) 127e271dbb8Schristos s = format_single_from_state(qitem, cmd, c, fs); 1286483eba0Schristos else 1296483eba0Schristos s = format_single(qitem, cmd, c, NULL, NULL, NULL); 1306483eba0Schristos } else 1316483eba0Schristos s = NULL; 1326483eba0Schristos new_item->command = s; 1336483eba0Schristos new_item->key = item->key; 1346483eba0Schristos 1356483eba0Schristos width = format_width(new_item->name); 13646548964Swiz if (*new_item->name == '-') 13746548964Swiz width--; 1386483eba0Schristos if (width > menu->width) 1396483eba0Schristos menu->width = width; 1406483eba0Schristos } 1416483eba0Schristos 1426483eba0Schristos struct menu * 1436483eba0Schristos menu_create(const char *title) 1446483eba0Schristos { 1456483eba0Schristos struct menu *menu; 1466483eba0Schristos 1476483eba0Schristos menu = xcalloc(1, sizeof *menu); 1486483eba0Schristos menu->title = xstrdup(title); 149e271dbb8Schristos menu->width = format_width(title); 1506483eba0Schristos 1516483eba0Schristos return (menu); 1526483eba0Schristos } 1536483eba0Schristos 1546483eba0Schristos void 1556483eba0Schristos menu_free(struct menu *menu) 1566483eba0Schristos { 1576483eba0Schristos u_int i; 1586483eba0Schristos 1596483eba0Schristos for (i = 0; i < menu->count; i++) { 16030744affSchristos free(__UNCONST(menu->items[i].name)); 16130744affSchristos free(__UNCONST(menu->items[i].command)); 1626483eba0Schristos } 1636483eba0Schristos free(menu->items); 1646483eba0Schristos 16530744affSchristos free(__UNCONST(menu->title)); 1666483eba0Schristos free(menu); 1676483eba0Schristos } 1686483eba0Schristos 16946548964Swiz struct screen * 170f844e94eSwiz menu_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy) 171e271dbb8Schristos { 17246548964Swiz struct menu_data *md = data; 173e271dbb8Schristos 174f844e94eSwiz *cx = md->px + 2; 175f844e94eSwiz if (md->choice == -1) 176f844e94eSwiz *cy = md->py; 177f844e94eSwiz else 178f844e94eSwiz *cy = md->py + 1 + md->choice; 179f844e94eSwiz 180e271dbb8Schristos return (&md->s); 181e271dbb8Schristos } 182e271dbb8Schristos 18346548964Swiz /* Return parts of the input range which are not obstructed by the menu. */ 18446548964Swiz void 18546548964Swiz menu_check_cb(__unused struct client *c, void *data, u_int px, u_int py, 18646548964Swiz u_int nx, struct overlay_ranges *r) 1876483eba0Schristos { 18846548964Swiz struct menu_data *md = data; 18946548964Swiz struct menu *menu = md->menu; 19046548964Swiz 19146548964Swiz server_client_overlay_range(md->px, md->py, menu->width + 4, 19246548964Swiz menu->count + 2, px, py, nx, r); 19346548964Swiz } 19446548964Swiz 19546548964Swiz void 19646548964Swiz menu_draw_cb(struct client *c, void *data, 19746548964Swiz __unused struct screen_redraw_ctx *rctx) 19846548964Swiz { 19946548964Swiz struct menu_data *md = data; 2006483eba0Schristos struct tty *tty = &c->tty; 2016483eba0Schristos struct screen *s = &md->s; 2026483eba0Schristos struct menu *menu = md->menu; 2036483eba0Schristos struct screen_write_ctx ctx; 204e271dbb8Schristos u_int i, px = md->px, py = md->py; 205e271dbb8Schristos 206e271dbb8Schristos screen_write_start(&ctx, s); 2076483eba0Schristos screen_write_clearscreen(&ctx, 8); 208f844e94eSwiz 209f844e94eSwiz if (md->border_lines != BOX_LINES_NONE) { 210f844e94eSwiz screen_write_box(&ctx, menu->width + 4, menu->count + 2, 211f844e94eSwiz md->border_lines, &md->border_style, menu->title); 212f844e94eSwiz } 213f844e94eSwiz 214f844e94eSwiz screen_write_menu(&ctx, menu, md->choice, md->border_lines, 215f844e94eSwiz &md->style, &md->border_style, &md->selected_style); 2166483eba0Schristos screen_write_stop(&ctx); 2176483eba0Schristos 218e271dbb8Schristos for (i = 0; i < screen_size_y(&md->s); i++) { 219e271dbb8Schristos tty_draw_line(tty, s, 0, i, menu->width + 4, px, py + i, 220e271dbb8Schristos &grid_default_cell, NULL); 221e271dbb8Schristos } 2226483eba0Schristos } 2236483eba0Schristos 22446548964Swiz void 22546548964Swiz menu_free_cb(__unused struct client *c, void *data) 2266483eba0Schristos { 22746548964Swiz struct menu_data *md = data; 2286483eba0Schristos 2296483eba0Schristos if (md->item != NULL) 2306483eba0Schristos cmdq_continue(md->item); 2316483eba0Schristos 2326483eba0Schristos if (md->cb != NULL) 2336483eba0Schristos md->cb(md->menu, UINT_MAX, KEYC_NONE, md->data); 2346483eba0Schristos 2356483eba0Schristos screen_free(&md->s); 2366483eba0Schristos menu_free(md->menu); 2376483eba0Schristos free(md); 2386483eba0Schristos } 2396483eba0Schristos 24046548964Swiz int 24146548964Swiz menu_key_cb(struct client *c, void *data, struct key_event *event) 2426483eba0Schristos { 24346548964Swiz struct menu_data *md = data; 2446483eba0Schristos struct menu *menu = md->menu; 2456483eba0Schristos struct mouse_event *m = &event->m; 2466483eba0Schristos u_int i; 2476483eba0Schristos int count = menu->count, old = md->choice; 248e271dbb8Schristos const char *name = NULL; 2496483eba0Schristos const struct menu_item *item; 250e271dbb8Schristos struct cmdq_state *state; 251e271dbb8Schristos enum cmd_parse_status status; 252e271dbb8Schristos char *error; 2536483eba0Schristos 2546483eba0Schristos if (KEYC_IS_MOUSE(event->key)) { 2556483eba0Schristos if (md->flags & MENU_NOMOUSE) { 25646548964Swiz if (MOUSE_BUTTONS(m->b) != MOUSE_BUTTON_1) 2576483eba0Schristos return (1); 2586483eba0Schristos return (0); 2596483eba0Schristos } 2606483eba0Schristos if (m->x < md->px || 2616483eba0Schristos m->x > md->px + 4 + menu->width || 2626483eba0Schristos m->y < md->py + 1 || 2636483eba0Schristos m->y > md->py + 1 + count - 1) { 264e271dbb8Schristos if (~md->flags & MENU_STAYOPEN) { 2656483eba0Schristos if (MOUSE_RELEASE(m->b)) 2666483eba0Schristos return (1); 267e271dbb8Schristos } else { 268e271dbb8Schristos if (!MOUSE_RELEASE(m->b) && 26946548964Swiz !MOUSE_WHEEL(m->b) && 270e271dbb8Schristos !MOUSE_DRAG(m->b)) 271e271dbb8Schristos return (1); 272e271dbb8Schristos } 2736483eba0Schristos if (md->choice != -1) { 2746483eba0Schristos md->choice = -1; 2756483eba0Schristos c->flags |= CLIENT_REDRAWOVERLAY; 2766483eba0Schristos } 2776483eba0Schristos return (0); 2786483eba0Schristos } 279e271dbb8Schristos if (~md->flags & MENU_STAYOPEN) { 2806483eba0Schristos if (MOUSE_RELEASE(m->b)) 2816483eba0Schristos goto chosen; 282e271dbb8Schristos } else { 28346548964Swiz if (!MOUSE_WHEEL(m->b) && !MOUSE_DRAG(m->b)) 284e271dbb8Schristos goto chosen; 285e271dbb8Schristos } 2866483eba0Schristos md->choice = m->y - (md->py + 1); 2876483eba0Schristos if (md->choice != old) 2886483eba0Schristos c->flags |= CLIENT_REDRAWOVERLAY; 2896483eba0Schristos return (0); 2906483eba0Schristos } 2916483eba0Schristos for (i = 0; i < (u_int)count; i++) { 2926483eba0Schristos name = menu->items[i].name; 2936483eba0Schristos if (name == NULL || *name == '-') 2946483eba0Schristos continue; 2956483eba0Schristos if (event->key == menu->items[i].key) { 2966483eba0Schristos md->choice = i; 2976483eba0Schristos goto chosen; 2986483eba0Schristos } 2996483eba0Schristos } 300e271dbb8Schristos switch (event->key & ~KEYC_MASK_FLAGS) { 3016483eba0Schristos case KEYC_UP: 3026483eba0Schristos case 'k': 3036483eba0Schristos if (old == -1) 3046483eba0Schristos old = 0; 3056483eba0Schristos do { 3066483eba0Schristos if (md->choice == -1 || md->choice == 0) 3076483eba0Schristos md->choice = count - 1; 3086483eba0Schristos else 3096483eba0Schristos md->choice--; 3106483eba0Schristos name = menu->items[md->choice].name; 3116483eba0Schristos } while ((name == NULL || *name == '-') && md->choice != old); 3126483eba0Schristos c->flags |= CLIENT_REDRAWOVERLAY; 3136483eba0Schristos return (0); 314e271dbb8Schristos case KEYC_BSPACE: 315e271dbb8Schristos if (~md->flags & MENU_TAB) 316e271dbb8Schristos break; 317e271dbb8Schristos return (1); 318e271dbb8Schristos case '\011': /* Tab */ 319e271dbb8Schristos if (~md->flags & MENU_TAB) 320e271dbb8Schristos break; 321e271dbb8Schristos if (md->choice == count - 1) 322e271dbb8Schristos return (1); 323e271dbb8Schristos /* FALLTHROUGH */ 3246483eba0Schristos case KEYC_DOWN: 3256483eba0Schristos case 'j': 3266483eba0Schristos if (old == -1) 3276483eba0Schristos old = 0; 3286483eba0Schristos do { 3296483eba0Schristos if (md->choice == -1 || md->choice == count - 1) 3306483eba0Schristos md->choice = 0; 3316483eba0Schristos else 3326483eba0Schristos md->choice++; 3336483eba0Schristos name = menu->items[md->choice].name; 3346483eba0Schristos } while ((name == NULL || *name == '-') && md->choice != old); 3356483eba0Schristos c->flags |= CLIENT_REDRAWOVERLAY; 3366483eba0Schristos return (0); 337e271dbb8Schristos case KEYC_PPAGE: 338*890b6d91Swiz case 'b'|KEYC_CTRL: 339f844e94eSwiz if (md->choice < 6) 340e271dbb8Schristos md->choice = 0; 341f844e94eSwiz else { 342f844e94eSwiz i = 5; 343f844e94eSwiz while (i > 0) { 344f844e94eSwiz md->choice--; 345f844e94eSwiz name = menu->items[md->choice].name; 346f844e94eSwiz if (md->choice != 0 && 347f844e94eSwiz (name != NULL && *name != '-')) 348f844e94eSwiz i--; 349f844e94eSwiz else if (md->choice == 0) 350f844e94eSwiz break; 351f844e94eSwiz } 352f844e94eSwiz } 353f844e94eSwiz c->flags |= CLIENT_REDRAWOVERLAY; 354f844e94eSwiz break; 355f844e94eSwiz case KEYC_NPAGE: 356f844e94eSwiz if (md->choice > count - 6) { 357f844e94eSwiz md->choice = count - 1; 358f844e94eSwiz name = menu->items[md->choice].name; 359f844e94eSwiz } else { 360f844e94eSwiz i = 5; 361f844e94eSwiz while (i > 0) { 362e271dbb8Schristos md->choice++; 363f844e94eSwiz name = menu->items[md->choice].name; 364f844e94eSwiz if (md->choice != count - 1 && 365f844e94eSwiz (name != NULL && *name != '-')) 366f844e94eSwiz i++; 367f844e94eSwiz else if (md->choice == count - 1) 368f844e94eSwiz break; 369f844e94eSwiz } 370f844e94eSwiz } 371f844e94eSwiz while (name == NULL || *name == '-') { 372f844e94eSwiz md->choice--; 373f844e94eSwiz name = menu->items[md->choice].name; 374f844e94eSwiz } 375f844e94eSwiz c->flags |= CLIENT_REDRAWOVERLAY; 376f844e94eSwiz break; 377f844e94eSwiz case 'g': 378f844e94eSwiz case KEYC_HOME: 379f844e94eSwiz md->choice = 0; 380f844e94eSwiz name = menu->items[md->choice].name; 381f844e94eSwiz while (name == NULL || *name == '-') { 382f844e94eSwiz md->choice++; 383f844e94eSwiz name = menu->items[md->choice].name; 384f844e94eSwiz } 385e271dbb8Schristos c->flags |= CLIENT_REDRAWOVERLAY; 386e271dbb8Schristos break; 387e271dbb8Schristos case 'G': 388f844e94eSwiz case KEYC_END: 389e271dbb8Schristos md->choice = count - 1; 390f844e94eSwiz name = menu->items[md->choice].name; 391f844e94eSwiz while (name == NULL || *name == '-') { 392e271dbb8Schristos md->choice--; 393f844e94eSwiz name = menu->items[md->choice].name; 394f844e94eSwiz } 395e271dbb8Schristos c->flags |= CLIENT_REDRAWOVERLAY; 396e271dbb8Schristos break; 397*890b6d91Swiz case 'f'|KEYC_CTRL: 398e271dbb8Schristos break; 3996483eba0Schristos case '\r': 4006483eba0Schristos goto chosen; 4016483eba0Schristos case '\033': /* Escape */ 402*890b6d91Swiz case 'c'|KEYC_CTRL: 403*890b6d91Swiz case 'g'|KEYC_CTRL: 4046483eba0Schristos case 'q': 4056483eba0Schristos return (1); 4066483eba0Schristos } 4076483eba0Schristos return (0); 4086483eba0Schristos 4096483eba0Schristos chosen: 4106483eba0Schristos if (md->choice == -1) 4116483eba0Schristos return (1); 4126483eba0Schristos item = &menu->items[md->choice]; 413e271dbb8Schristos if (item->name == NULL || *item->name == '-') { 414e271dbb8Schristos if (md->flags & MENU_STAYOPEN) 415e271dbb8Schristos return (0); 4166483eba0Schristos return (1); 417e271dbb8Schristos } 4186483eba0Schristos if (md->cb != NULL) { 4196483eba0Schristos md->cb(md->menu, md->choice, item->key, md->data); 4206483eba0Schristos md->cb = NULL; 4216483eba0Schristos return (1); 4226483eba0Schristos } 4236483eba0Schristos 4246483eba0Schristos if (md->item != NULL) 425e271dbb8Schristos event = cmdq_get_event(md->item); 4266483eba0Schristos else 427e271dbb8Schristos event = NULL; 428e271dbb8Schristos state = cmdq_new_state(&md->fs, event, 0); 429e271dbb8Schristos 430e271dbb8Schristos status = cmd_parse_and_append(item->command, NULL, c, state, &error); 431e271dbb8Schristos if (status == CMD_PARSE_ERROR) { 432e271dbb8Schristos cmdq_append(c, cmdq_get_error(error)); 433e271dbb8Schristos free(error); 4346483eba0Schristos } 435e271dbb8Schristos cmdq_free_state(state); 436e271dbb8Schristos 4376483eba0Schristos return (1); 4386483eba0Schristos } 4396483eba0Schristos 440f844e94eSwiz static void 441f844e94eSwiz menu_set_style(struct client *c, struct grid_cell *gc, const char *style, 442f844e94eSwiz const char *option) 443f844e94eSwiz { 444f844e94eSwiz struct style sytmp; 445f844e94eSwiz struct options *o = c->session->curw->window->options; 446f844e94eSwiz 447f844e94eSwiz memcpy(gc, &grid_default_cell, sizeof *gc); 448f844e94eSwiz style_apply(gc, o, option, NULL); 449f844e94eSwiz if (style != NULL) { 450f844e94eSwiz style_set(&sytmp, &grid_default_cell); 451f844e94eSwiz if (style_parse(&sytmp, gc, style) == 0) { 452f844e94eSwiz gc->fg = sytmp.gc.fg; 453f844e94eSwiz gc->bg = sytmp.gc.bg; 454f844e94eSwiz } 455f844e94eSwiz } 456f844e94eSwiz gc->attr = 0; 457f844e94eSwiz } 458f844e94eSwiz 45946548964Swiz struct menu_data * 460f844e94eSwiz menu_prepare(struct menu *menu, int flags, int starting_choice, 461f844e94eSwiz struct cmdq_item *item, u_int px, u_int py, struct client *c, 462f844e94eSwiz enum box_lines lines, const char *style, const char *selected_style, 463f844e94eSwiz const char *border_style, struct cmd_find_state *fs, menu_choice_cb cb, 4646483eba0Schristos void *data) 4656483eba0Schristos { 4666483eba0Schristos struct menu_data *md; 467f844e94eSwiz int choice; 468e271dbb8Schristos const char *name; 469f844e94eSwiz struct options *o = c->session->curw->window->options; 4706483eba0Schristos 4716483eba0Schristos if (c->tty.sx < menu->width + 4 || c->tty.sy < menu->count + 2) 47246548964Swiz return (NULL); 473e271dbb8Schristos if (px + menu->width + 4 > c->tty.sx) 474e271dbb8Schristos px = c->tty.sx - menu->width - 4; 475e271dbb8Schristos if (py + menu->count + 2 > c->tty.sy) 476e271dbb8Schristos py = c->tty.sy - menu->count - 2; 4776483eba0Schristos 478f844e94eSwiz if (lines == BOX_LINES_DEFAULT) 479f844e94eSwiz lines = options_get_number(o, "menu-border-lines"); 480f844e94eSwiz 4816483eba0Schristos md = xcalloc(1, sizeof *md); 4826483eba0Schristos md->item = item; 4836483eba0Schristos md->flags = flags; 484f844e94eSwiz md->border_lines = lines; 485f844e94eSwiz 486f844e94eSwiz menu_set_style(c, &md->style, style, "menu-style"); 487f844e94eSwiz menu_set_style(c, &md->selected_style, selected_style, 488f844e94eSwiz "menu-selected-style"); 489f844e94eSwiz menu_set_style(c, &md->border_style, border_style, "menu-border-style"); 4906483eba0Schristos 4916483eba0Schristos if (fs != NULL) 4926483eba0Schristos cmd_find_copy_state(&md->fs, fs); 4936483eba0Schristos screen_init(&md->s, menu->width + 4, menu->count + 2, 0); 494e271dbb8Schristos if (~md->flags & MENU_NOMOUSE) 49546548964Swiz md->s.mode |= (MODE_MOUSE_ALL|MODE_MOUSE_BUTTON); 496e271dbb8Schristos md->s.mode &= ~MODE_CURSOR; 4976483eba0Schristos 4986483eba0Schristos md->px = px; 4996483eba0Schristos md->py = py; 5006483eba0Schristos 5016483eba0Schristos md->menu = menu; 502f844e94eSwiz md->choice = -1; 503f844e94eSwiz 504e271dbb8Schristos if (md->flags & MENU_NOMOUSE) { 505f844e94eSwiz if (starting_choice >= (int)menu->count) { 506f844e94eSwiz starting_choice = menu->count - 1; 507f844e94eSwiz choice = starting_choice + 1; 508f844e94eSwiz for (;;) { 509f844e94eSwiz name = menu->items[choice - 1].name; 510f844e94eSwiz if (name != NULL && *name != '-') { 511f844e94eSwiz md->choice = choice - 1; 512e271dbb8Schristos break; 513e271dbb8Schristos } 514f844e94eSwiz if (--choice == 0) 515f844e94eSwiz choice = menu->count; 516f844e94eSwiz if (choice == starting_choice + 1) 517f844e94eSwiz break; 518f844e94eSwiz } 519f844e94eSwiz } else if (starting_choice >= 0) { 520f844e94eSwiz choice = starting_choice; 521f844e94eSwiz for (;;) { 522f844e94eSwiz name = menu->items[choice].name; 523f844e94eSwiz if (name != NULL && *name != '-') { 524f844e94eSwiz md->choice = choice; 525f844e94eSwiz break; 526f844e94eSwiz } 527f844e94eSwiz if (++choice == (int)menu->count) 528f844e94eSwiz choice = 0; 529f844e94eSwiz if (choice == starting_choice) 530f844e94eSwiz break; 531f844e94eSwiz } 532f844e94eSwiz } 533f844e94eSwiz } 5346483eba0Schristos 5356483eba0Schristos md->cb = cb; 5366483eba0Schristos md->data = data; 53746548964Swiz return (md); 53846548964Swiz } 5396483eba0Schristos 54046548964Swiz int 541f844e94eSwiz menu_display(struct menu *menu, int flags, int starting_choice, 542f844e94eSwiz struct cmdq_item *item, u_int px, u_int py, struct client *c, 543f844e94eSwiz enum box_lines lines, const char *style, const char *selected_style, 544f844e94eSwiz const char *border_style, struct cmd_find_state *fs, menu_choice_cb cb, 54546548964Swiz void *data) 54646548964Swiz { 54746548964Swiz struct menu_data *md; 54846548964Swiz 549f844e94eSwiz md = menu_prepare(menu, flags, starting_choice, item, px, py, c, lines, 550f844e94eSwiz style, selected_style, border_style, fs, cb, data); 55146548964Swiz if (md == NULL) 55246548964Swiz return (-1); 553e271dbb8Schristos server_client_set_overlay(c, 0, NULL, menu_mode_cb, menu_draw_cb, 55446548964Swiz menu_key_cb, menu_free_cb, NULL, md); 5556483eba0Schristos return (0); 5566483eba0Schristos } 557