]> icculus.org git repositories - divverent/darkplaces.git/blob - keys.c
u8_Init, cvar utf8_disabled
[divverent/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
27 cvar_t con_closeontoggleconsole = {CVAR_SAVE, "con_closeontoggleconsole","1", "allows toggleconsole binds to close the console as well"};
28
29 /*
30 key up events are sent even if in console mode
31 */
32
33 char            key_line[MAX_INPUTLINE];
34 int                     key_linepos;
35 qboolean        key_insert = true;      // insert key toggle (for editing)
36 keydest_t       key_dest;
37 int                     key_consoleactive;
38 char            *keybindings[MAX_BINDMAPS][MAX_KEYS];
39 int         history_line;
40 char            history_savedline[MAX_INPUTLINE];
41 conbuffer_t history;
42 #define HIST_TEXTSIZE 262144
43 #define HIST_MAXLINES 4096
44
45 extern cvar_t   con_textsize;
46
47
48 static void Key_History_Init(void)
49 {
50         qfile_t *historyfile;
51         ConBuffer_Init(&history, HIST_TEXTSIZE, HIST_MAXLINES, zonemempool);
52
53         historyfile = FS_OpenRealFile("darkplaces_history.txt", "rb", false); // rb to handle unix line endings on windows too
54         if(historyfile)
55         {
56                 char buf[MAX_INPUTLINE];
57                 int bufpos;
58                 int c;
59
60                 bufpos = 0;
61                 for(;;)
62                 {
63                         c = FS_Getc(historyfile);
64                         if(c < 0 || c == 0 || c == '\r' || c == '\n')
65                         {
66                                 if(bufpos > 0)
67                                 {
68                                         buf[bufpos] = 0;
69                                         ConBuffer_AddLine(&history, buf, bufpos, 0);
70                                         bufpos = 0;
71                                 }
72                                 if(c < 0)
73                                         break;
74                         }
75                         else
76                         {
77                                 if(bufpos < MAX_INPUTLINE - 1)
78                                         buf[bufpos++] = c;
79                         }
80                 }
81
82                 FS_Close(historyfile);
83         }
84
85         history_line = -1;
86 }
87
88 static void Key_History_Shutdown(void)
89 {
90         // TODO write history to a file
91
92         qfile_t *historyfile = FS_OpenRealFile("darkplaces_history.txt", "w", false);
93         if(historyfile)
94         {
95                 int i;
96                 for(i = 0; i < CONBUFFER_LINES_COUNT(&history); ++i)
97                         FS_Printf(historyfile, "%s\n", ConBuffer_GetLine(&history, i));
98                 FS_Close(historyfile);
99         }
100
101         ConBuffer_Shutdown(&history);
102 }
103
104 static void Key_History_Push(void)
105 {
106         if(key_line[1]) // empty?
107         if(strcmp(key_line, "]quit")) // putting these into the history just sucks
108         if(strncmp(key_line, "]quit ", 6)) // putting these into the history just sucks
109                 ConBuffer_AddLine(&history, key_line + 1, strlen(key_line) - 1, 0);
110         Con_Printf("%s\n", key_line); // don't mark empty lines as history
111         history_line = -1;
112 }
113
114 static void Key_History_Up(void)
115 {
116         if(history_line == -1) // editing the "new" line
117                 strlcpy(history_savedline, key_line + 1, sizeof(history_savedline));
118
119         if(history_line == -1)
120         {
121                 history_line = CONBUFFER_LINES_COUNT(&history) - 1;
122                 if(history_line != -1)
123                 {
124                         strlcpy(key_line + 1, ConBuffer_GetLine(&history, history_line), sizeof(key_line) - 1);
125                         key_linepos = strlen(key_line);
126                 }
127         }
128         else if(history_line > 0)
129         {
130                 --history_line; // this also does -1 -> 0, so it is good
131                 strlcpy(key_line + 1, ConBuffer_GetLine(&history, history_line), sizeof(key_line) - 1);
132                 key_linepos = strlen(key_line);
133         }
134 }
135
136 static void Key_History_Down(void)
137 {
138         if(history_line == -1) // editing the "new" line
139                 return;
140
141         if(history_line < CONBUFFER_LINES_COUNT(&history) - 1)
142         {
143                 ++history_line;
144                 strlcpy(key_line + 1, ConBuffer_GetLine(&history, history_line), sizeof(key_line) - 1);
145         }
146         else
147         {
148                 history_line = -1;
149                 strlcpy(key_line + 1, history_savedline, sizeof(key_line) - 1);
150         }
151
152         key_linepos = strlen(key_line);
153 }
154
155 static int      key_bmap, key_bmap2;
156 static unsigned char keydown[MAX_KEYS]; // 0 = up, 1 = down, 2 = repeating
157
158 typedef struct keyname_s
159 {
160         const char      *name;
161         int                     keynum;
162 }
163 keyname_t;
164
165 static const keyname_t   keynames[] = {
166         {"TAB", K_TAB},
167         {"ENTER", K_ENTER},
168         {"ESCAPE", K_ESCAPE},
169         {"SPACE", K_SPACE},
170
171         // spacer so it lines up with keys.h
172
173         {"BACKSPACE", K_BACKSPACE},
174         {"UPARROW", K_UPARROW},
175         {"DOWNARROW", K_DOWNARROW},
176         {"LEFTARROW", K_LEFTARROW},
177         {"RIGHTARROW", K_RIGHTARROW},
178
179         {"ALT", K_ALT},
180         {"CTRL", K_CTRL},
181         {"SHIFT", K_SHIFT},
182
183         {"F1", K_F1},
184         {"F2", K_F2},
185         {"F3", K_F3},
186         {"F4", K_F4},
187         {"F5", K_F5},
188         {"F6", K_F6},
189         {"F7", K_F7},
190         {"F8", K_F8},
191         {"F9", K_F9},
192         {"F10", K_F10},
193         {"F11", K_F11},
194         {"F12", K_F12},
195
196         {"INS", K_INS},
197         {"DEL", K_DEL},
198         {"PGDN", K_PGDN},
199         {"PGUP", K_PGUP},
200         {"HOME", K_HOME},
201         {"END", K_END},
202
203         {"PAUSE", K_PAUSE},
204
205         {"NUMLOCK", K_NUMLOCK},
206         {"CAPSLOCK", K_CAPSLOCK},
207         {"SCROLLOCK", K_SCROLLOCK},
208
209         {"KP_INS",                      K_KP_INS },
210         {"KP_0", K_KP_0},
211         {"KP_END",                      K_KP_END },
212         {"KP_1", K_KP_1},
213         {"KP_DOWNARROW",        K_KP_DOWNARROW },
214         {"KP_2", K_KP_2},
215         {"KP_PGDN",                     K_KP_PGDN },
216         {"KP_3", K_KP_3},
217         {"KP_LEFTARROW",        K_KP_LEFTARROW },
218         {"KP_4", K_KP_4},
219         {"KP_5", K_KP_5},
220         {"KP_RIGHTARROW",       K_KP_RIGHTARROW },
221         {"KP_6", K_KP_6},
222         {"KP_HOME",                     K_KP_HOME },
223         {"KP_7", K_KP_7},
224         {"KP_UPARROW",          K_KP_UPARROW },
225         {"KP_8", K_KP_8},
226         {"KP_PGUP",                     K_KP_PGUP },
227         {"KP_9", K_KP_9},
228         {"KP_DEL",                      K_KP_DEL },
229         {"KP_PERIOD", K_KP_PERIOD},
230         {"KP_SLASH",            K_KP_SLASH },
231         {"KP_DIVIDE", K_KP_DIVIDE},
232         {"KP_MULTIPLY", K_KP_MULTIPLY},
233         {"KP_MINUS", K_KP_MINUS},
234         {"KP_PLUS", K_KP_PLUS},
235         {"KP_ENTER", K_KP_ENTER},
236         {"KP_EQUALS", K_KP_EQUALS},
237
238
239
240         {"MOUSE1", K_MOUSE1},
241
242         {"MOUSE2", K_MOUSE2},
243         {"MOUSE3", K_MOUSE3},
244         {"MWHEELUP", K_MWHEELUP},
245         {"MWHEELDOWN", K_MWHEELDOWN},
246         {"MOUSE4", K_MOUSE4},
247         {"MOUSE5", K_MOUSE5},
248         {"MOUSE6", K_MOUSE6},
249         {"MOUSE7", K_MOUSE7},
250         {"MOUSE8", K_MOUSE8},
251         {"MOUSE9", K_MOUSE9},
252         {"MOUSE10", K_MOUSE10},
253         {"MOUSE11", K_MOUSE11},
254         {"MOUSE12", K_MOUSE12},
255         {"MOUSE13", K_MOUSE13},
256         {"MOUSE14", K_MOUSE14},
257         {"MOUSE15", K_MOUSE15},
258         {"MOUSE16", K_MOUSE16},
259
260
261
262
263         {"JOY1",  K_JOY1},
264         {"JOY2",  K_JOY2},
265         {"JOY3",  K_JOY3},
266         {"JOY4",  K_JOY4},
267         {"JOY5",  K_JOY5},
268         {"JOY6",  K_JOY6},
269         {"JOY7",  K_JOY7},
270         {"JOY8",  K_JOY8},
271         {"JOY9",  K_JOY9},
272         {"JOY10", K_JOY10},
273         {"JOY11", K_JOY11},
274         {"JOY12", K_JOY12},
275         {"JOY13", K_JOY13},
276         {"JOY14", K_JOY14},
277         {"JOY15", K_JOY15},
278         {"JOY16", K_JOY16},
279
280
281
282
283
284
285         {"AUX1", K_AUX1},
286         {"AUX2", K_AUX2},
287         {"AUX3", K_AUX3},
288         {"AUX4", K_AUX4},
289         {"AUX5", K_AUX5},
290         {"AUX6", K_AUX6},
291         {"AUX7", K_AUX7},
292         {"AUX8", K_AUX8},
293         {"AUX9", K_AUX9},
294         {"AUX10", K_AUX10},
295         {"AUX11", K_AUX11},
296         {"AUX12", K_AUX12},
297         {"AUX13", K_AUX13},
298         {"AUX14", K_AUX14},
299         {"AUX15", K_AUX15},
300         {"AUX16", K_AUX16},
301         {"AUX17", K_AUX17},
302         {"AUX18", K_AUX18},
303         {"AUX19", K_AUX19},
304         {"AUX20", K_AUX20},
305         {"AUX21", K_AUX21},
306         {"AUX22", K_AUX22},
307         {"AUX23", K_AUX23},
308         {"AUX24", K_AUX24},
309         {"AUX25", K_AUX25},
310         {"AUX26", K_AUX26},
311         {"AUX27", K_AUX27},
312         {"AUX28", K_AUX28},
313         {"AUX29", K_AUX29},
314         {"AUX30", K_AUX30},
315         {"AUX31", K_AUX31},
316         {"AUX32", K_AUX32},
317
318         {"SEMICOLON", ';'},                     // because a raw semicolon separates commands
319         {"TILDE", '~'},
320         {"BACKQUOTE", '`'},
321         {"QUOTE", '"'},
322         {"APOSTROPHE", '\''},
323         {"BACKSLASH", '\\'},            // because a raw backslash is used for special characters
324
325         {NULL, 0}
326 };
327
328 /*
329 ==============================================================================
330
331                         LINE TYPING INTO THE CONSOLE
332
333 ==============================================================================
334 */
335
336 void
337 Key_ClearEditLine (int edit_line)
338 {
339         memset (key_line, '\0', sizeof(key_line));
340         key_line[0] = ']';
341         key_linepos = 1;
342 }
343
344 /*
345 ====================
346 Interactive line editing and console scrollback
347 ====================
348 */
349 static void
350 Key_Console (int key, int unicode)
351 {
352         // LordHavoc: copied most of this from Q2 to improve keyboard handling
353         switch (key)
354         {
355         case K_KP_SLASH:
356                 key = '/';
357                 break;
358         case K_KP_MINUS:
359                 key = '-';
360                 break;
361         case K_KP_PLUS:
362                 key = '+';
363                 break;
364         case K_KP_HOME:
365                 key = '7';
366                 break;
367         case K_KP_UPARROW:
368                 key = '8';
369                 break;
370         case K_KP_PGUP:
371                 key = '9';
372                 break;
373         case K_KP_LEFTARROW:
374                 key = '4';
375                 break;
376         case K_KP_5:
377                 key = '5';
378                 break;
379         case K_KP_RIGHTARROW:
380                 key = '6';
381                 break;
382         case K_KP_END:
383                 key = '1';
384                 break;
385         case K_KP_DOWNARROW:
386                 key = '2';
387                 break;
388         case K_KP_PGDN:
389                 key = '3';
390                 break;
391         case K_KP_INS:
392                 key = '0';
393                 break;
394         case K_KP_DEL:
395                 key = '.';
396                 break;
397         }
398
399         if ((key == 'v' && keydown[K_CTRL]) || ((key == K_INS || key == K_KP_INS) && keydown[K_SHIFT]))
400         {
401                 char *cbd, *p;
402                 if ((cbd = Sys_GetClipboardData()) != 0)
403                 {
404                         int i;
405 #if 1
406                         p = cbd;
407                         while (*p)
408                         {
409                                 if (*p == '\r' && *(p+1) == '\n')
410                                 {
411                                         *p++ = ';';
412                                         *p++ = ' ';
413                                 }
414                                 else if (*p == '\n' || *p == '\r' || *p == '\b')
415                                         *p++ = ';';
416                                 p++;
417                         }
418 #else
419                         strtok(cbd, "\n\r\b");
420 #endif
421                         i = (int)strlen(cbd);
422                         if (i + key_linepos >= MAX_INPUTLINE)
423                                 i= MAX_INPUTLINE - key_linepos - 1;
424                         if (i > 0)
425                         {
426                                 // terencehill: insert the clipboard text between the characters of the line
427                                 /*
428                                 char *temp = (char *) Z_Malloc(MAX_INPUTLINE);
429                                 cbd[i]=0;
430                                 temp[0]=0;
431                                 if ( key_linepos < (int)strlen(key_line) )
432                                         strlcpy(temp, key_line + key_linepos, (int)strlen(key_line) - key_linepos +1);
433                                 key_line[key_linepos] = 0;
434                                 strlcat(key_line, cbd, sizeof(key_line));
435                                 if (temp[0])
436                                         strlcat(key_line, temp, sizeof(key_line));
437                                 Z_Free(temp);
438                                 key_linepos += i;
439                                 */
440                                 // blub: I'm changing this to use memmove() like the rest of the code does.
441                                 cbd[i] = 0;
442                                 memmove(key_line + key_linepos + i, key_line + key_linepos, sizeof(key_line) - key_linepos - i);
443                                 memcpy(key_line + key_linepos, cbd, i);
444                                 key_linepos += i;
445                         }
446                         Z_Free(cbd);
447                 }
448                 return;
449         }
450
451         if (key == 'l' && keydown[K_CTRL])
452         {
453                 Cbuf_AddText ("clear\n");
454                 return;
455         }
456
457         if (key == 'u' && keydown[K_CTRL]) // like vi/readline ^u: delete currently edited line
458         {
459                 // clear line
460                 key_line[0] = ']';
461                 key_line[1] = 0;
462                 key_linepos = 1;
463                 return;
464         }
465
466         if (key == 'q' && keydown[K_CTRL]) // like zsh ^q: push line to history, don't execute, and clear
467         {
468                 // clear line
469                 Key_History_Push();
470                 key_line[0] = ']';
471                 key_line[1] = 0;
472                 key_linepos = 1;
473                 return;
474         }
475
476         if (key == K_ENTER || key == K_KP_ENTER)
477         {
478                 Cbuf_AddText (key_line+1);      // skip the ]
479                 Cbuf_AddText ("\n");
480                 Key_History_Push();
481                 key_line[0] = ']';
482                 key_line[1] = 0;        // EvilTypeGuy: null terminate
483                 key_linepos = 1;
484                 // force an update, because the command may take some time
485                 if (cls.state == ca_disconnected)
486                         CL_UpdateScreen ();
487                 return;
488         }
489
490         if (key == K_TAB)
491         {
492                 if(keydown[K_CTRL]) // append to the cvar its value
493                 {
494                         int             cvar_len, cvar_str_len, chars_to_move;
495                         char    k;
496                         char    cvar[MAX_INPUTLINE];
497                         const char *cvar_str;
498                         
499                         // go to the start of the variable
500                         while(--key_linepos)
501                         {
502                                 k = key_line[key_linepos];
503                                 if(k == '\"' || k == ';' || k == ' ' || k == '\'')
504                                         break;
505                         }
506                         key_linepos++;
507                         
508                         // save the variable name in cvar
509                         for(cvar_len=0; (k = key_line[key_linepos + cvar_len]) != 0; cvar_len++)
510                         {
511                                 if(k == '\"' || k == ';' || k == ' ' || k == '\'')
512                                         break;
513                                 cvar[cvar_len] = k;
514                         }
515                         if (cvar_len==0)
516                                 return;
517                         cvar[cvar_len] = 0;
518                         
519                         // go to the end of the cvar
520                         key_linepos += cvar_len;
521                         
522                         // save the content of the variable in cvar_str
523                         cvar_str = Cvar_VariableString(cvar);
524                         cvar_str_len = strlen(cvar_str);
525                         if (cvar_str_len==0)
526                                 return;
527                         
528                         // insert space and cvar_str in key_line
529                         chars_to_move = strlen(&key_line[key_linepos]);
530                         if (key_linepos + 1 + cvar_str_len + chars_to_move < MAX_INPUTLINE)
531                         {
532                                 if (chars_to_move)
533                                         memmove(&key_line[key_linepos + 1 + cvar_str_len], &key_line[key_linepos], chars_to_move);
534                                 key_line[key_linepos++] = ' ';
535                                 memcpy(&key_line[key_linepos], cvar_str, cvar_str_len);
536                                 key_linepos += cvar_str_len;
537                                 key_line[key_linepos + chars_to_move] = 0;
538                         }
539                         else
540                                 Con_Printf("Couldn't append cvar value, edit line too long.\n");
541                         return;
542                 }
543                 // Enhanced command completion
544                 // by EvilTypeGuy eviltypeguy@qeradiant.com
545                 // Thanks to Fett, Taniwha
546                 Con_CompleteCommandLine();
547                 return;
548         }
549
550         // Advanced Console Editing by Radix radix@planetquake.com
551         // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
552         // Enhanced by [515]
553         // Enhanced by terencehill
554
555         // move cursor to the previous character
556         if (key == K_LEFTARROW || key == K_KP_LEFTARROW)
557         {
558                 if (key_linepos < 2)
559                         return;
560                 if(keydown[K_CTRL]) // move cursor to the previous word
561                 {
562                         int             pos;
563                         char    k;
564                         pos = key_linepos-1;
565
566                         if(pos) // skip all "; ' after the word
567                                 while(--pos)
568                                 {
569                                         k = key_line[pos];
570                                         if (!(k == '\"' || k == ';' || k == ' ' || k == '\''))
571                                                 break;
572                                 }
573
574                         if(pos)
575                                 while(--pos)
576                                 {
577                                         k = key_line[pos];
578                                         if(k == '\"' || k == ';' || k == ' ' || k == '\'')
579                                                 break;
580                                 }
581                         key_linepos = pos + 1;
582                 }
583                 else if(keydown[K_SHIFT]) // move cursor to the previous character ignoring colors
584                 {
585                         int             pos;
586                         size_t          inchar;
587                         pos = u8_prevbyte(key_line, key_linepos);
588                         while (pos)
589                                 if(pos-1 > 0 && key_line[pos-1] == STRING_COLOR_TAG && isdigit(key_line[pos]))
590                                         pos-=2;
591                                 else if(pos-4 > 0 && key_line[pos-4] == STRING_COLOR_TAG && key_line[pos-3] == STRING_COLOR_RGB_TAG_CHAR
592                                                 && isxdigit(key_line[pos-2]) && isxdigit(key_line[pos-1]) && isxdigit(key_line[pos]))
593                                         pos-=5;
594                                 else
595                                 {
596                                         if(pos-1 > 0 && key_line[pos-1] == STRING_COLOR_TAG && key_line[pos] == STRING_COLOR_TAG) // consider ^^ as a character
597                                                 pos--;
598                                         pos--;
599                                         break;
600                                 }
601                         // we need to move to the beginning of the character when in a wide character:
602                         u8_charidx(key_line, pos + 1, &inchar);
603                         key_linepos = pos + 1 - inchar;
604                 }
605                 else
606                 {
607                         key_linepos = u8_prevbyte(key_line, key_linepos);
608                 }
609                 return;
610         }
611
612         // delete char before cursor
613         if (key == K_BACKSPACE || (key == 'h' && keydown[K_CTRL]))
614         {
615                 if (key_linepos > 1)
616                 {
617                         int newpos = u8_prevbyte(key_line, key_linepos);
618                         strlcpy(key_line + newpos, key_line + key_linepos, sizeof(key_line) + 1 - key_linepos);
619                         key_linepos = newpos;
620                 }
621                 return;
622         }
623
624         // delete char on cursor
625         if (key == K_DEL || key == K_KP_DEL)
626         {
627                 size_t linelen;
628                 linelen = strlen(key_line);
629                 if (key_linepos < (int)linelen)
630                         memmove(key_line + key_linepos, key_line + key_linepos + u8_bytelen(key_line + key_linepos, 1), linelen - key_linepos);
631                 return;
632         }
633
634
635         // move cursor to the next character
636         if (key == K_RIGHTARROW || key == K_KP_RIGHTARROW)
637         {
638                 if (key_linepos >= (int)strlen(key_line))
639                         return;
640                 if(keydown[K_CTRL]) // move cursor to the next word
641                 {
642                         int             pos, len;
643                         char    k;
644                         len = (int)strlen(key_line);
645                         pos = key_linepos;
646
647                         while(++pos < len)
648                         {
649                                 k = key_line[pos];
650                                 if(k == '\"' || k == ';' || k == ' ' || k == '\'')
651                                         break;
652                         }
653                         
654                         if (pos < len) // skip all "; ' after the word
655                                 while(++pos < len)
656                                 {
657                                         k = key_line[pos];
658                                         if (!(k == '\"' || k == ';' || k == ' ' || k == '\''))
659                                                 break;
660                                 }
661                         key_linepos = pos;
662                 }
663                 else if(keydown[K_SHIFT]) // move cursor to the next character ignoring colors
664                 {
665                         int             pos, len;
666                         len = (int)strlen(key_line);
667                         pos = key_linepos;
668                         
669                         // go beyond all initial consecutive color tags, if any
670                         if(pos < len)
671                                 while (key_line[pos] == STRING_COLOR_TAG)
672                                 {
673                                         if(isdigit(key_line[pos+1]))
674                                                 pos+=2;
675                                         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]))
676                                                 pos+=5;
677                                         else
678                                                 break;
679                                 }
680                         
681                         // skip the char
682                         if (key_line[pos] == STRING_COLOR_TAG && key_line[pos+1] == STRING_COLOR_TAG) // consider ^^ as a character
683                                 pos++;
684                         pos += u8_bytelen(key_line + pos, 1);
685                         
686                         // now go beyond all next consecutive color tags, if any
687                         if(pos < len)
688                                 while (key_line[pos] == STRING_COLOR_TAG)
689                                 {
690                                         if(isdigit(key_line[pos+1]))
691                                                 pos+=2;
692                                         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]))
693                                                 pos+=5;
694                                         else
695                                                 break;
696                                 }
697                         key_linepos = pos;
698                 }
699                 else
700                         key_linepos += u8_bytelen(key_line + key_linepos, 1);
701                 return;
702         }
703
704         if (key == K_INS || key == K_KP_INS) // toggle insert mode
705         {
706                 key_insert ^= 1;
707                 return;
708         }
709
710         // End Advanced Console Editing
711
712         if (key == K_UPARROW || key == K_KP_UPARROW || (key == 'p' && keydown[K_CTRL]))
713         {
714                 Key_History_Up();
715                 return;
716         }
717
718         if (key == K_DOWNARROW || key == K_KP_DOWNARROW || (key == 'n' && keydown[K_CTRL]))
719         {
720                 Key_History_Down();
721                 return;
722         }
723         // ~1.0795 = 82/76  using con_textsize 64 76 is height of the char, 6 is the distance between 2 lines
724         if (key == K_PGUP || key == K_KP_PGUP)
725         {
726                 if(keydown[K_CTRL])
727                 {
728                         con_backscroll += (int)floor((vid_conheight.integer >> 2) / con_textsize.integer)-1;
729                 }
730                 else
731                         con_backscroll += (int)floor((vid_conheight.integer >> 1) / con_textsize.integer)-3;
732                 return;
733         }
734
735         if (key == K_PGDN || key == K_KP_PGDN)
736         {
737                 if(keydown[K_CTRL])
738                 {
739                         con_backscroll -= (int)floor((vid_conheight.integer >> 2) / con_textsize.integer)-1;
740                 }
741                 else
742                         con_backscroll -= (int)floor((vid_conheight.integer >> 1) / con_textsize.integer)-3;
743                 return;
744         }
745  
746         if (key == K_MWHEELUP)
747         {
748                 if(keydown[K_CTRL])
749                         con_backscroll += 1;
750                 else if(keydown[K_SHIFT])
751                         con_backscroll += (int)floor((vid_conheight.integer >> 2) / con_textsize.integer)-1;
752                 else
753                         con_backscroll += 5;
754                 return;
755         }
756
757         if (key == K_MWHEELDOWN)
758         {
759                 if(keydown[K_CTRL])
760                         con_backscroll -= 1;
761                 else if(keydown[K_SHIFT])
762                         con_backscroll -= (int)floor((vid_conheight.integer >> 2) / con_textsize.integer)-1;
763                 else
764                         con_backscroll -= 5;
765                 return;
766         }
767
768         if (keydown[K_CTRL])
769         {
770                 // text zoom in
771                 if (key == '+' || key == K_KP_PLUS)
772                 {
773                         if (con_textsize.integer < 128)
774                                 Cvar_SetValueQuick(&con_textsize, con_textsize.integer + 1);
775                         return;
776                 }
777                 // text zoom out
778                 if (key == '-' || key == K_KP_MINUS)
779                 {
780                         if (con_textsize.integer > 1)
781                                 Cvar_SetValueQuick(&con_textsize, con_textsize.integer - 1);
782                         return;
783                 }
784                 // text zoom reset
785                 if (key == '0' || key == K_KP_INS)
786                 {
787                         Cvar_SetValueQuick(&con_textsize, atoi(Cvar_VariableDefString("con_textsize")));
788                         return;
789                 }
790         }
791
792         if (key == K_HOME || key == K_KP_HOME)
793         {
794                 if (keydown[K_CTRL])
795                         con_backscroll = INT_MAX;
796                 else
797                         key_linepos = 1;
798                 return;
799         }
800
801         if (key == K_END || key == K_KP_END)
802         {
803                 if (keydown[K_CTRL])
804                         con_backscroll = 0;
805                 else
806                         key_linepos = (int)strlen(key_line);
807                 return;
808         }
809
810         // non printable
811         if (unicode < 32)
812                 return;
813
814         if (key_linepos < MAX_INPUTLINE-1)
815         {
816                 char buf[16];
817                 int len;
818                 int blen;
819                 blen = u8_fromchar(unicode, buf, sizeof(buf));
820                 if (!blen)
821                         return;
822                 len = (int)strlen(&key_line[key_linepos]);
823                 // check insert mode, or always insert if at end of line
824                 if (key_insert || len == 0)
825                 {
826                         // can't use strcpy to move string to right
827                         len++;
828                         //memmove(&key_line[key_linepos + u8_bytelen(key_line + key_linepos, 1)], &key_line[key_linepos], len);
829                         memmove(&key_line[key_linepos + blen], &key_line[key_linepos], len);
830                 }
831                 memcpy(key_line + key_linepos, buf, blen);
832                 key_linepos += blen;
833                 //key_linepos += u8_fromchar(unicode, key_line + key_linepos, sizeof(key_line) - key_linepos - 1);
834                 //key_line[key_linepos] = ascii;
835                 //key_linepos++;
836         }
837 }
838
839 //============================================================================
840
841 int chat_mode;
842 char            chat_buffer[MAX_INPUTLINE];
843 unsigned int    chat_bufferlen = 0;
844
845 extern int Nicks_CompleteChatLine(char *buffer, size_t size, unsigned int pos);
846
847 static void
848 Key_Message (int key, int ascii)
849 {
850         if (key == K_ENTER || ascii == 10 || ascii == 13)
851         {
852                 if(chat_mode < 0)
853                         Cmd_ExecuteString(chat_buffer, src_command); // not Cbuf_AddText to allow semiclons in args; however, this allows no variables then. Use aliases!
854                 else
855                         Cmd_ForwardStringToServer(va("%s %s", chat_mode ? "say_team" : "say ", chat_buffer));
856
857                 key_dest = key_game;
858                 chat_bufferlen = 0;
859                 chat_buffer[0] = 0;
860                 return;
861         }
862
863         // TODO add support for arrow keys and simple editing
864
865         if (key == K_ESCAPE) {
866                 key_dest = key_game;
867                 chat_bufferlen = 0;
868                 chat_buffer[0] = 0;
869                 return;
870         }
871
872         if (key == K_BACKSPACE) {
873                 if (chat_bufferlen) {
874                         chat_bufferlen = u8_prevbyte(chat_buffer, chat_bufferlen);
875                         chat_buffer[chat_bufferlen] = 0;
876                 }
877                 return;
878         }
879
880         if(key == K_TAB) {
881                 chat_bufferlen = Nicks_CompleteChatLine(chat_buffer, sizeof(chat_buffer), chat_bufferlen);
882                 return;
883         }
884
885         if (chat_bufferlen == sizeof (chat_buffer) - 1)
886                 return;                                                 // all full
887
888         if (!ascii)
889                 return;                                                 // non printable
890
891         chat_bufferlen += u8_fromchar(ascii, chat_buffer+chat_bufferlen, sizeof(chat_buffer) - chat_bufferlen - 1);
892
893         //chat_buffer[chat_bufferlen++] = ascii;
894         //chat_buffer[chat_bufferlen] = 0;
895 }
896
897 //============================================================================
898
899
900 /*
901 ===================
902 Returns a key number to be used to index keybindings[] by looking at
903 the given string.  Single ascii characters return themselves, while
904 the K_* names are matched up.
905 ===================
906 */
907 int
908 Key_StringToKeynum (const char *str)
909 {
910         const keyname_t  *kn;
911
912         if (!str || !str[0])
913                 return -1;
914         if (!str[1])
915                 return tolower(str[0]);
916
917         for (kn = keynames; kn->name; kn++) {
918                 if (!strcasecmp (str, kn->name))
919                         return kn->keynum;
920         }
921         return -1;
922 }
923
924 /*
925 ===================
926 Returns a string (either a single ascii char, or a K_* name) for the
927 given keynum.
928 FIXME: handle quote special (general escape sequence?)
929 ===================
930 */
931 const char *
932 Key_KeynumToString (int keynum)
933 {
934         const keyname_t  *kn;
935         static char tinystr[2];
936
937         // -1 is an invalid code
938         if (keynum < 0)
939                 return "<KEY NOT FOUND>";
940
941         // search overrides first, because some characters are special
942         for (kn = keynames; kn->name; kn++)
943                 if (keynum == kn->keynum)
944                         return kn->name;
945
946         // if it is printable, output it as a single character
947         if (keynum > 32 && keynum < 256)
948         {
949                 tinystr[0] = keynum;
950                 tinystr[1] = 0;
951                 return tinystr;
952         }
953
954         // if it is not overridden and not printable, we don't know what to do with it
955         return "<UNKNOWN KEYNUM>";
956 }
957
958
959 void
960 Key_SetBinding (int keynum, int bindmap, const char *binding)
961 {
962         char *newbinding;
963         size_t l;
964
965         if (keynum == -1 || keynum >= MAX_KEYS)
966                 return;
967
968 // free old bindings
969         if (keybindings[bindmap][keynum]) {
970                 Z_Free (keybindings[bindmap][keynum]);
971                 keybindings[bindmap][keynum] = NULL;
972         }
973         if(!binding[0]) // make "" binds be removed --blub
974                 return;
975 // allocate memory for new binding
976         l = strlen (binding);
977         newbinding = (char *)Z_Malloc (l + 1);
978         memcpy (newbinding, binding, l + 1);
979         newbinding[l] = 0;
980         keybindings[bindmap][keynum] = newbinding;
981 }
982
983 static void
984 Key_In_Unbind_f (void)
985 {
986         int         b, m;
987         char *errchar = NULL;
988
989         if (Cmd_Argc () != 3) {
990                 Con_Print("in_unbind <bindmap> <key> : remove commands from a key\n");
991                 return;
992         }
993
994         m = strtol(Cmd_Argv (1), &errchar, 0);
995         if ((m < 0) || (m >= 8) || (errchar && *errchar)) {
996                 Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(1));
997                 return;
998         }
999
1000         b = Key_StringToKeynum (Cmd_Argv (2));
1001         if (b == -1) {
1002                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (2));
1003                 return;
1004         }
1005
1006         Key_SetBinding (b, m, "");
1007 }
1008
1009 static void
1010 Key_In_Bind_f (void)
1011 {
1012         int         i, c, b, m;
1013         char        cmd[MAX_INPUTLINE];
1014         char *errchar = NULL;
1015
1016         c = Cmd_Argc ();
1017
1018         if (c != 3 && c != 4) {
1019                 Con_Print("in_bind <bindmap> <key> [command] : attach a command to a key\n");
1020                 return;
1021         }
1022
1023         m = strtol(Cmd_Argv (1), &errchar, 0);
1024         if ((m < 0) || (m >= 8) || (errchar && *errchar)) {
1025                 Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(1));
1026                 return;
1027         }
1028
1029         b = Key_StringToKeynum (Cmd_Argv (2));
1030         if (b == -1 || b >= MAX_KEYS) {
1031                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (2));
1032                 return;
1033         }
1034
1035         if (c == 3) {
1036                 if (keybindings[m][b])
1037                         Con_Printf("\"%s\" = \"%s\"\n", Cmd_Argv (2), keybindings[m][b]);
1038                 else
1039                         Con_Printf("\"%s\" is not bound\n", Cmd_Argv (2));
1040                 return;
1041         }
1042 // copy the rest of the command line
1043         cmd[0] = 0;                                                     // start out with a null string
1044         for (i = 3; i < c; i++) {
1045                 strlcat (cmd, Cmd_Argv (i), sizeof (cmd));
1046                 if (i != (c - 1))
1047                         strlcat (cmd, " ", sizeof (cmd));
1048         }
1049
1050         Key_SetBinding (b, m, cmd);
1051 }
1052
1053 static void
1054 Key_In_Bindmap_f (void)
1055 {
1056         int         m1, m2, c;
1057         char *errchar = NULL;
1058
1059         c = Cmd_Argc ();
1060
1061         if (c != 3) {
1062                 Con_Print("in_bindmap <bindmap> <fallback>: set current bindmap and fallback\n");
1063                 return;
1064         }
1065
1066         m1 = strtol(Cmd_Argv (1), &errchar, 0);
1067         if ((m1 < 0) || (m1 >= 8) || (errchar && *errchar)) {
1068                 Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(1));
1069                 return;
1070         }
1071
1072         m2 = strtol(Cmd_Argv (2), &errchar, 0);
1073         if ((m2 < 0) || (m2 >= 8) || (errchar && *errchar)) {
1074                 Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(2));
1075                 return;
1076         }
1077
1078         key_bmap = m1;
1079         key_bmap2 = m2;
1080 }
1081
1082 static void
1083 Key_Unbind_f (void)
1084 {
1085         int         b;
1086
1087         if (Cmd_Argc () != 2) {
1088                 Con_Print("unbind <key> : remove commands from a key\n");
1089                 return;
1090         }
1091
1092         b = Key_StringToKeynum (Cmd_Argv (1));
1093         if (b == -1) {
1094                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (1));
1095                 return;
1096         }
1097
1098         Key_SetBinding (b, 0, "");
1099 }
1100
1101 static void
1102 Key_Unbindall_f (void)
1103 {
1104         int         i, j;
1105
1106         for (j = 0; j < 8; j++)
1107                 for (i = 0; i < (int)(sizeof(keybindings[0])/sizeof(keybindings[0][0])); i++)
1108                         if (keybindings[j][i])
1109                                 Key_SetBinding (i, j, "");
1110 }
1111
1112 static void
1113 Key_PrintBindList(int j)
1114 {
1115         char bindbuf[MAX_INPUTLINE];
1116         const char *p;
1117         int i;
1118
1119         for (i = 0; i < (int)(sizeof(keybindings[0])/sizeof(keybindings[0][0])); i++)
1120         {
1121                 p = keybindings[j][i];
1122                 if (p)
1123                 {
1124                         Cmd_QuoteString(bindbuf, sizeof(bindbuf), p, "\"\\");
1125                         if (j == 0)
1126                                 Con_Printf("^2%s ^7= \"%s\"\n", Key_KeynumToString (i), bindbuf);
1127                         else
1128                                 Con_Printf("^3bindmap %d: ^2%s ^7= \"%s\"\n", j, Key_KeynumToString (i), bindbuf);
1129                 }
1130         }
1131 }
1132
1133 static void
1134 Key_In_BindList_f (void)
1135 {
1136         int m;
1137         char *errchar = NULL;
1138
1139         if(Cmd_Argc() >= 2)
1140         {
1141                 m = strtol(Cmd_Argv(1), &errchar, 0);
1142                 if ((m < 0) || (m >= 8) || (errchar && *errchar)) {
1143                         Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(1));
1144                         return;
1145                 }
1146                 Key_PrintBindList(m);
1147         }
1148         else
1149         {
1150                 for (m = 0; m < MAX_BINDMAPS; m++)
1151                         Key_PrintBindList(m);
1152         }
1153 }
1154
1155 static void
1156 Key_BindList_f (void)
1157 {
1158         Key_PrintBindList(0);
1159 }
1160
1161 static void
1162 Key_Bind_f (void)
1163 {
1164         int         i, c, b;
1165         char        cmd[MAX_INPUTLINE];
1166
1167         c = Cmd_Argc ();
1168
1169         if (c != 2 && c != 3) {
1170                 Con_Print("bind <key> [command] : attach a command to a key\n");
1171                 return;
1172         }
1173         b = Key_StringToKeynum (Cmd_Argv (1));
1174         if (b == -1 || b >= MAX_KEYS) {
1175                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (1));
1176                 return;
1177         }
1178
1179         if (c == 2) {
1180                 if (keybindings[0][b])
1181                         Con_Printf("\"%s\" = \"%s\"\n", Cmd_Argv (1), keybindings[0][b]);
1182                 else
1183                         Con_Printf("\"%s\" is not bound\n", Cmd_Argv (1));
1184                 return;
1185         }
1186 // copy the rest of the command line
1187         cmd[0] = 0;                                                     // start out with a null string
1188         for (i = 2; i < c; i++) {
1189                 strlcat (cmd, Cmd_Argv (i), sizeof (cmd));
1190                 if (i != (c - 1))
1191                         strlcat (cmd, " ", sizeof (cmd));
1192         }
1193
1194         Key_SetBinding (b, 0, cmd);
1195 }
1196
1197 /*
1198 ============
1199 Writes lines containing "bind key value"
1200 ============
1201 */
1202 void
1203 Key_WriteBindings (qfile_t *f)
1204 {
1205         int         i, j;
1206         char bindbuf[MAX_INPUTLINE];
1207         const char *p;
1208
1209         for (j = 0; j < MAX_BINDMAPS; j++)
1210         {
1211                 for (i = 0; i < (int)(sizeof(keybindings[0])/sizeof(keybindings[0][0])); i++)
1212                 {
1213                         p = keybindings[j][i];
1214                         if (p)
1215                         {
1216                                 Cmd_QuoteString(bindbuf, sizeof(bindbuf), p, "\"\\");
1217                                 if (j == 0)
1218                                         FS_Printf(f, "bind %s \"%s\"\n", Key_KeynumToString (i), bindbuf);
1219                                 else
1220                                         FS_Printf(f, "in_bind %d %s \"%s\"\n", j, Key_KeynumToString (i), bindbuf);
1221                         }
1222                 }
1223         }
1224 }
1225
1226
1227 void
1228 Key_Init (void)
1229 {
1230         Key_History_Init();
1231         key_line[0] = ']';
1232         key_line[1] = 0;
1233         key_linepos = 1;
1234
1235 //
1236 // register our functions
1237 //
1238         Cmd_AddCommand ("in_bind", Key_In_Bind_f, "binds a command to the specified key in the selected bindmap");
1239         Cmd_AddCommand ("in_unbind", Key_In_Unbind_f, "removes command on the specified key in the selected bindmap");
1240         Cmd_AddCommand ("in_bindlist", Key_In_BindList_f, "bindlist: displays bound keys for all bindmaps, or the given bindmap");
1241         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");
1242
1243         Cmd_AddCommand ("bind", Key_Bind_f, "binds a command to the specified key in bindmap 0");
1244         Cmd_AddCommand ("unbind", Key_Unbind_f, "removes a command on the specified key in bindmap 0");
1245         Cmd_AddCommand ("bindlist", Key_BindList_f, "bindlist: displays bound keys for bindmap 0 bindmaps");
1246         Cmd_AddCommand ("unbindall", Key_Unbindall_f, "removes all commands from all keys in all bindmaps (leaving only shift-escape and escape)");
1247
1248         Cvar_RegisterVariable (&con_closeontoggleconsole);
1249 }
1250
1251 void
1252 Key_Shutdown (void)
1253 {
1254         Key_History_Shutdown();
1255 }
1256
1257 const char *Key_GetBind (int key)
1258 {
1259         const char *bind;
1260         if (key < 0 || key >= MAX_KEYS)
1261                 return NULL;
1262         bind = keybindings[key_bmap][key];
1263         if (!bind)
1264                 bind = keybindings[key_bmap2][key];
1265         return bind;
1266 }
1267
1268 qboolean CL_VM_InputEvent (qboolean down, int key, int ascii);
1269
1270 /*
1271 ===================
1272 Called by the system between frames for both key up and key down events
1273 Should NOT be called during an interrupt!
1274 ===================
1275 */
1276 static char tbl_keyascii[MAX_KEYS];
1277 static keydest_t tbl_keydest[MAX_KEYS];
1278
1279 void
1280 Key_Event (int key, int ascii, qboolean down)
1281 {
1282         const char *bind;
1283         qboolean q;
1284         keydest_t keydest = key_dest;
1285
1286         if (key < 0 || key >= MAX_KEYS)
1287                 return;
1288
1289         // get key binding
1290         bind = keybindings[key_bmap][key];
1291         if (!bind)
1292                 bind = keybindings[key_bmap2][key];
1293
1294         if (developer.integer >= 1000)
1295                 Con_Printf("Key_Event(%i, '%c', %s) keydown %i bind \"%s\"\n", key, ascii, down ? "down" : "up", keydown[key], bind ? bind : "");
1296
1297         if(key_consoleactive)
1298                 keydest = key_console;
1299         
1300         if (down)
1301         {
1302                 // increment key repeat count each time a down is received so that things
1303                 // which want to ignore key repeat can ignore it
1304                 keydown[key] = min(keydown[key] + 1, 2);
1305                 if(keydown[key] == 1) {
1306                         tbl_keyascii[key] = ascii;
1307                         tbl_keydest[key] = keydest;
1308                 } else {
1309                         ascii = tbl_keyascii[key];
1310                         keydest = tbl_keydest[key];
1311                 }
1312         }
1313         else
1314         {
1315                 // clear repeat count now that the key is released
1316                 keydown[key] = 0;
1317                 keydest = tbl_keydest[key];
1318                 ascii = tbl_keyascii[key];
1319         }
1320
1321         if(keydest == key_void)
1322                 return;
1323         
1324         // key_consoleactive is a flag not a key_dest because the console is a
1325         // high priority overlay ontop of the normal screen (designed as a safety
1326         // feature so that developers and users can rescue themselves from a bad
1327         // situation).
1328         //
1329         // this also means that toggling the console on/off does not lose the old
1330         // key_dest state
1331
1332         // specially handle escape (togglemenu) and shift-escape (toggleconsole)
1333         // engine bindings, these are not handled as normal binds so that the user
1334         // can recover from a completely empty bindmap
1335         if (key == K_ESCAPE)
1336         {
1337                 // ignore key repeats on escape
1338                 if (keydown[key] > 1)
1339                         return;
1340
1341                 // escape does these things:
1342                 // key_consoleactive - close console
1343                 // key_message - abort messagemode
1344                 // key_menu - go to parent menu (or key_game)
1345                 // key_game - open menu
1346
1347                 // in all modes shift-escape toggles console
1348                 if (keydown[K_SHIFT])
1349                 {
1350                         if(down)
1351                         {
1352                                 Con_ToggleConsole_f ();
1353                                 tbl_keydest[key] = key_void; // esc release should go nowhere (especially not to key_menu or key_game)
1354                         }
1355                         return;
1356                 }
1357
1358                 switch (keydest)
1359                 {
1360                         case key_console:
1361                                 if(down)
1362                                 {
1363                                         if(key_consoleactive & KEY_CONSOLEACTIVE_FORCED)
1364                                         {
1365                                                 key_consoleactive &= ~KEY_CONSOLEACTIVE_USER;
1366                                                 MR_ToggleMenu_f ();
1367                                         }
1368                                         else
1369                                                 Con_ToggleConsole_f();
1370                                 }
1371                                 break;
1372
1373                         case key_message:
1374                                 if (down)
1375                                         Key_Message (key, ascii); // that'll close the message input
1376                                 break;
1377
1378                         case key_menu:
1379                         case key_menu_grabbed:
1380                                 MR_KeyEvent (key, ascii, down);
1381                                 break;
1382
1383                         case key_game:
1384                                 // csqc has priority over toggle menu if it wants to (e.g. handling escape for UI stuff in-game.. :sick:)
1385                                 q = CL_VM_InputEvent(down, key, ascii);
1386                                 if (!q && down)
1387                                         MR_ToggleMenu_f ();
1388                                 break;
1389
1390                         default:
1391                                 Con_Printf ("Key_Event: Bad key_dest\n");
1392                 }
1393                 return;
1394         }
1395
1396         // send function keydowns to interpreter no matter what mode is (unless the menu has specifically grabbed the keyboard, for rebinding keys)
1397         if (keydest != key_menu_grabbed)
1398         if (key >= K_F1 && key <= K_F12)
1399         {
1400                 if (bind)
1401                 {
1402                         if(keydown[key] == 1 && down)
1403                         {
1404                                 // button commands add keynum as a parm
1405                                 if (bind[0] == '+')
1406                                         Cbuf_AddText (va("%s %i\n", bind, key));
1407                                 else
1408                                 {
1409                                         Cbuf_AddText (bind);
1410                                         Cbuf_AddText ("\n");
1411                                 }
1412                         } else if(bind[0] == '+' && !down && keydown[key] == 0)
1413                                 Cbuf_AddText(va("-%s %i\n", bind + 1, key));
1414                 }
1415                 return;
1416         }
1417
1418         // send input to console if it wants it
1419         if (keydest == key_console)
1420         {
1421                 if (!down)
1422                         return;
1423                 // con_closeontoggleconsole enables toggleconsole keys to close the
1424                 // console, as long as they are not the color prefix character
1425                 // (special exemption for german keyboard layouts)
1426                 if (con_closeontoggleconsole.integer && bind && !strncmp(bind, "toggleconsole", strlen("toggleconsole")) && (key_consoleactive & KEY_CONSOLEACTIVE_USER) && ascii != STRING_COLOR_TAG)
1427                 {
1428                         Con_ToggleConsole_f ();
1429                         return;
1430                 }
1431                 Key_Console (key, ascii);
1432                 return;
1433         }
1434
1435         // handle toggleconsole in menu too
1436         if (keydest == key_menu)
1437         {
1438                 if (down && con_closeontoggleconsole.integer && bind && !strncmp(bind, "toggleconsole", strlen("toggleconsole")) && ascii != STRING_COLOR_TAG)
1439                 {
1440                         Con_ToggleConsole_f ();
1441                         tbl_keydest[key] = key_void; // key release should go nowhere (especially not to key_menu or key_game)
1442                         return;
1443                 }
1444         }
1445
1446         // ignore binds while a video is played, let the video system handle the key event
1447         if (cl_videoplaying)
1448         {
1449                 CL_Video_KeyEvent (key, ascii, keydown[key] != 0);
1450                 return;
1451         }
1452
1453         // anything else is a key press into the game, chat line, or menu
1454         switch (keydest)
1455         {
1456                 case key_message:
1457                         if (down)
1458                                 Key_Message (key, ascii);
1459                         break;
1460                 case key_menu:
1461                 case key_menu_grabbed:
1462                         MR_KeyEvent (key, ascii, down);
1463                         break;
1464                 case key_game:
1465                         q = CL_VM_InputEvent(down, key, ascii);
1466                         // ignore key repeats on binds and only send the bind if the event hasnt been already processed by csqc
1467                         if (!q && bind)
1468                         {
1469                                 if(keydown[key] == 1 && down)
1470                                 {
1471                                         // button commands add keynum as a parm
1472                                         if (bind[0] == '+')
1473                                                 Cbuf_AddText (va("%s %i\n", bind, key));
1474                                         else
1475                                         {
1476                                                 Cbuf_AddText (bind);
1477                                                 Cbuf_AddText ("\n");
1478                                         }
1479                                 } else if(bind[0] == '+' && !down && keydown[key] == 0)
1480                                         Cbuf_AddText(va("-%s %i\n", bind + 1, key));
1481                         }
1482                         break;
1483                 default:
1484                         Con_Printf ("Key_Event: Bad key_dest\n");
1485         }
1486 }
1487
1488 /*
1489 ===================
1490 Key_ClearStates
1491 ===================
1492 */
1493 void
1494 Key_ClearStates (void)
1495 {
1496         memset(keydown, 0, sizeof(keydown));
1497 }