]> icculus.org git repositories - divverent/darkplaces.git/blob - keys.c
Merge branch 'ft2' into 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
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 /*
479 static void Key_Console_SetCursor(size_t pos)
480 {
481         key_linepos = pos;
482         //Con_Printf("Position: %lu\n", pos);
483 }
484 */
485 static void
486 Key_Console (int key, int unicode)
487 {
488         /*
489         if (!UIM_Direct())
490         {
491                 if (UIM_Key(key, unicode))
492                     return;
493         }
494         */
495
496         // LordHavoc: copied most of this from Q2 to improve keyboard handling
497         switch (key)
498         {
499         case K_KP_SLASH:
500                 key = '/';
501                 break;
502         case K_KP_MINUS:
503                 key = '-';
504                 break;
505         case K_KP_PLUS:
506                 key = '+';
507                 break;
508         case K_KP_HOME:
509                 key = '7';
510                 break;
511         case K_KP_UPARROW:
512                 key = '8';
513                 break;
514         case K_KP_PGUP:
515                 key = '9';
516                 break;
517         case K_KP_LEFTARROW:
518                 key = '4';
519                 break;
520         case K_KP_5:
521                 key = '5';
522                 break;
523         case K_KP_RIGHTARROW:
524                 key = '6';
525                 break;
526         case K_KP_END:
527                 key = '1';
528                 break;
529         case K_KP_DOWNARROW:
530                 key = '2';
531                 break;
532         case K_KP_PGDN:
533                 key = '3';
534                 break;
535         case K_KP_INS:
536                 key = '0';
537                 break;
538         case K_KP_DEL:
539                 key = '.';
540                 break;
541         }
542
543         if ((key == 'v' && keydown[K_CTRL]) || ((key == K_INS || key == K_KP_INS) && keydown[K_SHIFT]))
544         {
545                 char *cbd, *p;
546                 if ((cbd = Sys_GetClipboardData()) != 0)
547                 {
548                         int i;
549 #if 1
550                         p = cbd;
551                         while (*p)
552                         {
553                                 if (*p == '\r' && *(p+1) == '\n')
554                                 {
555                                         *p++ = ';';
556                                         *p++ = ' ';
557                                 }
558                                 else if (*p == '\n' || *p == '\r' || *p == '\b')
559                                         *p++ = ';';
560                                 p++;
561                         }
562 #else
563                         strtok(cbd, "\n\r\b");
564 #endif
565                         i = (int)strlen(cbd);
566                         if (i + key_linepos >= MAX_INPUTLINE)
567                                 i= MAX_INPUTLINE - key_linepos - 1;
568                         if (i > 0)
569                         {
570                                 // terencehill: insert the clipboard text between the characters of the line
571                                 /*
572                                 char *temp = (char *) Z_Malloc(MAX_INPUTLINE);
573                                 cbd[i]=0;
574                                 temp[0]=0;
575                                 if ( key_linepos < (int)strlen(key_line) )
576                                         strlcpy(temp, key_line + key_linepos, (int)strlen(key_line) - key_linepos +1);
577                                 key_line[key_linepos] = 0;
578                                 strlcat(key_line, cbd, sizeof(key_line));
579                                 if (temp[0])
580                                         strlcat(key_line, temp, sizeof(key_line));
581                                 Z_Free(temp);
582                                 key_linepos += i;
583                                 */
584                                 // blub: I'm changing this to use memmove() like the rest of the code does.
585                                 cbd[i] = 0;
586                                 memmove(key_line + key_linepos + i, key_line + key_linepos, sizeof(key_line) - key_linepos - i);
587                                 memcpy(key_line + key_linepos, cbd, i);
588                                 key_linepos += i;
589                                 //UIM_SetCursor(key_linepos);
590                         }
591                         Z_Free(cbd);
592                 }
593                 return;
594         }
595
596         if (key == 'l' && keydown[K_CTRL])
597         {
598                 Cbuf_AddText ("clear\n");
599                 return;
600         }
601
602         if (key == 'u' && keydown[K_CTRL]) // like vi/readline ^u: delete currently edited line
603         {
604                 // clear line
605                 key_line[0] = ']';
606                 key_line[1] = 0;
607                 key_linepos = 1;
608                 //UIM_SetCursor(key_linepos);
609                 return;
610         }
611
612         if (key == 'q' && keydown[K_CTRL]) // like zsh ^q: push line to history, don't execute, and clear
613         {
614                 // clear line
615                 Key_History_Push();
616                 key_line[0] = ']';
617                 key_line[1] = 0;
618                 key_linepos = 1;
619                 //UIM_SetCursor(key_linepos);
620                 return;
621         }
622
623         if (key == K_ENTER || key == K_KP_ENTER)
624         {
625                 Cbuf_AddText (key_line+1);      // skip the ]
626                 Cbuf_AddText ("\n");
627                 Key_History_Push();
628                 key_line[0] = ']';
629                 key_line[1] = 0;        // EvilTypeGuy: null terminate
630                 key_linepos = 1;
631                 //UIM_CancelBuffer();
632                 //uim_in_console = false;
633                 // force an update, because the command may take some time
634                 if (cls.state == ca_disconnected)
635                         CL_UpdateScreen ();
636                 return;
637         }
638
639         if (key == K_TAB)
640         {
641                 if(keydown[K_CTRL]) // append to the cvar its value
642                 {
643                         int             cvar_len, cvar_str_len, chars_to_move;
644                         char    k;
645                         char    cvar[MAX_INPUTLINE];
646                         const char *cvar_str;
647                         
648                         // go to the start of the variable
649                         while(--key_linepos)
650                         {
651                                 k = key_line[key_linepos];
652                                 if(k == '\"' || k == ';' || k == ' ' || k == '\'')
653                                         break;
654                         }
655                         key_linepos++;
656                         
657                         // save the variable name in cvar
658                         for(cvar_len=0; (k = key_line[key_linepos + cvar_len]) != 0; cvar_len++)
659                         {
660                                 if(k == '\"' || k == ';' || k == ' ' || k == '\'')
661                                         break;
662                                 cvar[cvar_len] = k;
663                         }
664                         if (cvar_len==0)
665                         {
666                                 //UIM_SetCursor(key_linepos);
667                                 return;
668                         }
669                         cvar[cvar_len] = 0;
670                         
671                         // go to the end of the cvar
672                         key_linepos += cvar_len;
673                         
674                         // save the content of the variable in cvar_str
675                         cvar_str = Cvar_VariableString(cvar);
676                         cvar_str_len = strlen(cvar_str);
677                         if (cvar_str_len==0)
678                         {
679                                 //UIM_SetCursor(key_linepos);
680                                 return;
681                         }
682                         
683                         // insert space and cvar_str in key_line
684                         chars_to_move = strlen(&key_line[key_linepos]);
685                         if (key_linepos + 1 + cvar_str_len + chars_to_move < MAX_INPUTLINE)
686                         {
687                                 if (chars_to_move)
688                                         memmove(&key_line[key_linepos + 1 + cvar_str_len], &key_line[key_linepos], chars_to_move);
689                                 key_line[key_linepos++] = ' ';
690                                 memcpy(&key_line[key_linepos], cvar_str, cvar_str_len);
691                                 key_linepos += cvar_str_len;
692                                 key_line[key_linepos + chars_to_move] = 0;
693                         }
694                         else
695                                 Con_Printf("Couldn't append cvar value, edit line too long.\n");
696                         //UIM_SetCursor(key_linepos);
697                         return;
698                 }
699                 // Enhanced command completion
700                 // by EvilTypeGuy eviltypeguy@qeradiant.com
701                 // Thanks to Fett, Taniwha
702                 Con_CompleteCommandLine();
703                 return;
704         }
705
706         // Advanced Console Editing by Radix radix@planetquake.com
707         // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
708         // Enhanced by [515]
709         // Enhanced by terencehill
710
711         // move cursor to the previous character
712         if (key == K_LEFTARROW || key == K_KP_LEFTARROW)
713         {
714                 if (key_linepos < 2)
715                         return;
716                 if(keydown[K_CTRL]) // move cursor to the previous word
717                 {
718                         int             pos;
719                         char    k;
720                         pos = key_linepos-1;
721
722                         if(pos) // skip all "; ' after the word
723                                 while(--pos)
724                                 {
725                                         k = key_line[pos];
726                                         if (!(k == '\"' || k == ';' || k == ' ' || k == '\''))
727                                                 break;
728                                 }
729
730                         if(pos)
731                                 while(--pos)
732                                 {
733                                         k = key_line[pos];
734                                         if(k == '\"' || k == ';' || k == ' ' || k == '\'')
735                                                 break;
736                                 }
737                         key_linepos = pos + 1;
738                 }
739                 else if(keydown[K_SHIFT]) // move cursor to the previous character ignoring colors
740                 {
741                         int             pos;
742                         size_t          inchar;
743                         pos = u8_prevbyte(key_line, key_linepos);
744                         while (pos)
745                                 if(pos-1 > 0 && key_line[pos-1] == STRING_COLOR_TAG && isdigit(key_line[pos]))
746                                         pos-=2;
747                                 else if(pos-4 > 0 && key_line[pos-4] == STRING_COLOR_TAG && key_line[pos-3] == STRING_COLOR_RGB_TAG_CHAR
748                                                 && isxdigit(key_line[pos-2]) && isxdigit(key_line[pos-1]) && isxdigit(key_line[pos]))
749                                         pos-=5;
750                                 else
751                                 {
752                                         if(pos-1 > 0 && key_line[pos-1] == STRING_COLOR_TAG && key_line[pos] == STRING_COLOR_TAG) // consider ^^ as a character
753                                                 pos--;
754                                         pos--;
755                                         break;
756                                 }
757                         // we need to move to the beginning of the character when in a wide character:
758                         u8_charidx(key_line, pos + 1, &inchar);
759                         key_linepos = pos + 1 - inchar;
760                 }
761                 else
762                 {
763                         key_linepos = u8_prevbyte(key_line, key_linepos);
764                 }
765                 //UIM_SetCursor(key_linepos);
766                 return;
767         }
768
769         // delete char before cursor
770         if (key == K_BACKSPACE || (key == 'h' && keydown[K_CTRL]))
771         {
772                 if (key_linepos > 1)
773                 {
774                         int newpos = u8_prevbyte(key_line, key_linepos);
775                         strlcpy(key_line + newpos, key_line + key_linepos, sizeof(key_line) + 1 - key_linepos);
776                         key_linepos = newpos;
777                 }
778                 //UIM_SetCursor(key_linepos);
779                 return;
780         }
781
782         // delete char on cursor
783         if (key == K_DEL || key == K_KP_DEL)
784         {
785                 size_t linelen;
786                 linelen = strlen(key_line);
787                 if (key_linepos < (int)linelen)
788                         memmove(key_line + key_linepos, key_line + key_linepos + u8_bytelen(key_line + key_linepos, 1), linelen - key_linepos);
789                 return;
790         }
791
792
793         // move cursor to the next character
794         if (key == K_RIGHTARROW || key == K_KP_RIGHTARROW)
795         {
796                 if (key_linepos >= (int)strlen(key_line))
797                         return;
798                 if(keydown[K_CTRL]) // move cursor to the next word
799                 {
800                         int             pos, len;
801                         char    k;
802                         len = (int)strlen(key_line);
803                         pos = key_linepos;
804
805                         while(++pos < len)
806                         {
807                                 k = key_line[pos];
808                                 if(k == '\"' || k == ';' || k == ' ' || k == '\'')
809                                         break;
810                         }
811                         
812                         if (pos < len) // skip all "; ' after the word
813                                 while(++pos < len)
814                                 {
815                                         k = key_line[pos];
816                                         if (!(k == '\"' || k == ';' || k == ' ' || k == '\''))
817                                                 break;
818                                 }
819                         key_linepos = pos;
820                 }
821                 else if(keydown[K_SHIFT]) // move cursor to the next character ignoring colors
822                 {
823                         int             pos, len;
824                         len = (int)strlen(key_line);
825                         pos = key_linepos;
826                         
827                         // go beyond all initial consecutive color tags, if any
828                         if(pos < len)
829                                 while (key_line[pos] == STRING_COLOR_TAG)
830                                 {
831                                         if(isdigit(key_line[pos+1]))
832                                                 pos+=2;
833                                         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]))
834                                                 pos+=5;
835                                         else
836                                                 break;
837                                 }
838                         
839                         // skip the char
840                         if (key_line[pos] == STRING_COLOR_TAG && key_line[pos+1] == STRING_COLOR_TAG) // consider ^^ as a character
841                                 pos++;
842                         pos += u8_bytelen(key_line + pos, 1);
843                         
844                         // now go beyond all next consecutive color tags, if any
845                         if(pos < len)
846                                 while (key_line[pos] == STRING_COLOR_TAG)
847                                 {
848                                         if(isdigit(key_line[pos+1]))
849                                                 pos+=2;
850                                         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]))
851                                                 pos+=5;
852                                         else
853                                                 break;
854                                 }
855                         key_linepos = pos;
856                 }
857                 else
858                         key_linepos += u8_bytelen(key_line + key_linepos, 1);
859                 //UIM_SetCursor(key_linepos);
860                 return;
861         }
862
863         if (key == K_INS || key == K_KP_INS) // toggle insert mode
864         {
865                 key_insert ^= 1;
866                 return;
867         }
868
869         // End Advanced Console Editing
870
871         if (key == K_UPARROW || key == K_KP_UPARROW || (key == 'p' && keydown[K_CTRL]))
872         {
873                 Key_History_Up();
874                 //UIM_SetCursor(key_linepos);
875                 return;
876         }
877
878         if (key == K_DOWNARROW || key == K_KP_DOWNARROW || (key == 'n' && keydown[K_CTRL]))
879         {
880                 Key_History_Down();
881                 //UIM_SetCursor(key_linepos);
882                 return;
883         }
884         // ~1.0795 = 82/76  using con_textsize 64 76 is height of the char, 6 is the distance between 2 lines
885         if (key == K_PGUP || key == K_KP_PGUP)
886         {
887                 if(keydown[K_CTRL])
888                 {
889                         con_backscroll += ((vid_conheight.integer >> 2) / con_textsize.integer)-1;
890                 }
891                 else
892                         con_backscroll += ((vid_conheight.integer >> 1) / con_textsize.integer)-3;
893                 return;
894         }
895
896         if (key == K_PGDN || key == K_KP_PGDN)
897         {
898                 if(keydown[K_CTRL])
899                 {
900                         con_backscroll -= ((vid_conheight.integer >> 2) / con_textsize.integer)-1;
901                 }
902                 else
903                         con_backscroll -= ((vid_conheight.integer >> 1) / con_textsize.integer)-3;
904                 return;
905         }
906  
907         if (key == K_MWHEELUP)
908         {
909                 if(keydown[K_CTRL])
910                         con_backscroll += 1;
911                 else if(keydown[K_SHIFT])
912                         con_backscroll += ((vid_conheight.integer >> 2) / con_textsize.integer)-1;
913                 else
914                         con_backscroll += 5;
915                 return;
916         }
917
918         if (key == K_MWHEELDOWN)
919         {
920                 if(keydown[K_CTRL])
921                         con_backscroll -= 1;
922                 else if(keydown[K_SHIFT])
923                         con_backscroll -= ((vid_conheight.integer >> 2) / con_textsize.integer)-1;
924                 else
925                         con_backscroll -= 5;
926                 return;
927         }
928
929         if (keydown[K_CTRL])
930         {
931                 // text zoom in
932                 if (key == '+' || key == K_KP_PLUS)
933                 {
934                         if (con_textsize.integer < 128)
935                                 Cvar_SetValueQuick(&con_textsize, con_textsize.integer + 1);
936                         return;
937                 }
938                 // text zoom out
939                 if (key == '-' || key == K_KP_MINUS)
940                 {
941                         if (con_textsize.integer > 1)
942                                 Cvar_SetValueQuick(&con_textsize, con_textsize.integer - 1);
943                         return;
944                 }
945                 // text zoom reset
946                 if (key == '0' || key == K_KP_INS)
947                 {
948                         Cvar_SetValueQuick(&con_textsize, atoi(Cvar_VariableDefString("con_textsize")));
949                         return;
950                 }
951         }
952
953         if (key == K_HOME || key == K_KP_HOME)
954         {
955                 if (keydown[K_CTRL])
956                         con_backscroll = INT_MAX;
957                 else
958                         key_linepos = 1;
959                 //UIM_SetCursor(key_linepos);
960                 return;
961         }
962
963         if (key == K_END || key == K_KP_END)
964         {
965                 if (keydown[K_CTRL])
966                         con_backscroll = 0;
967                 else
968                         key_linepos = (int)strlen(key_line);
969                 //UIM_SetCursor(key_linepos);
970                 return;
971         }
972
973         // non printable
974         if (unicode < 32)
975                 return;
976
977         if (key_linepos < MAX_INPUTLINE-1)
978         {
979                 char buf[16];
980                 int len;
981                 int blen;
982                 blen = u8_fromchar(unicode, buf, sizeof(buf));
983                 if (!blen)
984                         return;
985                 len = (int)strlen(&key_line[key_linepos]);
986                 // check insert mode, or always insert if at end of line
987                 if (key_insert || len == 0)
988                 {
989                         // can't use strcpy to move string to right
990                         len++;
991                         //memmove(&key_line[key_linepos + u8_bytelen(key_line + key_linepos, 1)], &key_line[key_linepos], len);
992                         memmove(&key_line[key_linepos + blen], &key_line[key_linepos], len);
993                 }
994                 memcpy(key_line + key_linepos, buf, blen);
995                 key_linepos += blen;
996                 //UIM_SetCursor(key_linepos);
997                 //key_linepos += u8_fromchar(unicode, key_line + key_linepos, sizeof(key_line) - key_linepos - 1);
998                 //key_line[key_linepos] = ascii;
999                 //key_linepos++;
1000         }
1001 }
1002
1003 //============================================================================
1004
1005 int chat_mode;
1006 char            chat_buffer[MAX_INPUTLINE];
1007 unsigned int    chat_bufferlen = 0;
1008
1009 extern int Nicks_CompleteChatLine(char *buffer, size_t size, unsigned int pos);
1010
1011 static qboolean
1012 Key_Message (int key, int ascii)
1013 {
1014         if (!UIM_Direct())
1015         {
1016                 if (UIM_Key(key, ascii))
1017                         return true;
1018         }
1019         if (key >= K_F1 && key <= K_F12)
1020                 return false;
1021
1022         if (key == K_ENTER || ascii == 10 || ascii == 13)
1023         {
1024                 if(chat_mode < 0)
1025                         Cmd_ExecuteString(chat_buffer, src_command); // not Cbuf_AddText to allow semiclons in args; however, this allows no variables then. Use aliases!
1026                 else
1027                         Cmd_ForwardStringToServer(va("%s %s", chat_mode ? "say_team" : "say ", chat_buffer));
1028
1029                 key_dest = key_game;
1030                 chat_bufferlen = 0;
1031                 chat_buffer[0] = 0;
1032                 UIM_CancelBuffer();
1033                 return true;
1034         }
1035
1036         // TODO add support for arrow keys and simple editing
1037         if (key == K_ESCAPE) {
1038                 UIM_CancelBuffer();
1039                 key_dest = key_game;
1040                 chat_bufferlen = 0;
1041                 chat_buffer[0] = 0;
1042                 return true;
1043         }
1044
1045         if (key == K_BACKSPACE) {
1046                 if (chat_bufferlen) {
1047                         chat_bufferlen = u8_prevbyte(chat_buffer, chat_bufferlen);
1048                         UIM_SetCursor(chat_bufferlen);
1049                         chat_buffer[chat_bufferlen] = 0;
1050                 }
1051                 return true;
1052         }
1053
1054         if(key == K_TAB) {
1055                 chat_bufferlen = Nicks_CompleteChatLine(chat_buffer, sizeof(chat_buffer), chat_bufferlen);
1056                 UIM_SetCursor(chat_bufferlen);
1057                 return true;
1058         }
1059
1060         if (chat_bufferlen == sizeof (chat_buffer) - 1)
1061                 return true;                                            // all full
1062
1063         if (!ascii)
1064                 return true;                                            // non printable
1065
1066         if (keydown[K_CTRL] || keydown[K_ALT]) // no evil trickery, yeah?
1067                 return true;
1068
1069         chat_bufferlen += u8_fromchar(ascii, chat_buffer+chat_bufferlen, sizeof(chat_buffer) - chat_bufferlen - 1);
1070         UIM_SetCursor(chat_bufferlen);
1071
1072         //chat_buffer[chat_bufferlen++] = ascii;
1073         //chat_buffer[chat_bufferlen] = 0;
1074         return true;
1075 }
1076
1077 //============================================================================
1078
1079
1080 /*
1081 ===================
1082 Returns a key number to be used to index keybindings[] by looking at
1083 the given string.  Single ascii characters return themselves, while
1084 the K_* names are matched up.
1085 ===================
1086 */
1087 int
1088 Key_StringToKeynum (const char *str)
1089 {
1090         const keyname_t  *kn;
1091
1092         if (!str || !str[0])
1093                 return -1;
1094         if (!str[1])
1095                 return tolower(str[0]);
1096
1097         for (kn = keynames; kn->name; kn++) {
1098                 if (!strcasecmp (str, kn->name))
1099                         return kn->keynum;
1100         }
1101         return -1;
1102 }
1103
1104 /*
1105 ===================
1106 Returns a string (either a single ascii char, or a K_* name) for the
1107 given keynum.
1108 FIXME: handle quote special (general escape sequence?)
1109 ===================
1110 */
1111 const char *
1112 Key_KeynumToString (int keynum)
1113 {
1114         const keyname_t  *kn;
1115         static char tinystr[2];
1116
1117         // -1 is an invalid code
1118         if (keynum < 0)
1119                 return "<KEY NOT FOUND>";
1120
1121         // search overrides first, because some characters are special
1122         for (kn = keynames; kn->name; kn++)
1123                 if (keynum == kn->keynum)
1124                         return kn->name;
1125
1126         // if it is printable, output it as a single character
1127         if (keynum > 32 && keynum < 256)
1128         {
1129                 tinystr[0] = keynum;
1130                 tinystr[1] = 0;
1131                 return tinystr;
1132         }
1133
1134         // if it is not overridden and not printable, we don't know what to do with it
1135         return "<UNKNOWN KEYNUM>";
1136 }
1137
1138
1139 void
1140 Key_SetBinding (int keynum, int bindmap, const char *binding)
1141 {
1142         char *newbinding;
1143         size_t l;
1144
1145         if (keynum == -1 || keynum >= MAX_KEYS)
1146                 return;
1147
1148 // free old bindings
1149         if (keybindings[bindmap][keynum]) {
1150                 Z_Free (keybindings[bindmap][keynum]);
1151                 keybindings[bindmap][keynum] = NULL;
1152         }
1153         if(!binding[0]) // make "" binds be removed --blub
1154                 return;
1155 // allocate memory for new binding
1156         l = strlen (binding);
1157         newbinding = (char *)Z_Malloc (l + 1);
1158         memcpy (newbinding, binding, l + 1);
1159         newbinding[l] = 0;
1160         keybindings[bindmap][keynum] = newbinding;
1161 }
1162
1163 static void
1164 Key_In_Unbind_f (void)
1165 {
1166         int         b, m;
1167         char *errchar = NULL;
1168
1169         if (Cmd_Argc () != 3) {
1170                 Con_Print("in_unbind <bindmap> <key> : remove commands from a key\n");
1171                 return;
1172         }
1173
1174         m = strtol(Cmd_Argv (1), &errchar, 0);
1175         if ((m < 0) || (m >= 8) || (errchar && *errchar)) {
1176                 Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(1));
1177                 return;
1178         }
1179
1180         b = Key_StringToKeynum (Cmd_Argv (2));
1181         if (b == -1) {
1182                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (2));
1183                 return;
1184         }
1185
1186         Key_SetBinding (b, m, "");
1187 }
1188
1189 static void
1190 Key_In_Bind_f (void)
1191 {
1192         int         i, c, b, m;
1193         char        cmd[MAX_INPUTLINE];
1194         char *errchar = NULL;
1195
1196         c = Cmd_Argc ();
1197
1198         if (c != 3 && c != 4) {
1199                 Con_Print("in_bind <bindmap> <key> [command] : attach a command to a key\n");
1200                 return;
1201         }
1202
1203         m = strtol(Cmd_Argv (1), &errchar, 0);
1204         if ((m < 0) || (m >= 8) || (errchar && *errchar)) {
1205                 Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(1));
1206                 return;
1207         }
1208
1209         b = Key_StringToKeynum (Cmd_Argv (2));
1210         if (b == -1 || b >= MAX_KEYS) {
1211                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (2));
1212                 return;
1213         }
1214
1215         if (c == 3) {
1216                 if (keybindings[m][b])
1217                         Con_Printf("\"%s\" = \"%s\"\n", Cmd_Argv (2), keybindings[m][b]);
1218                 else
1219                         Con_Printf("\"%s\" is not bound\n", Cmd_Argv (2));
1220                 return;
1221         }
1222 // copy the rest of the command line
1223         cmd[0] = 0;                                                     // start out with a null string
1224         for (i = 3; i < c; i++) {
1225                 strlcat (cmd, Cmd_Argv (i), sizeof (cmd));
1226                 if (i != (c - 1))
1227                         strlcat (cmd, " ", sizeof (cmd));
1228         }
1229
1230         Key_SetBinding (b, m, cmd);
1231 }
1232
1233 static void
1234 Key_In_Bindmap_f (void)
1235 {
1236         int         m1, m2, c;
1237         char *errchar = NULL;
1238
1239         c = Cmd_Argc ();
1240
1241         if (c != 3) {
1242                 Con_Print("in_bindmap <bindmap> <fallback>: set current bindmap and fallback\n");
1243                 return;
1244         }
1245
1246         m1 = strtol(Cmd_Argv (1), &errchar, 0);
1247         if ((m1 < 0) || (m1 >= 8) || (errchar && *errchar)) {
1248                 Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(1));
1249                 return;
1250         }
1251
1252         m2 = strtol(Cmd_Argv (2), &errchar, 0);
1253         if ((m2 < 0) || (m2 >= 8) || (errchar && *errchar)) {
1254                 Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(2));
1255                 return;
1256         }
1257
1258         key_bmap = m1;
1259         key_bmap2 = m2;
1260 }
1261
1262 static void
1263 Key_Unbind_f (void)
1264 {
1265         int         b;
1266
1267         if (Cmd_Argc () != 2) {
1268                 Con_Print("unbind <key> : remove commands from a key\n");
1269                 return;
1270         }
1271
1272         b = Key_StringToKeynum (Cmd_Argv (1));
1273         if (b == -1) {
1274                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (1));
1275                 return;
1276         }
1277
1278         Key_SetBinding (b, 0, "");
1279 }
1280
1281 static void
1282 Key_Unbindall_f (void)
1283 {
1284         int         i, j;
1285
1286         for (j = 0; j < 8; j++)
1287                 for (i = 0; i < (int)(sizeof(keybindings[0])/sizeof(keybindings[0][0])); i++)
1288                         if (keybindings[j][i])
1289                                 Key_SetBinding (i, j, "");
1290 }
1291
1292 static void
1293 Key_PrintBindList(int j)
1294 {
1295         char bindbuf[MAX_INPUTLINE];
1296         const char *p;
1297         int i;
1298
1299         for (i = 0; i < (int)(sizeof(keybindings[0])/sizeof(keybindings[0][0])); i++)
1300         {
1301                 p = keybindings[j][i];
1302                 if (p)
1303                 {
1304                         Cmd_QuoteString(bindbuf, sizeof(bindbuf), p, "\"\\");
1305                         if (j == 0)
1306                                 Con_Printf("^2%s ^7= \"%s\"\n", Key_KeynumToString (i), bindbuf);
1307                         else
1308                                 Con_Printf("^3bindmap %d: ^2%s ^7= \"%s\"\n", j, Key_KeynumToString (i), bindbuf);
1309                 }
1310         }
1311 }
1312
1313 static void
1314 Key_In_BindList_f (void)
1315 {
1316         int m;
1317         char *errchar = NULL;
1318
1319         if(Cmd_Argc() >= 2)
1320         {
1321                 m = strtol(Cmd_Argv(1), &errchar, 0);
1322                 if ((m < 0) || (m >= 8) || (errchar && *errchar)) {
1323                         Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(1));
1324                         return;
1325                 }
1326                 Key_PrintBindList(m);
1327         }
1328         else
1329         {
1330                 for (m = 0; m < MAX_BINDMAPS; m++)
1331                         Key_PrintBindList(m);
1332         }
1333 }
1334
1335 static void
1336 Key_BindList_f (void)
1337 {
1338         Key_PrintBindList(0);
1339 }
1340
1341 static void
1342 Key_Bind_f (void)
1343 {
1344         int         i, c, b;
1345         char        cmd[MAX_INPUTLINE];
1346
1347         c = Cmd_Argc ();
1348
1349         if (c != 2 && c != 3) {
1350                 Con_Print("bind <key> [command] : attach a command to a key\n");
1351                 return;
1352         }
1353         b = Key_StringToKeynum (Cmd_Argv (1));
1354         if (b == -1 || b >= MAX_KEYS) {
1355                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (1));
1356                 return;
1357         }
1358
1359         if (c == 2) {
1360                 if (keybindings[0][b])
1361                         Con_Printf("\"%s\" = \"%s\"\n", Cmd_Argv (1), keybindings[0][b]);
1362                 else
1363                         Con_Printf("\"%s\" is not bound\n", Cmd_Argv (1));
1364                 return;
1365         }
1366 // copy the rest of the command line
1367         cmd[0] = 0;                                                     // start out with a null string
1368         for (i = 2; i < c; i++) {
1369                 strlcat (cmd, Cmd_Argv (i), sizeof (cmd));
1370                 if (i != (c - 1))
1371                         strlcat (cmd, " ", sizeof (cmd));
1372         }
1373
1374         Key_SetBinding (b, 0, cmd);
1375 }
1376
1377 /*
1378 ============
1379 Writes lines containing "bind key value"
1380 ============
1381 */
1382 void
1383 Key_WriteBindings (qfile_t *f)
1384 {
1385         int         i, j;
1386         char bindbuf[MAX_INPUTLINE];
1387         const char *p;
1388
1389         for (j = 0; j < MAX_BINDMAPS; j++)
1390         {
1391                 for (i = 0; i < (int)(sizeof(keybindings[0])/sizeof(keybindings[0][0])); i++)
1392                 {
1393                         p = keybindings[j][i];
1394                         if (p)
1395                         {
1396                                 Cmd_QuoteString(bindbuf, sizeof(bindbuf), p, "\"\\"); // don't need to escape $ because cvars are not expanded inside bind
1397                                 if (j == 0)
1398                                         FS_Printf(f, "bind %s \"%s\"\n", Key_KeynumToString (i), bindbuf);
1399                                 else
1400                                         FS_Printf(f, "in_bind %d %s \"%s\"\n", j, Key_KeynumToString (i), bindbuf);
1401                         }
1402                 }
1403         }
1404 }
1405
1406
1407 void
1408 Key_Init (void)
1409 {
1410         Key_History_Init();
1411         key_line[0] = ']';
1412         key_line[1] = 0;
1413         key_linepos = 1;
1414
1415 //
1416 // register our functions
1417 //
1418         Cmd_AddCommand ("in_bind", Key_In_Bind_f, "binds a command to the specified key in the selected bindmap");
1419         Cmd_AddCommand ("in_unbind", Key_In_Unbind_f, "removes command on the specified key in the selected bindmap");
1420         Cmd_AddCommand ("in_bindlist", Key_In_BindList_f, "bindlist: displays bound keys for all bindmaps, or the given bindmap");
1421         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");
1422
1423         Cmd_AddCommand ("bind", Key_Bind_f, "binds a command to the specified key in bindmap 0");
1424         Cmd_AddCommand ("unbind", Key_Unbind_f, "removes a command on the specified key in bindmap 0");
1425         Cmd_AddCommand ("bindlist", Key_BindList_f, "bindlist: displays bound keys for bindmap 0 bindmaps");
1426         Cmd_AddCommand ("unbindall", Key_Unbindall_f, "removes all commands from all keys in all bindmaps (leaving only shift-escape and escape)");
1427
1428         Cvar_RegisterVariable (&con_closeontoggleconsole);
1429 }
1430
1431 void
1432 Key_Shutdown (void)
1433 {
1434         Key_History_Shutdown();
1435 }
1436
1437 const char *Key_GetBind (int key)
1438 {
1439         const char *bind;
1440         if (key < 0 || key >= MAX_KEYS)
1441                 return NULL;
1442         bind = keybindings[key_bmap][key];
1443         if (!bind)
1444                 bind = keybindings[key_bmap2][key];
1445         return bind;
1446 }
1447
1448 qboolean CL_VM_InputEvent (qboolean down, int key, int ascii);
1449
1450 /*
1451 ===================
1452 Called by the system between frames for both key up and key down events
1453 Should NOT be called during an interrupt!
1454 ===================
1455 */
1456 static char tbl_keyascii[MAX_KEYS];
1457 static keydest_t tbl_keydest[MAX_KEYS];
1458
1459 void
1460 Key_Event (int key, int ascii, qboolean down)
1461 {
1462         const char *bind;
1463         qboolean q;
1464         keydest_t keydest = key_dest;
1465
1466         if (key < 0 || key >= MAX_KEYS)
1467                 return;
1468
1469         // get key binding
1470         bind = keybindings[key_bmap][key];
1471         if (!bind)
1472                 bind = keybindings[key_bmap2][key];
1473
1474         if (developer.integer >= 1000)
1475                 Con_Printf("Key_Event(%i, '%c', %s) keydown %i bind \"%s\"\n", key, ascii, down ? "down" : "up", keydown[key], bind ? bind : "");
1476
1477         if (key_consoleactive)
1478         {
1479                 /*
1480                 if (!uim_in_console)
1481                 {
1482                         UIM_EnterBuffer(key_line, sizeof(key_line), key_linepos, &Key_Console_SetCursor);
1483                         uim_in_console = true;
1484                 }
1485                 */
1486                 keydest = key_console;
1487         }
1488         else if (uim_in_console)
1489         {
1490                 /*
1491                 UIM_CancelBuffer();
1492                 uim_in_console = false;
1493                 */
1494         }
1495         
1496         if (down)
1497         {
1498                 // increment key repeat count each time a down is received so that things
1499                 // which want to ignore key repeat can ignore it
1500                 keydown[key] = min(keydown[key] + 1, 2);
1501                 if(keydown[key] == 1) {
1502                         tbl_keyascii[key] = ascii;
1503                         tbl_keydest[key] = keydest;
1504                 } else {
1505                         ascii = tbl_keyascii[key];
1506                         keydest = tbl_keydest[key];
1507                 }
1508         }
1509         else
1510         {
1511                 // clear repeat count now that the key is released
1512                 keydown[key] = 0;
1513                 keydest = tbl_keydest[key];
1514                 ascii = tbl_keyascii[key];
1515         }
1516
1517         if(keydest == key_void)
1518                 return;
1519         
1520         // key_consoleactive is a flag not a key_dest because the console is a
1521         // high priority overlay ontop of the normal screen (designed as a safety
1522         // feature so that developers and users can rescue themselves from a bad
1523         // situation).
1524         //
1525         // this also means that toggling the console on/off does not lose the old
1526         // key_dest state
1527
1528         // specially handle escape (togglemenu) and shift-escape (toggleconsole)
1529         // engine bindings, these are not handled as normal binds so that the user
1530         // can recover from a completely empty bindmap
1531         if (key == K_ESCAPE)
1532         {
1533                 // ignore key repeats on escape
1534                 if (keydown[key] > 1)
1535                         return;
1536
1537                 // escape does these things:
1538                 // key_consoleactive - close console
1539                 // key_message - abort messagemode
1540                 // key_menu - go to parent menu (or key_game)
1541                 // key_game - open menu
1542
1543                 // in all modes shift-escape toggles console
1544                 if (keydown[K_SHIFT])
1545                 {
1546                         if(down)
1547                         {
1548                                 Con_ToggleConsole_f ();
1549                                 tbl_keydest[key] = key_void; // esc release should go nowhere (especially not to key_menu or key_game)
1550                         }
1551                         return;
1552                 }
1553
1554                 switch (keydest)
1555                 {
1556                         case key_console:
1557                                 if(down)
1558                                 {
1559                                         if(key_consoleactive & KEY_CONSOLEACTIVE_FORCED)
1560                                         {
1561                                                 key_consoleactive &= ~KEY_CONSOLEACTIVE_USER;
1562                                                 MR_ToggleMenu_f ();
1563                                         }
1564                                         else
1565                                                 Con_ToggleConsole_f();
1566                                 }
1567                                 break;
1568
1569                         case key_message:
1570                                 if (down)
1571                                         Key_Message (key, ascii); // that'll close the message input
1572                                 break;
1573
1574                         case key_menu:
1575                         case key_menu_grabbed:
1576                                 MR_KeyEvent (key, ascii, down);
1577                                 break;
1578
1579                         case key_game:
1580                                 // csqc has priority over toggle menu if it wants to (e.g. handling escape for UI stuff in-game.. :sick:)
1581                                 q = CL_VM_InputEvent(down, key, ascii);
1582                                 if (!q && down)
1583                                         MR_ToggleMenu_f ();
1584                                 break;
1585
1586                         default:
1587                                 Con_Printf ("Key_Event: Bad key_dest\n");
1588                 }
1589                 return;
1590         }
1591
1592         // send function keydowns to interpreter no matter what mode is (unless the menu has specifically grabbed the keyboard, for rebinding keys)
1593         // NOTE: if UIM is active, let it handle function keys too:
1594         if (keydest == key_message && key >= K_F1 && key <= K_F12)
1595         {
1596                 if (down)
1597                         Key_Message(key, ascii);
1598                 return;
1599         }
1600         if (keydest != key_menu_grabbed)
1601         if (key >= K_F1 && key <= K_F12)
1602         {
1603                 if (bind)
1604                 {
1605                         if(keydown[key] == 1 && down)
1606                         {
1607                                 // button commands add keynum as a parm
1608                                 if (bind[0] == '+')
1609                                         Cbuf_AddText (va("%s %i\n", bind, key));
1610                                 else
1611                                 {
1612                                         Cbuf_AddText (bind);
1613                                         Cbuf_AddText ("\n");
1614                                 }
1615                         } else if(bind[0] == '+' && !down && keydown[key] == 0)
1616                                 Cbuf_AddText(va("-%s %i\n", bind + 1, key));
1617                 }
1618                 return;
1619         }
1620
1621         // send input to console if it wants it
1622         if (keydest == key_console)
1623         {
1624                 if (!down)
1625                         return;
1626                 // con_closeontoggleconsole enables toggleconsole keys to close the
1627                 // console, as long as they are not the color prefix character
1628                 // (special exemption for german keyboard layouts)
1629                 if (con_closeontoggleconsole.integer && bind && !strncmp(bind, "toggleconsole", strlen("toggleconsole")) && (key_consoleactive & KEY_CONSOLEACTIVE_USER) && ascii != STRING_COLOR_TAG)
1630                 {
1631                         Con_ToggleConsole_f ();
1632                         return;
1633                 }
1634                 Key_Console (key, ascii);
1635                 return;
1636         }
1637
1638         // handle toggleconsole in menu too
1639         if (keydest == key_menu)
1640         {
1641                 if (down && con_closeontoggleconsole.integer && bind && !strncmp(bind, "toggleconsole", strlen("toggleconsole")) && ascii != STRING_COLOR_TAG)
1642                 {
1643                         Con_ToggleConsole_f ();
1644                         tbl_keydest[key] = key_void; // key release should go nowhere (especially not to key_menu or key_game)
1645                         return;
1646                 }
1647         }
1648
1649         // ignore binds while a video is played, let the video system handle the key event
1650         if (cl_videoplaying)
1651         {
1652                 CL_Video_KeyEvent (key, ascii, keydown[key] != 0);
1653                 return;
1654         }
1655
1656         // anything else is a key press into the game, chat line, or menu
1657         switch (keydest)
1658         {
1659                 case key_message:
1660                         if (down)
1661                                 Key_Message (key, ascii);
1662                         break;
1663                 case key_menu:
1664                 case key_menu_grabbed:
1665                         MR_KeyEvent (key, ascii, down);
1666                         break;
1667                 case key_game:
1668                         q = CL_VM_InputEvent(down, key, ascii);
1669                         // ignore key repeats on binds and only send the bind if the event hasnt been already processed by csqc
1670                         if (!q && bind)
1671                         {
1672                                 if(keydown[key] == 1 && down)
1673                                 {
1674                                         // button commands add keynum as a parm
1675                                         if (bind[0] == '+')
1676                                                 Cbuf_AddText (va("%s %i\n", bind, key));
1677                                         else
1678                                         {
1679                                                 Cbuf_AddText (bind);
1680                                                 Cbuf_AddText ("\n");
1681                                         }
1682                                 } else if(bind[0] == '+' && !down && keydown[key] == 0)
1683                                         Cbuf_AddText(va("-%s %i\n", bind + 1, key));
1684                         }
1685                         break;
1686                 default:
1687                         Con_Printf ("Key_Event: Bad key_dest\n");
1688         }
1689 }
1690
1691 /*
1692 ===================
1693 Key_ClearStates
1694 ===================
1695 */
1696 void
1697 Key_ClearStates (void)
1698 {
1699         memset(keydown, 0, sizeof(keydown));
1700 }
1701
1702 /*
1703 ==================
1704 Key_IsPressed
1705 ==================
1706 */
1707 qboolean Key_IsPressed (int key)
1708 {
1709         if (key < 0 || key >= (int)sizeof(keydown))
1710                 return false;
1711         // handle repeats differently?
1712         if (keydown[key])
1713                 return true;
1714         return false;
1715 }