Coverage Report

Created: 2026-10-17 06:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/menu.c
Line
Count
Source
1
/* $OpenBSD: menu.c,v 1.73 2026/10/15 22:16:32 nicm Exp $ */
2
3
/*
4
 * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/types.h>
20
21
#include <stdlib.h>
22
#include <string.h>
23
24
#include "tmux.h"
25
26
struct menu_data {
27
  struct window   *w;
28
  int      flags;
29
30
  char      *style;
31
  char      *border_style;
32
  char      *selected_style;
33
34
  struct grid_cell   style_gc;
35
  struct grid_cell   border_style_gc;
36
  struct grid_cell   selected_style_gc;
37
  enum box_lines     border_lines;
38
39
  struct cmd_find_state  fs;
40
  key_code     key;
41
  struct mouse_event   m;
42
  struct screen    s;
43
44
  u_int      px;
45
  u_int      py;
46
47
  struct menu   *menu;
48
  int      choice;
49
50
  menu_choice_cb     cb;
51
  void      *data;
52
};
53
54
void
55
menu_get_size(struct menu *menu, enum box_lines lines, u_int *sx, u_int *sy)
56
0
{
57
0
  if (lines == BOX_LINES_NONE) {
58
0
    *sx = menu->item_width + 2;
59
0
    *sy = menu->count;
60
0
  } else {
61
0
    *sx = menu->width + 4;
62
0
    *sy = menu->count + 2;
63
0
  }
64
0
}
65
66
void
67
menu_add_items(struct menu *menu, const struct menu_item *items,
68
    struct cmdq_item *qitem, struct client *c, struct cmd_find_state *fs)
69
0
{
70
0
  const struct menu_item  *loop;
71
72
0
  for (loop = items; loop->name != NULL; loop++)
73
0
    menu_add_item(menu, loop, qitem, c, fs);
74
0
}
75
76
void
77
menu_add_item(struct menu *menu, const struct menu_item *item,
78
    struct cmdq_item *qitem, struct client *c, struct cmd_find_state *fs)
79
0
{
80
0
  struct menu_item  *new_item;
81
0
  const char    *key = NULL, *cmd, *suffix = "";
82
0
  char      *s, *trimmed, *name;
83
0
  u_int      width, max_width;
84
0
  int      line;
85
0
  size_t       keylen, slen;
86
87
0
  line = (item == NULL || item->name == NULL || *item->name == '\0');
88
0
  if (line && menu->count == 0)
89
0
    return;
90
0
  if (line && menu->items[menu->count - 1].name == NULL)
91
0
    return;
92
93
0
  menu->items = xreallocarray(menu->items, menu->count + 1,
94
0
      sizeof *menu->items);
95
0
  new_item = &menu->items[menu->count++];
96
0
  memset(new_item, 0, sizeof *new_item);
97
98
0
  if (line)
99
0
    return;
100
101
0
  if (fs != NULL)
102
0
    s = format_single_from_state(qitem, item->name, c, fs);
103
0
  else
104
0
    s = format_single(qitem, item->name, c, NULL, NULL, NULL);
105
0
  if (*s == '\0') { /* no item if empty after format expanded */
106
0
    free(s);
107
0
    menu->count--;
108
0
    return;
109
0
  }
110
0
  max_width = c->tty.sx - 4;
111
112
0
  slen = strlen(s);
113
0
  if (*s != '-' && item->key != KEYC_UNKNOWN && item->key != KEYC_NONE) {
114
0
    key = key_string_lookup_key(item->key, 0);
115
0
    keylen = strlen(key) + 3; /* 3 = space and two brackets */
116
117
    /*
118
     * Add the key if it is shorter than a quarter of the available
119
     * space or there is space for the entire item text and the
120
     * key.
121
     */
122
0
    if (keylen <= max_width / 4)
123
0
      max_width -= keylen;
124
0
    else if (keylen >= max_width || slen >= max_width - keylen)
125
0
      key = NULL;
126
0
  }
127
128
0
  if (slen > max_width) {
129
0
    max_width--;
130
0
    suffix = ">";
131
0
  }
132
0
  trimmed = format_trim_right(s, max_width);
133
0
  if (key != NULL) {
134
0
    xasprintf(&name, "%s%s#[default] #[align=right](%s)",
135
0
        trimmed, suffix, key);
136
0
  } else
137
0
    xasprintf(&name, "%s%s", trimmed, suffix);
138
0
  free(trimmed);
139
140
0
  new_item->name = name;
141
0
  free(s);
142
143
0
  cmd = item->command;
144
0
  if (cmd != NULL) {
145
0
    if (fs != NULL)
146
0
      s = format_single_from_state(qitem, cmd, c, fs);
147
0
    else
148
0
      s = format_single(qitem, cmd, c, NULL, NULL, NULL);
149
0
  } else
150
0
    s = NULL;
151
0
  new_item->command = s;
152
0
  new_item->key = item->key;
153
154
0
  width = format_width(new_item->name);
155
0
  if (*new_item->name == '-')
156
0
    width--;
157
0
  if (width > menu->item_width)
158
0
    menu->item_width = width;
159
0
  if (width > menu->width)
160
0
    menu->width = width;
161
0
}
162
163
struct menu *
164
menu_create(const char *title)
165
0
{
166
0
  struct menu *menu;
167
168
0
  menu = xcalloc(1, sizeof *menu);
169
0
  menu->title = xstrdup(title);
170
0
  menu->width = format_width(title);
171
172
0
  return (menu);
173
0
}
174
175
void
176
menu_free(struct menu *menu)
177
0
{
178
0
  u_int i;
179
180
0
  if (menu == NULL)
181
0
    return;
182
183
0
  for (i = 0; i < menu->count; i++) {
184
0
    free((void *)menu->items[i].name);
185
0
    free((void *)menu->items[i].command);
186
0
  }
187
0
  free(menu->items);
188
189
0
  free((void *)menu->title);
190
0
  free(menu);
191
0
}
192
193
static void
194
menu_reapply_styles(struct menu_data *md)
195
0
{
196
0
  struct options    *o = md->w->options;
197
0
  struct session    *s = NULL;
198
0
  struct winlink    *wl = NULL;
199
0
  struct window_pane  *wp = NULL;
200
0
  struct format_tree  *ft;
201
0
  struct style     sytmp;
202
203
  /* The pane (or session) the menu was opened for may be gone. */
204
0
  if (cmd_find_valid_state(&md->fs)) {
205
0
    s = md->fs.s;
206
0
    wl = md->fs.wl;
207
0
    wp = md->fs.wp;
208
0
    o = md->fs.w->options;
209
0
  }
210
0
  ft = format_create_defaults(NULL, NULL, s, wl, wp);
211
212
  /* Reapply menu style from options. */
213
0
  memcpy(&md->style_gc, &grid_default_cell, sizeof md->style_gc);
214
0
  style_apply(&md->style_gc, o, "menu-style", ft);
215
0
  if (md->style != NULL) {
216
0
    style_set(&sytmp, &grid_default_cell);
217
0
    if (style_parse(&sytmp, &md->style_gc, md->style) == 0) {
218
0
      md->style_gc.fg = sytmp.gc.fg;
219
0
      md->style_gc.bg = sytmp.gc.bg;
220
0
    }
221
0
  }
222
223
  /* Reapply selected style from options. */
224
0
  memcpy(&md->selected_style_gc, &grid_default_cell,
225
0
      sizeof md->selected_style_gc);
226
0
  style_apply(&md->selected_style_gc, o, "menu-selected-style", ft);
227
0
  if (md->selected_style != NULL) {
228
0
    style_set(&sytmp, &grid_default_cell);
229
0
    if (style_parse(&sytmp, &md->selected_style_gc,
230
0
        md->selected_style) == 0) {
231
0
      md->selected_style_gc.fg = sytmp.gc.fg;
232
0
      md->selected_style_gc.bg = sytmp.gc.bg;
233
0
    }
234
0
  }
235
236
  /* Reapply border style from options. */
237
0
  memcpy(&md->border_style_gc, &grid_default_cell,
238
0
      sizeof md->border_style_gc);
239
0
  style_apply(&md->border_style_gc, o, "menu-border-style", ft);
240
0
  if (md->border_style != NULL) {
241
0
    style_set(&sytmp, &grid_default_cell);
242
0
    if (style_parse(&sytmp, &md->border_style_gc,
243
0
        md->border_style) == 0) {
244
0
      md->border_style_gc.fg = sytmp.gc.fg;
245
0
      md->border_style_gc.bg = sytmp.gc.bg;
246
0
    }
247
0
  }
248
249
0
  format_free(ft);
250
0
}
251
252
void
253
menu_update(struct menu_data *md)
254
0
{
255
0
  struct screen   *s = &md->s;
256
0
  struct menu   *menu = md->menu;
257
0
  struct screen_write_ctx  ctx;
258
259
0
  menu_reapply_styles(md);
260
261
0
  screen_write_start(&ctx, s);
262
0
  screen_write_clearscreen(&ctx, 8);
263
264
0
  if (md->border_lines != BOX_LINES_NONE) {
265
0
    screen_write_box(&ctx, menu_width(md), menu_height(md),
266
0
        md->border_lines, &md->border_style_gc, menu->title);
267
0
  }
268
269
0
  screen_write_menu(&ctx, menu, md->choice, md->border_lines,
270
0
      &md->style_gc, &md->border_style_gc, &md->selected_style_gc);
271
0
  screen_write_stop(&ctx);
272
0
}
273
274
static void
275
menu_free_data(struct menu_data *md)
276
16.2k
{
277
16.2k
  if (md != NULL) {
278
0
    if (md->cb != NULL)
279
0
      md->cb(md->menu, UINT_MAX, KEYC_NONE, md->data);
280
281
0
    screen_free(&md->s);
282
0
    menu_free(md->menu);
283
0
    free(md->style);
284
0
    free(md->selected_style);
285
0
    free(md->border_style);
286
0
    free(md);
287
0
  }
288
16.2k
}
289
290
void
291
menu_close(struct window *w)
292
0
{
293
0
  if (w->menu != NULL) {
294
0
    menu_free_data(w->menu);
295
0
    w->menu = NULL;
296
297
0
    redraw_invalidate_scene(w);
298
0
    window_update_focus(w);
299
0
    server_redraw_window(w);
300
0
  }
301
0
}
302
303
void
304
menu_destroy(struct window *w)
305
16.2k
{
306
16.2k
  menu_free_data(w->menu);
307
16.2k
  w->menu = NULL;
308
16.2k
}
309
310
void
311
menu_get_cursor(struct menu_data *md, u_int *cx, u_int *cy)
312
0
{
313
0
  u_int border = (md->border_lines != BOX_LINES_NONE);
314
315
0
  *cx = md->px + 1 + border;
316
0
  if (md->choice == -1)
317
0
    *cy = md->py;
318
0
  else
319
0
    *cy = md->py + border + md->choice;
320
0
}
321
322
struct screen *
323
menu_screen(struct menu_data *md)
324
0
{
325
0
  return (&md->s);
326
0
}
327
328
u_int
329
menu_width(struct menu_data *md)
330
0
{
331
0
  u_int sx, sy;
332
333
0
  menu_get_size(md->menu, md->border_lines, &sx, &sy);
334
0
  return (sx);
335
0
}
336
337
u_int
338
menu_height(struct menu_data *md)
339
0
{
340
0
  u_int sx, sy;
341
342
0
  menu_get_size(md->menu, md->border_lines, &sx, &sy);
343
0
  return (sy);
344
0
}
345
346
u_int
347
menu_x(struct menu_data *md)
348
0
{
349
0
  return (md->px);
350
0
}
351
352
u_int
353
menu_y(struct menu_data *md)
354
0
{
355
0
  return (md->py);
356
0
}
357
358
int
359
menu_key(struct client *c, struct menu_data *md, struct key_event *event)
360
0
{
361
0
  struct menu     *menu = md->menu;
362
0
  struct mouse_event    *m = &event->m;
363
0
  u_int        i;
364
0
  int        n = menu->count, old = md->choice;
365
0
  int        move;
366
0
  const char      *name = NULL;
367
0
  const struct menu_item    *item;
368
0
  struct key_event     saved_event;
369
0
  struct cmdq_state   *state;
370
0
  enum cmd_parse_status    status;
371
0
  char        *error;
372
0
  key_code       key;
373
0
  u_int        border;
374
375
0
  if (KEYC_IS_MOUSE(event->key)) {
376
    /*
377
     * A mouse move with no button held reports as a release, so
378
     * treat it as highlight-only: it must never select or close the
379
     * menu, otherwise a menu opened without a button already down
380
     * (such as a submenu opened from another menu) would vanish as
381
     * soon as the mouse moved over it.
382
     */
383
0
    move = MOUSE_DRAG(m->b) && MOUSE_RELEASE(m->b);
384
0
    if (md->flags & MENU_NOMOUSE) {
385
0
      if (MOUSE_BUTTONS(m->b) != MOUSE_BUTTON_1)
386
0
        return (1);
387
0
      return (0);
388
0
    }
389
0
    border = (md->border_lines != BOX_LINES_NONE);
390
0
    if (m->x < md->px || m->x >= md->px + menu_width(md) ||
391
0
        m->y < md->py + border ||
392
0
        m->y >= md->py + border + n) {
393
0
      if (~md->flags & MENU_STAYOPEN) {
394
0
        if (!move && MOUSE_RELEASE(m->b))
395
0
          return (1);
396
0
      } else {
397
0
        if (!MOUSE_RELEASE(m->b) &&
398
0
            !MOUSE_WHEEL(m->b) &&
399
0
            !MOUSE_DRAG(m->b))
400
0
          return (1);
401
0
      }
402
0
      if (md->choice != -1) {
403
0
        md->choice = -1;
404
0
        server_redraw_window_menu(md->w);
405
0
      }
406
0
      return (0);
407
0
    }
408
0
    if (~md->flags & MENU_STAYOPEN) {
409
0
      if (!move && MOUSE_RELEASE(m->b))
410
0
        goto chosen;
411
0
    } else {
412
0
      if (!MOUSE_WHEEL(m->b) && !MOUSE_DRAG(m->b))
413
0
        goto chosen;
414
0
    }
415
0
    md->choice = m->y - (md->py + border);
416
0
    if (md->choice != old)
417
0
      server_redraw_window_menu(md->w);
418
0
    return (0);
419
0
  }
420
0
  for (i = 0; i < (u_int)n; i++) {
421
0
    name = menu->items[i].name;
422
0
    if (name == NULL || *name == '-')
423
0
      continue;
424
0
    key = (event->key & ~KEYC_MASK_FLAGS);
425
0
    if (key == (menu->items[i].key & ~KEYC_MASK_FLAGS)) {
426
0
      md->choice = i;
427
0
      goto chosen;
428
0
    }
429
0
  }
430
0
  switch (event->key & ~KEYC_MASK_FLAGS) {
431
0
  case KEYC_BTAB:
432
0
  case KEYC_UP:
433
0
  case 'k':
434
0
    if (old == -1)
435
0
      old = 0;
436
0
    do {
437
0
      if (md->choice == -1 || md->choice == 0)
438
0
        md->choice = n - 1;
439
0
      else
440
0
        md->choice--;
441
0
      name = menu->items[md->choice].name;
442
0
    } while ((name == NULL || *name == '-') && md->choice != old);
443
0
    server_redraw_window_menu(md->w);
444
0
    return (0);
445
0
  case KEYC_BSPACE:
446
0
    if (~md->flags & MENU_TAB)
447
0
      break;
448
0
    return (1);
449
0
  case '\011': /* Tab */
450
0
    if (~md->flags & MENU_TAB)
451
0
      break;
452
0
    if (md->choice == n - 1)
453
0
      return (1);
454
    /* FALLTHROUGH */
455
0
  case KEYC_DOWN:
456
0
  case 'j':
457
0
    if (old == -1)
458
0
      old = 0;
459
0
    do {
460
0
      if (md->choice == -1 || md->choice == n - 1)
461
0
        md->choice = 0;
462
0
      else
463
0
        md->choice++;
464
0
      name = menu->items[md->choice].name;
465
0
    } while ((name == NULL || *name == '-') && md->choice != old);
466
0
    server_redraw_window_menu(md->w);
467
0
    return (0);
468
0
  case KEYC_PPAGE:
469
0
  case 'b'|KEYC_CTRL:
470
0
    if (md->choice < 6)
471
0
      md->choice = 0;
472
0
    else {
473
0
      i = 5;
474
0
      while (i > 0) {
475
0
        md->choice--;
476
0
        name = menu->items[md->choice].name;
477
0
        if (md->choice != 0 &&
478
0
            (name != NULL && *name != '-'))
479
0
          i--;
480
0
        else if (md->choice == 0)
481
0
          break;
482
0
      }
483
0
    }
484
0
    server_redraw_window_menu(md->w);
485
0
    break;
486
0
  case KEYC_NPAGE:
487
0
    if (md->choice > n - 6) {
488
0
      md->choice = n - 1;
489
0
      name = menu->items[md->choice].name;
490
0
    } else {
491
0
      i = 5;
492
0
      while (i > 0) {
493
0
        md->choice++;
494
0
        name = menu->items[md->choice].name;
495
0
        if (md->choice != n - 1 &&
496
0
            (name != NULL && *name != '-'))
497
0
          i--;
498
0
        else if (md->choice == n - 1)
499
0
          break;
500
0
      }
501
0
    }
502
0
    while ((name == NULL || *name == '-') && md->choice != 0) {
503
0
      md->choice--;
504
0
      name = menu->items[md->choice].name;
505
0
    }
506
0
    server_redraw_window_menu(md->w);
507
0
    break;
508
0
  case 'g':
509
0
  case KEYC_HOME:
510
0
    md->choice = 0;
511
0
    name = menu->items[md->choice].name;
512
0
    while ((name == NULL || *name == '-') && md->choice != n - 1) {
513
0
      md->choice++;
514
0
      name = menu->items[md->choice].name;
515
0
    }
516
0
    server_redraw_window_menu(md->w);
517
0
    break;
518
0
  case 'G':
519
0
  case KEYC_END:
520
0
    md->choice = n - 1;
521
0
    name = menu->items[md->choice].name;
522
0
    while ((name == NULL || *name == '-') && md->choice != 0) {
523
0
      md->choice--;
524
0
      name = menu->items[md->choice].name;
525
0
    }
526
0
    server_redraw_window_menu(md->w);
527
0
    break;
528
0
  case 'f'|KEYC_CTRL:
529
0
    break;
530
0
  case '\r':
531
0
    goto chosen;
532
0
  case '\033': /* Escape */
533
0
  case '['|KEYC_CTRL:
534
0
  case 'c'|KEYC_CTRL:
535
0
  case 'g'|KEYC_CTRL:
536
0
  case 'q':
537
0
    return (1);
538
0
  }
539
0
  return (0);
540
541
0
chosen:
542
0
  if (md->choice == -1)
543
0
    return (1);
544
0
  item = &menu->items[md->choice];
545
0
  if (item->name == NULL || *item->name == '-') {
546
0
    if (md->flags & MENU_STAYOPEN)
547
0
      return (0);
548
0
    return (1);
549
0
  }
550
0
  if (md->cb != NULL) {
551
0
    md->cb(md->menu, md->choice, item->key, md->data);
552
0
    md->cb = NULL;
553
0
    return (1);
554
0
  }
555
556
0
  if (md->key != KEYC_NONE) {
557
0
    memset(&saved_event, 0, sizeof saved_event);
558
0
    saved_event.key = md->key;
559
0
    memcpy(&saved_event.m, &md->m, sizeof saved_event.m);
560
0
    event = &saved_event;
561
0
  } else
562
0
    event = NULL;
563
0
  state = cmdq_new_state(&md->fs, event, 0);
564
565
0
  status = cmd_parse_and_append(item->command, NULL, c, state, &error);
566
0
  if (status == CMD_PARSE_ERROR) {
567
0
    cmdq_append(c, cmdq_get_error(error));
568
0
    free(error);
569
0
  }
570
0
  cmdq_free_state(state);
571
572
0
  return (1);
573
0
}
574
575
void
576
menu_resize(struct menu_data *md, struct window *w)
577
0
{
578
0
  u_int nx, ny, sx, sy;
579
580
0
  if (md == NULL)
581
0
    return;
582
0
  nx = md->px;
583
0
  ny = md->py;
584
585
0
  menu_get_size(md->menu, md->border_lines, &sx, &sy);
586
0
  if (nx + sx > w->sx) {
587
0
    if (w->sx <= sx)
588
0
      nx = 0;
589
0
    else
590
0
      nx = w->sx - sx;
591
0
  }
592
0
  if (ny + sy > w->sy) {
593
0
    if (w->sy <= sy)
594
0
      ny = 0;
595
0
    else
596
0
      ny = w->sy - sy;
597
0
  }
598
0
  md->px = nx;
599
0
  md->py = ny;
600
0
}
601
602
int
603
menu_display(struct menu *menu, int flags, int starting_choice,
604
    struct cmdq_item *item, u_int px, u_int py, struct client *c,
605
    enum box_lines lines, const char *style, const char *selected_style,
606
    const char *border_style, struct cmd_find_state *fs, menu_choice_cb cb,
607
    void *data)
608
0
{
609
0
  struct menu_data  *md;
610
0
  struct key_event  *event = NULL;
611
0
  int      choice;
612
0
  const char    *name;
613
0
  u_int      sx, sy;
614
0
  struct window   *w;
615
0
  struct options    *o;
616
617
0
  if (item != NULL)
618
0
    event = cmdq_get_event(item);
619
0
  if (fs == NULL || (event != NULL && event->m.valid))
620
0
    w = c->session->curw->window;
621
0
  else
622
0
    w = fs->w;
623
0
  if (fs != NULL)
624
0
    o = fs->w->options;
625
0
  else
626
0
    o = w->options;
627
628
0
  if (lines == BOX_LINES_DEFAULT)
629
0
    lines = options_get_number(o, "menu-border-lines");
630
0
  menu_get_size(menu, lines, &sx, &sy);
631
0
  if (sx >= w->sx)
632
0
    px = 0;
633
0
  else if (px + sx > w->sx)
634
0
    px = w->sx - sx;
635
0
  if (sy >= w->sy)
636
0
    py = 0;
637
0
  else if (py + sy > w->sy)
638
0
    py = w->sy - sy;
639
0
  w->menu_last_px = px;
640
0
  w->menu_last_py = py;
641
642
0
  md = xcalloc(1, sizeof *md);
643
0
  md->w = w;
644
0
  md->flags = flags;
645
0
  md->border_lines = lines;
646
0
  md->key = KEYC_NONE;
647
648
0
  if (event != NULL) {
649
0
    md->key = event->key;
650
0
    memcpy(&md->m, &event->m, sizeof md->m);
651
0
  }
652
653
0
  if (style != NULL)
654
0
    md->style = xstrdup(style);
655
0
  if (selected_style != NULL)
656
0
    md->selected_style = xstrdup(selected_style);
657
0
  if (border_style != NULL)
658
0
    md->border_style = xstrdup(border_style);
659
660
0
  if (fs != NULL)
661
0
    cmd_find_copy_state(&md->fs, fs);
662
0
  else if (cmd_find_from_window(&md->fs, w, 0) != 0)
663
0
    cmd_find_clear_state(&md->fs, 0);
664
0
  screen_init(&md->s, sx, sy, 0);
665
0
  if (~md->flags & MENU_NOMOUSE)
666
0
    md->s.mode |= (MODE_MOUSE_ALL|MODE_MOUSE_BUTTON);
667
0
  md->s.mode &= ~MODE_CURSOR;
668
669
0
  md->px = px;
670
0
  md->py = py;
671
672
0
  md->menu = menu;
673
0
  md->choice = -1;
674
675
0
  md->cb = cb;
676
0
  md->data = data;
677
678
0
  if (md->flags & MENU_NOMOUSE) {
679
0
    if (starting_choice >= (int)menu->count) {
680
0
      starting_choice = menu->count - 1;
681
0
      choice = starting_choice + 1;
682
0
      for (;;) {
683
0
        name = menu->items[choice - 1].name;
684
0
        if (name != NULL && *name != '-') {
685
0
          md->choice = choice - 1;
686
0
          break;
687
0
        }
688
0
        if (--choice == 0)
689
0
          choice = menu->count;
690
0
        if (choice == starting_choice + 1)
691
0
          break;
692
0
      }
693
0
    } else if (starting_choice >= 0) {
694
0
      choice = starting_choice;
695
0
      for (;;) {
696
0
        name = menu->items[choice].name;
697
0
        if (name != NULL && *name != '-') {
698
0
          md->choice = choice;
699
0
          break;
700
0
        }
701
0
        if (++choice == (int)menu->count)
702
0
          choice = 0;
703
0
        if (choice == starting_choice)
704
0
          break;
705
0
      }
706
0
    }
707
0
  }
708
709
0
  menu_close(md->w);
710
0
  md->w->menu = md;
711
712
0
  redraw_invalidate_scene(md->w);
713
0
  window_update_focus(md->w);
714
0
  server_redraw_window(md->w);
715
0
  return (0);
716
0
}