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