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