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