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