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