remove prvm_boundscheck cvar (security hole) and force bounds check on unless DP...
[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
26 cvar_t con_closeontoggleconsole = {CVAR_SAVE, "con_closeontoggleconsole","1", "allows toggleconsole binds to close the console as well"};
27
28 /*
29 key up events are sent even if in console mode
30 */
31
32 int                     edit_line = MAX_INPUTLINES-1;
33 int                     history_line = MAX_INPUTLINES-1;
34 char            key_lines[MAX_INPUTLINES][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
41 static int      key_bmap, key_bmap2;
42 static unsigned char keydown[MAX_KEYS]; // 0 = up, 1 = down, 2 = repeating
43
44 typedef struct keyname_s
45 {
46         const char      *name;
47         int                     keynum;
48 }
49 keyname_t;
50
51 static const keyname_t   keynames[] = {
52         {"TAB", K_TAB},
53         {"ENTER", K_ENTER},
54         {"ESCAPE", K_ESCAPE},
55         {"SPACE", K_SPACE},
56
57         // spacer so it lines up with keys.h
58
59         {"BACKSPACE", K_BACKSPACE},
60         {"UPARROW", K_UPARROW},
61         {"DOWNARROW", K_DOWNARROW},
62         {"LEFTARROW", K_LEFTARROW},
63         {"RIGHTARROW", K_RIGHTARROW},
64
65         {"ALT", K_ALT},
66         {"CTRL", K_CTRL},
67         {"SHIFT", K_SHIFT},
68
69         {"F1", K_F1},
70         {"F2", K_F2},
71         {"F3", K_F3},
72         {"F4", K_F4},
73         {"F5", K_F5},
74         {"F6", K_F6},
75         {"F7", K_F7},
76         {"F8", K_F8},
77         {"F9", K_F9},
78         {"F10", K_F10},
79         {"F11", K_F11},
80         {"F12", K_F12},
81
82         {"INS", K_INS},
83         {"DEL", K_DEL},
84         {"PGDN", K_PGDN},
85         {"PGUP", K_PGUP},
86         {"HOME", K_HOME},
87         {"END", K_END},
88
89         {"PAUSE", K_PAUSE},
90
91         {"NUMLOCK", K_NUMLOCK},
92         {"CAPSLOCK", K_CAPSLOCK},
93         {"SCROLLOCK", K_SCROLLOCK},
94
95         {"KP_INS",                      K_KP_INS },
96         {"KP_0", K_KP_0},
97         {"KP_END",                      K_KP_END },
98         {"KP_1", K_KP_1},
99         {"KP_DOWNARROW",        K_KP_DOWNARROW },
100         {"KP_2", K_KP_2},
101         {"KP_PGDN",                     K_KP_PGDN },
102         {"KP_3", K_KP_3},
103         {"KP_LEFTARROW",        K_KP_LEFTARROW },
104         {"KP_4", K_KP_4},
105         {"KP_5", K_KP_5},
106         {"KP_RIGHTARROW",       K_KP_RIGHTARROW },
107         {"KP_6", K_KP_6},
108         {"KP_HOME",                     K_KP_HOME },
109         {"KP_7", K_KP_7},
110         {"KP_UPARROW",          K_KP_UPARROW },
111         {"KP_8", K_KP_8},
112         {"KP_PGUP",                     K_KP_PGUP },
113         {"KP_9", K_KP_9},
114         {"KP_DEL",                      K_KP_DEL },
115         {"KP_PERIOD", K_KP_PERIOD},
116         {"KP_SLASH",            K_KP_SLASH },
117         {"KP_DIVIDE", K_KP_DIVIDE},
118         {"KP_MULTIPLY", K_KP_MULTIPLY},
119         {"KP_MINUS", K_KP_MINUS},
120         {"KP_PLUS", K_KP_PLUS},
121         {"KP_ENTER", K_KP_ENTER},
122         {"KP_EQUALS", K_KP_EQUALS},
123
124
125
126         {"MOUSE1", K_MOUSE1},
127
128         {"MOUSE2", K_MOUSE2},
129         {"MOUSE3", K_MOUSE3},
130         {"MWHEELUP", K_MWHEELUP},
131         {"MWHEELDOWN", K_MWHEELDOWN},
132         {"MOUSE4", K_MOUSE4},
133         {"MOUSE5", K_MOUSE5},
134         {"MOUSE6", K_MOUSE6},
135         {"MOUSE7", K_MOUSE7},
136         {"MOUSE8", K_MOUSE8},
137         {"MOUSE9", K_MOUSE9},
138         {"MOUSE10", K_MOUSE10},
139         {"MOUSE11", K_MOUSE11},
140         {"MOUSE12", K_MOUSE12},
141         {"MOUSE13", K_MOUSE13},
142         {"MOUSE14", K_MOUSE14},
143         {"MOUSE15", K_MOUSE15},
144         {"MOUSE16", K_MOUSE16},
145
146
147
148
149         {"JOY1",  K_JOY1},
150         {"JOY2",  K_JOY2},
151         {"JOY3",  K_JOY3},
152         {"JOY4",  K_JOY4},
153         {"JOY5",  K_JOY5},
154         {"JOY6",  K_JOY6},
155         {"JOY7",  K_JOY7},
156         {"JOY8",  K_JOY8},
157         {"JOY9",  K_JOY9},
158         {"JOY10", K_JOY10},
159         {"JOY11", K_JOY11},
160         {"JOY12", K_JOY12},
161         {"JOY13", K_JOY13},
162         {"JOY14", K_JOY14},
163         {"JOY15", K_JOY15},
164         {"JOY16", K_JOY16},
165
166
167
168
169
170
171         {"AUX1", K_AUX1},
172         {"AUX2", K_AUX2},
173         {"AUX3", K_AUX3},
174         {"AUX4", K_AUX4},
175         {"AUX5", K_AUX5},
176         {"AUX6", K_AUX6},
177         {"AUX7", K_AUX7},
178         {"AUX8", K_AUX8},
179         {"AUX9", K_AUX9},
180         {"AUX10", K_AUX10},
181         {"AUX11", K_AUX11},
182         {"AUX12", K_AUX12},
183         {"AUX13", K_AUX13},
184         {"AUX14", K_AUX14},
185         {"AUX15", K_AUX15},
186         {"AUX16", K_AUX16},
187         {"AUX17", K_AUX17},
188         {"AUX18", K_AUX18},
189         {"AUX19", K_AUX19},
190         {"AUX20", K_AUX20},
191         {"AUX21", K_AUX21},
192         {"AUX22", K_AUX22},
193         {"AUX23", K_AUX23},
194         {"AUX24", K_AUX24},
195         {"AUX25", K_AUX25},
196         {"AUX26", K_AUX26},
197         {"AUX27", K_AUX27},
198         {"AUX28", K_AUX28},
199         {"AUX29", K_AUX29},
200         {"AUX30", K_AUX30},
201         {"AUX31", K_AUX31},
202         {"AUX32", K_AUX32},
203
204         {"SEMICOLON", ';'},                     // because a raw semicolon separates commands
205         {"TILDE", '~'},
206         {"BACKQUOTE", '`'},
207         {"QUOTE", '"'},
208         {"APOSTROPHE", '\''},
209         {"BACKSLASH", '\\'},            // because a raw backslash is used for special characters
210
211         {NULL, 0}
212 };
213
214 /*
215 ==============================================================================
216
217                         LINE TYPING INTO THE CONSOLE
218
219 ==============================================================================
220 */
221
222 void
223 Key_ClearEditLine (int edit_line)
224 {
225         memset (key_lines[edit_line], '\0', sizeof(key_lines[edit_line]));
226         key_lines[edit_line][0] = ']';
227         key_linepos = 1;
228 }
229
230 /*
231 ====================
232 Interactive line editing and console scrollback
233 ====================
234 */
235 static void
236 Key_Console (int key, char ascii)
237 {
238         // LordHavoc: copied most of this from Q2 to improve keyboard handling
239         switch (key)
240         {
241         case K_KP_SLASH:
242                 key = '/';
243                 break;
244         case K_KP_MINUS:
245                 key = '-';
246                 break;
247         case K_KP_PLUS:
248                 key = '+';
249                 break;
250         case K_KP_HOME:
251                 key = '7';
252                 break;
253         case K_KP_UPARROW:
254                 key = '8';
255                 break;
256         case K_KP_PGUP:
257                 key = '9';
258                 break;
259         case K_KP_LEFTARROW:
260                 key = '4';
261                 break;
262         case K_KP_5:
263                 key = '5';
264                 break;
265         case K_KP_RIGHTARROW:
266                 key = '6';
267                 break;
268         case K_KP_END:
269                 key = '1';
270                 break;
271         case K_KP_DOWNARROW:
272                 key = '2';
273                 break;
274         case K_KP_PGDN:
275                 key = '3';
276                 break;
277         case K_KP_INS:
278                 key = '0';
279                 break;
280         case K_KP_DEL:
281                 key = '.';
282                 break;
283         }
284
285         if ((toupper(key) == 'V' && keydown[K_CTRL]) || ((key == K_INS || key == K_KP_INS) && keydown[K_SHIFT]))
286         {
287                 char *cbd;
288                 if ((cbd = Sys_GetClipboardData()) != 0)
289                 {
290                         int i;
291                         strtok(cbd, "\n\r\b");
292                         i = (int)strlen(cbd);
293                         if (i + key_linepos >= MAX_INPUTLINE)
294                                 i= MAX_INPUTLINE - key_linepos;
295                         if (i > 0)
296                         {
297                                 cbd[i]=0;
298                                 strlcat(key_lines[edit_line], cbd, sizeof(key_lines[edit_line]));
299                                 key_linepos += i;
300                         }
301                         Z_Free(cbd);
302                 }
303                 return;
304         }
305
306         if (key == 'l')
307         {
308                 if (keydown[K_CTRL])
309                 {
310                         Cbuf_AddText ("clear\n");
311                         return;
312                 }
313         }
314
315         if (key == K_ENTER || key == K_KP_ENTER)
316         {
317                 Cbuf_AddText (key_lines[edit_line]+1);  // skip the ]
318                 Cbuf_AddText ("\n");
319                 Con_Printf("%s\n",key_lines[edit_line]);
320                 if(key_lines[edit_line][1] == 0) // empty line (just a ])?
321                         return; // no, no, you can't submit empty lines to the history...
322                 // LordHavoc: redesigned edit_line/history_line
323                 edit_line = 31;
324                 history_line = edit_line;
325                 memmove(key_lines[0], key_lines[1], sizeof(key_lines[0]) * edit_line);
326                 key_lines[edit_line][0] = ']';
327                 key_lines[edit_line][1] = 0;    // EvilTypeGuy: null terminate
328                 key_linepos = 1;
329                 // force an update, because the command may take some time
330                 if (cls.state == ca_disconnected)
331                         CL_UpdateScreen ();
332                 return;
333         }
334
335         if (key == K_TAB)
336         {
337                 // Enhanced command completion
338                 // by EvilTypeGuy eviltypeguy@qeradiant.com
339                 // Thanks to Fett, Taniwha
340                 Con_CompleteCommandLine();
341                 return;
342         }
343
344         // Advanced Console Editing by Radix radix@planetquake.com
345         // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
346         // Enhanced by [515]
347
348         // left arrow will just move left one without erasing, backspace will
349         // actually erase character
350         if (key == K_LEFTARROW || key == K_KP_LEFTARROW)
351         {
352                 if (key_linepos < 2)
353                         return;
354                 if(keydown[K_CTRL])
355                 {
356                         int             pos;
357                         char    k;
358                         pos = key_linepos-1;
359                         if(pos)
360                                 while(--pos)
361                                 {
362                                         k = key_lines[edit_line][pos];
363                                         if(k == '\"' || k == ';' || k == ' ' || k == '\'')
364                                                 break;
365                                 }
366                         key_linepos = pos + 1;
367                 }
368                 else
369                         key_linepos--;
370                 return;
371         }
372
373         // delete char before cursor
374         if (key == K_BACKSPACE || (key == 'h' && keydown[K_CTRL]))
375         {
376                 if (key_linepos > 1)
377                 {
378                         strlcpy(key_lines[edit_line] + key_linepos - 1, key_lines[edit_line] + key_linepos, sizeof(key_lines[edit_line]) + 1 - key_linepos);
379                         key_linepos--;
380                 }
381                 return;
382         }
383
384         // delete char on cursor
385         if (key == K_DEL || key == K_KP_DEL)
386         {
387                 size_t linelen;
388                 linelen = strlen(key_lines[edit_line]);
389                 if (key_linepos < (int)linelen)
390                         memmove(key_lines[edit_line] + key_linepos, key_lines[edit_line] + key_linepos + 1, linelen - key_linepos);
391                 return;
392         }
393
394
395         // if we're at the end, get one character from previous line,
396         // otherwise just go right one
397         if (key == K_RIGHTARROW || key == K_KP_RIGHTARROW)
398         {
399                 if (key_linepos >= (int)strlen(key_lines[edit_line]))
400                         return;
401                 if(keydown[K_CTRL])
402                 {
403                         int             pos, len;
404                         char    k;
405                         len = (int)strlen(key_lines[edit_line]);
406                         pos = key_linepos;
407                         while(++pos < len)
408                         {
409                                 k = key_lines[edit_line][pos];
410                                 if(k == '\"' || k == ';' || k == ' ' || k == '\'')
411                                         break;
412                         }
413                         key_linepos = pos;
414                 }
415                 else
416                         key_linepos++;
417                 return;
418         }
419
420         if (key == K_INS || key == K_KP_INS) // toggle insert mode
421         {
422                 key_insert ^= 1;
423                 return;
424         }
425
426         // End Advanced Console Editing
427
428         if (key == K_UPARROW || key == K_KP_UPARROW || (key == 'p' && keydown[K_CTRL]))
429         {
430                 if (history_line > 0 && key_lines[history_line-1][1])
431                 {
432                         size_t linelen;
433                         history_line--;
434                         linelen = strlen(key_lines[history_line]);
435                         memcpy(key_lines[edit_line], key_lines[history_line], linelen + 1);
436                         key_linepos = (int)linelen;
437                 }
438                 return;
439         }
440
441         if (key == K_DOWNARROW || key == K_KP_DOWNARROW || (key == 'n' && keydown[K_CTRL]))
442         {
443                 history_line++;
444                 if (history_line >= edit_line)
445                 {
446                         history_line = edit_line;
447                         key_lines[edit_line][0] = ']';
448                         key_lines[edit_line][1] = 0;
449                         key_linepos = 1;
450                 }
451                 else
452                 {
453                         size_t linelen;
454                         linelen = strlen(key_lines[history_line]);
455                         memcpy(key_lines[edit_line], key_lines[history_line], linelen + 1);
456                         key_linepos = (int)linelen;
457                 }
458                 return;
459         }
460
461         if (key == K_PGUP || key == K_KP_PGUP || key == K_MWHEELUP)
462         {
463                 con_backscroll += ((int) vid_conheight.integer >> 5);
464                 return;
465         }
466
467         if (key == K_PGDN || key == K_KP_PGDN || key == K_MWHEELDOWN)
468         {
469                 con_backscroll -= ((int) vid_conheight.integer >> 5);
470                 return;
471         }
472
473         if (key == K_HOME || key == K_KP_HOME)
474         {
475                 if (keydown[K_CTRL])
476                         con_backscroll = INT_MAX;
477                 else
478                         key_linepos = 1;
479                 return;
480         }
481
482         if (key == K_END || key == K_KP_END)
483         {
484                 if (keydown[K_CTRL])
485                         con_backscroll = 0;
486                 else
487                         key_linepos = (int)strlen(key_lines[edit_line]);
488                 return;
489         }
490
491         // non printable
492         if (ascii < 32)
493                 return;
494
495         if (key_linepos < MAX_INPUTLINE-1)
496         {
497                 int len;
498                 len = (int)strlen(&key_lines[edit_line][key_linepos]);
499                 // check insert mode, or always insert if at end of line
500                 if (key_insert || len == 0)
501                 {
502                         // can't use strcpy to move string to right
503                         len++;
504                         memmove(&key_lines[edit_line][key_linepos + 1], &key_lines[edit_line][key_linepos], len);
505                 }
506                 key_lines[edit_line][key_linepos] = ascii;
507                 key_linepos++;
508         }
509 }
510
511 //============================================================================
512
513 qboolean        chat_team;
514 char            chat_buffer[MAX_INPUTLINE];
515 unsigned int    chat_bufferlen = 0;
516
517 extern int Nicks_CompleteChatLine(char *buffer, size_t size, unsigned int pos);
518
519 static void
520 Key_Message (int key, char ascii)
521 {
522
523         if (key == K_ENTER || ascii == 10 || ascii == 13)
524         {
525                 Cmd_ForwardStringToServer(va("%s %s", chat_team ? "say_team" : "say ", chat_buffer));
526
527                 key_dest = key_game;
528                 chat_bufferlen = 0;
529                 chat_buffer[0] = 0;
530                 return;
531         }
532
533         if (key == K_ESCAPE) {
534                 key_dest = key_game;
535                 chat_bufferlen = 0;
536                 chat_buffer[0] = 0;
537                 return;
538         }
539
540         if (key == K_BACKSPACE) {
541                 if (chat_bufferlen) {
542                         chat_bufferlen--;
543                         chat_buffer[chat_bufferlen] = 0;
544                 }
545                 return;
546         }
547
548         if(key == K_TAB) {
549                 chat_bufferlen = Nicks_CompleteChatLine(chat_buffer, sizeof(chat_buffer), chat_bufferlen);
550                 return;
551         }
552
553         if (chat_bufferlen == sizeof (chat_buffer) - 1)
554                 return;                                                 // all full
555
556         if (!ascii)
557                 return;                                                 // non printable
558
559         chat_buffer[chat_bufferlen++] = ascii;
560         chat_buffer[chat_bufferlen] = 0;
561 }
562
563 //============================================================================
564
565
566 /*
567 ===================
568 Returns a key number to be used to index keybindings[] by looking at
569 the given string.  Single ascii characters return themselves, while
570 the K_* names are matched up.
571 ===================
572 */
573 int
574 Key_StringToKeynum (const char *str)
575 {
576         const keyname_t  *kn;
577
578         if (!str || !str[0])
579                 return -1;
580         if (!str[1])
581                 return tolower(str[0]);
582
583         for (kn = keynames; kn->name; kn++) {
584                 if (!strcasecmp (str, kn->name))
585                         return kn->keynum;
586         }
587         return -1;
588 }
589
590 /*
591 ===================
592 Returns a string (either a single ascii char, or a K_* name) for the
593 given keynum.
594 FIXME: handle quote special (general escape sequence?)
595 ===================
596 */
597 const char *
598 Key_KeynumToString (int keynum)
599 {
600         const keyname_t  *kn;
601         static char tinystr[2];
602
603         // -1 is an invalid code
604         if (keynum < 0)
605                 return "<KEY NOT FOUND>";
606
607         // search overrides first, because some characters are special
608         for (kn = keynames; kn->name; kn++)
609                 if (keynum == kn->keynum)
610                         return kn->name;
611
612         // if it is printable, output it as a single character
613         if (keynum > 32 && keynum < 256)
614         {
615                 tinystr[0] = keynum;
616                 tinystr[1] = 0;
617                 return tinystr;
618         }
619
620         // if it is not overridden and not printable, we don't know what to do with it
621         return "<UNKNOWN KEYNUM>";
622 }
623
624
625 void
626 Key_SetBinding (int keynum, int bindmap, const char *binding)
627 {
628         char *newbinding;
629         size_t l;
630
631         if (keynum == -1 || keynum >= MAX_KEYS)
632                 return;
633
634 // free old bindings
635         if (keybindings[bindmap][keynum]) {
636                 Z_Free (keybindings[bindmap][keynum]);
637                 keybindings[bindmap][keynum] = NULL;
638         }
639         if(!binding[0]) // make "" binds be removed --blub
640                 return;
641 // allocate memory for new binding
642         l = strlen (binding);
643         newbinding = (char *)Z_Malloc (l + 1);
644         memcpy (newbinding, binding, l + 1);
645         newbinding[l] = 0;
646         keybindings[bindmap][keynum] = newbinding;
647 }
648
649 static void
650 Key_In_Unbind_f (void)
651 {
652         int         b, m;
653
654         if (Cmd_Argc () != 3) {
655                 Con_Print("in_unbind <bindmap> <key> : remove commands from a key\n");
656                 return;
657         }
658
659         m = strtol(Cmd_Argv (1), NULL, 0);
660         if ((m < 0) || (m >= 8)) {
661                 Con_Printf("%d isn't a valid bindmap\n", m);
662                 return;
663         }
664
665         b = Key_StringToKeynum (Cmd_Argv (2));
666         if (b == -1) {
667                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (2));
668                 return;
669         }
670
671         Key_SetBinding (b, m, "");
672 }
673
674 static void
675 Key_In_Bind_f (void)
676 {
677         int         i, c, b, m;
678         char        cmd[MAX_INPUTLINE];
679
680         c = Cmd_Argc ();
681
682         if (c != 3 && c != 4) {
683                 Con_Print("in_bind <bindmap> <key> [command] : attach a command to a key\n");
684                 return;
685         }
686
687         m = strtol(Cmd_Argv (1), NULL, 0);
688         if ((m < 0) || (m >= 8)) {
689                 Con_Printf("%d isn't a valid bindmap\n", m);
690                 return;
691         }
692
693         b = Key_StringToKeynum (Cmd_Argv (2));
694         if (b == -1 || b >= MAX_KEYS) {
695                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (2));
696                 return;
697         }
698
699         if (c == 3) {
700                 if (keybindings[m][b])
701                         Con_Printf("\"%s\" = \"%s\"\n", Cmd_Argv (2), keybindings[m][b]);
702                 else
703                         Con_Printf("\"%s\" is not bound\n", Cmd_Argv (2));
704                 return;
705         }
706 // copy the rest of the command line
707         cmd[0] = 0;                                                     // start out with a null string
708         for (i = 3; i < c; i++) {
709                 strlcat (cmd, Cmd_Argv (i), sizeof (cmd));
710                 if (i != (c - 1))
711                         strlcat (cmd, " ", sizeof (cmd));
712         }
713
714         Key_SetBinding (b, m, cmd);
715 }
716
717 static void
718 Key_In_Bindmap_f (void)
719 {
720         int         m1, m2, c;
721
722         c = Cmd_Argc ();
723
724         if (c != 3) {
725                 Con_Print("in_bindmap <bindmap> <fallback>: set current bindmap and fallback\n");
726                 return;
727         }
728
729         m1 = strtol(Cmd_Argv (1), NULL, 0);
730         if ((m1 < 0) || (m1 >= 8)) {
731                 Con_Printf("%d isn't a valid bindmap\n", m1);
732                 return;
733         }
734
735         m2 = strtol(Cmd_Argv (2), NULL, 0);
736         if ((m2 < 0) || (m2 >= 8)) {
737                 Con_Printf("%d isn't a valid bindmap\n", m2);
738                 return;
739         }
740
741         key_bmap = m1;
742         key_bmap2 = m2;
743 }
744
745 static void
746 Key_Unbind_f (void)
747 {
748         int         b;
749
750         if (Cmd_Argc () != 2) {
751                 Con_Print("unbind <key> : remove commands from a key\n");
752                 return;
753         }
754
755         b = Key_StringToKeynum (Cmd_Argv (1));
756         if (b == -1) {
757                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (1));
758                 return;
759         }
760
761         Key_SetBinding (b, 0, "");
762 }
763
764 static void
765 Key_Unbindall_f (void)
766 {
767         int         i, j;
768
769         for (j = 0; j < 8; j++)
770                 for (i = 0; i < (int)(sizeof(keybindings[0])/sizeof(keybindings[0][0])); i++)
771                         if (keybindings[j][i])
772                                 Key_SetBinding (i, j, "");
773 }
774
775
776 static void
777 Key_Bind_f (void)
778 {
779         int         i, c, b;
780         char        cmd[MAX_INPUTLINE];
781
782         c = Cmd_Argc ();
783
784         if (c != 2 && c != 3) {
785                 Con_Print("bind <key> [command] : attach a command to a key\n");
786                 return;
787         }
788         b = Key_StringToKeynum (Cmd_Argv (1));
789         if (b == -1 || b >= MAX_KEYS) {
790                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (1));
791                 return;
792         }
793
794         if (c == 2) {
795                 if (keybindings[0][b])
796                         Con_Printf("\"%s\" = \"%s\"\n", Cmd_Argv (1), keybindings[0][b]);
797                 else
798                         Con_Printf("\"%s\" is not bound\n", Cmd_Argv (1));
799                 return;
800         }
801 // copy the rest of the command line
802         cmd[0] = 0;                                                     // start out with a null string
803         for (i = 2; i < c; i++) {
804                 strlcat (cmd, Cmd_Argv (i), sizeof (cmd));
805                 if (i != (c - 1))
806                         strlcat (cmd, " ", sizeof (cmd));
807         }
808
809         Key_SetBinding (b, 0, cmd);
810 }
811
812 /*
813 ============
814 Writes lines containing "bind key value"
815 ============
816 */
817 void
818 Key_WriteBindings (qfile_t *f)
819 {
820         int         i, j;
821         char bindbuf[MAX_INPUTLINE];
822         const char *p;
823
824         for (j = 0; j < MAX_BINDMAPS; j++)
825         {
826                 for (i = 0; i < (int)(sizeof(keybindings[0])/sizeof(keybindings[0][0])); i++)
827                 {
828                         p = keybindings[j][i];
829                         if (p)
830                         {
831                                 Cmd_QuoteString(bindbuf, sizeof(bindbuf), p, "\"\\");
832                                 if (j == 0)
833                                         FS_Printf(f, "bind %s \"%s\"\n", Key_KeynumToString (i), bindbuf);
834                                 else
835                                         FS_Printf(f, "in_bind %d %s \"%s\"\n", j, Key_KeynumToString (i), bindbuf);
836                         }
837                 }
838         }
839 }
840
841
842 void
843 Key_Init (void)
844 {
845         int         i;
846
847         for (i = 0; i < 32; i++) {
848                 key_lines[i][0] = ']';
849                 key_lines[i][1] = 0;
850         }
851         key_linepos = 1;
852
853 //
854 // register our functions
855 //
856         Cmd_AddCommand ("in_bind", Key_In_Bind_f, "binds a command to the specified key in the selected bindmap");
857         Cmd_AddCommand ("in_unbind", Key_In_Unbind_f, "removes command on the specified key in the selected bindmap");
858         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");
859
860         Cmd_AddCommand ("bind", Key_Bind_f, "binds a command to the specified key in bindmap 0");
861         Cmd_AddCommand ("unbind", Key_Unbind_f, "removes a command on the specified key in bindmap 0");
862         Cmd_AddCommand ("unbindall", Key_Unbindall_f, "removes all commands from all keys in all bindmaps (leaving only shift-escape and escape)");
863
864         Cvar_RegisterVariable (&con_closeontoggleconsole);
865 }
866
867 const char *Key_GetBind (int key)
868 {
869         const char *bind;
870         if (key < 0 || key >= MAX_KEYS)
871                 return NULL;
872         bind = keybindings[key_bmap][key];
873         if (!bind)
874                 bind = keybindings[key_bmap2][key];
875         return bind;
876 }
877
878 qboolean CL_VM_InputEvent (qboolean down, int key);
879
880 /*
881 ===================
882 Called by the system between frames for both key up and key down events
883 Should NOT be called during an interrupt!
884 ===================
885 */
886 void
887 Key_Event (int key, char ascii, qboolean down)
888 {
889         const char *bind;
890         qboolean q;
891
892         if (key < 0 || key >= MAX_KEYS)
893                 return;
894
895         // get key binding
896         bind = keybindings[key_bmap][key];
897         if (!bind)
898                 bind = keybindings[key_bmap2][key];
899
900         if (developer.integer >= 1000)
901                 Con_Printf("Key_Event(%i, '%c', %s) keydown %i bind \"%s\"\n", key, ascii, down ? "down" : "up", keydown[key], bind ? bind : "");
902
903 #if 0
904         if(key_dest == key_game)
905         {
906                 q = CL_VM_InputEvent(down, key);
907                 if(q)
908                 {
909                         if (down)
910                                 keydown[key] = min(keydown[key] + 1, 2);
911                         else
912                                 keydown[key] = 0;
913                         return;
914                 }
915         }
916 #endif
917
918         if (down)
919         {
920                 // increment key repeat count each time a down is received so that things
921                 // which want to ignore key repeat can ignore it
922                 keydown[key] = min(keydown[key] + 1, 2);
923         }
924         else
925         {
926                 // clear repeat count now that the key is released
927                 keydown[key] = 0;
928         }
929
930         // key_consoleactive is a flag not a key_dest because the console is a
931         // high priority overlay ontop of the normal screen (designed as a safety
932         // feature so that developers and users can rescue themselves from a bad
933         // situation).
934         //
935         // this also means that toggling the console on/off does not lose the old
936         // key_dest state
937
938         // specially handle escape (togglemenu) and shift-escape (toggleconsole)
939         // engine bindings, these are not handled as normal binds so that the user
940         // can recover from a completely empty bindmap
941         if (key == K_ESCAPE)
942         {
943                 // ignore key repeats on escape
944                 if (keydown[key] > 1)
945                         return;
946                 // escape does these things:
947                 // key_consoleactive - close console
948                 // key_message - abort messagemode
949                 // key_menu - go to parent menu (or key_game)
950                 // key_game - open menu
951                 // in all modes shift-escape toggles console
952                 if (((key_consoleactive & KEY_CONSOLEACTIVE_USER) || keydown[K_SHIFT]) && down)
953                 {
954                         Con_ToggleConsole_f ();
955                         return;
956                 }
957                 switch (key_dest)
958                 {
959                         case key_message:
960                                 if (down)
961                                         Key_Message (key, ascii);
962                                 break;
963                         case key_menu:
964                         case key_menu_grabbed:
965                                 MR_KeyEvent (key, ascii, down);
966                                 break;
967                         case key_game:
968                                 // csqc has priority over toggle menu if it wants to (e.g. handling escape for UI stuff in-game.. :sick:)
969                                 q = CL_VM_InputEvent(down, key);
970                                 if (!q && down)
971                                         MR_ToggleMenu_f ();
972                                 break;
973                         default:
974                                 Con_Printf ("Key_Event: Bad key_dest\n");
975                 }
976                 return;
977         }
978
979         // send function keydowns to interpreter no matter what mode is (unless the menu has specifically grabbed the keyboard, for rebinding keys)
980         if (key_dest != key_menu_grabbed)
981         if (key >= K_F1 && key <= K_F12 && down)
982         {
983                 // ignore key repeats on F1-F12 binds
984                 if (keydown[key] > 1)
985                         return;
986                 if (bind)
987                 {
988                         // button commands add keynum as a parm
989                         if (bind[0] == '+')
990                                 Cbuf_AddText (va("%s %i\n", bind, key));
991                         else
992                         {
993                                 Cbuf_AddText (bind);
994                                 Cbuf_AddText ("\n");
995                         }
996                 }
997                 return;
998         }
999
1000 #if 0
1001         // ignore binds (other than the above escape/F1-F12 keys) while in console
1002         if (key_consoleactive && down)
1003 #else
1004         // respond to toggleconsole binds while in console unless the pressed key
1005         // happens to be the color prefix character (such as on German keyboards)
1006         if (key_consoleactive && down && (!con_closeontoggleconsole.integer || !bind || strncmp(bind, "toggleconsole", strlen("toggleconsole")) || ascii == STRING_COLOR_TAG))
1007 #endif
1008         {
1009                 Key_Console (key, ascii);
1010                 return;
1011         }
1012
1013
1014         // FIXME: actually the up-bind should only be called if the button was actually pressed while key_dest == key_game [12/17/2007 Black]
1015         //                       especially CL_VM_InputEvent should be able to prevent it from being called (to intercept the binds)
1016         // key up events only generate commands if the game key binding is a button
1017         // command (leading + sign).  These will occur even in console mode, to
1018         // keep the character from continuing an action started before a console
1019         // switch.  Button commands include the kenum as a parameter, so multiple
1020         // downs can be matched with ups
1021         if (!down && bind && bind[0] == '+')
1022                 Cbuf_AddText(va("-%s %i\n", bind + 1, key));
1023
1024         // ignore binds while a video is played, let the video system handle the key event
1025         if (cl_videoplaying)
1026         {
1027                 CL_Video_KeyEvent (key, ascii, keydown[key] != 0);
1028                 return;
1029         }
1030
1031         // anything else is a key press into the game, chat line, or menu
1032         switch (key_dest)
1033         {
1034                 case key_message:
1035                         if (down)
1036                                 Key_Message (key, ascii);
1037                         break;
1038                 case key_menu:
1039                 case key_menu_grabbed:
1040                         MR_KeyEvent (key, ascii, down);
1041                         break;
1042                 case key_game:
1043                         q = CL_VM_InputEvent(down, key);
1044                         // ignore key repeats on binds and only send the bind if the event hasnt been already processed by csqc
1045                         if (!q && bind && keydown[key] == 1 && down)
1046                         {
1047                                 // button commands add keynum as a parm
1048                                 if (bind[0] == '+')
1049                                         Cbuf_AddText (va("%s %i\n", bind, key));
1050                                 else
1051                                 {
1052                                         Cbuf_AddText (bind);
1053                                         Cbuf_AddText ("\n");
1054                                 }
1055                         }
1056                         break;
1057                 default:
1058                         Con_Printf ("Key_Event: Bad key_dest\n");
1059         }
1060 }
1061
1062 /*
1063 ===================
1064 Key_ClearStates
1065 ===================
1066 */
1067 void
1068 Key_ClearStates (void)
1069 {
1070         memset(keydown, 0, sizeof(keydown));
1071 }