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