]> icculus.org git repositories - divverent/darkplaces.git/blob - keys.c
remove one unneeded cdPlaying check
[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, int 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, *p;
288                 if ((cbd = Sys_GetClipboardData()) != 0)
289                 {
290                         int i;
291 #if 1
292                         p = cbd;
293                         while (*p)
294                         {
295                                 if (*p == '\n' || *p == '\r' || *p == '\b')
296                                 {
297                                         *p++ = 0;
298                                         break;
299                                 }
300                                 p++;
301                         }
302 #else
303                         strtok(cbd, "\n\r\b");
304 #endif
305                         i = (int)strlen(cbd);
306                         if (i + key_linepos >= MAX_INPUTLINE)
307                                 i= MAX_INPUTLINE - key_linepos - 1;
308                         if (i > 0)
309                         {
310                                 // terencehill: insert the clipboard text between the characters of the line
311                                 char *temp = Z_Malloc(MAX_INPUTLINE);
312                                 cbd[i]=0;
313                                 temp[0]=0;
314                                 if ( key_linepos < (int)strlen(key_lines[edit_line]) )
315                                         strlcpy(temp, key_lines[edit_line] + key_linepos, (int)strlen(key_lines[edit_line]) - key_linepos +1);
316                                 key_lines[edit_line][key_linepos] = 0;
317                                 strlcat(key_lines[edit_line], cbd, sizeof(key_lines[edit_line]));
318                                 if (temp[0])
319                                         strlcat(key_lines[edit_line], temp, sizeof(key_lines[edit_line]));
320                                 Z_Free(temp);
321                                 key_linepos += i;
322                         }
323                         Z_Free(cbd);
324                 }
325                 return;
326         }
327
328         if (key == 'l')
329         {
330                 if (keydown[K_CTRL])
331                 {
332                         Cbuf_AddText ("clear\n");
333                         return;
334                 }
335         }
336
337         if (key == K_ENTER || key == K_KP_ENTER)
338         {
339                 Cbuf_AddText (key_lines[edit_line]+1);  // skip the ]
340                 Cbuf_AddText ("\n");
341                 Con_Printf("%s\n",key_lines[edit_line]);
342                 if(key_lines[edit_line][1] == 0) // empty line (just a ])?
343                         return; // no, no, you can't submit empty lines to the history...
344                 // LordHavoc: redesigned edit_line/history_line
345                 edit_line = 31;
346                 history_line = edit_line;
347                 memmove(key_lines[0], key_lines[1], sizeof(key_lines[0]) * edit_line);
348                 key_lines[edit_line][0] = ']';
349                 key_lines[edit_line][1] = 0;    // EvilTypeGuy: null terminate
350                 key_linepos = 1;
351                 // force an update, because the command may take some time
352                 if (cls.state == ca_disconnected)
353                         CL_UpdateScreen ();
354                 return;
355         }
356
357         if (key == K_TAB)
358         {
359                 // Enhanced command completion
360                 // by EvilTypeGuy eviltypeguy@qeradiant.com
361                 // Thanks to Fett, Taniwha
362                 Con_CompleteCommandLine();
363                 return;
364         }
365
366         // Advanced Console Editing by Radix radix@planetquake.com
367         // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
368         // Enhanced by [515]
369         // Enhanced by terencehill
370
371         // move cursor to the previous character
372         if (key == K_LEFTARROW || key == K_KP_LEFTARROW)
373         {
374                 if (key_linepos < 2)
375                         return;
376                 if(keydown[K_CTRL]) // move cursor to the previous word
377                 {
378                         int             pos;
379                         char    k;
380                         pos = key_linepos-1;
381                         if(pos)
382                                 while(--pos)
383                                 {
384                                         k = key_lines[edit_line][pos];
385                                         if(k == '\"' || k == ';' || k == ' ' || k == '\'')
386                                                 break;
387                                 }
388                         key_linepos = pos + 1;
389                 }
390                 else if(keydown[K_SHIFT]) // move cursor to the previous character ignoring colors
391                 {
392                         int             pos;
393                         pos = key_linepos-1;
394                         while (pos)
395                                 if(pos-1 > 0 && key_lines[edit_line][pos-1] == STRING_COLOR_TAG && isdigit(key_lines[edit_line][pos]))
396                                         pos-=2;
397                                 else if(pos-4 > 0 && key_lines[edit_line][pos-4] == STRING_COLOR_TAG && key_lines[edit_line][pos-3] == STRING_COLOR_RGB_TAG_CHAR
398                                                 && isxdigit(key_lines[edit_line][pos-2]) && isxdigit(key_lines[edit_line][pos-1]) && isxdigit(key_lines[edit_line][pos]))
399                                         pos-=5;
400                                 else
401                                 {
402                                         pos--;
403                                         break;
404                                 }
405                         key_linepos = pos + 1;
406                 }
407                 else
408                         key_linepos--;
409                 return;
410         }
411
412         // delete char before cursor
413         if (key == K_BACKSPACE || (key == 'h' && keydown[K_CTRL]))
414         {
415                 if (key_linepos > 1)
416                 {
417                         strlcpy(key_lines[edit_line] + key_linepos - 1, key_lines[edit_line] + key_linepos, sizeof(key_lines[edit_line]) + 1 - key_linepos);
418                         key_linepos--;
419                 }
420                 return;
421         }
422
423         // delete char on cursor
424         if (key == K_DEL || key == K_KP_DEL)
425         {
426                 size_t linelen;
427                 linelen = strlen(key_lines[edit_line]);
428                 if (key_linepos < (int)linelen)
429                         memmove(key_lines[edit_line] + key_linepos, key_lines[edit_line] + key_linepos + 1, linelen - key_linepos);
430                 return;
431         }
432
433
434         // move cursor to the next character
435         if (key == K_RIGHTARROW || key == K_KP_RIGHTARROW)
436         {
437                 if (key_linepos >= (int)strlen(key_lines[edit_line]))
438                         return;
439                 if(keydown[K_CTRL]) // move cursor to the next word
440                 {
441                         int             pos, len;
442                         char    k;
443                         len = (int)strlen(key_lines[edit_line]);
444                         pos = key_linepos;
445                         while(++pos < len)
446                         {
447                                 k = key_lines[edit_line][pos];
448                                 if(k == '\"' || k == ';' || k == ' ' || k == '\'')
449                                         break;
450                         }
451                         key_linepos = pos;
452                 }
453                 else if(keydown[K_SHIFT]) // move cursor to the next character ignoring colors
454                 {
455                         int             pos, len;
456                         len = (int)strlen(key_lines[edit_line]);
457                         pos = key_linepos;
458                         // check if there is a color tag right after the cursor
459                         if (key_lines[edit_line][pos] == STRING_COLOR_TAG)
460                         {
461                                 if(isdigit(key_lines[edit_line][pos+1]))
462                                         pos+=1;
463                                 else if(key_lines[edit_line][pos+1] == STRING_COLOR_RGB_TAG_CHAR && isxdigit(key_lines[edit_line][pos+2]) && isxdigit(key_lines[edit_line][pos+3]) && isxdigit(key_lines[edit_line][pos+4]))
464                                         pos+=4;
465                         }
466                         pos++;
467                         
468                         // now go beyond all next consecutive color tags, if any
469                         if(pos < len)
470                                 while (key_lines[edit_line][pos] == STRING_COLOR_TAG)
471                                 {
472                                         if(isdigit(key_lines[edit_line][pos+1]))
473                                                 pos+=2;
474                                         else if(key_lines[edit_line][pos+1] == STRING_COLOR_RGB_TAG_CHAR && isxdigit(key_lines[edit_line][pos+2]) && isxdigit(key_lines[edit_line][pos+3]) && isxdigit(key_lines[edit_line][pos+4]))
475                                                 pos+=5;
476                                         else
477                                                 break;
478                                 }
479                         key_linepos = pos;
480                 }
481                 else
482                         key_linepos++;
483                 return;
484         }
485
486         if (key == K_INS || key == K_KP_INS) // toggle insert mode
487         {
488                 key_insert ^= 1;
489                 return;
490         }
491
492         // End Advanced Console Editing
493
494         if (key == K_UPARROW || key == K_KP_UPARROW || (key == 'p' && keydown[K_CTRL]))
495         {
496                 if (history_line > 0 && key_lines[history_line-1][1])
497                 {
498                         size_t linelen;
499                         history_line--;
500                         linelen = strlen(key_lines[history_line]);
501                         memcpy(key_lines[edit_line], key_lines[history_line], linelen + 1);
502                         key_linepos = (int)linelen;
503                 }
504                 return;
505         }
506
507         if (key == K_DOWNARROW || key == K_KP_DOWNARROW || (key == 'n' && keydown[K_CTRL]))
508         {
509                 history_line++;
510                 if (history_line >= edit_line)
511                 {
512                         history_line = edit_line;
513                         key_lines[edit_line][0] = ']';
514                         key_lines[edit_line][1] = 0;
515                         key_linepos = 1;
516                 }
517                 else
518                 {
519                         size_t linelen;
520                         linelen = strlen(key_lines[history_line]);
521                         memcpy(key_lines[edit_line], key_lines[history_line], linelen + 1);
522                         key_linepos = (int)linelen;
523                 }
524                 return;
525         }
526
527         if (key == K_PGUP || key == K_KP_PGUP || key == K_MWHEELUP)
528         {
529                 if(keydown[K_CTRL])
530                 {
531                         con_backscroll += 3;
532                 }
533                 else
534                         con_backscroll += ((int) vid_conheight.integer >> 5);
535                 return;
536         }
537
538         if (key == K_PGDN || key == K_KP_PGDN || key == K_MWHEELDOWN)
539         {
540                 if(keydown[K_CTRL])
541                 {
542                         con_backscroll -= 3;
543                 }
544                 else
545                         con_backscroll -= ((int) vid_conheight.integer >> 5);
546                 return;
547         }
548
549         if (key == K_HOME || key == K_KP_HOME)
550         {
551                 if (keydown[K_CTRL])
552                         con_backscroll = INT_MAX;
553                 else
554                         key_linepos = 1;
555                 return;
556         }
557
558         if (key == K_END || key == K_KP_END)
559         {
560                 if (keydown[K_CTRL])
561                         con_backscroll = 0;
562                 else
563                         key_linepos = (int)strlen(key_lines[edit_line]);
564                 return;
565         }
566
567         // non printable
568         if (ascii < 32)
569                 return;
570
571         if (key_linepos < MAX_INPUTLINE-1)
572         {
573                 int len;
574                 len = (int)strlen(&key_lines[edit_line][key_linepos]);
575                 // check insert mode, or always insert if at end of line
576                 if (key_insert || len == 0)
577                 {
578                         // can't use strcpy to move string to right
579                         len++;
580                         memmove(&key_lines[edit_line][key_linepos + 1], &key_lines[edit_line][key_linepos], len);
581                 }
582                 key_lines[edit_line][key_linepos] = ascii;
583                 key_linepos++;
584         }
585 }
586
587 //============================================================================
588
589 int chat_mode;
590 char            chat_buffer[MAX_INPUTLINE];
591 unsigned int    chat_bufferlen = 0;
592
593 extern int Nicks_CompleteChatLine(char *buffer, size_t size, unsigned int pos);
594
595 static void
596 Key_Message (int key, int ascii)
597 {
598
599         if (key == K_ENTER || ascii == 10 || ascii == 13)
600         {
601                 if(chat_mode < 0)
602                         Cmd_ExecuteString(chat_buffer, src_command); // not Cbuf_AddText to allow semiclons in args; however, this allows no variables then. Use aliases!
603                 else
604                         Cmd_ForwardStringToServer(va("%s %s", chat_mode ? "say_team" : "say ", chat_buffer));
605
606                 key_dest = key_game;
607                 chat_bufferlen = 0;
608                 chat_buffer[0] = 0;
609                 return;
610         }
611
612         // TODO add support for arrow keys and simple editing
613
614         if (key == K_ESCAPE) {
615                 key_dest = key_game;
616                 chat_bufferlen = 0;
617                 chat_buffer[0] = 0;
618                 return;
619         }
620
621         if (key == K_BACKSPACE) {
622                 if (chat_bufferlen) {
623                         chat_bufferlen--;
624                         chat_buffer[chat_bufferlen] = 0;
625                 }
626                 return;
627         }
628
629         if(key == K_TAB) {
630                 chat_bufferlen = Nicks_CompleteChatLine(chat_buffer, sizeof(chat_buffer), chat_bufferlen);
631                 return;
632         }
633
634         if (chat_bufferlen == sizeof (chat_buffer) - 1)
635                 return;                                                 // all full
636
637         if (!ascii)
638                 return;                                                 // non printable
639
640         chat_buffer[chat_bufferlen++] = ascii;
641         chat_buffer[chat_bufferlen] = 0;
642 }
643
644 //============================================================================
645
646
647 /*
648 ===================
649 Returns a key number to be used to index keybindings[] by looking at
650 the given string.  Single ascii characters return themselves, while
651 the K_* names are matched up.
652 ===================
653 */
654 int
655 Key_StringToKeynum (const char *str)
656 {
657         const keyname_t  *kn;
658
659         if (!str || !str[0])
660                 return -1;
661         if (!str[1])
662                 return tolower(str[0]);
663
664         for (kn = keynames; kn->name; kn++) {
665                 if (!strcasecmp (str, kn->name))
666                         return kn->keynum;
667         }
668         return -1;
669 }
670
671 /*
672 ===================
673 Returns a string (either a single ascii char, or a K_* name) for the
674 given keynum.
675 FIXME: handle quote special (general escape sequence?)
676 ===================
677 */
678 const char *
679 Key_KeynumToString (int keynum)
680 {
681         const keyname_t  *kn;
682         static char tinystr[2];
683
684         // -1 is an invalid code
685         if (keynum < 0)
686                 return "<KEY NOT FOUND>";
687
688         // search overrides first, because some characters are special
689         for (kn = keynames; kn->name; kn++)
690                 if (keynum == kn->keynum)
691                         return kn->name;
692
693         // if it is printable, output it as a single character
694         if (keynum > 32 && keynum < 256)
695         {
696                 tinystr[0] = keynum;
697                 tinystr[1] = 0;
698                 return tinystr;
699         }
700
701         // if it is not overridden and not printable, we don't know what to do with it
702         return "<UNKNOWN KEYNUM>";
703 }
704
705
706 void
707 Key_SetBinding (int keynum, int bindmap, const char *binding)
708 {
709         char *newbinding;
710         size_t l;
711
712         if (keynum == -1 || keynum >= MAX_KEYS)
713                 return;
714
715 // free old bindings
716         if (keybindings[bindmap][keynum]) {
717                 Z_Free (keybindings[bindmap][keynum]);
718                 keybindings[bindmap][keynum] = NULL;
719         }
720         if(!binding[0]) // make "" binds be removed --blub
721                 return;
722 // allocate memory for new binding
723         l = strlen (binding);
724         newbinding = (char *)Z_Malloc (l + 1);
725         memcpy (newbinding, binding, l + 1);
726         newbinding[l] = 0;
727         keybindings[bindmap][keynum] = newbinding;
728 }
729
730 static void
731 Key_In_Unbind_f (void)
732 {
733         int         b, m;
734         char *errchar = NULL;
735
736         if (Cmd_Argc () != 3) {
737                 Con_Print("in_unbind <bindmap> <key> : remove commands from a key\n");
738                 return;
739         }
740
741         m = strtol(Cmd_Argv (1), &errchar, 0);
742         if ((m < 0) || (m >= 8) || (errchar && *errchar)) {
743                 Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(1));
744                 return;
745         }
746
747         b = Key_StringToKeynum (Cmd_Argv (2));
748         if (b == -1) {
749                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (2));
750                 return;
751         }
752
753         Key_SetBinding (b, m, "");
754 }
755
756 static void
757 Key_In_Bind_f (void)
758 {
759         int         i, c, b, m;
760         char        cmd[MAX_INPUTLINE];
761         char *errchar = NULL;
762
763         c = Cmd_Argc ();
764
765         if (c != 3 && c != 4) {
766                 Con_Print("in_bind <bindmap> <key> [command] : attach a command to a key\n");
767                 return;
768         }
769
770         m = strtol(Cmd_Argv (1), &errchar, 0);
771         if ((m < 0) || (m >= 8) || (errchar && *errchar)) {
772                 Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(1));
773                 return;
774         }
775
776         b = Key_StringToKeynum (Cmd_Argv (2));
777         if (b == -1 || b >= MAX_KEYS) {
778                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (2));
779                 return;
780         }
781
782         if (c == 3) {
783                 if (keybindings[m][b])
784                         Con_Printf("\"%s\" = \"%s\"\n", Cmd_Argv (2), keybindings[m][b]);
785                 else
786                         Con_Printf("\"%s\" is not bound\n", Cmd_Argv (2));
787                 return;
788         }
789 // copy the rest of the command line
790         cmd[0] = 0;                                                     // start out with a null string
791         for (i = 3; 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, m, cmd);
798 }
799
800 static void
801 Key_In_Bindmap_f (void)
802 {
803         int         m1, m2, c;
804         char *errchar = NULL;
805
806         c = Cmd_Argc ();
807
808         if (c != 3) {
809                 Con_Print("in_bindmap <bindmap> <fallback>: set current bindmap and fallback\n");
810                 return;
811         }
812
813         m1 = strtol(Cmd_Argv (1), &errchar, 0);
814         if ((m1 < 0) || (m1 >= 8) || (errchar && *errchar)) {
815                 Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(1));
816                 return;
817         }
818
819         m2 = strtol(Cmd_Argv (2), &errchar, 0);
820         if ((m2 < 0) || (m2 >= 8) || (errchar && *errchar)) {
821                 Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(2));
822                 return;
823         }
824
825         key_bmap = m1;
826         key_bmap2 = m2;
827 }
828
829 static void
830 Key_Unbind_f (void)
831 {
832         int         b;
833
834         if (Cmd_Argc () != 2) {
835                 Con_Print("unbind <key> : remove commands from a key\n");
836                 return;
837         }
838
839         b = Key_StringToKeynum (Cmd_Argv (1));
840         if (b == -1) {
841                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (1));
842                 return;
843         }
844
845         Key_SetBinding (b, 0, "");
846 }
847
848 static void
849 Key_Unbindall_f (void)
850 {
851         int         i, j;
852
853         for (j = 0; j < 8; j++)
854                 for (i = 0; i < (int)(sizeof(keybindings[0])/sizeof(keybindings[0][0])); i++)
855                         if (keybindings[j][i])
856                                 Key_SetBinding (i, j, "");
857 }
858
859 static void
860 Key_PrintBindList(int j)
861 {
862         char bindbuf[MAX_INPUTLINE];
863         const char *p;
864         int i;
865
866         for (i = 0; i < (int)(sizeof(keybindings[0])/sizeof(keybindings[0][0])); i++)
867         {
868                 p = keybindings[j][i];
869                 if (p)
870                 {
871                         Cmd_QuoteString(bindbuf, sizeof(bindbuf), p, "\"\\");
872                         if (j == 0)
873                                 Con_Printf("^2%s ^7= \"%s\"\n", Key_KeynumToString (i), bindbuf);
874                         else
875                                 Con_Printf("^3bindmap %d: ^2%s ^7= \"%s\"\n", j, Key_KeynumToString (i), bindbuf);
876                 }
877         }
878 }
879
880 static void
881 Key_In_BindList_f (void)
882 {
883         int m;
884         char *errchar = NULL;
885
886         if(Cmd_Argc() >= 2)
887         {
888                 m = strtol(Cmd_Argv(1), &errchar, 0);
889                 if ((m < 0) || (m >= 8) || (errchar && *errchar)) {
890                         Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(1));
891                         return;
892                 }
893                 Key_PrintBindList(m);
894         }
895         else
896         {
897                 for (m = 0; m < MAX_BINDMAPS; m++)
898                         Key_PrintBindList(m);
899         }
900 }
901
902 static void
903 Key_BindList_f (void)
904 {
905         Key_PrintBindList(0);
906 }
907
908 static void
909 Key_Bind_f (void)
910 {
911         int         i, c, b;
912         char        cmd[MAX_INPUTLINE];
913
914         c = Cmd_Argc ();
915
916         if (c != 2 && c != 3) {
917                 Con_Print("bind <key> [command] : attach a command to a key\n");
918                 return;
919         }
920         b = Key_StringToKeynum (Cmd_Argv (1));
921         if (b == -1 || b >= MAX_KEYS) {
922                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (1));
923                 return;
924         }
925
926         if (c == 2) {
927                 if (keybindings[0][b])
928                         Con_Printf("\"%s\" = \"%s\"\n", Cmd_Argv (1), keybindings[0][b]);
929                 else
930                         Con_Printf("\"%s\" is not bound\n", Cmd_Argv (1));
931                 return;
932         }
933 // copy the rest of the command line
934         cmd[0] = 0;                                                     // start out with a null string
935         for (i = 2; i < c; i++) {
936                 strlcat (cmd, Cmd_Argv (i), sizeof (cmd));
937                 if (i != (c - 1))
938                         strlcat (cmd, " ", sizeof (cmd));
939         }
940
941         Key_SetBinding (b, 0, cmd);
942 }
943
944 /*
945 ============
946 Writes lines containing "bind key value"
947 ============
948 */
949 void
950 Key_WriteBindings (qfile_t *f)
951 {
952         int         i, j;
953         char bindbuf[MAX_INPUTLINE];
954         const char *p;
955
956         for (j = 0; j < MAX_BINDMAPS; j++)
957         {
958                 for (i = 0; i < (int)(sizeof(keybindings[0])/sizeof(keybindings[0][0])); i++)
959                 {
960                         p = keybindings[j][i];
961                         if (p)
962                         {
963                                 Cmd_QuoteString(bindbuf, sizeof(bindbuf), p, "\"\\");
964                                 if (j == 0)
965                                         FS_Printf(f, "bind %s \"%s\"\n", Key_KeynumToString (i), bindbuf);
966                                 else
967                                         FS_Printf(f, "in_bind %d %s \"%s\"\n", j, Key_KeynumToString (i), bindbuf);
968                         }
969                 }
970         }
971 }
972
973
974 void
975 Key_Init (void)
976 {
977         int         i;
978
979         for (i = 0; i < 32; i++) {
980                 key_lines[i][0] = ']';
981                 key_lines[i][1] = 0;
982         }
983         key_linepos = 1;
984
985 //
986 // register our functions
987 //
988         Cmd_AddCommand ("in_bind", Key_In_Bind_f, "binds a command to the specified key in the selected bindmap");
989         Cmd_AddCommand ("in_unbind", Key_In_Unbind_f, "removes command on the specified key in the selected bindmap");
990         Cmd_AddCommand ("in_bindlist", Key_In_BindList_f, "bindlist: displays bound keys for all bindmaps, or the given bindmap");
991         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");
992
993         Cmd_AddCommand ("bind", Key_Bind_f, "binds a command to the specified key in bindmap 0");
994         Cmd_AddCommand ("unbind", Key_Unbind_f, "removes a command on the specified key in bindmap 0");
995         Cmd_AddCommand ("bindlist", Key_BindList_f, "bindlist: displays bound keys for bindmap 0 bindmaps");
996         Cmd_AddCommand ("unbindall", Key_Unbindall_f, "removes all commands from all keys in all bindmaps (leaving only shift-escape and escape)");
997
998         Cvar_RegisterVariable (&con_closeontoggleconsole);
999 }
1000
1001 const char *Key_GetBind (int key)
1002 {
1003         const char *bind;
1004         if (key < 0 || key >= MAX_KEYS)
1005                 return NULL;
1006         bind = keybindings[key_bmap][key];
1007         if (!bind)
1008                 bind = keybindings[key_bmap2][key];
1009         return bind;
1010 }
1011
1012 qboolean CL_VM_InputEvent (qboolean down, int key, int ascii);
1013
1014 /*
1015 ===================
1016 Called by the system between frames for both key up and key down events
1017 Should NOT be called during an interrupt!
1018 ===================
1019 */
1020 static char tbl_keyascii[MAX_KEYS];
1021 static keydest_t tbl_keydest[MAX_KEYS];
1022
1023 void
1024 Key_Event (int key, int ascii, qboolean down)
1025 {
1026         const char *bind;
1027         qboolean q;
1028         keydest_t keydest = key_dest;
1029
1030         if (key < 0 || key >= MAX_KEYS)
1031                 return;
1032
1033         // get key binding
1034         bind = keybindings[key_bmap][key];
1035         if (!bind)
1036                 bind = keybindings[key_bmap2][key];
1037
1038         if (developer.integer >= 1000)
1039                 Con_Printf("Key_Event(%i, '%c', %s) keydown %i bind \"%s\"\n", key, ascii, down ? "down" : "up", keydown[key], bind ? bind : "");
1040
1041         if(key_consoleactive)
1042                 keydest = key_console;
1043         
1044         if (down)
1045         {
1046                 // increment key repeat count each time a down is received so that things
1047                 // which want to ignore key repeat can ignore it
1048                 keydown[key] = min(keydown[key] + 1, 2);
1049                 if(keydown[key] == 1) {
1050                         tbl_keyascii[key] = ascii;
1051                         tbl_keydest[key] = keydest;
1052                 } else {
1053                         ascii = tbl_keyascii[key];
1054                         keydest = tbl_keydest[key];
1055                 }
1056         }
1057         else
1058         {
1059                 // clear repeat count now that the key is released
1060                 keydown[key] = 0;
1061                 keydest = tbl_keydest[key];
1062                 ascii = tbl_keyascii[key];
1063         }
1064
1065         if(keydest == key_void)
1066                 return;
1067         
1068         // key_consoleactive is a flag not a key_dest because the console is a
1069         // high priority overlay ontop of the normal screen (designed as a safety
1070         // feature so that developers and users can rescue themselves from a bad
1071         // situation).
1072         //
1073         // this also means that toggling the console on/off does not lose the old
1074         // key_dest state
1075
1076         // specially handle escape (togglemenu) and shift-escape (toggleconsole)
1077         // engine bindings, these are not handled as normal binds so that the user
1078         // can recover from a completely empty bindmap
1079         if (key == K_ESCAPE)
1080         {
1081                 // ignore key repeats on escape
1082                 if (keydown[key] > 1)
1083                         return;
1084
1085                 // escape does these things:
1086                 // key_consoleactive - close console
1087                 // key_message - abort messagemode
1088                 // key_menu - go to parent menu (or key_game)
1089                 // key_game - open menu
1090
1091                 // in all modes shift-escape toggles console
1092                 if (keydown[K_SHIFT])
1093                 {
1094                         if(down)
1095                         {
1096                                 Con_ToggleConsole_f ();
1097                                 tbl_keydest[key] = key_void; // esc release should go nowhere (especially not to key_menu or key_game)
1098                         }
1099                         return;
1100                 }
1101
1102                 switch (keydest)
1103                 {
1104                         case key_console:
1105                                 if(down)
1106                                 {
1107                                         if(key_consoleactive & KEY_CONSOLEACTIVE_FORCED)
1108                                         {
1109                                                 key_consoleactive &= ~KEY_CONSOLEACTIVE_USER;
1110                                                 MR_ToggleMenu_f ();
1111                                         }
1112                                         else
1113                                                 Con_ToggleConsole_f();
1114                                 }
1115                                 break;
1116
1117                         case key_message:
1118                                 if (down)
1119                                         Key_Message (key, ascii); // that'll close the message input
1120                                 break;
1121
1122                         case key_menu:
1123                         case key_menu_grabbed:
1124                                 MR_KeyEvent (key, ascii, down);
1125                                 break;
1126
1127                         case key_game:
1128                                 // csqc has priority over toggle menu if it wants to (e.g. handling escape for UI stuff in-game.. :sick:)
1129                                 q = CL_VM_InputEvent(down, key, ascii);
1130                                 if (!q && down)
1131                                         MR_ToggleMenu_f ();
1132                                 break;
1133
1134                         default:
1135                                 Con_Printf ("Key_Event: Bad key_dest\n");
1136                 }
1137                 return;
1138         }
1139
1140         // send function keydowns to interpreter no matter what mode is (unless the menu has specifically grabbed the keyboard, for rebinding keys)
1141         if (keydest != key_menu_grabbed)
1142         if (key >= K_F1 && key <= K_F12)
1143         {
1144                 if (bind)
1145                 {
1146                         if(keydown[key] == 1 && down)
1147                         {
1148                                 // button commands add keynum as a parm
1149                                 if (bind[0] == '+')
1150                                         Cbuf_AddText (va("%s %i\n", bind, key));
1151                                 else
1152                                 {
1153                                         Cbuf_AddText (bind);
1154                                         Cbuf_AddText ("\n");
1155                                 }
1156                         } else if(bind[0] == '+' && !down && keydown[key] == 0)
1157                                 Cbuf_AddText(va("-%s %i\n", bind + 1, key));
1158                 }
1159                 return;
1160         }
1161
1162         // send input to console if it wants it
1163         if (keydest == key_console)
1164         {
1165                 if (!down)
1166                         return;
1167                 // con_closeontoggleconsole enables toggleconsole keys to close the
1168                 // console, as long as they are not the color prefix character
1169                 // (special exemption for german keyboard layouts)
1170                 if (con_closeontoggleconsole.integer && bind && !strncmp(bind, "toggleconsole", strlen("toggleconsole")) && (key_consoleactive & KEY_CONSOLEACTIVE_USER) && ascii != STRING_COLOR_TAG)
1171                 {
1172                         Con_ToggleConsole_f ();
1173                         return;
1174                 }
1175                 Key_Console (key, ascii);
1176                 return;
1177         }
1178
1179         // handle toggleconsole in menu too
1180         if (keydest == key_menu)
1181         {
1182                 if (down && con_closeontoggleconsole.integer && bind && !strncmp(bind, "toggleconsole", strlen("toggleconsole")) && ascii != STRING_COLOR_TAG)
1183                 {
1184                         Con_ToggleConsole_f ();
1185                         tbl_keydest[key] = key_void; // key release should go nowhere (especially not to key_menu or key_game)
1186                         return;
1187                 }
1188         }
1189
1190         // ignore binds while a video is played, let the video system handle the key event
1191         if (cl_videoplaying)
1192         {
1193                 CL_Video_KeyEvent (key, ascii, keydown[key] != 0);
1194                 return;
1195         }
1196
1197         // anything else is a key press into the game, chat line, or menu
1198         switch (keydest)
1199         {
1200                 case key_message:
1201                         if (down)
1202                                 Key_Message (key, ascii);
1203                         break;
1204                 case key_menu:
1205                 case key_menu_grabbed:
1206                         MR_KeyEvent (key, ascii, down);
1207                         break;
1208                 case key_game:
1209                         q = CL_VM_InputEvent(down, key, ascii);
1210                         // ignore key repeats on binds and only send the bind if the event hasnt been already processed by csqc
1211                         if (!q && bind)
1212                         {
1213                                 if(keydown[key] == 1 && down)
1214                                 {
1215                                         // button commands add keynum as a parm
1216                                         if (bind[0] == '+')
1217                                                 Cbuf_AddText (va("%s %i\n", bind, key));
1218                                         else
1219                                         {
1220                                                 Cbuf_AddText (bind);
1221                                                 Cbuf_AddText ("\n");
1222                                         }
1223                                 } else if(bind[0] == '+' && !down && keydown[key] == 0)
1224                                         Cbuf_AddText(va("-%s %i\n", bind + 1, key));
1225                         }
1226                         break;
1227                 default:
1228                         Con_Printf ("Key_Event: Bad key_dest\n");
1229         }
1230 }
1231
1232 /*
1233 ===================
1234 Key_ClearStates
1235 ===================
1236 */
1237 void
1238 Key_ClearStates (void)
1239 {
1240         memset(keydown, 0, sizeof(keydown));
1241 }