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