1 /*
2 menu.c
3
4 Menu support code and interface to QC
5
6 Copyright (C) 2001 Bill Currie
7
8 Author: Bill Currie
9 Date: 2002/1/18
10
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU General Public License
13 as published by the Free Software Foundation; either version 2
14 of the License, or (at your option) any later version.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19
20 See the GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to:
24
25 Free Software Foundation, Inc.
26 59 Temple Place - Suite 330
27 Boston, MA 02111-1307, USA
28
29 */
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include "QF/cmd.h"
38 #include "QF/console.h"
39 #include "QF/csqc.h"
40 #include "QF/cvar.h"
41 #include "QF/draw.h"
42 #include "QF/hash.h"
43 #include "QF/progs.h"
44 #include "QF/quakefs.h"
45 #include "QF/render.h"
46 #include "QF/ruamoko.h"
47 #include "QF/sound.h"
48 #include "QF/sys.h"
49 #include "QF/view.h"
50
51 #include "QF/plugin/console.h"
52 #include "QF/plugin/vid_render.h"
53
54 typedef struct menu_pic_s {
55 struct menu_pic_s *next;
56 int x, y;
57 int srcx, srcy, width, height;
58 const char *name;
59 } menu_pic_t;
60
61 typedef struct menu_item_s {
62 struct menu_item_s *parent;
63 struct menu_item_s **items;
64 int num_items;
65 int max_items;
66 int cur_item;
67 int x, y;
68 func_t func;
69 func_t cursor;
70 func_t keyevent;
71 func_t draw;
72 func_t enter_hook;
73 func_t leave_hook;
74 unsigned fadescreen:1;
75 unsigned allkeys:1;
76 const char *text;
77 menu_pic_t *pics;
78 } menu_item_t;
79
80 static cvar_t *confirm_quit;
81
82 static progs_t menu_pr_state;
83 static menu_item_t *menu;
84 static hashtab_t *menu_hash;
85 static func_t menu_init;
86 static func_t menu_quit;
87 static func_t menu_draw_hud;
88 static func_t menu_pre;
89 static func_t menu_post;
90 static const char *top_menu;
91
92 typedef struct menu_func_s {
93 const char *name;
94 func_t *func;
95 } menu_func_t;
96
97 static menu_func_t menu_functions[] = {
98 {"menu_init", &menu_init},
99 {"menu_draw_hud", &menu_draw_hud},
100 {"menu_pre", &menu_pre},
101 {"menu_post", &menu_post},
102 };
103
104 static void
run_menu_pre(void)105 run_menu_pre (void)
106 {
107 PR_ExecuteProgram (&menu_pr_state, menu_pre);
108 }
109
110 static void
run_menu_post(void)111 run_menu_post (void)
112 {
113 PR_ExecuteProgram (&menu_pr_state, menu_post);
114 }
115
116 static int
menu_resolve_globals(progs_t * pr)117 menu_resolve_globals (progs_t *pr)
118 {
119 const char *sym;
120 ddef_t *def;
121 dfunction_t *f;
122 size_t i;
123
124 for (i = 0;
125 i < sizeof (menu_functions) / sizeof (menu_functions[0]); i++) {
126 sym = menu_functions[i].name;
127 if (!(f = PR_FindFunction (pr, sym)))
128 goto error;
129 *menu_functions[i].func = (func_t) (f - menu_pr_state.pr_functions);
130 }
131
132 if (!(def = PR_FindGlobal (pr, sym = "time")))
133 goto error;
134 menu_pr_state.globals.time = &G_FLOAT (pr, def->ofs);
135 return 1;
136 error:
137 Sys_Printf ("%s: undefined symbol %s\n", pr->progs_name, sym);
138 return 0;
139 }
140
141 static const char *
menu_get_key(const void * m,void * unused)142 menu_get_key (const void *m, void *unused)
143 {
144 return ((menu_item_t *) m)->text;
145 }
146
147 static void
menu_free(void * _m,void * unused)148 menu_free (void *_m, void *unused)
149 {
150 int i;
151 menu_item_t *m = (menu_item_t *) _m;
152
153 if (m->text)
154 free ((char *) m->text);
155 if (m->parent) {
156 // remove self from parent list to avoid double frees
157 for (i = 0; i < m->parent->num_items; i++)
158 if (m->parent->items[i] == m)
159 m->parent->items[i] = 0;
160 }
161 if (m->items) {
162 for (i = 0; i < m->num_items; i++)
163 if (m->items[i]) {
164 m->items[i]->parent = 0;
165 if (m->items[i]->text)
166 Hash_Del (menu_hash, m->items[i]->text);
167 menu_free (m->items[i], 0);
168 }
169 free (m->items);
170 }
171 while (m->pics) {
172 menu_pic_t *p = m->pics;
173 m->pics = p->next;
174 if (p->name)
175 free ((char *) p->name);
176 free (p);
177 }
178 free (m);
179 }
180
181 static void
menu_add_item(menu_item_t * m,menu_item_t * i)182 menu_add_item (menu_item_t *m, menu_item_t *i)
183 {
184 if (m->num_items == m->max_items) {
185 m->items = realloc (m->items,
186 (m->max_items + 8) * sizeof (menu_item_t *));
187 m->max_items += 8;
188 }
189 i->parent = m;
190 m->items[m->num_items++] = i;
191 }
192
193 static void
menu_pic(int x,int y,const char * name,int srcx,int srcy,int width,int height)194 menu_pic (int x, int y, const char *name,
195 int srcx, int srcy, int width, int height)
196 {
197 menu_pic_t *pic = malloc (sizeof (menu_pic_t));
198
199 pic->x = x;
200 pic->y = y;
201 pic->name = strdup (name);
202 pic->srcx = srcx;
203 pic->srcy = srcy;
204 pic->width = width;
205 pic->height = height;
206
207 pic->next = menu->pics;
208 menu->pics = pic;
209 }
210
211 static void
bi_Menu_Begin(progs_t * pr)212 bi_Menu_Begin (progs_t *pr)
213 {
214 int x = P_INT (pr, 0);
215 int y = P_INT (pr, 1);
216 const char *text = P_GSTRING (pr, 2);
217 menu_item_t *m = calloc (sizeof (menu_item_t), 1);
218
219 m->x = x;
220 m->y = y;
221 m->text = text && text[0] ? strdup (text) : 0;
222 if (menu)
223 menu_add_item (menu, m);
224 menu = m;
225 if (m->text)
226 Hash_Add (menu_hash, m);
227 }
228
229 static void
bi_Menu_FadeScreen(progs_t * pr)230 bi_Menu_FadeScreen (progs_t *pr)
231 {
232 menu->fadescreen = P_INT (pr, 0);
233 }
234
235 static void
bi_Menu_Draw(progs_t * pr)236 bi_Menu_Draw (progs_t *pr)
237 {
238 menu->draw = P_FUNCTION (pr, 0);
239 }
240
241 static void
bi_Menu_EnterHook(progs_t * pr)242 bi_Menu_EnterHook (progs_t *pr)
243 {
244 menu->enter_hook = P_FUNCTION (pr, 0);
245 }
246
247 static void
bi_Menu_LeaveHook(progs_t * pr)248 bi_Menu_LeaveHook (progs_t *pr)
249 {
250 menu->leave_hook = P_FUNCTION (pr, 0);
251 }
252
253 static void
bi_Menu_Pic(progs_t * pr)254 bi_Menu_Pic (progs_t *pr)
255 {
256 int x = P_INT (pr, 0);
257 int y = P_INT (pr, 1);
258 const char *name = P_GSTRING (pr, 2);
259
260 menu_pic (x, y, name, 0, 0, -1, -1);
261 }
262
263 static void
bi_Menu_SubPic(progs_t * pr)264 bi_Menu_SubPic (progs_t *pr)
265 {
266 int x = P_INT (pr, 0);
267 int y = P_INT (pr, 1);
268 const char *name = P_GSTRING (pr, 2);
269 int srcx = P_INT (pr, 3);
270 int srcy = P_INT (pr, 4);
271 int width = P_INT (pr, 5);
272 int height = P_INT (pr, 6);
273
274 menu_pic (x, y, name, srcx, srcy, width, height);
275 }
276
277 static void
bi_Menu_CenterPic(progs_t * pr)278 bi_Menu_CenterPic (progs_t *pr)
279 {
280 int x = P_INT (pr, 0);
281 int y = P_INT (pr, 1);
282 const char *name = P_GSTRING (pr, 2);
283 qpic_t *qpic = r_funcs->Draw_CachePic (name, 1);
284
285 if (!qpic)
286 return;
287
288 menu_pic (x - qpic->width / 2, y, name, 0, 0, -1, -1);
289 }
290
291 static void
bi_Menu_CenterSubPic(progs_t * pr)292 bi_Menu_CenterSubPic (progs_t *pr)
293 {
294 int x = P_INT (pr, 0);
295 int y = P_INT (pr, 1);
296 const char *name = P_GSTRING (pr, 2);
297 qpic_t *qpic = r_funcs->Draw_CachePic (name, 1);
298 int srcx = P_INT (pr, 3);
299 int srcy = P_INT (pr, 4);
300 int width = P_INT (pr, 5);
301 int height = P_INT (pr, 6);
302
303 if (!qpic)
304 return;
305
306 menu_pic (x - qpic->width / 2, y, name, srcx, srcy, width, height);
307 }
308
309 static void
bi_Menu_Item(progs_t * pr)310 bi_Menu_Item (progs_t *pr)
311 {
312 int x = P_INT (pr, 0);
313 int y = P_INT (pr, 1);
314 const char *text = P_GSTRING (pr, 2);
315 func_t func = P_FUNCTION (pr, 3);
316 int allkeys = P_INT (pr, 4);
317 menu_item_t *mi = calloc (sizeof (menu_item_t), 1);
318
319 mi->x = x;
320 mi->y = y;
321 mi->text = text && text[0] ? strdup (text) : 0;
322 mi->func = func;
323 mi->parent = menu;
324 mi->allkeys = allkeys;
325 menu_add_item (menu, mi);
326 }
327
328 static void
bi_Menu_Cursor(progs_t * pr)329 bi_Menu_Cursor (progs_t *pr)
330 {
331 func_t func = P_FUNCTION (pr, 0);
332
333 menu->cursor = func;
334 }
335
336 static void
bi_Menu_KeyEvent(progs_t * pr)337 bi_Menu_KeyEvent (progs_t *pr)
338 {
339 func_t func = P_FUNCTION (pr, 0);
340
341 menu->keyevent = func;
342 }
343
344 static void
bi_Menu_End(progs_t * pr)345 bi_Menu_End (progs_t *pr)
346 {
347 menu = menu->parent;
348 }
349
350 static void
bi_Menu_TopMenu(progs_t * pr)351 bi_Menu_TopMenu (progs_t *pr)
352 {
353 const char *name = P_GSTRING (pr, 0);
354
355 if (top_menu)
356 free ((char *) top_menu);
357 top_menu = strdup (name);
358 }
359
360 static void
bi_Menu_SelectMenu(progs_t * pr)361 bi_Menu_SelectMenu (progs_t *pr)
362 {
363 const char *name = P_GSTRING (pr, 0);
364
365 menu = 0;
366 if (name && *name)
367 menu = Hash_Find (menu_hash, name);
368 if (menu) {
369 Key_SetKeyDest (key_menu);
370 if (menu->enter_hook) {
371 run_menu_pre ();
372 PR_ExecuteProgram (&menu_pr_state, menu->enter_hook);
373 run_menu_post ();
374 }
375 } else {
376 if (name && *name)
377 Sys_Printf ("no menu \"%s\"\n", name);
378 if (con_data.force_commandline) {
379 Key_SetKeyDest (key_console);
380 } else {
381 Key_SetKeyDest (key_game);
382 }
383 }
384 }
385
386 static void
bi_Menu_SetQuit(progs_t * pr)387 bi_Menu_SetQuit (progs_t *pr)
388 {
389 func_t func = P_FUNCTION (pr, 0);
390
391 menu_quit = func;
392 }
393
394 static void
bi_Menu_Quit(progs_t * pr)395 bi_Menu_Quit (progs_t *pr)
396 {
397 if (con_data.quit)
398 con_data.quit ();
399 Sys_Quit ();
400 }
401
402 static void
bi_Menu_GetIndex(progs_t * pr)403 bi_Menu_GetIndex (progs_t *pr)
404 {
405 if (menu) {
406 R_INT (pr) = menu->cur_item;
407 } else {
408 R_INT (pr) = -1;
409 }
410 }
411
412 static void
bi_Menu_Next(progs_t * pr)413 bi_Menu_Next (progs_t *pr)
414 {
415 menu->cur_item++;
416 menu->cur_item %= menu->num_items;
417 }
418
419 static void
bi_Menu_Prev(progs_t * pr)420 bi_Menu_Prev (progs_t *pr)
421 {
422 menu->cur_item += menu->num_items - 1;
423 menu->cur_item %= menu->num_items;
424 }
425
426 static void
bi_Menu_Enter(progs_t * pr)427 bi_Menu_Enter (progs_t *pr)
428 {
429 menu_item_t *item;
430
431 if (!menu)
432 return;
433
434 item = menu->items[menu->cur_item];
435 if (item->func) {
436 run_menu_pre ();
437 PR_PushFrame (&menu_pr_state);
438 PR_RESET_PARAMS (&menu_pr_state);
439 P_STRING (&menu_pr_state, 0) =
440 PR_SetTempString (&menu_pr_state, item->text);
441 P_INT (&menu_pr_state, 1) = 0;
442 PR_ExecuteProgram (&menu_pr_state, item->func);
443 PR_PopFrame (&menu_pr_state);
444 run_menu_post ();
445 } else {
446 menu = item;
447 if (menu->enter_hook) {
448 run_menu_pre ();
449 PR_ExecuteProgram (&menu_pr_state, menu->enter_hook);
450 run_menu_post ();
451 }
452 }
453 }
454
455 static void
togglemenu_f(void)456 togglemenu_f (void)
457 {
458 if (menu)
459 Menu_Leave ();
460 else
461 Menu_Enter ();
462 }
463
464 static void
quit_f(void)465 quit_f (void)
466 {
467 int ret;
468
469 if (confirm_quit->int_val && menu_quit) {
470 run_menu_pre ();
471 PR_ExecuteProgram (&menu_pr_state, menu_quit);
472 ret = R_INT (&menu_pr_state);
473 run_menu_post ();
474 if (!ret)
475 return;
476 }
477 bi_Menu_Quit (&menu_pr_state);
478 }
479
480 static void *
menu_allocate_progs_mem(progs_t * pr,int size)481 menu_allocate_progs_mem (progs_t *pr, int size)
482 {
483 return malloc (size);
484 }
485
486 static void
menu_free_progs_mem(progs_t * pr,void * mem)487 menu_free_progs_mem (progs_t *pr, void *mem)
488 {
489 free (mem);
490 }
491
492 static void *
menu_load_file(progs_t * pr,const char * path)493 menu_load_file (progs_t *pr, const char *path)
494 {
495 return QFS_LoadFile (path, 0);
496 }
497
498 static builtin_t builtins[] = {
499 {"Menu_Begin", bi_Menu_Begin, -1},
500 {"Menu_FadeScreen", bi_Menu_FadeScreen, -1},
501 {"Menu_Draw", bi_Menu_Draw, -1},
502 {"Menu_EnterHook", bi_Menu_EnterHook, -1},
503 {"Menu_LeaveHook", bi_Menu_LeaveHook, -1},
504 {"Menu_Pic", bi_Menu_Pic, -1},
505 {"Menu_SubPic", bi_Menu_SubPic, -1},
506 {"Menu_CenterPic", bi_Menu_CenterPic, -1},
507 {"Menu_CenterSubPic", bi_Menu_CenterSubPic, -1},
508 {"Menu_Item", bi_Menu_Item, -1},
509 {"Menu_Cursor", bi_Menu_Cursor, -1},
510 {"Menu_KeyEvent", bi_Menu_KeyEvent, -1},
511 {"Menu_End", bi_Menu_End, -1},
512 {"Menu_TopMenu", bi_Menu_TopMenu, -1},
513 {"Menu_SelectMenu", bi_Menu_SelectMenu, -1},
514 {"Menu_SetQuit", bi_Menu_SetQuit, -1},
515 {"Menu_Quit", bi_Menu_Quit, -1},
516 {"Menu_GetIndex", bi_Menu_GetIndex, -1},
517 {"Menu_Next", bi_Menu_Next, -1},
518 {"Menu_Prev", bi_Menu_Prev, -1},
519 {"Menu_Enter", bi_Menu_Enter, -1},
520 {0},
521 };
522
523 void
Menu_Init(void)524 Menu_Init (void)
525 {
526 menu_pr_state.progs_name = "menu.dat";
527 menu_pr_state.allocate_progs_mem = menu_allocate_progs_mem;
528 menu_pr_state.free_progs_mem = menu_free_progs_mem;
529 menu_pr_state.load_file = menu_load_file;
530 menu_pr_state.resolve = menu_resolve_globals;
531
532 menu_hash = Hash_NewTable (61, menu_get_key, menu_free, 0);
533
534 PR_RegisterBuiltins (&menu_pr_state, builtins);
535
536 RUA_Init (&menu_pr_state, 1);
537
538 InputLine_Progs_Init (&menu_pr_state);
539 Key_Progs_Init (&menu_pr_state);
540 GIB_Progs_Init (&menu_pr_state);
541 PR_Cmds_Init (&menu_pr_state);
542 R_Progs_Init (&menu_pr_state);
543 S_Progs_Init (&menu_pr_state);
544
545 confirm_quit = Cvar_Get ("confirm_quit", "1", CVAR_ARCHIVE, NULL,
546 "confirm quit command");
547
548 Cmd_AddCommand ("togglemenu", togglemenu_f,
549 "Toggle the display of the menu");
550 Cmd_RemoveCommand ("quit");
551 Cmd_AddCommand ("quit", quit_f, "Exit the program");
552 }
553
554 void
Menu_Load(void)555 Menu_Load (void)
556 {
557 int size;
558 QFile *file;
559
560 Hash_FlushTable (menu_hash);
561 menu = 0;
562 top_menu = 0;
563
564 menu_pr_state.progs = 0;
565 if ((size = QFS_FOpenFile (menu_pr_state.progs_name, &file)) != -1) {
566 PR_LoadProgsFile (&menu_pr_state, file, size, 0, 1024 * 1024);
567 Qclose (file);
568
569 if (!PR_RunLoadFuncs (&menu_pr_state)) {
570 free (menu_pr_state.progs);
571 menu_pr_state.progs = 0;
572 }
573 }
574 if (!menu_pr_state.progs) {
575 // Not a fatal error, just means no menus
576 Con_SetOrMask (0x80);
577 Sys_Printf ("Menu_Load: could not load %s\n",
578 menu_pr_state.progs_name);
579 Con_SetOrMask (0x00);
580 return;
581 }
582 run_menu_pre ();
583 RUA_Cbuf_SetCbuf (&menu_pr_state, con_data.cbuf);
584 InputLine_Progs_SetDraw (&menu_pr_state, C_DrawInputLine);
585 PR_ExecuteProgram (&menu_pr_state, menu_init);
586 run_menu_post ();
587 }
588
589 void
Menu_Draw(view_t * view)590 Menu_Draw (view_t *view)
591 {
592 menu_pic_t *m_pic;
593 int i, x, y;
594 menu_item_t *item;
595
596 if (!menu)
597 return;
598
599 x = view->xabs;
600 y = view->yabs;
601
602 if (menu->fadescreen)
603 r_funcs->Draw_FadeScreen ();
604
605 *menu_pr_state.globals.time = *con_data.realtime;
606
607 if (menu->draw) {
608 int ret;
609
610 run_menu_pre ();
611 PR_RESET_PARAMS (&menu_pr_state);
612 P_INT (&menu_pr_state, 0) = x;
613 P_INT (&menu_pr_state, 1) = y;
614 PR_ExecuteProgram (&menu_pr_state, menu->draw);
615 ret = R_INT (&menu_pr_state);
616 run_menu_post ();
617 if (!ret)
618 return;
619 }
620
621
622 for (m_pic = menu->pics; m_pic; m_pic = m_pic->next) {
623 qpic_t *pic = r_funcs->Draw_CachePic (m_pic->name, 1);
624 if (!pic)
625 continue;
626 if (m_pic->width > 0 && m_pic->height > 0)
627 r_funcs->Draw_SubPic (x + m_pic->x, y + m_pic->y, pic,
628 m_pic->srcx, m_pic->srcy,
629 m_pic->width, m_pic->height);
630 else
631 r_funcs->Draw_Pic (x + m_pic->x, y + m_pic->y, pic);
632 }
633 for (i = 0; i < menu->num_items; i++) {
634 if (menu->items[i]->text) {
635 r_funcs->Draw_String (x + menu->items[i]->x + 8,
636 y + menu->items[i]->y,
637 menu->items[i]->text);
638 }
639 }
640 if (!menu->items)
641 return;
642 item = menu->items[menu->cur_item];
643 if (menu->cursor) {
644 run_menu_pre ();
645 PR_RESET_PARAMS (&menu_pr_state);
646 P_INT (&menu_pr_state, 0) = x + item->x;
647 P_INT (&menu_pr_state, 1) = y + item->y;
648 PR_ExecuteProgram (&menu_pr_state, menu->cursor);
649 run_menu_post ();
650 } else {
651 r_funcs->Draw_Character (x + item->x, y + item->y,
652 12 + ((int) (*con_data.realtime * 4) & 1));
653 }
654 }
655
656 void
Menu_Draw_Hud(view_t * view)657 Menu_Draw_Hud (view_t *view)
658 {
659 run_menu_pre ();
660 *menu_pr_state.globals.time = *con_data.realtime;
661
662 PR_ExecuteProgram (&menu_pr_state, menu_draw_hud);
663 run_menu_post ();
664 }
665
666 int
Menu_KeyEvent(knum_t key,short unicode,qboolean down)667 Menu_KeyEvent (knum_t key, short unicode, qboolean down)
668 {
669 menu_item_t *item;
670 int ret;
671
672 if (!menu)
673 return 0;
674 if (menu->keyevent) {
675 run_menu_pre ();
676 PR_RESET_PARAMS (&menu_pr_state);
677 P_INT (&menu_pr_state, 0) = key;
678 P_INT (&menu_pr_state, 1) = unicode;
679 P_INT (&menu_pr_state, 2) = down;
680 PR_ExecuteProgram (&menu_pr_state, menu->keyevent);
681 ret = R_INT (&menu_pr_state);
682 run_menu_post ();
683 if (ret)
684 return 1;
685 } else if (menu->items && menu->items[menu->cur_item]->func
686 && menu->items[menu->cur_item]->allkeys) {
687 run_menu_pre ();
688 PR_PushFrame (&menu_pr_state);
689 item = menu->items[menu->cur_item];
690 PR_RESET_PARAMS (&menu_pr_state);
691 P_STRING (&menu_pr_state, 0) = PR_SetTempString (&menu_pr_state,
692 item->text);
693 P_INT (&menu_pr_state, 1) = key;
694 PR_ExecuteProgram (&menu_pr_state, item->func);
695 PR_PopFrame (&menu_pr_state);
696 ret = R_INT (&menu_pr_state);
697 run_menu_post ();
698 if (ret)
699 return 1;
700 }
701 if (!menu || !menu->items)
702 return 0;
703 switch (key) {
704 case QFK_DOWN:
705 case QFM_WHEEL_DOWN:
706 bi_Menu_Next (&menu_pr_state);
707 return 1;
708 case QFK_UP:
709 case QFM_WHEEL_UP:
710 bi_Menu_Prev (&menu_pr_state);
711 return 1;
712 case QFK_RETURN:
713 case QFM_BUTTON1:
714 bi_Menu_Enter (&menu_pr_state);
715 return 1;
716 default:
717 return 0;
718 }
719 }
720
721 void
Menu_Enter()722 Menu_Enter ()
723 {
724 if (!top_menu) {
725 Key_SetKeyDest (key_console);
726 return;
727 }
728 Key_SetKeyDest (key_menu);
729 menu = Hash_Find (menu_hash, top_menu);
730 if (menu && menu->enter_hook) {
731 run_menu_pre ();
732 PR_ExecuteProgram (&menu_pr_state, menu->enter_hook);
733 run_menu_post ();
734 }
735 }
736
737 void
Menu_Leave()738 Menu_Leave ()
739 {
740 if (menu) {
741 if (menu->leave_hook) {
742 run_menu_pre ();
743 PR_ExecuteProgram (&menu_pr_state, menu->leave_hook);
744 run_menu_post ();
745 }
746 menu = menu->parent;
747 if (!menu) {
748 if (con_data.force_commandline) {
749 Key_SetKeyDest (key_console);
750 } else {
751 Key_SetKeyDest (key_game);
752 }
753 }
754 }
755 r_data->vid->recalc_refdef = true;
756 }
757