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