Coverage Report

Created: 2026-10-28 06:08

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.74 2026/10/27 08:49:17 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
0
{
277
0
  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
0
}
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
0
{
306
0
  menu_free_data(w->menu);
307
0
  w->menu = NULL;
308
0
}
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
0
  menu_choice_cb       cb;
375
376
0
  if (KEYC_IS_MOUSE(event->key)) {
377
    /*
378
     * A mouse move with no button held reports as a release, so
379
     * treat it as highlight-only: it must never select or close the
380
     * menu, otherwise a menu opened without a button already down
381
     * (such as a submenu opened from another menu) would vanish as
382
     * soon as the mouse moved over it.
383
     */
384
0
    move = MOUSE_DRAG(m->b) && MOUSE_RELEASE(m->b);
385
0
    if (md->flags & MENU_NOMOUSE) {
386
0
      if (MOUSE_BUTTONS(m->b) != MOUSE_BUTTON_1)
387
0
        return (1);
388
0
      return (0);
389
0
    }
390
0
    border = (md->border_lines != BOX_LINES_NONE);
391
0
    if (m->x < md->px || m->x >= md->px + menu_width(md) ||
392
0
        m->y < md->py + border ||
393
0
        m->y >= md->py + border + n) {
394
0
      if (~md->flags & MENU_STAYOPEN) {
395
0
        if (!move && MOUSE_RELEASE(m->b))
396
0
          return (1);
397
0
      } else {
398
0
        if (!MOUSE_RELEASE(m->b) &&
399
0
            !MOUSE_WHEEL(m->b) &&
400
0
            !MOUSE_DRAG(m->b))
401
0
          return (1);
402
0
      }
403
0
      if (md->choice != -1) {
404
0
        md->choice = -1;
405
0
        server_redraw_window_menu(md->w);
406
0
      }
407
0
      return (0);
408
0
    }
409
0
    if (~md->flags & MENU_STAYOPEN) {
410
0
      if (!move && MOUSE_RELEASE(m->b))
411
0
        goto chosen;
412
0
    } else {
413
0
      if (!MOUSE_WHEEL(m->b) && !MOUSE_DRAG(m->b))
414
0
        goto chosen;
415
0
    }
416
0
    md->choice = m->y - (md->py + border);
417
0
    if (md->choice != old)
418
0
      server_redraw_window_menu(md->w);
419
0
    return (0);
420
0
  }
421
0
  for (i = 0; i < (u_int)n; i++) {
422
0
    name = menu->items[i].name;
423
0
    if (name == NULL || *name == '-')
424
0
      continue;
425
0
    key = (event->key & ~KEYC_MASK_FLAGS);
426
0
    if (key == (menu->items[i].key & ~KEYC_MASK_FLAGS)) {
427
0
      md->choice = i;
428
0
      goto chosen;
429
0
    }
430
0
  }
431
0
  switch (event->key & ~KEYC_MASK_FLAGS) {
432
0
  case KEYC_BTAB:
433
0
  case KEYC_UP:
434
0
  case 'k':
435
0
    if (old == -1)
436
0
      old = 0;
437
0
    do {
438
0
      if (md->choice == -1 || md->choice == 0)
439
0
        md->choice = n - 1;
440
0
      else
441
0
        md->choice--;
442
0
      name = menu->items[md->choice].name;
443
0
    } while ((name == NULL || *name == '-') && md->choice != old);
444
0
    server_redraw_window_menu(md->w);
445
0
    return (0);
446
0
  case KEYC_BSPACE:
447
0
    if (~md->flags & MENU_TAB)
448
0
      break;
449
0
    return (1);
450
0
  case '\011': /* Tab */
451
0
    if (~md->flags & MENU_TAB)
452
0
      break;
453
0
    if (md->choice == n - 1)
454
0
      return (1);
455
    /* FALLTHROUGH */
456
0
  case KEYC_DOWN:
457
0
  case 'j':
458
0
    if (old == -1)
459
0
      old = 0;
460
0
    do {
461
0
      if (md->choice == -1 || md->choice == n - 1)
462
0
        md->choice = 0;
463
0
      else
464
0
        md->choice++;
465
0
      name = menu->items[md->choice].name;
466
0
    } while ((name == NULL || *name == '-') && md->choice != old);
467
0
    server_redraw_window_menu(md->w);
468
0
    return (0);
469
0
  case KEYC_PPAGE:
470
0
  case 'b'|KEYC_CTRL:
471
0
    if (md->choice < 6)
472
0
      md->choice = 0;
473
0
    else {
474
0
      i = 5;
475
0
      while (i > 0) {
476
0
        md->choice--;
477
0
        name = menu->items[md->choice].name;
478
0
        if (md->choice != 0 &&
479
0
            (name != NULL && *name != '-'))
480
0
          i--;
481
0
        else if (md->choice == 0)
482
0
          break;
483
0
      }
484
0
    }
485
0
    server_redraw_window_menu(md->w);
486
0
    break;
487
0
  case KEYC_NPAGE:
488
0
    if (md->choice > n - 6) {
489
0
      md->choice = n - 1;
490
0
      name = menu->items[md->choice].name;
491
0
    } else {
492
0
      i = 5;
493
0
      while (i > 0) {
494
0
        md->choice++;
495
0
        name = menu->items[md->choice].name;
496
0
        if (md->choice != n - 1 &&
497
0
            (name != NULL && *name != '-'))
498
0
          i--;
499
0
        else if (md->choice == n - 1)
500
0
          break;
501
0
      }
502
0
    }
503
0
    while ((name == NULL || *name == '-') && md->choice != 0) {
504
0
      md->choice--;
505
0
      name = menu->items[md->choice].name;
506
0
    }
507
0
    server_redraw_window_menu(md->w);
508
0
    break;
509
0
  case 'g':
510
0
  case KEYC_HOME:
511
0
    md->choice = 0;
512
0
    name = menu->items[md->choice].name;
513
0
    while ((name == NULL || *name == '-') && md->choice != n - 1) {
514
0
      md->choice++;
515
0
      name = menu->items[md->choice].name;
516
0
    }
517
0
    server_redraw_window_menu(md->w);
518
0
    break;
519
0
  case 'G':
520
0
  case KEYC_END:
521
0
    md->choice = n - 1;
522
0
    name = menu->items[md->choice].name;
523
0
    while ((name == NULL || *name == '-') && md->choice != 0) {
524
0
      md->choice--;
525
0
      name = menu->items[md->choice].name;
526
0
    }
527
0
    server_redraw_window_menu(md->w);
528
0
    break;
529
0
  case 'f'|KEYC_CTRL:
530
0
    break;
531
0
  case '\r':
532
0
    goto chosen;
533
0
  case '\033': /* Escape */
534
0
  case '['|KEYC_CTRL:
535
0
  case 'c'|KEYC_CTRL:
536
0
  case 'g'|KEYC_CTRL:
537
0
  case 'q':
538
0
    return (1);
539
0
  }
540
0
  return (0);
541
542
0
chosen:
543
0
  if (md->choice == -1)
544
0
    return (1);
545
0
  item = &menu->items[md->choice];
546
0
  if (item->name == NULL || *item->name == '-') {
547
0
    if (md->flags & MENU_STAYOPEN)
548
0
      return (0);
549
0
    return (1);
550
0
  }
551
0
  if (md->cb != NULL) {
552
0
    cb = md->cb;
553
0
    md->cb = NULL;
554
0
    cb(md->menu, md->choice, item->key, md->data);
555
0
    return (1);
556
0
  }
557
558
0
  if (md->key != KEYC_NONE) {
559
0
    memset(&saved_event, 0, sizeof saved_event);
560
0
    saved_event.key = md->key;
561
0
    memcpy(&saved_event.m, &md->m, sizeof saved_event.m);
562
0
    event = &saved_event;
563
0
  } else
564
0
    event = NULL;
565
0
  state = cmdq_new_state(&md->fs, event, 0);
566
567
0
  status = cmd_parse_and_append(item->command, NULL, c, state, &error);
568
0
  if (status == CMD_PARSE_ERROR) {
569
0
    cmdq_append(c, cmdq_get_error(error));
570
0
    free(error);
571
0
  }
572
0
  cmdq_free_state(state);
573
574
0
  return (1);
575
0
}
576
577
void
578
menu_resize(struct menu_data *md, struct window *w)
579
0
{
580
0
  u_int nx, ny, sx, sy;
581
582
0
  if (md == NULL)
583
0
    return;
584
0
  nx = md->px;
585
0
  ny = md->py;
586
587
0
  menu_get_size(md->menu, md->border_lines, &sx, &sy);
588
0
  if (nx + sx > w->sx) {
589
0
    if (w->sx <= sx)
590
0
      nx = 0;
591
0
    else
592
0
      nx = w->sx - sx;
593
0
  }
594
0
  if (ny + sy > w->sy) {
595
0
    if (w->sy <= sy)
596
0
      ny = 0;
597
0
    else
598
0
      ny = w->sy - sy;
599
0
  }
600
0
  md->px = nx;
601
0
  md->py = ny;
602
0
}
603
604
int
605
menu_display(struct menu *menu, int flags, int starting_choice,
606
    struct cmdq_item *item, u_int px, u_int py, struct client *c,
607
    enum box_lines lines, const char *style, const char *selected_style,
608
    const char *border_style, struct cmd_find_state *fs, menu_choice_cb cb,
609
    void *data)
610
0
{
611
0
  struct menu_data  *md;
612
0
  struct key_event  *event = NULL;
613
0
  int      choice;
614
0
  const char    *name;
615
0
  u_int      sx, sy;
616
0
  struct window   *w;
617
0
  struct options    *o;
618
619
0
  if (item != NULL)
620
0
    event = cmdq_get_event(item);
621
0
  if (fs == NULL || (event != NULL && event->m.valid))
622
0
    w = c->session->curw->window;
623
0
  else
624
0
    w = fs->w;
625
0
  if (fs != NULL)
626
0
    o = fs->w->options;
627
0
  else
628
0
    o = w->options;
629
630
0
  if (lines == BOX_LINES_DEFAULT)
631
0
    lines = options_get_number(o, "menu-border-lines");
632
0
  menu_get_size(menu, lines, &sx, &sy);
633
0
  if (sx >= w->sx)
634
0
    px = 0;
635
0
  else if (px + sx > w->sx)
636
0
    px = w->sx - sx;
637
0
  if (sy >= w->sy)
638
0
    py = 0;
639
0
  else if (py + sy > w->sy)
640
0
    py = w->sy - sy;
641
0
  w->menu_last_px = px;
642
0
  w->menu_last_py = py;
643
644
0
  md = xcalloc(1, sizeof *md);
645
0
  md->w = w;
646
0
  md->flags = flags;
647
0
  md->border_lines = lines;
648
0
  md->key = KEYC_NONE;
649
650
0
  if (event != NULL) {
651
0
    md->key = event->key;
652
0
    memcpy(&md->m, &event->m, sizeof md->m);
653
0
  }
654
655
0
  if (style != NULL)
656
0
    md->style = xstrdup(style);
657
0
  if (selected_style != NULL)
658
0
    md->selected_style = xstrdup(selected_style);
659
0
  if (border_style != NULL)
660
0
    md->border_style = xstrdup(border_style);
661
662
0
  if (fs != NULL)
663
0
    cmd_find_copy_state(&md->fs, fs);
664
0
  else if (cmd_find_from_window(&md->fs, w, 0) != 0)
665
0
    cmd_find_clear_state(&md->fs, 0);
666
0
  screen_init(&md->s, sx, sy, 0);
667
0
  if (~md->flags & MENU_NOMOUSE)
668
0
    md->s.mode |= (MODE_MOUSE_ALL|MODE_MOUSE_BUTTON);
669
0
  md->s.mode &= ~MODE_CURSOR;
670
671
0
  md->px = px;
672
0
  md->py = py;
673
674
0
  md->menu = menu;
675
0
  md->choice = -1;
676
677
0
  md->cb = cb;
678
0
  md->data = data;
679
680
0
  if (md->flags & MENU_NOMOUSE) {
681
0
    if (starting_choice >= (int)menu->count) {
682
0
      starting_choice = menu->count - 1;
683
0
      choice = starting_choice + 1;
684
0
      for (;;) {
685
0
        name = menu->items[choice - 1].name;
686
0
        if (name != NULL && *name != '-') {
687
0
          md->choice = choice - 1;
688
0
          break;
689
0
        }
690
0
        if (--choice == 0)
691
0
          choice = menu->count;
692
0
        if (choice == starting_choice + 1)
693
0
          break;
694
0
      }
695
0
    } else if (starting_choice >= 0) {
696
0
      choice = starting_choice;
697
0
      for (;;) {
698
0
        name = menu->items[choice].name;
699
0
        if (name != NULL && *name != '-') {
700
0
          md->choice = choice;
701
0
          break;
702
0
        }
703
0
        if (++choice == (int)menu->count)
704
0
          choice = 0;
705
0
        if (choice == starting_choice)
706
0
          break;
707
0
      }
708
0
    }
709
0
  }
710
711
0
  menu_close(md->w);
712
0
  md->w->menu = md;
713
714
0
  redraw_invalidate_scene(md->w);
715
0
  window_update_focus(md->w);
716
0
  server_redraw_window(md->w);
717
0
  return (0);
718
0
}