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