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