1 /*
2 	Copyright (C) 1996-1997  Id Software, Inc.
3 
4 	This program is free software; you can redistribute it and/or
5 	modify it under the terms of the GNU General Public License
6 	as published by the Free Software Foundation; either version 2
7 	of the License, or (at your option) any later version.
8 
9 	This program is distributed in the hope that it will be useful,
10 	but WITHOUT ANY WARRANTY; without even the implied warranty of
11 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 	See the GNU General Public License for more details.
14 
15 	You should have received a copy of the GNU General Public License
16 	along with this program; if not, write to:
17 
18 		Free Software Foundation, Inc.
19 		59 Temple Place - Suite 330
20 		Boston, MA  02111-1307, USA
21 */
22 
23 #include "quakedef.h"
24 #include "cl_video.h"
25 #include "utf8lib.h"
26 #include "csprogs.h"
27 
28 cvar_t con_closeontoggleconsole = {CVAR_SAVE, "con_closeontoggleconsole","1", "allows toggleconsole binds to close the console as well; when set to 2, this even works when not at the start of the line in console input; when set to 3, this works even if the toggleconsole key is the color tag"};
29 
30 /*
31 key up events are sent even if in console mode
32 */
33 
34 char		key_line[MAX_INPUTLINE];
35 int			key_linepos;
36 qboolean	key_insert = true;	// insert key toggle (for editing)
37 keydest_t	key_dest;
38 int			key_consoleactive;
39 char		*keybindings[MAX_BINDMAPS][MAX_KEYS];
40 
41 int			history_line;
42 char		history_savedline[MAX_INPUTLINE];
43 char		history_searchstring[MAX_INPUTLINE];
44 qboolean	history_matchfound = false;
45 conbuffer_t history;
46 
47 extern cvar_t	con_textsize;
48 
49 
Key_History_Init(void)50 static void Key_History_Init(void)
51 {
52 	qfile_t *historyfile;
53 	ConBuffer_Init(&history, HIST_TEXTSIZE, HIST_MAXLINES, zonemempool);
54 
55 // not necessary for mobile
56 #ifndef DP_MOBILETOUCH
57 	historyfile = FS_OpenRealFile("darkplaces_history.txt", "rb", false); // rb to handle unix line endings on windows too
58 	if(historyfile)
59 	{
60 		char buf[MAX_INPUTLINE];
61 		int bufpos;
62 		int c;
63 
64 		bufpos = 0;
65 		for(;;)
66 		{
67 			c = FS_Getc(historyfile);
68 			if(c < 0 || c == 0 || c == '\r' || c == '\n')
69 			{
70 				if(bufpos > 0)
71 				{
72 					buf[bufpos] = 0;
73 					ConBuffer_AddLine(&history, buf, bufpos, 0);
74 					bufpos = 0;
75 				}
76 				if(c < 0)
77 					break;
78 			}
79 			else
80 			{
81 				if(bufpos < MAX_INPUTLINE - 1)
82 					buf[bufpos++] = c;
83 			}
84 		}
85 
86 		FS_Close(historyfile);
87 	}
88 #endif
89 
90 	history_line = -1;
91 }
92 
Key_History_Shutdown(void)93 static void Key_History_Shutdown(void)
94 {
95 	// TODO write history to a file
96 
97 // not necessary for mobile
98 #ifndef DP_MOBILETOUCH
99 	qfile_t *historyfile = FS_OpenRealFile("darkplaces_history.txt", "w", false);
100 	if(historyfile)
101 	{
102 		int i;
103 		for(i = 0; i < CONBUFFER_LINES_COUNT(&history); ++i)
104 			FS_Printf(historyfile, "%s\n", ConBuffer_GetLine(&history, i));
105 		FS_Close(historyfile);
106 	}
107 #endif
108 
109 	ConBuffer_Shutdown(&history);
110 }
111 
Key_History_Push(void)112 static void Key_History_Push(void)
113 {
114 	if(key_line[1]) // empty?
115 	if(strcmp(key_line, "]quit")) // putting these into the history just sucks
116 	if(strncmp(key_line, "]quit ", 6)) // putting these into the history just sucks
117 	if(strcmp(key_line, "]rcon_password")) // putting these into the history just sucks
118 	if(strncmp(key_line, "]rcon_password ", 15)) // putting these into the history just sucks
119 		ConBuffer_AddLine(&history, key_line + 1, (int)strlen(key_line) - 1, 0);
120 	Con_Printf("%s\n", key_line); // don't mark empty lines as history
121 	history_line = -1;
122 	if (history_matchfound)
123 		history_matchfound = false;
124 }
125 
Key_History_Get_foundCommand(void)126 static qboolean Key_History_Get_foundCommand(void)
127 {
128 	if (!history_matchfound)
129 		return false;
130 	strlcpy(key_line + 1, ConBuffer_GetLine(&history, history_line), sizeof(key_line) - 1);
131 	key_linepos = (int)strlen(key_line);
132 	history_matchfound = false;
133 	return true;
134 }
135 
Key_History_Up(void)136 static void Key_History_Up(void)
137 {
138 	if(history_line == -1) // editing the "new" line
139 		strlcpy(history_savedline, key_line + 1, sizeof(history_savedline));
140 
141 	if (Key_History_Get_foundCommand())
142 		return;
143 
144 	if(history_line == -1)
145 	{
146 		history_line = CONBUFFER_LINES_COUNT(&history) - 1;
147 		if(history_line != -1)
148 		{
149 			strlcpy(key_line + 1, ConBuffer_GetLine(&history, history_line), sizeof(key_line) - 1);
150 			key_linepos = (int)strlen(key_line);
151 		}
152 	}
153 	else if(history_line > 0)
154 	{
155 		--history_line; // this also does -1 -> 0, so it is good
156 		strlcpy(key_line + 1, ConBuffer_GetLine(&history, history_line), sizeof(key_line) - 1);
157 		key_linepos = (int)strlen(key_line);
158 	}
159 }
160 
Key_History_Down(void)161 static void Key_History_Down(void)
162 {
163 	if(history_line == -1) // editing the "new" line
164 		return;
165 
166 	if (Key_History_Get_foundCommand())
167 		return;
168 
169 	if(history_line < CONBUFFER_LINES_COUNT(&history) - 1)
170 	{
171 		++history_line;
172 		strlcpy(key_line + 1, ConBuffer_GetLine(&history, history_line), sizeof(key_line) - 1);
173 	}
174 	else
175 	{
176 		history_line = -1;
177 		strlcpy(key_line + 1, history_savedline, sizeof(key_line) - 1);
178 	}
179 
180 	key_linepos = (int)strlen(key_line);
181 }
182 
Key_History_First(void)183 static void Key_History_First(void)
184 {
185 	if(history_line == -1) // editing the "new" line
186 		strlcpy(history_savedline, key_line + 1, sizeof(history_savedline));
187 
188 	if (CONBUFFER_LINES_COUNT(&history) > 0)
189 	{
190 		history_line = 0;
191 		strlcpy(key_line + 1, ConBuffer_GetLine(&history, history_line), sizeof(key_line) - 1);
192 		key_linepos = (int)strlen(key_line);
193 	}
194 }
195 
Key_History_Last(void)196 static void Key_History_Last(void)
197 {
198 	if(history_line == -1) // editing the "new" line
199 		strlcpy(history_savedline, key_line + 1, sizeof(history_savedline));
200 
201 	if (CONBUFFER_LINES_COUNT(&history) > 0)
202 	{
203 		history_line = CONBUFFER_LINES_COUNT(&history) - 1;
204 		strlcpy(key_line + 1, ConBuffer_GetLine(&history, history_line), sizeof(key_line) - 1);
205 		key_linepos = (int)strlen(key_line);
206 	}
207 }
208 
Key_History_Find_Backwards(void)209 static void Key_History_Find_Backwards(void)
210 {
211 	int i;
212 	const char *partial = key_line + 1;
213 	char vabuf[1024];
214 	size_t digits = strlen(va(vabuf, sizeof(vabuf), "%i", HIST_MAXLINES));
215 
216 	if (history_line == -1) // editing the "new" line
217 		strlcpy(history_savedline, key_line + 1, sizeof(history_savedline));
218 
219 	if (strcmp(key_line + 1, history_searchstring)) // different string? Start a new search
220 	{
221 		strlcpy(history_searchstring, key_line + 1, sizeof(history_searchstring));
222 		i = CONBUFFER_LINES_COUNT(&history) - 1;
223 	}
224 	else if (history_line == -1)
225 		i = CONBUFFER_LINES_COUNT(&history) - 1;
226 	else
227 		i = history_line - 1;
228 
229 	if (!*partial)
230 		partial = "*";
231 	else if (!( strchr(partial, '*') || strchr(partial, '?') )) // no pattern?
232 		partial = va(vabuf, sizeof(vabuf), "*%s*", partial);
233 
234 	for ( ; i >= 0; i--)
235 		if (matchpattern_with_separator(ConBuffer_GetLine(&history, i), partial, true, "", false))
236 		{
237 			Con_Printf("^2%*i^7 %s\n", (int)digits, i+1, ConBuffer_GetLine(&history, i));
238 			history_line = i;
239 			history_matchfound = true;
240 			return;
241 		}
242 }
243 
Key_History_Find_Forwards(void)244 static void Key_History_Find_Forwards(void)
245 {
246 	int i;
247 	const char *partial = key_line + 1;
248 	char vabuf[1024];
249 	size_t digits = strlen(va(vabuf, sizeof(vabuf), "%i", HIST_MAXLINES));
250 
251 	if (history_line == -1) // editing the "new" line
252 		return;
253 
254 	if (strcmp(key_line + 1, history_searchstring)) // different string? Start a new search
255 	{
256 		strlcpy(history_searchstring, key_line + 1, sizeof(history_searchstring));
257 		i = 0;
258 	}
259 	else i = history_line + 1;
260 
261 	if (!*partial)
262 		partial = "*";
263 	else if (!( strchr(partial, '*') || strchr(partial, '?') )) // no pattern?
264 		partial = va(vabuf, sizeof(vabuf), "*%s*", partial);
265 
266 	for ( ; i < CONBUFFER_LINES_COUNT(&history); i++)
267 		if (matchpattern_with_separator(ConBuffer_GetLine(&history, i), partial, true, "", false))
268 		{
269 			Con_Printf("^2%*i^7 %s\n", (int)digits, i+1, ConBuffer_GetLine(&history, i));
270 			history_line = i;
271 			history_matchfound = true;
272 			return;
273 		}
274 }
275 
Key_History_Find_All(void)276 static void Key_History_Find_All(void)
277 {
278 	const char *partial = key_line + 1;
279 	int i, count = 0;
280 	char vabuf[1024];
281 	size_t digits = strlen(va(vabuf, sizeof(vabuf), "%i", HIST_MAXLINES));
282 	Con_Printf("History commands containing \"%s\":\n", key_line + 1);
283 
284 	if (!*partial)
285 		partial = "*";
286 	else if (!( strchr(partial, '*') || strchr(partial, '?') )) // no pattern?
287 		partial = va(vabuf, sizeof(vabuf), "*%s*", partial);
288 
289 	for (i=0; i<CONBUFFER_LINES_COUNT(&history); i++)
290 		if (matchpattern_with_separator(ConBuffer_GetLine(&history, i), partial, true, "", false))
291 		{
292 			Con_Printf("%s%*i^7 %s\n", (i == history_line) ? "^2" : "^3", (int)digits, i+1, ConBuffer_GetLine(&history, i));
293 			count++;
294 		}
295 	Con_Printf("%i result%s\n\n", count, (count != 1) ? "s" : "");
296 }
297 
Key_History_f(void)298 static void Key_History_f(void)
299 {
300 	char *errchar = NULL;
301 	int i = 0;
302 	char vabuf[1024];
303 	size_t digits = strlen(va(vabuf, sizeof(vabuf), "%i", HIST_MAXLINES));
304 
305 	if (Cmd_Argc () > 1)
306 	{
307 		if (!strcmp(Cmd_Argv (1), "-c"))
308 		{
309 			ConBuffer_Clear(&history);
310 			return;
311 		}
312 		i = strtol(Cmd_Argv (1), &errchar, 0);
313 		if ((i < 0) || (i > CONBUFFER_LINES_COUNT(&history)) || (errchar && *errchar))
314 			i = 0;
315 		else
316 			i = CONBUFFER_LINES_COUNT(&history) - i;
317 	}
318 
319 	for ( ; i<CONBUFFER_LINES_COUNT(&history); i++)
320 		Con_Printf("^3%*i^7 %s\n", (int)digits, i+1, ConBuffer_GetLine(&history, i));
321 	Con_Printf("\n");
322 }
323 
324 static int	key_bmap, key_bmap2;
325 static unsigned char keydown[MAX_KEYS];	// 0 = up, 1 = down, 2 = repeating
326 
327 typedef struct keyname_s
328 {
329 	const char	*name;
330 	int			keynum;
331 }
332 keyname_t;
333 
334 static const keyname_t   keynames[] = {
335 	{"TAB", K_TAB},
336 	{"ENTER", K_ENTER},
337 	{"ESCAPE", K_ESCAPE},
338 	{"SPACE", K_SPACE},
339 
340 	// spacer so it lines up with keys.h
341 
342 	{"BACKSPACE", K_BACKSPACE},
343 	{"UPARROW", K_UPARROW},
344 	{"DOWNARROW", K_DOWNARROW},
345 	{"LEFTARROW", K_LEFTARROW},
346 	{"RIGHTARROW", K_RIGHTARROW},
347 
348 	{"ALT", K_ALT},
349 	{"CTRL", K_CTRL},
350 	{"SHIFT", K_SHIFT},
351 
352 	{"F1", K_F1},
353 	{"F2", K_F2},
354 	{"F3", K_F3},
355 	{"F4", K_F4},
356 	{"F5", K_F5},
357 	{"F6", K_F6},
358 	{"F7", K_F7},
359 	{"F8", K_F8},
360 	{"F9", K_F9},
361 	{"F10", K_F10},
362 	{"F11", K_F11},
363 	{"F12", K_F12},
364 
365 	{"INS", K_INS},
366 	{"DEL", K_DEL},
367 	{"PGDN", K_PGDN},
368 	{"PGUP", K_PGUP},
369 	{"HOME", K_HOME},
370 	{"END", K_END},
371 
372 	{"PAUSE", K_PAUSE},
373 
374 	{"NUMLOCK", K_NUMLOCK},
375 	{"CAPSLOCK", K_CAPSLOCK},
376 	{"SCROLLOCK", K_SCROLLOCK},
377 
378 	{"KP_INS",			K_KP_INS },
379 	{"KP_0", K_KP_0},
380 	{"KP_END",			K_KP_END },
381 	{"KP_1", K_KP_1},
382 	{"KP_DOWNARROW",	K_KP_DOWNARROW },
383 	{"KP_2", K_KP_2},
384 	{"KP_PGDN",			K_KP_PGDN },
385 	{"KP_3", K_KP_3},
386 	{"KP_LEFTARROW",	K_KP_LEFTARROW },
387 	{"KP_4", K_KP_4},
388 	{"KP_5", K_KP_5},
389 	{"KP_RIGHTARROW",	K_KP_RIGHTARROW },
390 	{"KP_6", K_KP_6},
391 	{"KP_HOME",			K_KP_HOME },
392 	{"KP_7", K_KP_7},
393 	{"KP_UPARROW",		K_KP_UPARROW },
394 	{"KP_8", K_KP_8},
395 	{"KP_PGUP",			K_KP_PGUP },
396 	{"KP_9", K_KP_9},
397 	{"KP_DEL",			K_KP_DEL },
398 	{"KP_PERIOD", K_KP_PERIOD},
399 	{"KP_SLASH",		K_KP_SLASH },
400 	{"KP_DIVIDE", K_KP_DIVIDE},
401 	{"KP_MULTIPLY", K_KP_MULTIPLY},
402 	{"KP_MINUS", K_KP_MINUS},
403 	{"KP_PLUS", K_KP_PLUS},
404 	{"KP_ENTER", K_KP_ENTER},
405 	{"KP_EQUALS", K_KP_EQUALS},
406 
407 	{"PRINTSCREEN", K_PRINTSCREEN},
408 
409 
410 
411 	{"MOUSE1", K_MOUSE1},
412 
413 	{"MOUSE2", K_MOUSE2},
414 	{"MOUSE3", K_MOUSE3},
415 	{"MWHEELUP", K_MWHEELUP},
416 	{"MWHEELDOWN", K_MWHEELDOWN},
417 	{"MOUSE4", K_MOUSE4},
418 	{"MOUSE5", K_MOUSE5},
419 	{"MOUSE6", K_MOUSE6},
420 	{"MOUSE7", K_MOUSE7},
421 	{"MOUSE8", K_MOUSE8},
422 	{"MOUSE9", K_MOUSE9},
423 	{"MOUSE10", K_MOUSE10},
424 	{"MOUSE11", K_MOUSE11},
425 	{"MOUSE12", K_MOUSE12},
426 	{"MOUSE13", K_MOUSE13},
427 	{"MOUSE14", K_MOUSE14},
428 	{"MOUSE15", K_MOUSE15},
429 	{"MOUSE16", K_MOUSE16},
430 
431 
432 
433 
434 	{"JOY1",  K_JOY1},
435 	{"JOY2",  K_JOY2},
436 	{"JOY3",  K_JOY3},
437 	{"JOY4",  K_JOY4},
438 	{"JOY5",  K_JOY5},
439 	{"JOY6",  K_JOY6},
440 	{"JOY7",  K_JOY7},
441 	{"JOY8",  K_JOY8},
442 	{"JOY9",  K_JOY9},
443 	{"JOY10", K_JOY10},
444 	{"JOY11", K_JOY11},
445 	{"JOY12", K_JOY12},
446 	{"JOY13", K_JOY13},
447 	{"JOY14", K_JOY14},
448 	{"JOY15", K_JOY15},
449 	{"JOY16", K_JOY16},
450 
451 
452 
453 
454 
455 
456 	{"AUX1", K_AUX1},
457 	{"AUX2", K_AUX2},
458 	{"AUX3", K_AUX3},
459 	{"AUX4", K_AUX4},
460 	{"AUX5", K_AUX5},
461 	{"AUX6", K_AUX6},
462 	{"AUX7", K_AUX7},
463 	{"AUX8", K_AUX8},
464 	{"AUX9", K_AUX9},
465 	{"AUX10", K_AUX10},
466 	{"AUX11", K_AUX11},
467 	{"AUX12", K_AUX12},
468 	{"AUX13", K_AUX13},
469 	{"AUX14", K_AUX14},
470 	{"AUX15", K_AUX15},
471 	{"AUX16", K_AUX16},
472 	{"AUX17", K_AUX17},
473 	{"AUX18", K_AUX18},
474 	{"AUX19", K_AUX19},
475 	{"AUX20", K_AUX20},
476 	{"AUX21", K_AUX21},
477 	{"AUX22", K_AUX22},
478 	{"AUX23", K_AUX23},
479 	{"AUX24", K_AUX24},
480 	{"AUX25", K_AUX25},
481 	{"AUX26", K_AUX26},
482 	{"AUX27", K_AUX27},
483 	{"AUX28", K_AUX28},
484 	{"AUX29", K_AUX29},
485 	{"AUX30", K_AUX30},
486 	{"AUX31", K_AUX31},
487 	{"AUX32", K_AUX32},
488 
489 	{"X360_DPAD_UP", K_X360_DPAD_UP},
490 	{"X360_DPAD_DOWN", K_X360_DPAD_DOWN},
491 	{"X360_DPAD_LEFT", K_X360_DPAD_LEFT},
492 	{"X360_DPAD_RIGHT", K_X360_DPAD_RIGHT},
493 	{"X360_START", K_X360_START},
494 	{"X360_BACK", K_X360_BACK},
495 	{"X360_LEFT_THUMB", K_X360_LEFT_THUMB},
496 	{"X360_RIGHT_THUMB", K_X360_RIGHT_THUMB},
497 	{"X360_LEFT_SHOULDER", K_X360_LEFT_SHOULDER},
498 	{"X360_RIGHT_SHOULDER", K_X360_RIGHT_SHOULDER},
499 	{"X360_A", K_X360_A},
500 	{"X360_B", K_X360_B},
501 	{"X360_X", K_X360_X},
502 	{"X360_Y", K_X360_Y},
503 	{"X360_LEFT_TRIGGER", K_X360_LEFT_TRIGGER},
504 	{"X360_RIGHT_TRIGGER", K_X360_RIGHT_TRIGGER},
505 	{"X360_LEFT_THUMB_UP", K_X360_LEFT_THUMB_UP},
506 	{"X360_LEFT_THUMB_DOWN", K_X360_LEFT_THUMB_DOWN},
507 	{"X360_LEFT_THUMB_LEFT", K_X360_LEFT_THUMB_LEFT},
508 	{"X360_LEFT_THUMB_RIGHT", K_X360_LEFT_THUMB_RIGHT},
509 	{"X360_RIGHT_THUMB_UP", K_X360_RIGHT_THUMB_UP},
510 	{"X360_RIGHT_THUMB_DOWN", K_X360_RIGHT_THUMB_DOWN},
511 	{"X360_RIGHT_THUMB_LEFT", K_X360_RIGHT_THUMB_LEFT},
512 	{"X360_RIGHT_THUMB_RIGHT", K_X360_RIGHT_THUMB_RIGHT},
513 
514 	{"JOY_UP", K_JOY_UP},
515 	{"JOY_DOWN", K_JOY_DOWN},
516 	{"JOY_LEFT", K_JOY_LEFT},
517 	{"JOY_RIGHT", K_JOY_RIGHT},
518 
519 	{"SEMICOLON", ';'},			// because a raw semicolon separates commands
520 	{"TILDE", '~'},
521 	{"BACKQUOTE", '`'},
522 	{"QUOTE", '"'},
523 	{"APOSTROPHE", '\''},
524 	{"BACKSLASH", '\\'},		// because a raw backslash is used for special characters
525 
526 	{"MIDINOTE0", K_MIDINOTE0},
527 	{"MIDINOTE1", K_MIDINOTE1},
528 	{"MIDINOTE2", K_MIDINOTE2},
529 	{"MIDINOTE3", K_MIDINOTE3},
530 	{"MIDINOTE4", K_MIDINOTE4},
531 	{"MIDINOTE5", K_MIDINOTE5},
532 	{"MIDINOTE6", K_MIDINOTE6},
533 	{"MIDINOTE7", K_MIDINOTE7},
534 	{"MIDINOTE8", K_MIDINOTE8},
535 	{"MIDINOTE9", K_MIDINOTE9},
536 	{"MIDINOTE10", K_MIDINOTE10},
537 	{"MIDINOTE11", K_MIDINOTE11},
538 	{"MIDINOTE12", K_MIDINOTE12},
539 	{"MIDINOTE13", K_MIDINOTE13},
540 	{"MIDINOTE14", K_MIDINOTE14},
541 	{"MIDINOTE15", K_MIDINOTE15},
542 	{"MIDINOTE16", K_MIDINOTE16},
543 	{"MIDINOTE17", K_MIDINOTE17},
544 	{"MIDINOTE18", K_MIDINOTE18},
545 	{"MIDINOTE19", K_MIDINOTE19},
546 	{"MIDINOTE20", K_MIDINOTE20},
547 	{"MIDINOTE21", K_MIDINOTE21},
548 	{"MIDINOTE22", K_MIDINOTE22},
549 	{"MIDINOTE23", K_MIDINOTE23},
550 	{"MIDINOTE24", K_MIDINOTE24},
551 	{"MIDINOTE25", K_MIDINOTE25},
552 	{"MIDINOTE26", K_MIDINOTE26},
553 	{"MIDINOTE27", K_MIDINOTE27},
554 	{"MIDINOTE28", K_MIDINOTE28},
555 	{"MIDINOTE29", K_MIDINOTE29},
556 	{"MIDINOTE30", K_MIDINOTE30},
557 	{"MIDINOTE31", K_MIDINOTE31},
558 	{"MIDINOTE32", K_MIDINOTE32},
559 	{"MIDINOTE33", K_MIDINOTE33},
560 	{"MIDINOTE34", K_MIDINOTE34},
561 	{"MIDINOTE35", K_MIDINOTE35},
562 	{"MIDINOTE36", K_MIDINOTE36},
563 	{"MIDINOTE37", K_MIDINOTE37},
564 	{"MIDINOTE38", K_MIDINOTE38},
565 	{"MIDINOTE39", K_MIDINOTE39},
566 	{"MIDINOTE40", K_MIDINOTE40},
567 	{"MIDINOTE41", K_MIDINOTE41},
568 	{"MIDINOTE42", K_MIDINOTE42},
569 	{"MIDINOTE43", K_MIDINOTE43},
570 	{"MIDINOTE44", K_MIDINOTE44},
571 	{"MIDINOTE45", K_MIDINOTE45},
572 	{"MIDINOTE46", K_MIDINOTE46},
573 	{"MIDINOTE47", K_MIDINOTE47},
574 	{"MIDINOTE48", K_MIDINOTE48},
575 	{"MIDINOTE49", K_MIDINOTE49},
576 	{"MIDINOTE50", K_MIDINOTE50},
577 	{"MIDINOTE51", K_MIDINOTE51},
578 	{"MIDINOTE52", K_MIDINOTE52},
579 	{"MIDINOTE53", K_MIDINOTE53},
580 	{"MIDINOTE54", K_MIDINOTE54},
581 	{"MIDINOTE55", K_MIDINOTE55},
582 	{"MIDINOTE56", K_MIDINOTE56},
583 	{"MIDINOTE57", K_MIDINOTE57},
584 	{"MIDINOTE58", K_MIDINOTE58},
585 	{"MIDINOTE59", K_MIDINOTE59},
586 	{"MIDINOTE60", K_MIDINOTE60},
587 	{"MIDINOTE61", K_MIDINOTE61},
588 	{"MIDINOTE62", K_MIDINOTE62},
589 	{"MIDINOTE63", K_MIDINOTE63},
590 	{"MIDINOTE64", K_MIDINOTE64},
591 	{"MIDINOTE65", K_MIDINOTE65},
592 	{"MIDINOTE66", K_MIDINOTE66},
593 	{"MIDINOTE67", K_MIDINOTE67},
594 	{"MIDINOTE68", K_MIDINOTE68},
595 	{"MIDINOTE69", K_MIDINOTE69},
596 	{"MIDINOTE70", K_MIDINOTE70},
597 	{"MIDINOTE71", K_MIDINOTE71},
598 	{"MIDINOTE72", K_MIDINOTE72},
599 	{"MIDINOTE73", K_MIDINOTE73},
600 	{"MIDINOTE74", K_MIDINOTE74},
601 	{"MIDINOTE75", K_MIDINOTE75},
602 	{"MIDINOTE76", K_MIDINOTE76},
603 	{"MIDINOTE77", K_MIDINOTE77},
604 	{"MIDINOTE78", K_MIDINOTE78},
605 	{"MIDINOTE79", K_MIDINOTE79},
606 	{"MIDINOTE80", K_MIDINOTE80},
607 	{"MIDINOTE81", K_MIDINOTE81},
608 	{"MIDINOTE82", K_MIDINOTE82},
609 	{"MIDINOTE83", K_MIDINOTE83},
610 	{"MIDINOTE84", K_MIDINOTE84},
611 	{"MIDINOTE85", K_MIDINOTE85},
612 	{"MIDINOTE86", K_MIDINOTE86},
613 	{"MIDINOTE87", K_MIDINOTE87},
614 	{"MIDINOTE88", K_MIDINOTE88},
615 	{"MIDINOTE89", K_MIDINOTE89},
616 	{"MIDINOTE90", K_MIDINOTE90},
617 	{"MIDINOTE91", K_MIDINOTE91},
618 	{"MIDINOTE92", K_MIDINOTE92},
619 	{"MIDINOTE93", K_MIDINOTE93},
620 	{"MIDINOTE94", K_MIDINOTE94},
621 	{"MIDINOTE95", K_MIDINOTE95},
622 	{"MIDINOTE96", K_MIDINOTE96},
623 	{"MIDINOTE97", K_MIDINOTE97},
624 	{"MIDINOTE98", K_MIDINOTE98},
625 	{"MIDINOTE99", K_MIDINOTE99},
626 	{"MIDINOTE100", K_MIDINOTE100},
627 	{"MIDINOTE101", K_MIDINOTE101},
628 	{"MIDINOTE102", K_MIDINOTE102},
629 	{"MIDINOTE103", K_MIDINOTE103},
630 	{"MIDINOTE104", K_MIDINOTE104},
631 	{"MIDINOTE105", K_MIDINOTE105},
632 	{"MIDINOTE106", K_MIDINOTE106},
633 	{"MIDINOTE107", K_MIDINOTE107},
634 	{"MIDINOTE108", K_MIDINOTE108},
635 	{"MIDINOTE109", K_MIDINOTE109},
636 	{"MIDINOTE110", K_MIDINOTE110},
637 	{"MIDINOTE111", K_MIDINOTE111},
638 	{"MIDINOTE112", K_MIDINOTE112},
639 	{"MIDINOTE113", K_MIDINOTE113},
640 	{"MIDINOTE114", K_MIDINOTE114},
641 	{"MIDINOTE115", K_MIDINOTE115},
642 	{"MIDINOTE116", K_MIDINOTE116},
643 	{"MIDINOTE117", K_MIDINOTE117},
644 	{"MIDINOTE118", K_MIDINOTE118},
645 	{"MIDINOTE119", K_MIDINOTE119},
646 	{"MIDINOTE120", K_MIDINOTE120},
647 	{"MIDINOTE121", K_MIDINOTE121},
648 	{"MIDINOTE122", K_MIDINOTE122},
649 	{"MIDINOTE123", K_MIDINOTE123},
650 	{"MIDINOTE124", K_MIDINOTE124},
651 	{"MIDINOTE125", K_MIDINOTE125},
652 	{"MIDINOTE126", K_MIDINOTE126},
653 	{"MIDINOTE127", K_MIDINOTE127},
654 
655 	{NULL, 0}
656 };
657 
658 /*
659 ==============================================================================
660 
661 			LINE TYPING INTO THE CONSOLE
662 
663 ==============================================================================
664 */
665 
666 void
Key_ClearEditLine(int edit_line)667 Key_ClearEditLine (int edit_line)
668 {
669 	memset (key_line, '\0', sizeof(key_line));
670 	key_line[0] = ']';
671 	key_linepos = 1;
672 }
673 
674 /*
675 ====================
676 Interactive line editing and console scrollback
677 ====================
678 */
679 static void
Key_Console(int key,int unicode)680 Key_Console (int key, int unicode)
681 {
682 	// LordHavoc: copied most of this from Q2 to improve keyboard handling
683 	switch (key)
684 	{
685 	case K_KP_SLASH:
686 		key = '/';
687 		break;
688 	case K_KP_MINUS:
689 		key = '-';
690 		break;
691 	case K_KP_PLUS:
692 		key = '+';
693 		break;
694 	case K_KP_HOME:
695 		key = '7';
696 		break;
697 	case K_KP_UPARROW:
698 		key = '8';
699 		break;
700 	case K_KP_PGUP:
701 		key = '9';
702 		break;
703 	case K_KP_LEFTARROW:
704 		key = '4';
705 		break;
706 	case K_KP_5:
707 		key = '5';
708 		break;
709 	case K_KP_RIGHTARROW:
710 		key = '6';
711 		break;
712 	case K_KP_END:
713 		key = '1';
714 		break;
715 	case K_KP_DOWNARROW:
716 		key = '2';
717 		break;
718 	case K_KP_PGDN:
719 		key = '3';
720 		break;
721 	case K_KP_INS:
722 		key = '0';
723 		break;
724 	case K_KP_DEL:
725 		key = '.';
726 		break;
727 	}
728 
729 	if ((key == 'v' && keydown[K_CTRL]) || ((key == K_INS || key == K_KP_INS) && keydown[K_SHIFT]))
730 	{
731 		char *cbd, *p;
732 		if ((cbd = Sys_GetClipboardData()) != 0)
733 		{
734 			int i;
735 #if 1
736 			p = cbd;
737 			while (*p)
738 			{
739 				if (*p == '\r' && *(p+1) == '\n')
740 				{
741 					*p++ = ';';
742 					*p++ = ' ';
743 				}
744 				else if (*p == '\n' || *p == '\r' || *p == '\b')
745 					*p++ = ';';
746 				p++;
747 			}
748 #else
749 			strtok(cbd, "\n\r\b");
750 #endif
751 			i = (int)strlen(cbd);
752 			if (i + key_linepos >= MAX_INPUTLINE)
753 				i= MAX_INPUTLINE - key_linepos - 1;
754 			if (i > 0)
755 			{
756 				cbd[i] = 0;
757 				memmove(key_line + key_linepos + i, key_line + key_linepos, sizeof(key_line) - key_linepos - i);
758 				memcpy(key_line + key_linepos, cbd, i);
759 				key_linepos += i;
760 			}
761 			Z_Free(cbd);
762 		}
763 		return;
764 	}
765 
766 	if (key == 'l' && keydown[K_CTRL])
767 	{
768 		Cbuf_AddText ("clear\n");
769 		return;
770 	}
771 
772 	if (key == 'u' && keydown[K_CTRL]) // like vi/readline ^u: delete currently edited line
773 	{
774 		// clear line
775 		key_line[0] = ']';
776 		key_line[1] = 0;
777 		key_linepos = 1;
778 		return;
779 	}
780 
781 	if (key == 'q' && keydown[K_CTRL]) // like zsh ^q: push line to history, don't execute, and clear
782 	{
783 		// clear line
784 		Key_History_Push();
785 		key_line[0] = ']';
786 		key_line[1] = 0;
787 		key_linepos = 1;
788 		return;
789 	}
790 
791 	if (key == K_ENTER || key == K_KP_ENTER)
792 	{
793 		Cbuf_AddText (key_line+1);	// skip the ]
794 		Cbuf_AddText ("\n");
795 		Key_History_Push();
796 		key_line[0] = ']';
797 		key_line[1] = 0;	// EvilTypeGuy: null terminate
798 		key_linepos = 1;
799 		// force an update, because the command may take some time
800 		if (cls.state == ca_disconnected)
801 			CL_UpdateScreen ();
802 		return;
803 	}
804 
805 	if (key == K_TAB)
806 	{
807 		if(keydown[K_CTRL]) // append to the cvar its value
808 		{
809 			int		cvar_len, cvar_str_len, chars_to_move;
810 			char	k;
811 			char	cvar[MAX_INPUTLINE];
812 			const char *cvar_str;
813 
814 			// go to the start of the variable
815 			while(--key_linepos)
816 			{
817 				k = key_line[key_linepos];
818 				if(k == '\"' || k == ';' || k == ' ' || k == '\'')
819 					break;
820 			}
821 			key_linepos++;
822 
823 			// save the variable name in cvar
824 			for(cvar_len=0; (k = key_line[key_linepos + cvar_len]) != 0; cvar_len++)
825 			{
826 				if(k == '\"' || k == ';' || k == ' ' || k == '\'')
827 					break;
828 				cvar[cvar_len] = k;
829 			}
830 			if (cvar_len==0)
831 				return;
832 			cvar[cvar_len] = 0;
833 
834 			// go to the end of the cvar
835 			key_linepos += cvar_len;
836 
837 			// save the content of the variable in cvar_str
838 			cvar_str = Cvar_VariableString(cvar);
839 			cvar_str_len = (int)strlen(cvar_str);
840 			if (cvar_str_len==0)
841 				return;
842 
843 			// insert space and cvar_str in key_line
844 			chars_to_move = (int)strlen(&key_line[key_linepos]);
845 			if (key_linepos + 1 + cvar_str_len + chars_to_move < MAX_INPUTLINE)
846 			{
847 				if (chars_to_move)
848 					memmove(&key_line[key_linepos + 1 + cvar_str_len], &key_line[key_linepos], chars_to_move);
849 				key_line[key_linepos++] = ' ';
850 				memcpy(&key_line[key_linepos], cvar_str, cvar_str_len);
851 				key_linepos += cvar_str_len;
852 				key_line[key_linepos + chars_to_move] = 0;
853 			}
854 			else
855 				Con_Printf("Couldn't append cvar value, edit line too long.\n");
856 			return;
857 		}
858 		// Enhanced command completion
859 		// by EvilTypeGuy eviltypeguy@qeradiant.com
860 		// Thanks to Fett, Taniwha
861 		Con_CompleteCommandLine();
862 		return;
863 	}
864 
865 	// Advanced Console Editing by Radix radix@planetquake.com
866 	// Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
867 	// Enhanced by [515]
868 	// Enhanced by terencehill
869 
870 	// move cursor to the previous character
871 	if (key == K_LEFTARROW || key == K_KP_LEFTARROW)
872 	{
873 		if (key_linepos < 2)
874 			return;
875 		if(keydown[K_CTRL]) // move cursor to the previous word
876 		{
877 			int		pos;
878 			char	k;
879 			pos = key_linepos-1;
880 
881 			if(pos) // skip all "; ' after the word
882 				while(--pos)
883 				{
884 					k = key_line[pos];
885 					if (!(k == '\"' || k == ';' || k == ' ' || k == '\''))
886 						break;
887 				}
888 
889 			if(pos)
890 				while(--pos)
891 				{
892 					k = key_line[pos];
893 					if(k == '\"' || k == ';' || k == ' ' || k == '\'')
894 						break;
895 				}
896 			key_linepos = pos + 1;
897 		}
898 		else if(keydown[K_SHIFT]) // move cursor to the previous character ignoring colors
899 		{
900 			int		pos;
901 			size_t          inchar = 0;
902 			pos = (int)u8_prevbyte(key_line+1, key_linepos-1) + 1; // do NOT give the ']' to u8_prevbyte
903 			while (pos)
904 				if(pos-1 > 0 && key_line[pos-1] == STRING_COLOR_TAG && isdigit(key_line[pos]))
905 					pos-=2;
906 				else if(pos-4 > 0 && key_line[pos-4] == STRING_COLOR_TAG && key_line[pos-3] == STRING_COLOR_RGB_TAG_CHAR
907 						&& isxdigit(key_line[pos-2]) && isxdigit(key_line[pos-1]) && isxdigit(key_line[pos]))
908 					pos-=5;
909 				else
910 				{
911 					if(pos-1 > 0 && key_line[pos-1] == STRING_COLOR_TAG && key_line[pos] == STRING_COLOR_TAG) // consider ^^ as a character
912 						pos--;
913 					pos--;
914 					break;
915 				}
916 			// we need to move to the beginning of the character when in a wide character:
917 			u8_charidx(key_line, pos + 1, &inchar);
918 			key_linepos = (int)(pos + 1 - inchar);
919 		}
920 		else
921 		{
922 			key_linepos = (int)u8_prevbyte(key_line+1, key_linepos-1) + 1; // do NOT give the ']' to u8_prevbyte
923 		}
924 		return;
925 	}
926 
927 	// delete char before cursor
928 	if (key == K_BACKSPACE || (key == 'h' && keydown[K_CTRL]))
929 	{
930 		if (key_linepos > 1)
931 		{
932 			int newpos = (int)u8_prevbyte(key_line+1, key_linepos-1) + 1; // do NOT give the ']' to u8_prevbyte
933 			strlcpy(key_line + newpos, key_line + key_linepos, sizeof(key_line) + 1 - key_linepos);
934 			key_linepos = newpos;
935 		}
936 		return;
937 	}
938 
939 	// delete char on cursor
940 	if (key == K_DEL || key == K_KP_DEL)
941 	{
942 		size_t linelen;
943 		linelen = strlen(key_line);
944 		if (key_linepos < (int)linelen)
945 			memmove(key_line + key_linepos, key_line + key_linepos + u8_bytelen(key_line + key_linepos, 1), linelen - key_linepos);
946 		return;
947 	}
948 
949 
950 	// move cursor to the next character
951 	if (key == K_RIGHTARROW || key == K_KP_RIGHTARROW)
952 	{
953 		if (key_linepos >= (int)strlen(key_line))
954 			return;
955 		if(keydown[K_CTRL]) // move cursor to the next word
956 		{
957 			int		pos, len;
958 			char	k;
959 			len = (int)strlen(key_line);
960 			pos = key_linepos;
961 
962 			while(++pos < len)
963 			{
964 				k = key_line[pos];
965 				if(k == '\"' || k == ';' || k == ' ' || k == '\'')
966 					break;
967 			}
968 
969 			if (pos < len) // skip all "; ' after the word
970 				while(++pos < len)
971 				{
972 					k = key_line[pos];
973 					if (!(k == '\"' || k == ';' || k == ' ' || k == '\''))
974 						break;
975 				}
976 			key_linepos = pos;
977 		}
978 		else if(keydown[K_SHIFT]) // move cursor to the next character ignoring colors
979 		{
980 			int		pos, len;
981 			len = (int)strlen(key_line);
982 			pos = key_linepos;
983 
984 			// go beyond all initial consecutive color tags, if any
985 			if(pos < len)
986 				while (key_line[pos] == STRING_COLOR_TAG)
987 				{
988 					if(isdigit(key_line[pos+1]))
989 						pos+=2;
990 					else if(key_line[pos+1] == STRING_COLOR_RGB_TAG_CHAR && isxdigit(key_line[pos+2]) && isxdigit(key_line[pos+3]) && isxdigit(key_line[pos+4]))
991 						pos+=5;
992 					else
993 						break;
994 				}
995 
996 			// skip the char
997 			if (key_line[pos] == STRING_COLOR_TAG && key_line[pos+1] == STRING_COLOR_TAG) // consider ^^ as a character
998 				pos++;
999 			pos += (int)u8_bytelen(key_line + pos, 1);
1000 
1001 			// now go beyond all next consecutive color tags, if any
1002 			if(pos < len)
1003 				while (key_line[pos] == STRING_COLOR_TAG)
1004 				{
1005 					if(isdigit(key_line[pos+1]))
1006 						pos+=2;
1007 					else if(key_line[pos+1] == STRING_COLOR_RGB_TAG_CHAR && isxdigit(key_line[pos+2]) && isxdigit(key_line[pos+3]) && isxdigit(key_line[pos+4]))
1008 						pos+=5;
1009 					else
1010 						break;
1011 				}
1012 			key_linepos = pos;
1013 		}
1014 		else
1015 			key_linepos += (int)u8_bytelen(key_line + key_linepos, 1);
1016 		return;
1017 	}
1018 
1019 	if (key == K_INS || key == K_KP_INS) // toggle insert mode
1020 	{
1021 		key_insert ^= 1;
1022 		return;
1023 	}
1024 
1025 	// End Advanced Console Editing
1026 
1027 	if (key == K_UPARROW || key == K_KP_UPARROW || (key == 'p' && keydown[K_CTRL]))
1028 	{
1029 		Key_History_Up();
1030 		return;
1031 	}
1032 
1033 	if (key == K_DOWNARROW || key == K_KP_DOWNARROW || (key == 'n' && keydown[K_CTRL]))
1034 	{
1035 		Key_History_Down();
1036 		return;
1037 	}
1038 
1039 	if (keydown[K_CTRL])
1040 	{
1041 		// prints all the matching commands
1042 		if (key == 'f')
1043 		{
1044 			Key_History_Find_All();
1045 			return;
1046 		}
1047 		// Search forwards/backwards, pointing the history's index to the
1048 		// matching command but without fetching it to let one continue the search.
1049 		// To fetch it, it suffices to just press UP or DOWN.
1050 		if (key == 'r')
1051 		{
1052 			if (keydown[K_SHIFT])
1053 				Key_History_Find_Forwards();
1054 			else
1055 				Key_History_Find_Backwards();
1056 			return;
1057 		}
1058 		// go to the last/first command of the history
1059 		if (key == ',')
1060 		{
1061 			Key_History_First();
1062 			return;
1063 		}
1064 		if (key == '.')
1065 		{
1066 			Key_History_Last();
1067 			return;
1068 		}
1069 	}
1070 
1071 	if (key == K_PGUP || key == K_KP_PGUP)
1072 	{
1073 		if(keydown[K_CTRL])
1074 		{
1075 			con_backscroll += ((vid_conheight.integer >> 2) / con_textsize.integer)-1;
1076 		}
1077 		else
1078 			con_backscroll += ((vid_conheight.integer >> 1) / con_textsize.integer)-3;
1079 		return;
1080 	}
1081 
1082 	if (key == K_PGDN || key == K_KP_PGDN)
1083 	{
1084 		if(keydown[K_CTRL])
1085 		{
1086 			con_backscroll -= ((vid_conheight.integer >> 2) / con_textsize.integer)-1;
1087 		}
1088 		else
1089 			con_backscroll -= ((vid_conheight.integer >> 1) / con_textsize.integer)-3;
1090 		return;
1091 	}
1092 
1093 	if (key == K_MWHEELUP)
1094 	{
1095 		if(keydown[K_CTRL])
1096 			con_backscroll += 1;
1097 		else if(keydown[K_SHIFT])
1098 			con_backscroll += ((vid_conheight.integer >> 2) / con_textsize.integer)-1;
1099 		else
1100 			con_backscroll += 5;
1101 		return;
1102 	}
1103 
1104 	if (key == K_MWHEELDOWN)
1105 	{
1106 		if(keydown[K_CTRL])
1107 			con_backscroll -= 1;
1108 		else if(keydown[K_SHIFT])
1109 			con_backscroll -= ((vid_conheight.integer >> 2) / con_textsize.integer)-1;
1110 		else
1111 			con_backscroll -= 5;
1112 		return;
1113 	}
1114 
1115 	if (keydown[K_CTRL])
1116 	{
1117 		// text zoom in
1118 		if (key == '+' || key == K_KP_PLUS)
1119 		{
1120 			if (con_textsize.integer < 128)
1121 				Cvar_SetValueQuick(&con_textsize, con_textsize.integer + 1);
1122 			return;
1123 		}
1124 		// text zoom out
1125 		if (key == '-' || key == K_KP_MINUS)
1126 		{
1127 			if (con_textsize.integer > 1)
1128 				Cvar_SetValueQuick(&con_textsize, con_textsize.integer - 1);
1129 			return;
1130 		}
1131 		// text zoom reset
1132 		if (key == '0' || key == K_KP_INS)
1133 		{
1134 			Cvar_SetValueQuick(&con_textsize, atoi(Cvar_VariableDefString("con_textsize")));
1135 			return;
1136 		}
1137 	}
1138 
1139 	if (key == K_HOME || key == K_KP_HOME)
1140 	{
1141 		if (keydown[K_CTRL])
1142 			con_backscroll = CON_TEXTSIZE;
1143 		else
1144 			key_linepos = 1;
1145 		return;
1146 	}
1147 
1148 	if (key == K_END || key == K_KP_END)
1149 	{
1150 		if (keydown[K_CTRL])
1151 			con_backscroll = 0;
1152 		else
1153 			key_linepos = (int)strlen(key_line);
1154 		return;
1155 	}
1156 
1157 	// non printable
1158 	if (unicode < 32)
1159 		return;
1160 
1161 	if (key_linepos < MAX_INPUTLINE-1)
1162 	{
1163 		char buf[16];
1164 		int len;
1165 		int blen;
1166 		blen = u8_fromchar(unicode, buf, sizeof(buf));
1167 		if (!blen)
1168 			return;
1169 		len = (int)strlen(&key_line[key_linepos]);
1170 		// check insert mode, or always insert if at end of line
1171 		if (key_insert || len == 0)
1172 		{
1173 			if (key_linepos + len + blen >= MAX_INPUTLINE)
1174 				return;
1175 			// can't use strcpy to move string to right
1176 			len++;
1177 			if (key_linepos + blen + len >= MAX_INPUTLINE)
1178 				return;
1179 			memmove(&key_line[key_linepos + blen], &key_line[key_linepos], len);
1180 		}
1181 		else if (key_linepos + len + blen - u8_bytelen(key_line + key_linepos, 1) >= MAX_INPUTLINE)
1182 			return;
1183 		memcpy(key_line + key_linepos, buf, blen);
1184 		if (blen > len)
1185 			key_line[key_linepos + blen] = 0;
1186 		// END OF FIXME
1187 		key_linepos += blen;
1188 	}
1189 }
1190 
1191 //============================================================================
1192 
1193 int chat_mode;
1194 char		chat_buffer[MAX_INPUTLINE];
1195 unsigned int	chat_bufferlen = 0;
1196 
1197 static void
Key_Message(int key,int ascii)1198 Key_Message (int key, int ascii)
1199 {
1200 	char vabuf[1024];
1201 	if (key == K_ENTER || key == K_KP_ENTER || ascii == 10 || ascii == 13)
1202 	{
1203 		if(chat_mode < 0)
1204 			Cmd_ExecuteString(chat_buffer, src_command, true); // not Cbuf_AddText to allow semiclons in args; however, this allows no variables then. Use aliases!
1205 		else
1206 			Cmd_ForwardStringToServer(va(vabuf, sizeof(vabuf), "%s %s", chat_mode ? "say_team" : "say ", chat_buffer));
1207 
1208 		key_dest = key_game;
1209 		chat_bufferlen = 0;
1210 		chat_buffer[0] = 0;
1211 		return;
1212 	}
1213 
1214 	// TODO add support for arrow keys and simple editing
1215 
1216 	if (key == K_ESCAPE) {
1217 		key_dest = key_game;
1218 		chat_bufferlen = 0;
1219 		chat_buffer[0] = 0;
1220 		return;
1221 	}
1222 
1223 	if (key == K_BACKSPACE) {
1224 		if (chat_bufferlen) {
1225 			chat_bufferlen = (unsigned int)u8_prevbyte(chat_buffer, chat_bufferlen);
1226 			chat_buffer[chat_bufferlen] = 0;
1227 		}
1228 		return;
1229 	}
1230 
1231 	if(key == K_TAB) {
1232 		chat_bufferlen = Nicks_CompleteChatLine(chat_buffer, sizeof(chat_buffer), chat_bufferlen);
1233 		return;
1234 	}
1235 
1236 	// ctrl+key generates an ascii value < 32 and shows a char from the charmap
1237 	if (ascii > 0 && ascii < 32 && utf8_enable.integer)
1238 		ascii = 0xE000 + ascii;
1239 
1240 	if (chat_bufferlen == sizeof (chat_buffer) - 1)
1241 		return;							// all full
1242 
1243 	if (!ascii)
1244 		return;							// non printable
1245 
1246 	chat_bufferlen += u8_fromchar(ascii, chat_buffer+chat_bufferlen, sizeof(chat_buffer) - chat_bufferlen - 1);
1247 
1248 	//chat_buffer[chat_bufferlen++] = ascii;
1249 	//chat_buffer[chat_bufferlen] = 0;
1250 }
1251 
1252 //============================================================================
1253 
1254 
1255 /*
1256 ===================
1257 Returns a key number to be used to index keybindings[] by looking at
1258 the given string.  Single ascii characters return themselves, while
1259 the K_* names are matched up.
1260 ===================
1261 */
1262 int
Key_StringToKeynum(const char * str)1263 Key_StringToKeynum (const char *str)
1264 {
1265 	const keyname_t  *kn;
1266 
1267 	if (!str || !str[0])
1268 		return -1;
1269 	if (!str[1])
1270 		return tolower(str[0]);
1271 
1272 	for (kn = keynames; kn->name; kn++) {
1273 		if (!strcasecmp (str, kn->name))
1274 			return kn->keynum;
1275 	}
1276 	return -1;
1277 }
1278 
1279 /*
1280 ===================
1281 Returns a string (either a single ascii char, or a K_* name) for the
1282 given keynum.
1283 FIXME: handle quote special (general escape sequence?)
1284 ===================
1285 */
1286 const char *
Key_KeynumToString(int keynum,char * tinystr,size_t tinystrlength)1287 Key_KeynumToString (int keynum, char *tinystr, size_t tinystrlength)
1288 {
1289 	const keyname_t  *kn;
1290 
1291 	// -1 is an invalid code
1292 	if (keynum < 0)
1293 		return "<KEY NOT FOUND>";
1294 
1295 	// search overrides first, because some characters are special
1296 	for (kn = keynames; kn->name; kn++)
1297 		if (keynum == kn->keynum)
1298 			return kn->name;
1299 
1300 	// if it is printable, output it as a single character
1301 	if (keynum > 32 && keynum < 256)
1302 	{
1303 		if (tinystrlength >= 2)
1304 		{
1305 			tinystr[0] = keynum;
1306 			tinystr[1] = 0;
1307 		}
1308 		return tinystr;
1309 	}
1310 
1311 	// if it is not overridden and not printable, we don't know what to do with it
1312 	return "<UNKNOWN KEYNUM>";
1313 }
1314 
1315 
1316 qboolean
Key_SetBinding(int keynum,int bindmap,const char * binding)1317 Key_SetBinding (int keynum, int bindmap, const char *binding)
1318 {
1319 	char *newbinding;
1320 	size_t l;
1321 
1322 	if (keynum == -1 || keynum >= MAX_KEYS)
1323 		return false;
1324 	if ((bindmap < 0) || (bindmap >= MAX_BINDMAPS))
1325 		return false;
1326 
1327 // free old bindings
1328 	if (keybindings[bindmap][keynum]) {
1329 		Z_Free (keybindings[bindmap][keynum]);
1330 		keybindings[bindmap][keynum] = NULL;
1331 	}
1332 	if(!binding[0]) // make "" binds be removed --blub
1333 		return true;
1334 // allocate memory for new binding
1335 	l = strlen (binding);
1336 	newbinding = (char *)Z_Malloc (l + 1);
1337 	memcpy (newbinding, binding, l + 1);
1338 	newbinding[l] = 0;
1339 	keybindings[bindmap][keynum] = newbinding;
1340 	return true;
1341 }
1342 
Key_GetBindMap(int * fg,int * bg)1343 void Key_GetBindMap(int *fg, int *bg)
1344 {
1345 	if(fg)
1346 		*fg = key_bmap;
1347 	if(bg)
1348 		*bg = key_bmap2;
1349 }
1350 
Key_SetBindMap(int fg,int bg)1351 qboolean Key_SetBindMap(int fg, int bg)
1352 {
1353 	if(fg >= MAX_BINDMAPS)
1354 		return false;
1355 	if(bg >= MAX_BINDMAPS)
1356 		return false;
1357 	if(fg >= 0)
1358 		key_bmap = fg;
1359 	if(bg >= 0)
1360 		key_bmap2 = bg;
1361 	return true;
1362 }
1363 
1364 static void
Key_In_Unbind_f(void)1365 Key_In_Unbind_f (void)
1366 {
1367 	int         b, m;
1368 	char *errchar = NULL;
1369 
1370 	if (Cmd_Argc () != 3) {
1371 		Con_Print("in_unbind <bindmap> <key> : remove commands from a key\n");
1372 		return;
1373 	}
1374 
1375 	m = strtol(Cmd_Argv (1), &errchar, 0);
1376 	if ((m < 0) || (m >= MAX_BINDMAPS) || (errchar && *errchar)) {
1377 		Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(1));
1378 		return;
1379 	}
1380 
1381 	b = Key_StringToKeynum (Cmd_Argv (2));
1382 	if (b == -1) {
1383 		Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (2));
1384 		return;
1385 	}
1386 
1387 	if(!Key_SetBinding (b, m, ""))
1388 		Con_Printf("Key_SetBinding failed for unknown reason\n");
1389 }
1390 
1391 static void
Key_In_Bind_f(void)1392 Key_In_Bind_f (void)
1393 {
1394 	int         i, c, b, m;
1395 	char        cmd[MAX_INPUTLINE];
1396 	char *errchar = NULL;
1397 
1398 	c = Cmd_Argc ();
1399 
1400 	if (c != 3 && c != 4) {
1401 		Con_Print("in_bind <bindmap> <key> [command] : attach a command to a key\n");
1402 		return;
1403 	}
1404 
1405 	m = strtol(Cmd_Argv (1), &errchar, 0);
1406 	if ((m < 0) || (m >= MAX_BINDMAPS) || (errchar && *errchar)) {
1407 		Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(1));
1408 		return;
1409 	}
1410 
1411 	b = Key_StringToKeynum (Cmd_Argv (2));
1412 	if (b == -1 || b >= MAX_KEYS) {
1413 		Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (2));
1414 		return;
1415 	}
1416 
1417 	if (c == 3) {
1418 		if (keybindings[m][b])
1419 			Con_Printf("\"%s\" = \"%s\"\n", Cmd_Argv (2), keybindings[m][b]);
1420 		else
1421 			Con_Printf("\"%s\" is not bound\n", Cmd_Argv (2));
1422 		return;
1423 	}
1424 // copy the rest of the command line
1425 	cmd[0] = 0;							// start out with a null string
1426 	for (i = 3; i < c; i++) {
1427 		strlcat (cmd, Cmd_Argv (i), sizeof (cmd));
1428 		if (i != (c - 1))
1429 			strlcat (cmd, " ", sizeof (cmd));
1430 	}
1431 
1432 	if(!Key_SetBinding (b, m, cmd))
1433 		Con_Printf("Key_SetBinding failed for unknown reason\n");
1434 }
1435 
1436 static void
Key_In_Bindmap_f(void)1437 Key_In_Bindmap_f (void)
1438 {
1439 	int         m1, m2, c;
1440 	char *errchar = NULL;
1441 
1442 	c = Cmd_Argc ();
1443 
1444 	if (c != 3) {
1445 		Con_Print("in_bindmap <bindmap> <fallback>: set current bindmap and fallback\n");
1446 		return;
1447 	}
1448 
1449 	m1 = strtol(Cmd_Argv (1), &errchar, 0);
1450 	if ((m1 < 0) || (m1 >= MAX_BINDMAPS) || (errchar && *errchar)) {
1451 		Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(1));
1452 		return;
1453 	}
1454 
1455 	m2 = strtol(Cmd_Argv (2), &errchar, 0);
1456 	if ((m2 < 0) || (m2 >= MAX_BINDMAPS) || (errchar && *errchar)) {
1457 		Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(2));
1458 		return;
1459 	}
1460 
1461 	key_bmap = m1;
1462 	key_bmap2 = m2;
1463 }
1464 
1465 static void
Key_Unbind_f(void)1466 Key_Unbind_f (void)
1467 {
1468 	int         b;
1469 
1470 	if (Cmd_Argc () != 2) {
1471 		Con_Print("unbind <key> : remove commands from a key\n");
1472 		return;
1473 	}
1474 
1475 	b = Key_StringToKeynum (Cmd_Argv (1));
1476 	if (b == -1) {
1477 		Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (1));
1478 		return;
1479 	}
1480 
1481 	if(!Key_SetBinding (b, 0, ""))
1482 		Con_Printf("Key_SetBinding failed for unknown reason\n");
1483 }
1484 
1485 static void
Key_Unbindall_f(void)1486 Key_Unbindall_f (void)
1487 {
1488 	int         i, j;
1489 
1490 	for (j = 0; j < MAX_BINDMAPS; j++)
1491 		for (i = 0; i < (int)(sizeof(keybindings[0])/sizeof(keybindings[0][0])); i++)
1492 			if (keybindings[j][i])
1493 				Key_SetBinding (i, j, "");
1494 }
1495 
1496 static void
Key_PrintBindList(int j)1497 Key_PrintBindList(int j)
1498 {
1499 	char bindbuf[MAX_INPUTLINE];
1500 	char tinystr[2];
1501 	const char *p;
1502 	int i;
1503 
1504 	for (i = 0; i < (int)(sizeof(keybindings[0])/sizeof(keybindings[0][0])); i++)
1505 	{
1506 		p = keybindings[j][i];
1507 		if (p)
1508 		{
1509 			Cmd_QuoteString(bindbuf, sizeof(bindbuf), p, "\"\\", false);
1510 			if (j == 0)
1511 				Con_Printf("^2%s ^7= \"%s\"\n", Key_KeynumToString (i, tinystr, sizeof(tinystr)), bindbuf);
1512 			else
1513 				Con_Printf("^3bindmap %d: ^2%s ^7= \"%s\"\n", j, Key_KeynumToString (i, tinystr, sizeof(tinystr)), bindbuf);
1514 		}
1515 	}
1516 }
1517 
1518 static void
Key_In_BindList_f(void)1519 Key_In_BindList_f (void)
1520 {
1521 	int m;
1522 	char *errchar = NULL;
1523 
1524 	if(Cmd_Argc() >= 2)
1525 	{
1526 		m = strtol(Cmd_Argv(1), &errchar, 0);
1527 		if ((m < 0) || (m >= MAX_BINDMAPS) || (errchar && *errchar)) {
1528 			Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(1));
1529 			return;
1530 		}
1531 		Key_PrintBindList(m);
1532 	}
1533 	else
1534 	{
1535 		for (m = 0; m < MAX_BINDMAPS; m++)
1536 			Key_PrintBindList(m);
1537 	}
1538 }
1539 
1540 static void
Key_BindList_f(void)1541 Key_BindList_f (void)
1542 {
1543 	Key_PrintBindList(0);
1544 }
1545 
1546 static void
Key_Bind_f(void)1547 Key_Bind_f (void)
1548 {
1549 	int         i, c, b;
1550 	char        cmd[MAX_INPUTLINE];
1551 
1552 	c = Cmd_Argc ();
1553 
1554 	if (c != 2 && c != 3) {
1555 		Con_Print("bind <key> [command] : attach a command to a key\n");
1556 		return;
1557 	}
1558 	b = Key_StringToKeynum (Cmd_Argv (1));
1559 	if (b == -1 || b >= MAX_KEYS) {
1560 		Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (1));
1561 		return;
1562 	}
1563 
1564 	if (c == 2) {
1565 		if (keybindings[0][b])
1566 			Con_Printf("\"%s\" = \"%s\"\n", Cmd_Argv (1), keybindings[0][b]);
1567 		else
1568 			Con_Printf("\"%s\" is not bound\n", Cmd_Argv (1));
1569 		return;
1570 	}
1571 // copy the rest of the command line
1572 	cmd[0] = 0;							// start out with a null string
1573 	for (i = 2; i < c; i++) {
1574 		strlcat (cmd, Cmd_Argv (i), sizeof (cmd));
1575 		if (i != (c - 1))
1576 			strlcat (cmd, " ", sizeof (cmd));
1577 	}
1578 
1579 	if(!Key_SetBinding (b, 0, cmd))
1580 		Con_Printf("Key_SetBinding failed for unknown reason\n");
1581 }
1582 
1583 /*
1584 ============
1585 Writes lines containing "bind key value"
1586 ============
1587 */
1588 void
Key_WriteBindings(qfile_t * f)1589 Key_WriteBindings (qfile_t *f)
1590 {
1591 	int         i, j;
1592 	char bindbuf[MAX_INPUTLINE];
1593 	char tinystr[2];
1594 	const char *p;
1595 
1596 	for (j = 0; j < MAX_BINDMAPS; j++)
1597 	{
1598 		for (i = 0; i < (int)(sizeof(keybindings[0])/sizeof(keybindings[0][0])); i++)
1599 		{
1600 			p = keybindings[j][i];
1601 			if (p)
1602 			{
1603 				Cmd_QuoteString(bindbuf, sizeof(bindbuf), p, "\"\\", false); // don't need to escape $ because cvars are not expanded inside bind
1604 				if (j == 0)
1605 					FS_Printf(f, "bind %s \"%s\"\n", Key_KeynumToString (i, tinystr, sizeof(tinystr)), bindbuf);
1606 				else
1607 					FS_Printf(f, "in_bind %d %s \"%s\"\n", j, Key_KeynumToString (i, tinystr, sizeof(tinystr)), bindbuf);
1608 			}
1609 		}
1610 	}
1611 }
1612 
1613 
1614 void
Key_Init(void)1615 Key_Init (void)
1616 {
1617 	Key_History_Init();
1618 	key_line[0] = ']';
1619 	key_line[1] = 0;
1620 	key_linepos = 1;
1621 
1622 //
1623 // register our functions
1624 //
1625 	Cmd_AddCommand ("in_bind", Key_In_Bind_f, "binds a command to the specified key in the selected bindmap");
1626 	Cmd_AddCommand ("in_unbind", Key_In_Unbind_f, "removes command on the specified key in the selected bindmap");
1627 	Cmd_AddCommand ("in_bindlist", Key_In_BindList_f, "bindlist: displays bound keys for all bindmaps, or the given bindmap");
1628 	Cmd_AddCommand ("in_bindmap", Key_In_Bindmap_f, "selects active foreground and background (used only if a key is not bound in the foreground) bindmaps for typing");
1629 
1630 	Cmd_AddCommand ("bind", Key_Bind_f, "binds a command to the specified key in bindmap 0");
1631 	Cmd_AddCommand ("unbind", Key_Unbind_f, "removes a command on the specified key in bindmap 0");
1632 	Cmd_AddCommand ("bindlist", Key_BindList_f, "bindlist: displays bound keys for bindmap 0 bindmaps");
1633 	Cmd_AddCommand ("unbindall", Key_Unbindall_f, "removes all commands from all keys in all bindmaps (leaving only shift-escape and escape)");
1634 
1635 	Cmd_AddCommand ("history", Key_History_f, "prints the history of executed commands (history X prints the last X entries, history -c clears the whole history)");
1636 
1637 	Cvar_RegisterVariable (&con_closeontoggleconsole);
1638 }
1639 
1640 void
Key_Shutdown(void)1641 Key_Shutdown (void)
1642 {
1643 	Key_History_Shutdown();
1644 }
1645 
Key_GetBind(int key,int bindmap)1646 const char *Key_GetBind (int key, int bindmap)
1647 {
1648 	const char *bind;
1649 	if (key < 0 || key >= MAX_KEYS)
1650 		return NULL;
1651 	if(bindmap >= MAX_BINDMAPS)
1652 		return NULL;
1653 	if(bindmap >= 0)
1654 	{
1655 		bind = keybindings[bindmap][key];
1656 	}
1657 	else
1658 	{
1659 		bind = keybindings[key_bmap][key];
1660 		if (!bind)
1661 			bind = keybindings[key_bmap2][key];
1662 	}
1663 	return bind;
1664 }
1665 
Key_FindKeysForCommand(const char * command,int * keys,int numkeys,int bindmap)1666 void Key_FindKeysForCommand (const char *command, int *keys, int numkeys, int bindmap)
1667 {
1668 	int		count;
1669 	int		j;
1670 	const char	*b;
1671 
1672 	for (j = 0;j < numkeys;j++)
1673 		keys[j] = -1;
1674 
1675 	if(bindmap >= MAX_BINDMAPS)
1676 		return;
1677 
1678 	count = 0;
1679 
1680 	for (j = 0; j < MAX_KEYS; ++j)
1681 	{
1682 		b = Key_GetBind(j, bindmap);
1683 		if (!b)
1684 			continue;
1685 		if (!strcmp (b, command) )
1686 		{
1687 			keys[count++] = j;
1688 			if (count == numkeys)
1689 				break;
1690 		}
1691 	}
1692 }
1693 
1694 /*
1695 ===================
1696 Called by the system between frames for both key up and key down events
1697 Should NOT be called during an interrupt!
1698 ===================
1699 */
1700 static char tbl_keyascii[MAX_KEYS];
1701 static keydest_t tbl_keydest[MAX_KEYS];
1702 
1703 typedef struct eventqueueitem_s
1704 {
1705 	int key;
1706 	int ascii;
1707 	qboolean down;
1708 }
1709 eventqueueitem_t;
1710 static int events_blocked = 0;
1711 static eventqueueitem_t eventqueue[32];
1712 static unsigned eventqueue_idx = 0;
1713 
Key_EventQueue_Add(int key,int ascii,qboolean down)1714 static void Key_EventQueue_Add(int key, int ascii, qboolean down)
1715 {
1716 	if(eventqueue_idx < sizeof(eventqueue) / sizeof(*eventqueue))
1717 	{
1718 		eventqueue[eventqueue_idx].key = key;
1719 		eventqueue[eventqueue_idx].ascii = ascii;
1720 		eventqueue[eventqueue_idx].down = down;
1721 		++eventqueue_idx;
1722 	}
1723 }
1724 
Key_EventQueue_Block(void)1725 void Key_EventQueue_Block(void)
1726 {
1727 	// block key events until call to Unblock
1728 	events_blocked = true;
1729 }
1730 
Key_EventQueue_Unblock(void)1731 void Key_EventQueue_Unblock(void)
1732 {
1733 	// unblocks key events again
1734 	unsigned i;
1735 	events_blocked = false;
1736 	for(i = 0; i < eventqueue_idx; ++i)
1737 		Key_Event(eventqueue[i].key, eventqueue[i].ascii, eventqueue[i].down);
1738 	eventqueue_idx = 0;
1739 }
1740 
1741 void
Key_Event(int key,int ascii,qboolean down)1742 Key_Event (int key, int ascii, qboolean down)
1743 {
1744 	const char *bind;
1745 	qboolean q;
1746 	keydest_t keydest = key_dest;
1747 	char vabuf[1024];
1748 
1749 	if (key < 0 || key >= MAX_KEYS)
1750 		return;
1751 
1752 	if(events_blocked)
1753 	{
1754 		Key_EventQueue_Add(key, ascii, down);
1755 		return;
1756 	}
1757 
1758 	// get key binding
1759 	bind = keybindings[key_bmap][key];
1760 	if (!bind)
1761 		bind = keybindings[key_bmap2][key];
1762 
1763 	if (developer_insane.integer)
1764 		Con_DPrintf("Key_Event(%i, '%c', %s) keydown %i bind \"%s\"\n", key, ascii ? ascii : '?', down ? "down" : "up", keydown[key], bind ? bind : "");
1765 
1766 	if(key_consoleactive)
1767 		keydest = key_console;
1768 
1769 	if (down)
1770 	{
1771 		// increment key repeat count each time a down is received so that things
1772 		// which want to ignore key repeat can ignore it
1773 		keydown[key] = min(keydown[key] + 1, 2);
1774 		if(keydown[key] == 1) {
1775 			tbl_keyascii[key] = ascii;
1776 			tbl_keydest[key] = keydest;
1777 		} else {
1778 			ascii = tbl_keyascii[key];
1779 			keydest = tbl_keydest[key];
1780 		}
1781 	}
1782 	else
1783 	{
1784 		// clear repeat count now that the key is released
1785 		keydown[key] = 0;
1786 		keydest = tbl_keydest[key];
1787 		ascii = tbl_keyascii[key];
1788 	}
1789 
1790 	if(keydest == key_void)
1791 		return;
1792 
1793 	// key_consoleactive is a flag not a key_dest because the console is a
1794 	// high priority overlay ontop of the normal screen (designed as a safety
1795 	// feature so that developers and users can rescue themselves from a bad
1796 	// situation).
1797 	//
1798 	// this also means that toggling the console on/off does not lose the old
1799 	// key_dest state
1800 
1801 	// specially handle escape (togglemenu) and shift-escape (toggleconsole)
1802 	// engine bindings, these are not handled as normal binds so that the user
1803 	// can recover from a completely empty bindmap
1804 	if (key == K_ESCAPE)
1805 	{
1806 		// ignore key repeats on escape
1807 		if (keydown[key] > 1)
1808 			return;
1809 
1810 		// escape does these things:
1811 		// key_consoleactive - close console
1812 		// key_message - abort messagemode
1813 		// key_menu - go to parent menu (or key_game)
1814 		// key_game - open menu
1815 
1816 		// in all modes shift-escape toggles console
1817 		if (keydown[K_SHIFT])
1818 		{
1819 			if(down)
1820 			{
1821 				Con_ToggleConsole_f ();
1822 				tbl_keydest[key] = key_void; // esc release should go nowhere (especially not to key_menu or key_game)
1823 			}
1824 			return;
1825 		}
1826 
1827 		switch (keydest)
1828 		{
1829 			case key_console:
1830 				if(down)
1831 				{
1832 					if(key_consoleactive & KEY_CONSOLEACTIVE_FORCED)
1833 					{
1834 						key_consoleactive &= ~KEY_CONSOLEACTIVE_USER;
1835 #ifdef CONFIG_MENU
1836 						MR_ToggleMenu(1);
1837 #endif
1838 					}
1839 					else
1840 						Con_ToggleConsole_f();
1841 				}
1842 				break;
1843 
1844 			case key_message:
1845 				if (down)
1846 					Key_Message (key, ascii); // that'll close the message input
1847 				break;
1848 
1849 			case key_menu:
1850 			case key_menu_grabbed:
1851 #ifdef CONFIG_MENU
1852 				MR_KeyEvent (key, ascii, down);
1853 #endif
1854 				break;
1855 
1856 			case key_game:
1857 				// csqc has priority over toggle menu if it wants to (e.g. handling escape for UI stuff in-game.. :sick:)
1858 				q = CL_VM_InputEvent(down ? 0 : 1, key, ascii);
1859 #ifdef CONFIG_MENU
1860 				if (!q && down)
1861 					MR_ToggleMenu(1);
1862 #endif
1863 				break;
1864 
1865 			default:
1866 				Con_Printf ("Key_Event: Bad key_dest\n");
1867 		}
1868 		return;
1869 	}
1870 
1871 	// send function keydowns to interpreter no matter what mode is (unless the menu has specifically grabbed the keyboard, for rebinding keys)
1872 	// VorteX: Omnicide does bind F* keys
1873 	if (keydest != key_menu_grabbed)
1874 	if (key >= K_F1 && key <= K_F12 && gamemode != GAME_BLOODOMNICIDE)
1875 	{
1876 		if (bind)
1877 		{
1878 			if(keydown[key] == 1 && down)
1879 			{
1880 				// button commands add keynum as a parm
1881 				if (bind[0] == '+')
1882 					Cbuf_AddText (va(vabuf, sizeof(vabuf), "%s %i\n", bind, key));
1883 				else
1884 				{
1885 					Cbuf_AddText (bind);
1886 					Cbuf_AddText ("\n");
1887 				}
1888 			} else if(bind[0] == '+' && !down && keydown[key] == 0)
1889 				Cbuf_AddText(va(vabuf, sizeof(vabuf), "-%s %i\n", bind + 1, key));
1890 		}
1891 		return;
1892 	}
1893 
1894 	// send input to console if it wants it
1895 	if (keydest == key_console)
1896 	{
1897 		if (!down)
1898 			return;
1899 		// con_closeontoggleconsole enables toggleconsole keys to close the
1900 		// console, as long as they are not the color prefix character
1901 		// (special exemption for german keyboard layouts)
1902 		if (con_closeontoggleconsole.integer && bind && !strncmp(bind, "toggleconsole", strlen("toggleconsole")) && (key_consoleactive & KEY_CONSOLEACTIVE_USER) && (con_closeontoggleconsole.integer >= ((ascii != STRING_COLOR_TAG) ? 2 : 3) || key_linepos == 1))
1903 		{
1904 			Con_ToggleConsole_f ();
1905 			return;
1906 		}
1907 
1908 		if (COM_CheckParm ("-noconsole"))
1909 			return; // only allow the key bind to turn off console
1910 
1911 		Key_Console (key, ascii);
1912 		return;
1913 	}
1914 
1915 	// handle toggleconsole in menu too
1916 	if (keydest == key_menu)
1917 	{
1918 		if (down && con_closeontoggleconsole.integer && bind && !strncmp(bind, "toggleconsole", strlen("toggleconsole")) && ascii != STRING_COLOR_TAG)
1919 		{
1920 			Cbuf_AddText("toggleconsole\n");  // Deferred to next frame so we're not sending the text event to the console.
1921 			tbl_keydest[key] = key_void; // key release should go nowhere (especially not to key_menu or key_game)
1922 			return;
1923 		}
1924 	}
1925 
1926 	// ignore binds while a video is played, let the video system handle the key event
1927 	if (cl_videoplaying)
1928 	{
1929 		if (gamemode == GAME_BLOODOMNICIDE) // menu controls key events
1930 #ifdef CONFIG_MENU
1931 			MR_KeyEvent(key, ascii, down);
1932 #else
1933 			{
1934 			}
1935 #endif
1936 		else
1937 			CL_Video_KeyEvent (key, ascii, keydown[key] != 0);
1938 		return;
1939 	}
1940 
1941 	// anything else is a key press into the game, chat line, or menu
1942 	switch (keydest)
1943 	{
1944 		case key_message:
1945 			if (down)
1946 				Key_Message (key, ascii);
1947 			break;
1948 		case key_menu:
1949 		case key_menu_grabbed:
1950 #ifdef CONFIG_MENU
1951 			MR_KeyEvent (key, ascii, down);
1952 #endif
1953 			break;
1954 		case key_game:
1955 			q = CL_VM_InputEvent(down ? 0 : 1, key, ascii);
1956 			// ignore key repeats on binds and only send the bind if the event hasnt been already processed by csqc
1957 			if (!q && bind)
1958 			{
1959 				if(keydown[key] == 1 && down)
1960 				{
1961 					// button commands add keynum as a parm
1962 					if (bind[0] == '+')
1963 						Cbuf_AddText (va(vabuf, sizeof(vabuf), "%s %i\n", bind, key));
1964 					else
1965 					{
1966 						Cbuf_AddText (bind);
1967 						Cbuf_AddText ("\n");
1968 					}
1969 				} else if(bind[0] == '+' && !down && keydown[key] == 0)
1970 					Cbuf_AddText(va(vabuf, sizeof(vabuf), "-%s %i\n", bind + 1, key));
1971 			}
1972 			break;
1973 		default:
1974 			Con_Printf ("Key_Event: Bad key_dest\n");
1975 	}
1976 }
1977 
1978 // a helper to simulate release of ALL keys
1979 void
Key_ReleaseAll(void)1980 Key_ReleaseAll (void)
1981 {
1982 	int key;
1983 	// clear the event queue first
1984 	eventqueue_idx = 0;
1985 	// then send all down events (possibly into the event queue)
1986 	for(key = 0; key < MAX_KEYS; ++key)
1987 		if(keydown[key])
1988 			Key_Event(key, 0, false);
1989 	// now all keys are guaranteed down (once the event queue is unblocked)
1990 	// and only future events count
1991 }
1992 
1993 /*
1994 ===================
1995 Key_ClearStates
1996 ===================
1997 */
1998 void
Key_ClearStates(void)1999 Key_ClearStates (void)
2000 {
2001 	memset(keydown, 0, sizeof(keydown));
2002 }
2003