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