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