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