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