]> icculus.org git repositories - divverent/darkplaces.git/blob - keys.c
da4320e0ba311169feb8b90f5c77aecede172160
[divverent/darkplaces.git] / keys.c
1 /*
2         Copyright (C) 1996-1997  Id Software, Inc.
3
4         This program is free software; you can redistribute it and/or
5         modify it under the terms of the GNU General Public License
6         as published by the Free Software Foundation; either version 2
7         of the License, or (at your option) any later version.
8
9         This program is distributed in the hope that it will be useful,
10         but WITHOUT ANY WARRANTY; without even the implied warranty of
11         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13         See the GNU General Public License for more details.
14
15         You should have received a copy of the GNU General Public License
16         along with this program; if not, write to:
17
18                 Free Software Foundation, Inc.
19                 59 Temple Place - Suite 330
20                 Boston, MA  02111-1307, USA
21 */
22
23 #include "quakedef.h"
24
25
26 /*
27 key up events are sent even if in console mode
28 */
29
30 #define MAX_INPUTLINE 256
31 int                     edit_line = 31;
32 int                     history_line = 31;
33 char            key_lines[32][MAX_INPUTLINE];
34 int                     key_linepos;
35 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 qbyte keydown[1024];     // 0 = up, 1 = down, 2 = repeating
48
49 typedef struct {
50         const char      *name;
51         int                     keynum;
52 } keyname_t;
53
54 static const keyname_t   keynames[] = {
55         {"TAB", K_TAB},
56         {"ENTER", K_ENTER},
57         {"ESCAPE", K_ESCAPE},
58         {"SPACE", K_SPACE},
59         {"BACKSPACE", K_BACKSPACE},
60         {"UPARROW", K_UPARROW},
61         {"DOWNARROW", K_DOWNARROW},
62         {"LEFTARROW", K_LEFTARROW},
63         {"RIGHTARROW", K_RIGHTARROW},
64
65         {"ALT", K_ALT},
66         {"CTRL", K_CTRL},
67         {"SHIFT", K_SHIFT},
68
69         {"F1", K_F1},
70         {"F2", K_F2},
71         {"F3", K_F3},
72         {"F4", K_F4},
73         {"F5", K_F5},
74         {"F6", K_F6},
75         {"F7", K_F7},
76         {"F8", K_F8},
77         {"F9", K_F9},
78         {"F10", K_F10},
79         {"F11", K_F11},
80         {"F12", K_F12},
81
82         {"INS", K_INS},
83         {"DEL", K_DEL},
84         {"PGDN", K_PGDN},
85         {"PGUP", K_PGUP},
86         {"HOME", K_HOME},
87         {"END", K_END},
88
89         {"PAUSE", K_PAUSE},
90
91         {"MWHEELUP", K_MWHEELUP},
92         {"MWHEELDOWN", K_MWHEELDOWN},
93
94         {"MOUSE1", K_MOUSE1},
95         {"MOUSE2", K_MOUSE2},
96         {"MOUSE3", K_MOUSE3},
97         {"MOUSE4", K_MOUSE4},
98         {"MOUSE5", K_MOUSE5},
99         {"MOUSE6", K_MOUSE6},
100         {"MOUSE7", K_MOUSE7},
101         {"MOUSE8", K_MOUSE8},
102         {"MOUSE9", K_MOUSE9},
103         {"MOUSE10", K_MOUSE10},
104         {"MOUSE11", K_MOUSE11},
105         {"MOUSE12", K_MOUSE12},
106         {"MOUSE13", K_MOUSE13},
107         {"MOUSE14", K_MOUSE14},
108         {"MOUSE15", K_MOUSE15},
109         {"MOUSE16", K_MOUSE16},
110
111         {"NUMLOCK", K_NUMLOCK},
112         {"CAPSLOCK", K_CAPSLOCK},
113         {"SCROLLOCK", K_SCROLLOCK},
114
115         {"KP_HOME",                     K_KP_HOME },
116         {"KP_UPARROW",          K_KP_UPARROW },
117         {"KP_PGUP",                     K_KP_PGUP },
118         {"KP_LEFTARROW",        K_KP_LEFTARROW },
119         {"KP_RIGHTARROW",       K_KP_RIGHTARROW },
120         {"KP_END",                      K_KP_END },
121         {"KP_DOWNARROW",        K_KP_DOWNARROW },
122         {"KP_PGDN",                     K_KP_PGDN },
123         {"KP_INS",                      K_KP_INS },
124         {"KP_DEL",                      K_KP_DEL },
125         {"KP_SLASH",            K_KP_SLASH },
126
127         {"KP_0", K_KP_0},
128         {"KP_1", K_KP_1},
129         {"KP_2", K_KP_2},
130         {"KP_3", K_KP_3},
131         {"KP_4", K_KP_4},
132         {"KP_5", K_KP_5},
133         {"KP_6", K_KP_6},
134         {"KP_7", K_KP_7},
135         {"KP_8", K_KP_8},
136         {"KP_9", K_KP_9},
137         {"KP_PERIOD", K_KP_PERIOD},
138         {"KP_DIVIDE", K_KP_DIVIDE},
139         {"KP_MULTIPLY", K_KP_MULTIPLY},
140         {"KP_MINUS", K_KP_MINUS},
141         {"KP_PLUS", K_KP_PLUS},
142         {"KP_ENTER", K_KP_ENTER},
143         {"KP_EQUALS", K_KP_EQUALS},
144
145         {"JOY1",  K_JOY1},
146         {"JOY2",  K_JOY2},
147         {"JOY3",  K_JOY3},
148         {"JOY4",  K_JOY4},
149         {"JOY5",  K_JOY5},
150         {"JOY6",  K_JOY6},
151         {"JOY7",  K_JOY7},
152         {"JOY8",  K_JOY8},
153         {"JOY9",  K_JOY9},
154         {"JOY10", K_JOY10},
155         {"JOY11", K_JOY11},
156         {"JOY12", K_JOY12},
157         {"JOY13", K_JOY13},
158         {"JOY14", K_JOY14},
159         {"JOY15", K_JOY15},
160         {"JOY16", K_JOY16},
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         if ((toupper(key) == 'V' && keydown[K_CTRL]) || ((key == K_INS || key == K_KP_INS) && keydown[K_SHIFT]))
276         {
277                 char *cbd;
278                 if ((cbd = Sys_GetClipboardData()) != 0)
279                 {
280                         int i;
281                         strtok(cbd, "\n\r\b");
282                         i = strlen(cbd);
283                         if (i + key_linepos >= MAX_INPUTLINE)
284                                 i= MAX_INPUTLINE - key_linepos;
285                         if (i > 0)
286                         {
287                                 cbd[i]=0;
288                                 strcat(key_lines[edit_line], cbd);
289                                 key_linepos += i;
290                         }
291                         free(cbd);
292                 }
293                 return;
294         }
295
296         if (key == 'l')
297         {
298                 if (keydown[K_CTRL])
299                 {
300                         Cbuf_AddText ("clear\n");
301                         return;
302                 }
303         }
304
305         if (key == K_ENTER || key == K_KP_ENTER)
306         {
307                 Cbuf_AddText (key_lines[edit_line]+1);  // skip the ]
308                 Cbuf_AddText ("\n");
309                 Con_Printf("%s\n",key_lines[edit_line]);
310                 // LordHavoc: redesigned edit_line/history_line
311                 edit_line = 31;
312                 history_line = edit_line;
313                 memmove(key_lines[0], key_lines[1], sizeof(key_lines[0]) * edit_line);
314                 key_lines[edit_line][0] = ']';
315                 key_lines[edit_line][1] = 0;    // EvilTypeGuy: null terminate
316                 key_linepos = 1;
317                 // force an update, because the command may take some time
318                 if (cls.state == ca_disconnected)
319                         CL_UpdateScreen ();
320                 return;
321         }
322
323         if (key == K_TAB)
324         {
325                 // Enhanced command completion
326                 // by EvilTypeGuy eviltypeguy@qeradiant.com
327                 // Thanks to Fett, Taniwha
328                 Con_CompleteCommandLine();
329                 return;
330         }
331
332         // Advanced Console Editing by Radix radix@planetquake.com
333         // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
334
335         // left arrow will just move left one without erasing, backspace will
336         // actually erase charcter
337         if (key == K_LEFTARROW || key == K_KP_LEFTARROW)
338         {
339                 if (key_linepos > 1)
340                         key_linepos--;
341                 return;
342         }
343
344         // delete char before cursor
345         if (key == K_BACKSPACE || (key == 'h' && keydown[K_CTRL]))
346         {
347                 if (key_linepos > 1)
348                 {
349                         strcpy(key_lines[edit_line] + key_linepos - 1, key_lines[edit_line] + key_linepos);
350                         key_linepos--;
351                 }
352                 return;
353         }
354
355         // delete char on cursor
356         if (key == K_DEL || key == K_KP_DEL)
357         {
358                 if ((size_t)key_linepos < strlen(key_lines[edit_line]))
359                         strcpy(key_lines[edit_line] + key_linepos, key_lines[edit_line] + key_linepos + 1);
360                 return;
361         }
362
363
364         // if we're at the end, get one character from previous line,
365         // otherwise just go right one
366         if (key == K_RIGHTARROW || key == K_KP_RIGHTARROW)
367         {
368                 if ((size_t)key_linepos < strlen(key_lines[edit_line]))
369                         key_linepos++;
370
371                 return;
372         }
373
374         if (key == K_INS || key == K_KP_INS) // toggle insert mode
375         {
376                 key_insert ^= 1;
377                 return;
378         }
379
380         // End Advanced Console Editing
381
382         if (key == K_UPARROW || key == K_KP_UPARROW || (key == 'p' && keydown[K_CTRL]))
383         {
384                 if (history_line > 0 && key_lines[history_line-1][1])
385                 {
386                         history_line--;
387                         strcpy(key_lines[edit_line], key_lines[history_line]);
388                         key_linepos = strlen(key_lines[edit_line]);
389                 }
390                 return;
391         }
392
393         if (key == K_DOWNARROW || key == K_KP_DOWNARROW || (key == 'n' && keydown[K_CTRL]))
394         {
395                 history_line++;
396                 if (history_line >= edit_line)
397                 {
398                         history_line = edit_line;
399                         key_lines[edit_line][0] = ']';
400                         key_lines[edit_line][1] = 0;
401                         key_linepos = 1;
402                 }
403                 else
404                 {
405                         strcpy(key_lines[edit_line], key_lines[history_line]);
406                         key_linepos = strlen(key_lines[edit_line]);
407                 }
408                 return;
409         }
410
411         if (key == K_PGUP || key == K_KP_PGUP || key == K_MWHEELUP)
412         {
413                 con_backscroll += ((int) vid_conheight.integer >> 5);
414                 if (con_backscroll > con_totallines - (vid_conheight.integer>>3) - 1)
415                         con_backscroll = con_totallines - (vid_conheight.integer>>3) - 1;
416                 return;
417         }
418
419         if (key == K_PGDN || key == K_KP_PGDN || key == K_MWHEELDOWN)
420         {
421                 con_backscroll -= ((int) vid_conheight.integer >> 5);
422                 if (con_backscroll < 0)
423                         con_backscroll = 0;
424                 return;
425         }
426
427         if (key == K_HOME || key == K_KP_HOME)
428         {
429                 con_backscroll = con_totallines - (vid_conheight.integer>>3) - 1;
430                 return;
431         }
432
433         if (key == K_END || key == K_KP_END)
434         {
435                 con_backscroll = 0;
436                 return;
437         }
438
439         // non printable
440         if (ascii < 32)
441                 return;
442
443         if (key_linepos < MAX_INPUTLINE-1)
444         {
445                 int len;
446                 len = strlen(&key_lines[edit_line][key_linepos]);
447                 // check insert mode, or always insert if at end of line
448                 if (key_insert || len == 0)
449                 {
450                         // can't use strcpy to move string to right
451                         len++;
452                         memmove(&key_lines[edit_line][key_linepos + 1], &key_lines[edit_line][key_linepos], len);
453                 }
454                 key_lines[edit_line][key_linepos] = ascii;
455                 key_linepos++;
456         }
457 }
458
459 //============================================================================
460
461 qboolean        chat_team;
462 char            chat_buffer[MAX_INPUTLINE];
463 unsigned int    chat_bufferlen = 0;
464
465 static void
466 Key_Message (int key, char ascii)
467 {
468
469         if (key == K_ENTER)
470         {
471                 Cmd_ForwardStringToServer(va("%s %s", chat_team ? "say_team" : "say ", chat_buffer));
472
473                 key_dest = key_game;
474                 chat_bufferlen = 0;
475                 chat_buffer[0] = 0;
476                 return;
477         }
478
479         if (key == K_ESCAPE) {
480                 key_dest = key_game;
481                 chat_bufferlen = 0;
482                 chat_buffer[0] = 0;
483                 return;
484         }
485
486         if (key == K_BACKSPACE) {
487                 if (chat_bufferlen) {
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         if (!ascii)
498                 return;                                                 // non printable
499
500         chat_buffer[chat_bufferlen++] = ascii;
501         chat_buffer[chat_bufferlen] = 0;
502 }
503
504 //============================================================================
505
506
507 /*
508 ===================
509 Returns a key number to be used to index keybindings[] by looking at
510 the given string.  Single ascii characters return themselves, while
511 the K_* names are matched up.
512 ===================
513 */
514 int
515 Key_StringToKeynum (const char *str)
516 {
517         const keyname_t  *kn;
518
519         if (!str || !str[0])
520                 return -1;
521         if (!str[1])
522                 return tolower(str[0]);
523
524         for (kn = keynames; kn->name; kn++) {
525                 if (!strcasecmp (str, kn->name))
526                         return kn->keynum;
527         }
528         return -1;
529 }
530
531 /*
532 ===================
533 Returns a string (either a single ascii char, or a K_* name) for the
534 given keynum.
535 FIXME: handle quote special (general escape sequence?)
536 ===================
537 */
538 const char *
539 Key_KeynumToString (int keynum)
540 {
541         const keyname_t  *kn;
542         static char tinystr[2];
543
544         if (keynum == -1)
545                 return "<KEY NOT FOUND>";
546         if (keynum > 32 && keynum < 127) {      // printable ascii
547                 tinystr[0] = keynum;
548                 tinystr[1] = 0;
549                 return tinystr;
550         }
551
552         for (kn = keynames; kn->name; kn++)
553                 if (keynum == kn->keynum)
554                         return kn->name;
555
556         return "<UNKNOWN KEYNUM>";
557 }
558
559
560 void
561 Key_SetBinding (int keynum, int bindmap, const char *binding)
562 {
563         char       *new;
564         int         l;
565
566         if (keynum == -1)
567                 return;
568
569 // free old bindings
570         if (keybindings[bindmap][keynum]) {
571                 Z_Free (keybindings[bindmap][keynum]);
572                 keybindings[bindmap][keynum] = NULL;
573         }
574 // allocate memory for new binding
575         l = strlen (binding);
576         new = Z_Malloc (l + 1);
577         strcpy (new, binding);
578         new[l] = 0;
579         keybindings[bindmap][keynum] = new;
580 }
581
582 static void
583 Key_In_Unbind_f (void)
584 {
585         int         b, m;
586
587         if (Cmd_Argc () != 3) {
588                 Con_Print("in_unbind <bindmap> <key> : remove commands from a key\n");
589                 return;
590         }
591
592         m = strtol(Cmd_Argv (1), NULL, 0);
593         if ((m < 0) || (m >= 8)) {
594                 Con_Printf("%d isn't a valid bindmap\n", m);
595                 return;
596         }
597
598         b = Key_StringToKeynum (Cmd_Argv (2));
599         if (b == -1) {
600                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (2));
601                 return;
602         }
603
604         Key_SetBinding (b, m, "");
605 }
606
607 static void
608 Key_In_Bind_f (void)
609 {
610         int         i, c, b, m;
611         char        cmd[1024];
612
613         c = Cmd_Argc ();
614
615         if (c != 3 && c != 4) {
616                 Con_Print("in_bind <bindmap> <key> [command] : attach a command to a key\n");
617                 return;
618         }
619
620         m = strtol(Cmd_Argv (1), NULL, 0);
621         if ((m < 0) || (m >= 8)) {
622                 Con_Printf("%d isn't a valid bindmap\n", m);
623                 return;
624         }
625
626         b = Key_StringToKeynum (Cmd_Argv (2));
627         if (b == -1) {
628                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (2));
629                 return;
630         }
631
632         if (c == 3) {
633                 if (keybindings[m][b])
634                         Con_Printf("\"%s\" = \"%s\"\n", Cmd_Argv (2), keybindings[m][b]);
635                 else
636                         Con_Printf("\"%s\" is not bound\n", Cmd_Argv (2));
637                 return;
638         }
639 // copy the rest of the command line
640         cmd[0] = 0;                                                     // start out with a null string
641         for (i = 3; i < c; i++) {
642                 strlcat (cmd, Cmd_Argv (i), sizeof (cmd));
643                 if (i != (c - 1))
644                         strlcat (cmd, " ", sizeof (cmd));
645         }
646
647         Key_SetBinding (b, m, cmd);
648 }
649
650 static void
651 Key_In_Bindmap_f (void)
652 {
653         int         m1, m2, c;
654
655         c = Cmd_Argc ();
656
657         if (c != 3) {
658                 Con_Print("in_bindmap <bindmap> <fallback>: set current bindmap and fallback\n");
659                 return;
660         }
661
662         m1 = strtol(Cmd_Argv (1), NULL, 0);
663         if ((m1 < 0) || (m1 >= 8)) {
664                 Con_Printf("%d isn't a valid bindmap\n", m1);
665                 return;
666         }
667
668         m2 = strtol(Cmd_Argv (2), NULL, 0);
669         if ((m2 < 0) || (m2 >= 8)) {
670                 Con_Printf("%d isn't a valid bindmap\n", m2);
671                 return;
672         }
673
674         key_bmap = m1;
675         key_bmap2 = m2;
676 }
677
678 static void
679 Key_Unbind_f (void)
680 {
681         int         b;
682
683         if (Cmd_Argc () != 2) {
684                 Con_Print("unbind <key> : remove commands from a key\n");
685                 return;
686         }
687
688         b = Key_StringToKeynum (Cmd_Argv (1));
689         if (b == -1) {
690                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (1));
691                 return;
692         }
693
694         Key_SetBinding (b, 0, "");
695 }
696
697 static void
698 Key_Unbindall_f (void)
699 {
700         int         i, j;
701
702         for (j = 0; j < 8; j++)
703                 for (i = 0; i < (int)(sizeof(keybindings[0])/sizeof(keybindings[0][0])); i++)
704                         if (keybindings[j][i])
705                                 Key_SetBinding (i, j, "");
706 }
707
708
709 static void
710 Key_Bind_f (void)
711 {
712         int         i, c, b;
713         char        cmd[1024];
714
715         c = Cmd_Argc ();
716
717         if (c != 2 && c != 3) {
718                 Con_Print("bind <key> [command] : attach a command to a key\n");
719                 return;
720         }
721         b = Key_StringToKeynum (Cmd_Argv (1));
722         if (b == -1) {
723                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (1));
724                 return;
725         }
726
727         if (c == 2) {
728                 if (keybindings[0][b])
729                         Con_Printf("\"%s\" = \"%s\"\n", Cmd_Argv (1), keybindings[0][b]);
730                 else
731                         Con_Printf("\"%s\" is not bound\n", Cmd_Argv (1));
732                 return;
733         }
734 // copy the rest of the command line
735         cmd[0] = 0;                                                     // start out with a null string
736         for (i = 2; i < c; i++) {
737                 strlcat (cmd, Cmd_Argv (i), sizeof (cmd));
738                 if (i != (c - 1))
739                         strlcat (cmd, " ", sizeof (cmd));
740         }
741
742         Key_SetBinding (b, 0, cmd);
743 }
744
745 /*
746 ============
747 Writes lines containing "bind key value"
748 ============
749 */
750 void
751 Key_WriteBindings (qfile_t *f)
752 {
753         int         i, j;
754
755         for (i = 0; i < (int)(sizeof(keybindings[0])/sizeof(keybindings[0][0])); i++)
756                 if (keybindings[0][i])
757                         FS_Printf(f, "bind \"%s\" \"%s\"\n",
758                                         Key_KeynumToString (i), keybindings[0][i]);
759         for (j = 1; j < 8; j++)
760                 for (i = 0; i < (int)(sizeof(keybindings[0])/sizeof(keybindings[0][0])); i++)
761                         if (keybindings[j][i])
762                                 FS_Printf(f, "in_bind %d \"%s\" \"%s\"\n",
763                                                 j, Key_KeynumToString (i), keybindings[j][i]);
764 }
765
766
767 void
768 Key_Init (void)
769 {
770         int         i;
771
772         for (i = 0; i < 32; i++) {
773                 key_lines[i][0] = ']';
774                 key_lines[i][1] = 0;
775         }
776         key_linepos = 1;
777
778 //
779 // init ascii characters in console mode
780 //
781         for (i = 32; i < 128; i++)
782                 consolekeys[i] = true;
783         consolekeys[K_ENTER] = true; consolekeys[K_KP_ENTER] = true;
784         consolekeys[K_TAB] = true;
785         consolekeys[K_LEFTARROW] = true; consolekeys[K_KP_LEFTARROW] = true;
786         consolekeys[K_RIGHTARROW] = true; consolekeys[K_KP_RIGHTARROW] = true;
787         consolekeys[K_UPARROW] = true; consolekeys[K_KP_UPARROW] = true;
788         consolekeys[K_DOWNARROW] = true; consolekeys[K_KP_DOWNARROW] = true;
789         consolekeys[K_BACKSPACE] = true;
790         consolekeys[K_DEL] = true; consolekeys[K_KP_DEL] = true;
791         consolekeys[K_INS] = true; consolekeys[K_KP_INS] = true;
792         consolekeys[K_HOME] = true; consolekeys[K_KP_HOME] = true;
793         consolekeys[K_END] = true; consolekeys[K_KP_END] = true;
794         consolekeys[K_PGUP] = true; consolekeys[K_KP_PGUP] = true;
795         consolekeys[K_PGDN] = true; consolekeys[K_KP_PGDN] = true;
796         consolekeys[K_SHIFT] = true;
797         consolekeys[K_MWHEELUP] = true;
798         consolekeys[K_MWHEELDOWN] = true;
799         consolekeys[K_KP_PLUS] = true;
800         consolekeys[K_KP_MINUS] = true;
801         consolekeys[K_KP_DIVIDE] = true;
802         consolekeys[K_KP_MULTIPLY] = true;
803         consolekeys['`'] = false;
804         consolekeys['~'] = false;
805
806         menubound[K_ESCAPE] = true;
807         for (i = 0; i < 12; i++)
808                 menubound[K_F1 + i] = true;
809
810 //
811 // register our functions
812 //
813         Cmd_AddCommand ("in_bind", Key_In_Bind_f);
814         Cmd_AddCommand ("in_unbind", Key_In_Unbind_f);
815         Cmd_AddCommand ("in_bindmap", Key_In_Bindmap_f);
816
817         Cmd_AddCommand ("bind", Key_Bind_f);
818         Cmd_AddCommand ("unbind", Key_Unbind_f);
819         Cmd_AddCommand ("unbindall", Key_Unbindall_f);
820 }
821
822
823 /*
824 ===================
825 Called by the system between frames for both key up and key down events
826 Should NOT be called during an interrupt!
827 ===================
828 */
829 void
830 Key_Event (int key, char ascii, qboolean down)
831 {
832 #if 0
833 #define USERPLAYING()   ( !key_consoleactive && key_dest == key_game && (cls.state == ca_connected && cls.signon == SIGNONS) )
834         const char *bind;
835
836         // get key binding
837         bind = keybindings[ key_bmap ][ key ];
838         if( !bind ) {
839                 bind = keybindings[ key_bmap2 ][ key ];
840         }
841
842         // update key repeats
843         if( down ) {
844                 keydown[ key ] = min(keydown[ key ] + 1, 2);
845                 if( keydown[ key ] > 1 ) {
846                         if( (key_consoleactive && !consolekeys[key]) || USERPLAYING() )
847                                 return;                                         // ignore most autorepeats
848                 }
849         } else {
850                 keydown[ key ] = 0;
851         }
852
853         if( !down ) {
854                 if( bind && bind[ 0 ] == '+') {
855                         Cbuf_AddText( va( "-%s %i\n", bind + 1, key) );
856                 }
857         } else {
858                 // handle ESCAPE specially, so unbinding wont help
859                 if( key == K_ESCAPE ) {
860                         // shift-escape is a safety measure for users who cant toggle the console otherwise
861                         if( keydown[K_SHIFT] ) {
862                                 Con_ToggleConsole_f();
863                                 return;
864                         }
865                         switch( key_dest ) {
866                                 case key_message:
867                                         Key_Message( key, ascii );
868                                         break;
869                                 case key_menu:
870                                         MR_Keydown( key, ascii );
871                                         break;
872                                 case key_game:
873                                         if (COM_CheckParm ("-demolooponly"))
874                                         {
875                                                 CL_Disconnect ();
876                                                 return;
877                                         }
878                                         MR_ToggleMenu_f();
879                                         break;
880                                 default:
881                                         Sys_Error( "Bad key_dest" );
882                         }
883                         return;
884                 }
885
886                 if( !(key_consoleactive && consolekeys[ key ]) && bind && !strncmp( bind, "toggleconsole", strlen( "toggleconsole" ) ) ) {
887                         Cbuf_AddText( bind );
888                         Cbuf_AddText( "\n" );
889                         if( ascii != STRING_COLOR_TAG ) {
890                                 return;
891                         }
892                 } else {
893                         // during demo playback, all keys ingame bring up the main menu
894                         if( cls.demoplayback && !key_consoleactive && key_dest == key_game ) {
895                                 if (!COM_CheckParm ("-demolooponly"))
896                                         MR_ToggleMenu_f ();
897                                 return;
898                         }
899
900                         // menu bind/function keys or normal binds
901                         if( (key_dest == key_menu && menubound[key]) || USERPLAYING() ) {
902                                 if( bind ) {
903                                         if( bind[0] == '+' ) { // button commands add keynum as a parm
904                                                 Cbuf_AddText( va( "%s %i\n", bind, key ) );
905                                         } else {
906                                                 Cbuf_AddText( bind );
907                                                 Cbuf_AddText( "\n" );
908                                         }
909                                 }
910                                 return;
911                         }
912                 }
913
914                 // either console or game state key functions
915                 if( key_consoleactive ) {
916                                 Key_Console( key, ascii );
917                 } else {
918                         switch (key_dest) {
919                                 case key_message:
920                                         Key_Message( key, ascii );
921                                         break;
922                                 case key_menu:
923                                         MR_Keydown( key, ascii );
924                                         break;
925                                 case key_game:
926                                         // unbound key
927                                         break;
928                                 default:
929                                         Sys_Error( "Bad key_dest" );
930                         }
931                 }
932         }
933 #elif 1
934 #define USERPLAYING()   ( !key_consoleactive && key_dest == key_game && (cls.state == ca_connected && cls.signon == SIGNONS) )
935         const char *bind;
936
937         // get key binding
938         bind = keybindings[key_bmap][key];
939         if (!bind)
940                 bind = keybindings[key_bmap2][key];
941
942         if (!down)
943         {
944                 // clear repeat count now that the key is released
945                 keydown[key] = 0;
946                 // key up events only generate commands if the game key binding is a button
947                 // command (leading + sign).  These will occur even in console mode, to
948                 // keep the character from continuing an action started before a console
949                 // switch.  Button commands include the kenum as a parameter, so multiple
950                 // downs can be matched with ups
951                 if (bind && bind[0] == '+')
952                         Cbuf_AddText(va("-%s %i\n", bind + 1, key));
953                 return;
954         }
955
956         // from here on we know this is a down event
957
958         // increment key repeat count each time a down is received so that things
959         // which want to ignore key repeat can ignore it
960         keydown[key] = min(keydown[key] + 1, 2);
961
962         // key_consoleactive is a flag not a key_dest because the console is a
963         // high priority overlay ontop of the normal screen (designed as a safety
964         // feature so that developers and users can rescue themselves from a bad
965         // situation).
966         //
967         // this also means that toggling the console on/off does not lose the old
968         // key_dest state
969
970         // specially handle escape (togglemenu) and shift-escape (toggleconsole)
971         // engine bindings, these are not handled as normal binds so that the user
972         // can recover from a completely empty bindmap
973         if (key == K_ESCAPE)
974         {
975                 // ignore key repeats on escape
976                 if (keydown[key] > 1)
977                         return;
978                 // escape does these things:
979                 // key_consoleactive - close console
980                 // key_message - abort messagemode
981                 // key_menu - go to parent menu (or key_game)
982                 // key_game - open menu
983                 // in all modes shift-escape toggles console
984                 if (key_consoleactive || keydown[K_SHIFT])
985                 {
986                         Con_ToggleConsole_f ();
987                         return;
988                 }
989                 switch (key_dest)
990                 {
991                         case key_message:
992                                 Key_Message (key, ascii);
993                                 break;
994                         case key_menu:
995                                 MR_Keydown (key, ascii);
996                                 break;
997                         case key_game:
998                                 MR_ToggleMenu_f ();
999                                 break;
1000                         default:
1001                                 if(UI_Callback_IsSlotUsed(key_dest - 3))
1002                                         UI_Callback_KeyDown (key, ascii);
1003                                 else
1004                                         Sys_Error ("Bad key_dest");
1005                 }
1006                 return;
1007         }
1008
1009         // send function keydowns to interpreter no matter what mode is
1010         if (key >= K_F1 && key <= K_F12)
1011         {
1012                 // ignore key repeats on F1-F12 binds
1013                 if (keydown[key] > 1)
1014                         return;
1015                 if (bind)
1016                 {
1017                         // button commands add keynum as a parm
1018                         if (bind[0] == '+')
1019                                 Cbuf_AddText (va("%s %i\n", bind, key));
1020                         else
1021                         {
1022                                 Cbuf_AddText (bind);
1023                                 Cbuf_AddText ("\n");
1024                         }
1025                 }
1026                 return;
1027         }
1028
1029 #if 1
1030         // ignore binds (other than the above escape/F1-F12 keys) while in console
1031         if (key_consoleactive)
1032 #else
1033         // respond to toggleconsole binds while in console unless the pressed key
1034         // happens to be the color prefix character (such as on German keyboards)
1035         if (key_consoleactive && (strncmp(bind, "toggleconsole", strlen("toggleconsole")) || ascii == STRING_COLOR_TAG))
1036 #endif
1037         {
1038                 Key_Console (key, ascii);
1039                 return;
1040         }
1041
1042         // anything else is a key press into the game, chat line, or menu
1043         switch (key_dest)
1044         {
1045                 case key_message:
1046                         Key_Message (key, ascii);
1047                         break;
1048                 case key_menu:
1049                         MR_Keydown (key, ascii);
1050                         break;
1051                 case key_game:
1052                         // ignore key repeats on binds
1053                         if (bind && keydown[key] == 1)
1054                         {
1055                                 // button commands add keynum as a parm
1056                                 if (bind[0] == '+')
1057                                         Cbuf_AddText (va("%s %i\n", bind, key));
1058                                 else
1059                                 {
1060                                         Cbuf_AddText (bind);
1061                                         Cbuf_AddText ("\n");
1062                                 }
1063                         }
1064                         break;
1065                 default:
1066                         if(UI_Callback_IsSlotUsed(key_dest - 3))
1067                                 UI_Callback_KeyDown (key, ascii);
1068                         else
1069                                 Sys_Error ("Bad key_dest");
1070         }
1071 #else
1072         const char      *kb;
1073         char            cmd[1024];
1074
1075         if (!down)
1076                 keydown[key] = 0;
1077
1078         key_lastpress = key;
1079         key_count++;
1080         if (key_count <= 0) {
1081                 return;                                                 // just catching keys for Con_NotifyBox
1082         }
1083
1084         // update auto-repeat status
1085         if (down) {
1086                 keydown[key] = min(keydown[key] + 1, 2);
1087                 if (keydown[key] > 1) {
1088                         if ((key_consoleactive && !consolekeys[key]) ||
1089                                         (!key_consoleactive && key_dest == key_game &&
1090                                          (cls.state == ca_connected && cls.signon == SIGNONS)))
1091                                 return;                                         // ignore most autorepeats
1092                 }
1093         }
1094
1095         if (key == K_CTRL)
1096                 ctrl_down = down;
1097
1098         //
1099         // handle escape specially, so the user can never unbind it
1100         //
1101         if (key == K_ESCAPE) {
1102                 if (!down)
1103                         return;
1104                 // ctrl-escape is a safety measure
1105                 if (ctrl_down)
1106                 {
1107                         Con_ToggleConsole_f ();
1108                         return;
1109                 }
1110                 switch (key_dest) {
1111                         case key_message:
1112                                 Key_Message (key, ascii);
1113                                 break;
1114                         case key_menu:
1115                                 MR_Keydown (key, ascii);
1116                                 break;
1117                         case key_game:
1118                                 MR_ToggleMenu_f ();
1119                                 break;
1120                         default:
1121                                 if(UI_Callback_IsSlotUsed(key_dest - 3))
1122                                         UI_Callback_KeyDown (key, ascii);
1123                                 else
1124                                         Sys_Error ("Bad key_dest");
1125                 }
1126                 return;
1127         }
1128
1129         if (down)
1130         {
1131                 if (!(kb = keybindings[key_bmap][key]))
1132                         kb = keybindings[key_bmap2][key];
1133                 if (kb && !strncmp(kb, "toggleconsole", strlen("toggleconsole")))
1134                 {
1135                         Cbuf_AddText (kb);
1136                         Cbuf_AddText ("\n");
1137                         return;
1138                 }
1139         }
1140
1141         if (key_consoleactive && consolekeys[key] && down)
1142                 Key_Console (key, ascii);
1143         else
1144         {
1145                 //
1146                 // key up events only generate commands if the game key binding is a button
1147                 // command (leading + sign).  These will occur even in console mode, to
1148                 // keep the character from continuing an action started before a console
1149                 // switch.  Button commands include the kenum as a parameter, so multiple
1150                 // downs can be matched with ups
1151                 //
1152                 if (!down) {
1153                         if (!(kb = keybindings[key_bmap][key]))
1154                                 kb = keybindings[key_bmap2][key];
1155
1156                         if (kb && kb[0] == '+') {
1157                                 dpsnprintf (cmd, sizeof(cmd), "-%s %i\n", kb + 1, key);
1158                                 Cbuf_AddText (cmd);
1159                         }
1160                         return;
1161                 }
1162
1163                 //
1164                 // during demo playback, most keys bring up the main menu
1165                 //
1166                 if (cls.demoplayback && down && consolekeys[key] && key_dest == key_game) {
1167                         MR_ToggleMenu_f ();
1168                         return;
1169                 }
1170
1171                 //
1172                 // if not a consolekey, send to the interpreter no matter what mode is
1173                 //
1174                 if ((key_dest == key_menu && menubound[key])
1175                                 || (key_consoleactive && !consolekeys[key])
1176                                 || (key_dest == key_game &&
1177                                         ((cls.state == ca_connected) || !consolekeys[key]))) {
1178                         if (!(kb = keybindings[key_bmap][key]))
1179                                 kb = keybindings[key_bmap2][key];
1180                         if (kb) {
1181                                 if (kb[0] == '+') {                     // button commands add keynum as a parm
1182                                         dpsnprintf (cmd, sizeof(cmd), "%s %i\n", kb, key);
1183                                         Cbuf_AddText (cmd);
1184                                 } else {
1185                                         Cbuf_AddText (kb);
1186                                         Cbuf_AddText ("\n");
1187                                 }
1188                         }
1189                         return;
1190                 }
1191
1192                 if (!down)
1193                         return;                                                 // other systems only care about key
1194                 // down events
1195
1196                 switch (key_dest) {
1197                         case key_message:
1198                                 Key_Message (key, ascii);
1199                                 break;
1200                         case key_menu:
1201                                 MR_Keydown (key, ascii);
1202                                 break;
1203                         case key_game:
1204                                 Key_Console (key, ascii);
1205                                 break;
1206                         default:
1207                                 if(UI_Callback_IsSlotUsed(key_dest - 3))
1208                                         UI_Callback_KeyDown (key, ascii);
1209                                 else
1210                                         Sys_Error ("Bad key_dest");
1211                 }
1212         }
1213 #endif
1214 }
1215
1216 /*
1217 ===================
1218 Key_ClearStates
1219 ===================
1220 */
1221 void
1222 Key_ClearStates (void)
1223 {
1224         memset(keydown, 0, sizeof(keydown));
1225 }