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