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