]> icculus.org git repositories - divverent/darkplaces.git/blob - keys.c
Adding GAME_XONOTIC and recognizing xonotic executable names
[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 = 0;
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 qboolean
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 false;
1096         if ((bindmap < 0) || (bindmap >= MAX_BINDMAPS))
1097                 return false;
1098
1099 // free old bindings
1100         if (keybindings[bindmap][keynum]) {
1101                 Z_Free (keybindings[bindmap][keynum]);
1102                 keybindings[bindmap][keynum] = NULL;
1103         }
1104         if(!binding[0]) // make "" binds be removed --blub
1105                 return true;
1106 // allocate memory for new binding
1107         l = strlen (binding);
1108         newbinding = (char *)Z_Malloc (l + 1);
1109         memcpy (newbinding, binding, l + 1);
1110         newbinding[l] = 0;
1111         keybindings[bindmap][keynum] = newbinding;
1112         return true;
1113 }
1114
1115 void Key_GetBindMap(int *fg, int *bg)
1116 {
1117         if(fg)
1118                 *fg = key_bmap;
1119         if(bg)
1120                 *bg = key_bmap2;
1121 }
1122
1123 qboolean Key_SetBindMap(int fg, int bg)
1124 {
1125         if(fg >= MAX_BINDMAPS)
1126                 return false;
1127         if(bg >= MAX_BINDMAPS)
1128                 return false;
1129         if(fg >= 0)
1130                 key_bmap = fg;
1131         if(bg >= 0)
1132                 key_bmap2 = bg;
1133         return true;
1134 }
1135
1136 static void
1137 Key_In_Unbind_f (void)
1138 {
1139         int         b, m;
1140         char *errchar = NULL;
1141
1142         if (Cmd_Argc () != 3) {
1143                 Con_Print("in_unbind <bindmap> <key> : remove commands from a key\n");
1144                 return;
1145         }
1146
1147         m = strtol(Cmd_Argv (1), &errchar, 0);
1148         if ((m < 0) || (m >= MAX_BINDMAPS) || (errchar && *errchar)) {
1149                 Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(1));
1150                 return;
1151         }
1152
1153         b = Key_StringToKeynum (Cmd_Argv (2));
1154         if (b == -1) {
1155                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (2));
1156                 return;
1157         }
1158
1159         if(!Key_SetBinding (b, m, ""))
1160                 Con_Printf("Key_SetBinding failed for unknown reason\n");
1161 }
1162
1163 static void
1164 Key_In_Bind_f (void)
1165 {
1166         int         i, c, b, m;
1167         char        cmd[MAX_INPUTLINE];
1168         char *errchar = NULL;
1169
1170         c = Cmd_Argc ();
1171
1172         if (c != 3 && c != 4) {
1173                 Con_Print("in_bind <bindmap> <key> [command] : attach a command to a key\n");
1174                 return;
1175         }
1176
1177         m = strtol(Cmd_Argv (1), &errchar, 0);
1178         if ((m < 0) || (m >= MAX_BINDMAPS) || (errchar && *errchar)) {
1179                 Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(1));
1180                 return;
1181         }
1182
1183         b = Key_StringToKeynum (Cmd_Argv (2));
1184         if (b == -1 || b >= MAX_KEYS) {
1185                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (2));
1186                 return;
1187         }
1188
1189         if (c == 3) {
1190                 if (keybindings[m][b])
1191                         Con_Printf("\"%s\" = \"%s\"\n", Cmd_Argv (2), keybindings[m][b]);
1192                 else
1193                         Con_Printf("\"%s\" is not bound\n", Cmd_Argv (2));
1194                 return;
1195         }
1196 // copy the rest of the command line
1197         cmd[0] = 0;                                                     // start out with a null string
1198         for (i = 3; i < c; i++) {
1199                 strlcat (cmd, Cmd_Argv (i), sizeof (cmd));
1200                 if (i != (c - 1))
1201                         strlcat (cmd, " ", sizeof (cmd));
1202         }
1203
1204         if(!Key_SetBinding (b, m, cmd))
1205                 Con_Printf("Key_SetBinding failed for unknown reason\n");
1206 }
1207
1208 static void
1209 Key_In_Bindmap_f (void)
1210 {
1211         int         m1, m2, c;
1212         char *errchar = NULL;
1213
1214         c = Cmd_Argc ();
1215
1216         if (c != 3) {
1217                 Con_Print("in_bindmap <bindmap> <fallback>: set current bindmap and fallback\n");
1218                 return;
1219         }
1220
1221         m1 = strtol(Cmd_Argv (1), &errchar, 0);
1222         if ((m1 < 0) || (m1 >= MAX_BINDMAPS) || (errchar && *errchar)) {
1223                 Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(1));
1224                 return;
1225         }
1226
1227         m2 = strtol(Cmd_Argv (2), &errchar, 0);
1228         if ((m2 < 0) || (m2 >= MAX_BINDMAPS) || (errchar && *errchar)) {
1229                 Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(2));
1230                 return;
1231         }
1232
1233         key_bmap = m1;
1234         key_bmap2 = m2;
1235 }
1236
1237 static void
1238 Key_Unbind_f (void)
1239 {
1240         int         b;
1241
1242         if (Cmd_Argc () != 2) {
1243                 Con_Print("unbind <key> : remove commands from a key\n");
1244                 return;
1245         }
1246
1247         b = Key_StringToKeynum (Cmd_Argv (1));
1248         if (b == -1) {
1249                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (1));
1250                 return;
1251         }
1252
1253         if(!Key_SetBinding (b, 0, ""))
1254                 Con_Printf("Key_SetBinding failed for unknown reason\n");
1255 }
1256
1257 static void
1258 Key_Unbindall_f (void)
1259 {
1260         int         i, j;
1261
1262         for (j = 0; j < MAX_BINDMAPS; j++)
1263                 for (i = 0; i < (int)(sizeof(keybindings[0])/sizeof(keybindings[0][0])); i++)
1264                         if (keybindings[j][i])
1265                                 Key_SetBinding (i, j, "");
1266 }
1267
1268 static void
1269 Key_PrintBindList(int j)
1270 {
1271         char bindbuf[MAX_INPUTLINE];
1272         const char *p;
1273         int i;
1274
1275         for (i = 0; i < (int)(sizeof(keybindings[0])/sizeof(keybindings[0][0])); i++)
1276         {
1277                 p = keybindings[j][i];
1278                 if (p)
1279                 {
1280                         Cmd_QuoteString(bindbuf, sizeof(bindbuf), p, "\"\\");
1281                         if (j == 0)
1282                                 Con_Printf("^2%s ^7= \"%s\"\n", Key_KeynumToString (i), bindbuf);
1283                         else
1284                                 Con_Printf("^3bindmap %d: ^2%s ^7= \"%s\"\n", j, Key_KeynumToString (i), bindbuf);
1285                 }
1286         }
1287 }
1288
1289 static void
1290 Key_In_BindList_f (void)
1291 {
1292         int m;
1293         char *errchar = NULL;
1294
1295         if(Cmd_Argc() >= 2)
1296         {
1297                 m = strtol(Cmd_Argv(1), &errchar, 0);
1298                 if ((m < 0) || (m >= MAX_BINDMAPS) || (errchar && *errchar)) {
1299                         Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(1));
1300                         return;
1301                 }
1302                 Key_PrintBindList(m);
1303         }
1304         else
1305         {
1306                 for (m = 0; m < MAX_BINDMAPS; m++)
1307                         Key_PrintBindList(m);
1308         }
1309 }
1310
1311 static void
1312 Key_BindList_f (void)
1313 {
1314         Key_PrintBindList(0);
1315 }
1316
1317 static void
1318 Key_Bind_f (void)
1319 {
1320         int         i, c, b;
1321         char        cmd[MAX_INPUTLINE];
1322
1323         c = Cmd_Argc ();
1324
1325         if (c != 2 && c != 3) {
1326                 Con_Print("bind <key> [command] : attach a command to a key\n");
1327                 return;
1328         }
1329         b = Key_StringToKeynum (Cmd_Argv (1));
1330         if (b == -1 || b >= MAX_KEYS) {
1331                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (1));
1332                 return;
1333         }
1334
1335         if (c == 2) {
1336                 if (keybindings[0][b])
1337                         Con_Printf("\"%s\" = \"%s\"\n", Cmd_Argv (1), keybindings[0][b]);
1338                 else
1339                         Con_Printf("\"%s\" is not bound\n", Cmd_Argv (1));
1340                 return;
1341         }
1342 // copy the rest of the command line
1343         cmd[0] = 0;                                                     // start out with a null string
1344         for (i = 2; i < c; i++) {
1345                 strlcat (cmd, Cmd_Argv (i), sizeof (cmd));
1346                 if (i != (c - 1))
1347                         strlcat (cmd, " ", sizeof (cmd));
1348         }
1349
1350         if(!Key_SetBinding (b, 0, cmd))
1351                 Con_Printf("Key_SetBinding failed for unknown reason\n");
1352 }
1353
1354 /*
1355 ============
1356 Writes lines containing "bind key value"
1357 ============
1358 */
1359 void
1360 Key_WriteBindings (qfile_t *f)
1361 {
1362         int         i, j;
1363         char bindbuf[MAX_INPUTLINE];
1364         const char *p;
1365
1366         for (j = 0; j < MAX_BINDMAPS; j++)
1367         {
1368                 for (i = 0; i < (int)(sizeof(keybindings[0])/sizeof(keybindings[0][0])); i++)
1369                 {
1370                         p = keybindings[j][i];
1371                         if (p)
1372                         {
1373                                 Cmd_QuoteString(bindbuf, sizeof(bindbuf), p, "\"\\"); // don't need to escape $ because cvars are not expanded inside bind
1374                                 if (j == 0)
1375                                         FS_Printf(f, "bind %s \"%s\"\n", Key_KeynumToString (i), bindbuf);
1376                                 else
1377                                         FS_Printf(f, "in_bind %d %s \"%s\"\n", j, Key_KeynumToString (i), bindbuf);
1378                         }
1379                 }
1380         }
1381 }
1382
1383
1384 void
1385 Key_Init (void)
1386 {
1387         Key_History_Init();
1388         key_line[0] = ']';
1389         key_line[1] = 0;
1390         key_linepos = 1;
1391
1392 //
1393 // register our functions
1394 //
1395         Cmd_AddCommand ("in_bind", Key_In_Bind_f, "binds a command to the specified key in the selected bindmap");
1396         Cmd_AddCommand ("in_unbind", Key_In_Unbind_f, "removes command on the specified key in the selected bindmap");
1397         Cmd_AddCommand ("in_bindlist", Key_In_BindList_f, "bindlist: displays bound keys for all bindmaps, or the given bindmap");
1398         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");
1399
1400         Cmd_AddCommand ("bind", Key_Bind_f, "binds a command to the specified key in bindmap 0");
1401         Cmd_AddCommand ("unbind", Key_Unbind_f, "removes a command on the specified key in bindmap 0");
1402         Cmd_AddCommand ("bindlist", Key_BindList_f, "bindlist: displays bound keys for bindmap 0 bindmaps");
1403         Cmd_AddCommand ("unbindall", Key_Unbindall_f, "removes all commands from all keys in all bindmaps (leaving only shift-escape and escape)");
1404
1405         Cvar_RegisterVariable (&con_closeontoggleconsole);
1406 }
1407
1408 void
1409 Key_Shutdown (void)
1410 {
1411         Key_History_Shutdown();
1412 }
1413
1414 const char *Key_GetBind (int key, int bindmap)
1415 {
1416         const char *bind;
1417         if (key < 0 || key >= MAX_KEYS)
1418                 return NULL;
1419         if(bindmap >= MAX_BINDMAPS)
1420                 return NULL;
1421         if(bindmap >= 0)
1422         {
1423                 bind = keybindings[bindmap][key];
1424         }
1425         else
1426         {
1427                 bind = keybindings[key_bmap][key];
1428                 if (!bind)
1429                         bind = keybindings[key_bmap2][key];
1430         }
1431         return bind;
1432 }
1433
1434 void Key_FindKeysForCommand (const char *command, int *keys, int numkeys, int bindmap)
1435 {
1436         int             count;
1437         int             j;
1438         const char      *b;
1439
1440         for (j = 0;j < numkeys;j++)
1441                 keys[j] = -1;
1442
1443         if(bindmap >= MAX_BINDMAPS)
1444                 return;
1445
1446         count = 0;
1447
1448         for (j = 0; j < MAX_KEYS; ++j)
1449         {
1450                 b = Key_GetBind(j, bindmap);
1451                 if (!b)
1452                         continue;
1453                 if (!strcmp (b, command) )
1454                 {
1455                         keys[count++] = j;
1456                         if (count == numkeys)
1457                                 break;
1458                 }
1459         }
1460 }
1461
1462 qboolean CL_VM_InputEvent (qboolean down, int key, int ascii);
1463
1464 /*
1465 ===================
1466 Called by the system between frames for both key up and key down events
1467 Should NOT be called during an interrupt!
1468 ===================
1469 */
1470 static char tbl_keyascii[MAX_KEYS];
1471 static keydest_t tbl_keydest[MAX_KEYS];
1472
1473 typedef struct eventqueueitem_s
1474 {
1475         int key;
1476         int ascii;
1477         qboolean down;
1478 }
1479 eventqueueitem_t;
1480 static int events_blocked = 0;
1481 static eventqueueitem_t eventqueue[32];
1482 static unsigned eventqueue_idx = 0;
1483
1484 static void Key_EventQueue_Add(int key, int ascii, qboolean down)
1485 {
1486         if(eventqueue_idx < sizeof(eventqueue) / sizeof(*eventqueue))
1487         {
1488                 eventqueue[eventqueue_idx].key = key;
1489                 eventqueue[eventqueue_idx].ascii = ascii;
1490                 eventqueue[eventqueue_idx].down = down;
1491                 ++eventqueue_idx;
1492         }
1493 }
1494
1495 void Key_EventQueue_Block(void)
1496 {
1497         // block key events until call to Unblock
1498         events_blocked = true;
1499 }
1500
1501 void Key_EventQueue_Unblock(void)
1502 {
1503         // unblocks key events again
1504         unsigned i;
1505         events_blocked = false;
1506         for(i = 0; i < eventqueue_idx; ++i)
1507                 Key_Event(eventqueue[i].key, eventqueue[i].ascii, eventqueue[i].down);
1508         eventqueue_idx = 0;
1509 }
1510
1511 void
1512 Key_Event (int key, int ascii, qboolean down)
1513 {
1514         const char *bind;
1515         qboolean q;
1516         keydest_t keydest = key_dest;
1517
1518         if (key < 0 || key >= MAX_KEYS)
1519                 return;
1520
1521         if(events_blocked)
1522         {
1523                 Key_EventQueue_Add(key, ascii, down);
1524                 return;
1525         }
1526
1527         // get key binding
1528         bind = keybindings[key_bmap][key];
1529         if (!bind)
1530                 bind = keybindings[key_bmap2][key];
1531
1532         if (developer_insane.integer)
1533                 Con_DPrintf("Key_Event(%i, '%c', %s) keydown %i bind \"%s\"\n", key, ascii ? ascii : '?', down ? "down" : "up", keydown[key], bind ? bind : "");
1534
1535         if(key_consoleactive)
1536                 keydest = key_console;
1537         
1538         if (down)
1539         {
1540                 // increment key repeat count each time a down is received so that things
1541                 // which want to ignore key repeat can ignore it
1542                 keydown[key] = min(keydown[key] + 1, 2);
1543                 if(keydown[key] == 1) {
1544                         tbl_keyascii[key] = ascii;
1545                         tbl_keydest[key] = keydest;
1546                 } else {
1547                         ascii = tbl_keyascii[key];
1548                         keydest = tbl_keydest[key];
1549                 }
1550         }
1551         else
1552         {
1553                 // clear repeat count now that the key is released
1554                 keydown[key] = 0;
1555                 keydest = tbl_keydest[key];
1556                 ascii = tbl_keyascii[key];
1557         }
1558
1559         if(keydest == key_void)
1560                 return;
1561         
1562         // key_consoleactive is a flag not a key_dest because the console is a
1563         // high priority overlay ontop of the normal screen (designed as a safety
1564         // feature so that developers and users can rescue themselves from a bad
1565         // situation).
1566         //
1567         // this also means that toggling the console on/off does not lose the old
1568         // key_dest state
1569
1570         // specially handle escape (togglemenu) and shift-escape (toggleconsole)
1571         // engine bindings, these are not handled as normal binds so that the user
1572         // can recover from a completely empty bindmap
1573         if (key == K_ESCAPE)
1574         {
1575                 // ignore key repeats on escape
1576                 if (keydown[key] > 1)
1577                         return;
1578
1579                 // escape does these things:
1580                 // key_consoleactive - close console
1581                 // key_message - abort messagemode
1582                 // key_menu - go to parent menu (or key_game)
1583                 // key_game - open menu
1584
1585                 // in all modes shift-escape toggles console
1586                 if (keydown[K_SHIFT])
1587                 {
1588                         if(down)
1589                         {
1590                                 Con_ToggleConsole_f ();
1591                                 tbl_keydest[key] = key_void; // esc release should go nowhere (especially not to key_menu or key_game)
1592                         }
1593                         return;
1594                 }
1595
1596                 switch (keydest)
1597                 {
1598                         case key_console:
1599                                 if(down)
1600                                 {
1601                                         if(key_consoleactive & KEY_CONSOLEACTIVE_FORCED)
1602                                         {
1603                                                 key_consoleactive &= ~KEY_CONSOLEACTIVE_USER;
1604                                                 MR_ToggleMenu(1);
1605                                         }
1606                                         else
1607                                                 Con_ToggleConsole_f();
1608                                 }
1609                                 break;
1610
1611                         case key_message:
1612                                 if (down)
1613                                         Key_Message (key, ascii); // that'll close the message input
1614                                 break;
1615
1616                         case key_menu:
1617                         case key_menu_grabbed:
1618                                 MR_KeyEvent (key, ascii, down);
1619                                 break;
1620
1621                         case key_game:
1622                                 // csqc has priority over toggle menu if it wants to (e.g. handling escape for UI stuff in-game.. :sick:)
1623                                 q = CL_VM_InputEvent(down, key, ascii);
1624                                 if (!q && down)
1625                                         MR_ToggleMenu(1);
1626                                 break;
1627
1628                         default:
1629                                 Con_Printf ("Key_Event: Bad key_dest\n");
1630                 }
1631                 return;
1632         }
1633
1634         // send function keydowns to interpreter no matter what mode is (unless the menu has specifically grabbed the keyboard, for rebinding keys)
1635         // VorteX: Omnicide does bind F* keys
1636         if (keydest != key_menu_grabbed)
1637         if (key >= K_F1 && key <= K_F12 && gamemode != GAME_BLOODOMNICIDE)
1638         {
1639                 if (bind)
1640                 {
1641                         if(keydown[key] == 1 && down)
1642                         {
1643                                 // button commands add keynum as a parm
1644                                 if (bind[0] == '+')
1645                                         Cbuf_AddText (va("%s %i\n", bind, key));
1646                                 else
1647                                 {
1648                                         Cbuf_AddText (bind);
1649                                         Cbuf_AddText ("\n");
1650                                 }
1651                         } else if(bind[0] == '+' && !down && keydown[key] == 0)
1652                                 Cbuf_AddText(va("-%s %i\n", bind + 1, key));
1653                 }
1654                 return;
1655         }
1656
1657         // send input to console if it wants it
1658         if (keydest == key_console)
1659         {
1660                 if (!down)
1661                         return;
1662                 // con_closeontoggleconsole enables toggleconsole keys to close the
1663                 // console, as long as they are not the color prefix character
1664                 // (special exemption for german keyboard layouts)
1665                 if (con_closeontoggleconsole.integer && bind && !strncmp(bind, "toggleconsole", strlen("toggleconsole")) && (key_consoleactive & KEY_CONSOLEACTIVE_USER) && ascii != STRING_COLOR_TAG)
1666                 {
1667                         Con_ToggleConsole_f ();
1668                         return;
1669                 }
1670                 Key_Console (key, ascii);
1671                 return;
1672         }
1673
1674         // handle toggleconsole in menu too
1675         if (keydest == key_menu)
1676         {
1677                 if (down && con_closeontoggleconsole.integer && bind && !strncmp(bind, "toggleconsole", strlen("toggleconsole")) && ascii != STRING_COLOR_TAG)
1678                 {
1679                         Con_ToggleConsole_f ();
1680                         tbl_keydest[key] = key_void; // key release should go nowhere (especially not to key_menu or key_game)
1681                         return;
1682                 }
1683         }
1684
1685         // ignore binds while a video is played, let the video system handle the key event
1686         if (cl_videoplaying)
1687         {
1688                 CL_Video_KeyEvent (key, ascii, keydown[key] != 0);
1689                 return;
1690         }
1691
1692         // anything else is a key press into the game, chat line, or menu
1693         switch (keydest)
1694         {
1695                 case key_message:
1696                         if (down)
1697                                 Key_Message (key, ascii);
1698                         break;
1699                 case key_menu:
1700                 case key_menu_grabbed:
1701                         MR_KeyEvent (key, ascii, down);
1702                         break;
1703                 case key_game:
1704                         q = CL_VM_InputEvent(down, key, ascii);
1705                         // ignore key repeats on binds and only send the bind if the event hasnt been already processed by csqc
1706                         if (!q && bind)
1707                         {
1708                                 if(keydown[key] == 1 && down)
1709                                 {
1710                                         // button commands add keynum as a parm
1711                                         if (bind[0] == '+')
1712                                                 Cbuf_AddText (va("%s %i\n", bind, key));
1713                                         else
1714                                         {
1715                                                 Cbuf_AddText (bind);
1716                                                 Cbuf_AddText ("\n");
1717                                         }
1718                                 } else if(bind[0] == '+' && !down && keydown[key] == 0)
1719                                         Cbuf_AddText(va("-%s %i\n", bind + 1, key));
1720                         }
1721                         break;
1722                 default:
1723                         Con_Printf ("Key_Event: Bad key_dest\n");
1724         }
1725 }
1726
1727 /*
1728 ===================
1729 Key_ClearStates
1730 ===================
1731 */
1732 void
1733 Key_ClearStates (void)
1734 {
1735         memset(keydown, 0, sizeof(keydown));
1736 }