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