Line | Count | Source (jump to first uncovered line) |
1 | | /* $OpenBSD$ */ |
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 cmdq_item *item; |
28 | | int flags; |
29 | | |
30 | | struct grid_cell style; |
31 | | struct grid_cell border_style; |
32 | | struct grid_cell selected_style; |
33 | | enum box_lines border_lines; |
34 | | |
35 | | struct cmd_find_state fs; |
36 | | struct screen s; |
37 | | |
38 | | u_int px; |
39 | | u_int py; |
40 | | |
41 | | struct menu *menu; |
42 | | int choice; |
43 | | |
44 | | menu_choice_cb cb; |
45 | | void *data; |
46 | | }; |
47 | | |
48 | | void |
49 | | menu_add_items(struct menu *menu, const struct menu_item *items, |
50 | | struct cmdq_item *qitem, struct client *c, struct cmd_find_state *fs) |
51 | 0 | { |
52 | 0 | const struct menu_item *loop; |
53 | |
|
54 | 0 | for (loop = items; loop->name != NULL; loop++) |
55 | 0 | menu_add_item(menu, loop, qitem, c, fs); |
56 | 0 | } |
57 | | |
58 | | void |
59 | | menu_add_item(struct menu *menu, const struct menu_item *item, |
60 | | struct cmdq_item *qitem, struct client *c, struct cmd_find_state *fs) |
61 | 0 | { |
62 | 0 | struct menu_item *new_item; |
63 | 0 | const char *key = NULL, *cmd, *suffix = ""; |
64 | 0 | char *s, *trimmed, *name; |
65 | 0 | u_int width, max_width; |
66 | 0 | int line; |
67 | 0 | size_t keylen, slen; |
68 | |
|
69 | 0 | line = (item == NULL || item->name == NULL || *item->name == '\0'); |
70 | 0 | if (line && menu->count == 0) |
71 | 0 | return; |
72 | 0 | if (line && menu->items[menu->count - 1].name == NULL) |
73 | 0 | return; |
74 | | |
75 | 0 | menu->items = xreallocarray(menu->items, menu->count + 1, |
76 | 0 | sizeof *menu->items); |
77 | 0 | new_item = &menu->items[menu->count++]; |
78 | 0 | memset(new_item, 0, sizeof *new_item); |
79 | |
|
80 | 0 | if (line) |
81 | 0 | return; |
82 | | |
83 | 0 | if (fs != NULL) |
84 | 0 | s = format_single_from_state(qitem, item->name, c, fs); |
85 | 0 | else |
86 | 0 | s = format_single(qitem, item->name, c, NULL, NULL, NULL); |
87 | 0 | if (*s == '\0') { /* no item if empty after format expanded */ |
88 | 0 | menu->count--; |
89 | 0 | return; |
90 | 0 | } |
91 | 0 | max_width = c->tty.sx - 4; |
92 | |
|
93 | 0 | slen = strlen(s); |
94 | 0 | if (*s != '-' && item->key != KEYC_UNKNOWN && item->key != KEYC_NONE) { |
95 | 0 | key = key_string_lookup_key(item->key, 0); |
96 | 0 | keylen = strlen(key) + 3; /* 3 = space and two brackets */ |
97 | | |
98 | | /* |
99 | | * Add the key if it is shorter than a quarter of the available |
100 | | * space or there is space for the entire item text and the |
101 | | * key. |
102 | | */ |
103 | 0 | if (keylen <= max_width / 4) |
104 | 0 | max_width -= keylen; |
105 | 0 | else if (keylen >= max_width || slen >= max_width - keylen) |
106 | 0 | key = NULL; |
107 | 0 | } |
108 | |
|
109 | 0 | if (slen > max_width) { |
110 | 0 | max_width--; |
111 | 0 | suffix = ">"; |
112 | 0 | } |
113 | 0 | trimmed = format_trim_right(s, max_width); |
114 | 0 | if (key != NULL) { |
115 | 0 | xasprintf(&name, "%s%s#[default] #[align=right](%s)", |
116 | 0 | trimmed, suffix, key); |
117 | 0 | } else |
118 | 0 | xasprintf(&name, "%s%s", trimmed, suffix); |
119 | 0 | free(trimmed); |
120 | |
|
121 | 0 | new_item->name = name; |
122 | 0 | free(s); |
123 | |
|
124 | 0 | cmd = item->command; |
125 | 0 | if (cmd != NULL) { |
126 | 0 | if (fs != NULL) |
127 | 0 | s = format_single_from_state(qitem, cmd, c, fs); |
128 | 0 | else |
129 | 0 | s = format_single(qitem, cmd, c, NULL, NULL, NULL); |
130 | 0 | } else |
131 | 0 | s = NULL; |
132 | 0 | new_item->command = s; |
133 | 0 | new_item->key = item->key; |
134 | |
|
135 | 0 | width = format_width(new_item->name); |
136 | 0 | if (*new_item->name == '-') |
137 | 0 | width--; |
138 | 0 | if (width > menu->width) |
139 | 0 | menu->width = width; |
140 | 0 | } |
141 | | |
142 | | struct menu * |
143 | | menu_create(const char *title) |
144 | 0 | { |
145 | 0 | struct menu *menu; |
146 | |
|
147 | 0 | menu = xcalloc(1, sizeof *menu); |
148 | 0 | menu->title = xstrdup(title); |
149 | 0 | menu->width = format_width(title); |
150 | |
|
151 | 0 | return (menu); |
152 | 0 | } |
153 | | |
154 | | void |
155 | | menu_free(struct menu *menu) |
156 | 0 | { |
157 | 0 | u_int i; |
158 | |
|
159 | 0 | for (i = 0; i < menu->count; i++) { |
160 | 0 | free((void *)menu->items[i].name); |
161 | 0 | free((void *)menu->items[i].command); |
162 | 0 | } |
163 | 0 | free(menu->items); |
164 | |
|
165 | 0 | free((void *)menu->title); |
166 | 0 | free(menu); |
167 | 0 | } |
168 | | |
169 | | struct screen * |
170 | | menu_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy) |
171 | 0 | { |
172 | 0 | struct menu_data *md = data; |
173 | |
|
174 | 0 | *cx = md->px + 2; |
175 | 0 | if (md->choice == -1) |
176 | 0 | *cy = md->py; |
177 | 0 | else |
178 | 0 | *cy = md->py + 1 + md->choice; |
179 | |
|
180 | 0 | return (&md->s); |
181 | 0 | } |
182 | | |
183 | | /* Return parts of the input range which are not obstructed by the menu. */ |
184 | | void |
185 | | menu_check_cb(__unused struct client *c, void *data, u_int px, u_int py, |
186 | | u_int nx, struct overlay_ranges *r) |
187 | 0 | { |
188 | 0 | struct menu_data *md = data; |
189 | 0 | struct menu *menu = md->menu; |
190 | |
|
191 | 0 | server_client_overlay_range(md->px, md->py, menu->width + 4, |
192 | 0 | menu->count + 2, px, py, nx, r); |
193 | 0 | } |
194 | | |
195 | | void |
196 | | menu_draw_cb(struct client *c, void *data, |
197 | | __unused struct screen_redraw_ctx *rctx) |
198 | 0 | { |
199 | 0 | struct menu_data *md = data; |
200 | 0 | struct tty *tty = &c->tty; |
201 | 0 | struct screen *s = &md->s; |
202 | 0 | struct menu *menu = md->menu; |
203 | 0 | struct screen_write_ctx ctx; |
204 | 0 | u_int i, px = md->px, py = md->py; |
205 | |
|
206 | 0 | screen_write_start(&ctx, s); |
207 | 0 | screen_write_clearscreen(&ctx, 8); |
208 | |
|
209 | 0 | if (md->border_lines != BOX_LINES_NONE) { |
210 | 0 | screen_write_box(&ctx, menu->width + 4, menu->count + 2, |
211 | 0 | md->border_lines, &md->border_style, menu->title); |
212 | 0 | } |
213 | |
|
214 | 0 | screen_write_menu(&ctx, menu, md->choice, md->border_lines, |
215 | 0 | &md->style, &md->border_style, &md->selected_style); |
216 | 0 | screen_write_stop(&ctx); |
217 | |
|
218 | 0 | for (i = 0; i < screen_size_y(&md->s); i++) { |
219 | 0 | tty_draw_line(tty, s, 0, i, menu->width + 4, px, py + i, |
220 | 0 | &grid_default_cell, NULL); |
221 | 0 | } |
222 | 0 | } |
223 | | |
224 | | void |
225 | | menu_free_cb(__unused struct client *c, void *data) |
226 | 0 | { |
227 | 0 | struct menu_data *md = data; |
228 | |
|
229 | 0 | if (md->item != NULL) |
230 | 0 | cmdq_continue(md->item); |
231 | |
|
232 | 0 | if (md->cb != NULL) |
233 | 0 | md->cb(md->menu, UINT_MAX, KEYC_NONE, md->data); |
234 | |
|
235 | 0 | screen_free(&md->s); |
236 | 0 | menu_free(md->menu); |
237 | 0 | free(md); |
238 | 0 | } |
239 | | |
240 | | int |
241 | | menu_key_cb(struct client *c, void *data, struct key_event *event) |
242 | 0 | { |
243 | 0 | struct menu_data *md = data; |
244 | 0 | struct menu *menu = md->menu; |
245 | 0 | struct mouse_event *m = &event->m; |
246 | 0 | u_int i; |
247 | 0 | int count = menu->count, old = md->choice; |
248 | 0 | const char *name = NULL; |
249 | 0 | const struct menu_item *item; |
250 | 0 | struct cmdq_state *state; |
251 | 0 | enum cmd_parse_status status; |
252 | 0 | char *error; |
253 | |
|
254 | 0 | if (KEYC_IS_MOUSE(event->key)) { |
255 | 0 | if (md->flags & MENU_NOMOUSE) { |
256 | 0 | if (MOUSE_BUTTONS(m->b) != MOUSE_BUTTON_1) |
257 | 0 | return (1); |
258 | 0 | return (0); |
259 | 0 | } |
260 | 0 | if (m->x < md->px || |
261 | 0 | m->x > md->px + 4 + menu->width || |
262 | 0 | m->y < md->py + 1 || |
263 | 0 | m->y > md->py + 1 + count - 1) { |
264 | 0 | if (~md->flags & MENU_STAYOPEN) { |
265 | 0 | if (MOUSE_RELEASE(m->b)) |
266 | 0 | return (1); |
267 | 0 | } else { |
268 | 0 | if (!MOUSE_RELEASE(m->b) && |
269 | 0 | !MOUSE_WHEEL(m->b) && |
270 | 0 | !MOUSE_DRAG(m->b)) |
271 | 0 | return (1); |
272 | 0 | } |
273 | 0 | if (md->choice != -1) { |
274 | 0 | md->choice = -1; |
275 | 0 | c->flags |= CLIENT_REDRAWOVERLAY; |
276 | 0 | } |
277 | 0 | return (0); |
278 | 0 | } |
279 | 0 | if (~md->flags & MENU_STAYOPEN) { |
280 | 0 | if (MOUSE_RELEASE(m->b)) |
281 | 0 | goto chosen; |
282 | 0 | } else { |
283 | 0 | if (!MOUSE_WHEEL(m->b) && !MOUSE_DRAG(m->b)) |
284 | 0 | goto chosen; |
285 | 0 | } |
286 | 0 | md->choice = m->y - (md->py + 1); |
287 | 0 | if (md->choice != old) |
288 | 0 | c->flags |= CLIENT_REDRAWOVERLAY; |
289 | 0 | return (0); |
290 | 0 | } |
291 | 0 | for (i = 0; i < (u_int)count; i++) { |
292 | 0 | name = menu->items[i].name; |
293 | 0 | if (name == NULL || *name == '-') |
294 | 0 | continue; |
295 | 0 | if (event->key == menu->items[i].key) { |
296 | 0 | md->choice = i; |
297 | 0 | goto chosen; |
298 | 0 | } |
299 | 0 | } |
300 | 0 | switch (event->key & ~KEYC_MASK_FLAGS) { |
301 | 0 | case KEYC_UP: |
302 | 0 | case 'k': |
303 | 0 | if (old == -1) |
304 | 0 | old = 0; |
305 | 0 | do { |
306 | 0 | if (md->choice == -1 || md->choice == 0) |
307 | 0 | md->choice = count - 1; |
308 | 0 | else |
309 | 0 | md->choice--; |
310 | 0 | name = menu->items[md->choice].name; |
311 | 0 | } while ((name == NULL || *name == '-') && md->choice != old); |
312 | 0 | c->flags |= CLIENT_REDRAWOVERLAY; |
313 | 0 | return (0); |
314 | 0 | case KEYC_BSPACE: |
315 | 0 | if (~md->flags & MENU_TAB) |
316 | 0 | break; |
317 | 0 | return (1); |
318 | 0 | case '\011': /* Tab */ |
319 | 0 | if (~md->flags & MENU_TAB) |
320 | 0 | break; |
321 | 0 | if (md->choice == count - 1) |
322 | 0 | return (1); |
323 | | /* FALLTHROUGH */ |
324 | 0 | case KEYC_DOWN: |
325 | 0 | case 'j': |
326 | 0 | if (old == -1) |
327 | 0 | old = 0; |
328 | 0 | do { |
329 | 0 | if (md->choice == -1 || md->choice == count - 1) |
330 | 0 | md->choice = 0; |
331 | 0 | else |
332 | 0 | md->choice++; |
333 | 0 | name = menu->items[md->choice].name; |
334 | 0 | } while ((name == NULL || *name == '-') && md->choice != old); |
335 | 0 | c->flags |= CLIENT_REDRAWOVERLAY; |
336 | 0 | return (0); |
337 | 0 | case KEYC_PPAGE: |
338 | 0 | case 'b'|KEYC_CTRL: |
339 | 0 | if (md->choice < 6) |
340 | 0 | md->choice = 0; |
341 | 0 | else { |
342 | 0 | i = 5; |
343 | 0 | while (i > 0) { |
344 | 0 | md->choice--; |
345 | 0 | name = menu->items[md->choice].name; |
346 | 0 | if (md->choice != 0 && |
347 | 0 | (name != NULL && *name != '-')) |
348 | 0 | i--; |
349 | 0 | else if (md->choice == 0) |
350 | 0 | break; |
351 | 0 | } |
352 | 0 | } |
353 | 0 | c->flags |= CLIENT_REDRAWOVERLAY; |
354 | 0 | break; |
355 | 0 | case KEYC_NPAGE: |
356 | 0 | if (md->choice > count - 6) { |
357 | 0 | md->choice = count - 1; |
358 | 0 | name = menu->items[md->choice].name; |
359 | 0 | } else { |
360 | 0 | i = 5; |
361 | 0 | while (i > 0) { |
362 | 0 | md->choice++; |
363 | 0 | name = menu->items[md->choice].name; |
364 | 0 | if (md->choice != count - 1 && |
365 | 0 | (name != NULL && *name != '-')) |
366 | 0 | i++; |
367 | 0 | else if (md->choice == count - 1) |
368 | 0 | break; |
369 | 0 | } |
370 | 0 | } |
371 | 0 | while (name == NULL || *name == '-') { |
372 | 0 | md->choice--; |
373 | 0 | name = menu->items[md->choice].name; |
374 | 0 | } |
375 | 0 | c->flags |= CLIENT_REDRAWOVERLAY; |
376 | 0 | break; |
377 | 0 | case 'g': |
378 | 0 | case KEYC_HOME: |
379 | 0 | md->choice = 0; |
380 | 0 | name = menu->items[md->choice].name; |
381 | 0 | while (name == NULL || *name == '-') { |
382 | 0 | md->choice++; |
383 | 0 | name = menu->items[md->choice].name; |
384 | 0 | } |
385 | 0 | c->flags |= CLIENT_REDRAWOVERLAY; |
386 | 0 | break; |
387 | 0 | case 'G': |
388 | 0 | case KEYC_END: |
389 | 0 | md->choice = count - 1; |
390 | 0 | name = menu->items[md->choice].name; |
391 | 0 | while (name == NULL || *name == '-') { |
392 | 0 | md->choice--; |
393 | 0 | name = menu->items[md->choice].name; |
394 | 0 | } |
395 | 0 | c->flags |= CLIENT_REDRAWOVERLAY; |
396 | 0 | break; |
397 | 0 | case 'f'|KEYC_CTRL: |
398 | 0 | break; |
399 | 0 | case '\r': |
400 | 0 | goto chosen; |
401 | 0 | case '\033': /* Escape */ |
402 | 0 | case 'c'|KEYC_CTRL: |
403 | 0 | case 'g'|KEYC_CTRL: |
404 | 0 | case 'q': |
405 | 0 | return (1); |
406 | 0 | } |
407 | 0 | return (0); |
408 | | |
409 | 0 | chosen: |
410 | 0 | if (md->choice == -1) |
411 | 0 | return (1); |
412 | 0 | item = &menu->items[md->choice]; |
413 | 0 | if (item->name == NULL || *item->name == '-') { |
414 | 0 | if (md->flags & MENU_STAYOPEN) |
415 | 0 | return (0); |
416 | 0 | return (1); |
417 | 0 | } |
418 | 0 | if (md->cb != NULL) { |
419 | 0 | md->cb(md->menu, md->choice, item->key, md->data); |
420 | 0 | md->cb = NULL; |
421 | 0 | return (1); |
422 | 0 | } |
423 | | |
424 | 0 | if (md->item != NULL) |
425 | 0 | event = cmdq_get_event(md->item); |
426 | 0 | else |
427 | 0 | event = NULL; |
428 | 0 | state = cmdq_new_state(&md->fs, event, 0); |
429 | |
|
430 | 0 | status = cmd_parse_and_append(item->command, NULL, c, state, &error); |
431 | 0 | if (status == CMD_PARSE_ERROR) { |
432 | 0 | cmdq_append(c, cmdq_get_error(error)); |
433 | 0 | free(error); |
434 | 0 | } |
435 | 0 | cmdq_free_state(state); |
436 | |
|
437 | 0 | return (1); |
438 | 0 | } |
439 | | |
440 | | static void |
441 | | menu_set_style(struct client *c, struct grid_cell *gc, const char *style, |
442 | | const char *option) |
443 | 0 | { |
444 | 0 | struct style sytmp; |
445 | 0 | struct options *o = c->session->curw->window->options; |
446 | |
|
447 | 0 | memcpy(gc, &grid_default_cell, sizeof *gc); |
448 | 0 | style_apply(gc, o, option, NULL); |
449 | 0 | if (style != NULL) { |
450 | 0 | style_set(&sytmp, &grid_default_cell); |
451 | 0 | if (style_parse(&sytmp, gc, style) == 0) { |
452 | 0 | gc->fg = sytmp.gc.fg; |
453 | 0 | gc->bg = sytmp.gc.bg; |
454 | 0 | } |
455 | 0 | } |
456 | 0 | gc->attr = 0; |
457 | 0 | } |
458 | | |
459 | | struct menu_data * |
460 | | menu_prepare(struct menu *menu, int flags, int starting_choice, |
461 | | struct cmdq_item *item, u_int px, u_int py, struct client *c, |
462 | | enum box_lines lines, const char *style, const char *selected_style, |
463 | | const char *border_style, struct cmd_find_state *fs, menu_choice_cb cb, |
464 | | void *data) |
465 | 0 | { |
466 | 0 | struct menu_data *md; |
467 | 0 | int choice; |
468 | 0 | const char *name; |
469 | 0 | struct options *o = c->session->curw->window->options; |
470 | |
|
471 | 0 | if (c->tty.sx < menu->width + 4 || c->tty.sy < menu->count + 2) |
472 | 0 | return (NULL); |
473 | 0 | if (px + menu->width + 4 > c->tty.sx) |
474 | 0 | px = c->tty.sx - menu->width - 4; |
475 | 0 | if (py + menu->count + 2 > c->tty.sy) |
476 | 0 | py = c->tty.sy - menu->count - 2; |
477 | |
|
478 | 0 | if (lines == BOX_LINES_DEFAULT) |
479 | 0 | lines = options_get_number(o, "menu-border-lines"); |
480 | |
|
481 | 0 | md = xcalloc(1, sizeof *md); |
482 | 0 | md->item = item; |
483 | 0 | md->flags = flags; |
484 | 0 | md->border_lines = lines; |
485 | |
|
486 | 0 | menu_set_style(c, &md->style, style, "menu-style"); |
487 | 0 | menu_set_style(c, &md->selected_style, selected_style, |
488 | 0 | "menu-selected-style"); |
489 | 0 | menu_set_style(c, &md->border_style, border_style, "menu-border-style"); |
490 | |
|
491 | 0 | if (fs != NULL) |
492 | 0 | cmd_find_copy_state(&md->fs, fs); |
493 | 0 | screen_init(&md->s, menu->width + 4, menu->count + 2, 0); |
494 | 0 | if (~md->flags & MENU_NOMOUSE) |
495 | 0 | md->s.mode |= (MODE_MOUSE_ALL|MODE_MOUSE_BUTTON); |
496 | 0 | md->s.mode &= ~MODE_CURSOR; |
497 | |
|
498 | 0 | md->px = px; |
499 | 0 | md->py = py; |
500 | |
|
501 | 0 | md->menu = menu; |
502 | 0 | md->choice = -1; |
503 | |
|
504 | 0 | if (md->flags & MENU_NOMOUSE) { |
505 | 0 | if (starting_choice >= (int)menu->count) { |
506 | 0 | starting_choice = menu->count - 1; |
507 | 0 | choice = starting_choice + 1; |
508 | 0 | for (;;) { |
509 | 0 | name = menu->items[choice - 1].name; |
510 | 0 | if (name != NULL && *name != '-') { |
511 | 0 | md->choice = choice - 1; |
512 | 0 | break; |
513 | 0 | } |
514 | 0 | if (--choice == 0) |
515 | 0 | choice = menu->count; |
516 | 0 | if (choice == starting_choice + 1) |
517 | 0 | break; |
518 | 0 | } |
519 | 0 | } else if (starting_choice >= 0) { |
520 | 0 | choice = starting_choice; |
521 | 0 | for (;;) { |
522 | 0 | name = menu->items[choice].name; |
523 | 0 | if (name != NULL && *name != '-') { |
524 | 0 | md->choice = choice; |
525 | 0 | break; |
526 | 0 | } |
527 | 0 | if (++choice == (int)menu->count) |
528 | 0 | choice = 0; |
529 | 0 | if (choice == starting_choice) |
530 | 0 | break; |
531 | 0 | } |
532 | 0 | } |
533 | 0 | } |
534 | |
|
535 | 0 | md->cb = cb; |
536 | 0 | md->data = data; |
537 | 0 | return (md); |
538 | 0 | } |
539 | | |
540 | | int |
541 | | menu_display(struct menu *menu, int flags, int starting_choice, |
542 | | struct cmdq_item *item, u_int px, u_int py, struct client *c, |
543 | | enum box_lines lines, const char *style, const char *selected_style, |
544 | | const char *border_style, struct cmd_find_state *fs, menu_choice_cb cb, |
545 | | void *data) |
546 | 0 | { |
547 | 0 | struct menu_data *md; |
548 | |
|
549 | 0 | md = menu_prepare(menu, flags, starting_choice, item, px, py, c, lines, |
550 | 0 | style, selected_style, border_style, fs, cb, data); |
551 | 0 | if (md == NULL) |
552 | 0 | return (-1); |
553 | 0 | server_client_set_overlay(c, 0, NULL, menu_mode_cb, menu_draw_cb, |
554 | 0 | menu_key_cb, menu_free_cb, NULL, md); |
555 | 0 | return (0); |
556 | 0 | } |