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