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