]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/framework/KeyInput.cpp
Various Mac OS X tweaks to get this to build. Probably breaking things.
[icculus/iodoom3.git] / neo / framework / KeyInput.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 #include "../idlib/precompiled.h"
30 #pragma hdrstop
31
32 typedef struct {
33         char    *name;
34         int             keynum;
35         char    *strId; // localized string id
36 } keyname_t;
37
38 // keys that can be set without a special name
39 static const char unnamedkeys[] = "*,-=./[\\]1234567890abcdefghijklmnopqrstuvwxyz";
40
41 #if MACOS_X
42 const char* OSX_GetLocalizedString( const char* );
43 #endif
44
45 // names not in this list can either be lowercase ascii, or '0xnn' hex sequences
46 keyname_t keynames[] =
47 {
48         {"TAB",                         K_TAB,                          "#str_07018"},
49         {"ENTER",                       K_ENTER,                        "#str_07019"},
50         {"ESCAPE",                      K_ESCAPE,                       "#str_07020"},
51         {"SPACE",                       K_SPACE,                        "#str_07021"},
52         {"BACKSPACE",           K_BACKSPACE,            "#str_07022"},
53         {"UPARROW",                     K_UPARROW,                      "#str_07023"},
54         {"DOWNARROW",           K_DOWNARROW,            "#str_07024"},
55         {"LEFTARROW",           K_LEFTARROW,            "#str_07025"},
56         {"RIGHTARROW",          K_RIGHTARROW,           "#str_07026"},
57
58         {"ALT",                         K_ALT,                          "#str_07027"},
59         {"RIGHTALT",            K_RIGHT_ALT,            "#str_07027"},
60         {"CTRL",                        K_CTRL,                         "#str_07028"},
61         {"SHIFT",                       K_SHIFT,                        "#str_07029"},
62
63         {"LWIN",                        K_LWIN,                         "#str_07030"},
64         {"RWIN",                        K_RWIN,                         "#str_07031"},
65         {"MENU",                        K_MENU,                         "#str_07032"},
66
67         {"COMMAND",                     K_COMMAND,                      "#str_07033"},
68
69         {"CAPSLOCK",            K_CAPSLOCK,                     "#str_07034"},
70         {"SCROLL",                      K_SCROLL,                       "#str_07035"},
71         {"PRINTSCREEN",         K_PRINT_SCR,            "#str_07179"},
72         
73         {"F1",                          K_F1,                           "#str_07036"},
74         {"F2",                          K_F2,                           "#str_07037"},
75         {"F3",                          K_F3,                           "#str_07038"},
76         {"F4",                          K_F4,                           "#str_07039"},
77         {"F5",                          K_F5,                           "#str_07040"},
78         {"F6",                          K_F6,                           "#str_07041"},
79         {"F7",                          K_F7,                           "#str_07042"},
80         {"F8",                          K_F8,                           "#str_07043"},
81         {"F9",                          K_F9,                           "#str_07044"},
82         {"F10",                         K_F10,                          "#str_07045"},
83         {"F11",                         K_F11,                          "#str_07046"},
84         {"F12",                         K_F12,                          "#str_07047"},
85
86         {"INS",                         K_INS,                          "#str_07048"},
87         {"DEL",                         K_DEL,                          "#str_07049"},
88         {"PGDN",                        K_PGDN,                         "#str_07050"},
89         {"PGUP",                        K_PGUP,                         "#str_07051"},
90         {"HOME",                        K_HOME,                         "#str_07052"},
91         {"END",                         K_END,                          "#str_07053"},
92
93         {"MOUSE1",                      K_MOUSE1,                       "#str_07054"},
94         {"MOUSE2",                      K_MOUSE2,                       "#str_07055"},
95         {"MOUSE3",                      K_MOUSE3,                       "#str_07056"},
96         {"MOUSE4",                      K_MOUSE4,                       "#str_07057"},
97         {"MOUSE5",                      K_MOUSE5,                       "#str_07058"},
98         {"MOUSE6",                      K_MOUSE6,                       "#str_07059"},
99         {"MOUSE7",                      K_MOUSE7,                       "#str_07060"},
100         {"MOUSE8",                      K_MOUSE8,                       "#str_07061"},
101
102         {"MWHEELUP",            K_MWHEELUP,                     "#str_07131"},
103         {"MWHEELDOWN",          K_MWHEELDOWN,           "#str_07132"},
104
105         {"JOY1",                        K_JOY1,                         "#str_07062"},
106         {"JOY2",                        K_JOY2,                         "#str_07063"},
107         {"JOY3",                        K_JOY3,                         "#str_07064"},
108         {"JOY4",                        K_JOY4,                         "#str_07065"},
109         {"JOY5",                        K_JOY5,                         "#str_07066"},
110         {"JOY6",                        K_JOY6,                         "#str_07067"},
111         {"JOY7",                        K_JOY7,                         "#str_07068"},
112         {"JOY8",                        K_JOY8,                         "#str_07069"},
113         {"JOY9",                        K_JOY9,                         "#str_07070"},
114         {"JOY10",                       K_JOY10,                        "#str_07071"},
115         {"JOY11",                       K_JOY11,                        "#str_07072"},
116         {"JOY12",                       K_JOY12,                        "#str_07073"},
117         {"JOY13",                       K_JOY13,                        "#str_07074"},
118         {"JOY14",                       K_JOY14,                        "#str_07075"},
119         {"JOY15",                       K_JOY15,                        "#str_07076"},
120         {"JOY16",                       K_JOY16,                        "#str_07077"},
121         {"JOY17",                       K_JOY17,                        "#str_07078"},
122         {"JOY18",                       K_JOY18,                        "#str_07079"},
123         {"JOY19",                       K_JOY19,                        "#str_07080"},
124         {"JOY20",                       K_JOY20,                        "#str_07081"},
125         {"JOY21",                       K_JOY21,                        "#str_07082"},
126         {"JOY22",                       K_JOY22,                        "#str_07083"},
127         {"JOY23",                       K_JOY23,                        "#str_07084"},
128         {"JOY24",                       K_JOY24,                        "#str_07085"},
129         {"JOY25",                       K_JOY25,                        "#str_07086"},
130         {"JOY26",                       K_JOY26,                        "#str_07087"},
131         {"JOY27",                       K_JOY27,                        "#str_07088"},
132         {"JOY28",                       K_JOY28,                        "#str_07089"},
133         {"JOY29",                       K_JOY29,                        "#str_07090"},
134         {"JOY30",                       K_JOY30,                        "#str_07091"},
135         {"JOY31",                       K_JOY31,                        "#str_07092"},
136         {"JOY32",                       K_JOY32,                        "#str_07093"},
137
138         {"AUX1",                        K_AUX1,                         "#str_07094"},
139         {"AUX2",                        K_AUX2,                         "#str_07095"},
140         {"AUX3",                        K_AUX3,                         "#str_07096"},
141         {"AUX4",                        K_AUX4,                         "#str_07097"},
142         {"AUX5",                        K_AUX5,                         "#str_07098"},
143         {"AUX6",                        K_AUX6,                         "#str_07099"},
144         {"AUX7",                        K_AUX7,                         "#str_07100"},
145         {"AUX8",                        K_AUX8,                         "#str_07101"},
146         {"AUX9",                        K_AUX9,                         "#str_07102"},
147         {"AUX10",                       K_AUX10,                        "#str_07103"},
148         {"AUX11",                       K_AUX11,                        "#str_07104"},
149         {"AUX12",                       K_AUX12,                        "#str_07105"},
150         {"AUX13",                       K_AUX13,                        "#str_07106"},
151         {"AUX14",                       K_AUX14,                        "#str_07107"},
152         {"AUX15",                       K_AUX15,                        "#str_07108"},
153         {"AUX16",                       K_AUX16,                        "#str_07109"},
154
155         {"KP_HOME",                     K_KP_HOME,                      "#str_07110"},
156         {"KP_UPARROW",          K_KP_UPARROW,           "#str_07111"},
157         {"KP_PGUP",                     K_KP_PGUP,                      "#str_07112"},
158         {"KP_LEFTARROW",        K_KP_LEFTARROW,         "#str_07113"},
159         {"KP_5",                        K_KP_5,                         "#str_07114"},
160         {"KP_RIGHTARROW",       K_KP_RIGHTARROW,        "#str_07115"},
161         {"KP_END",                      K_KP_END,                       "#str_07116"},
162         {"KP_DOWNARROW",        K_KP_DOWNARROW,         "#str_07117"},
163         {"KP_PGDN",                     K_KP_PGDN,                      "#str_07118"},
164         {"KP_ENTER",            K_KP_ENTER,                     "#str_07119"},
165         {"KP_INS",                      K_KP_INS,                       "#str_07120"},
166         {"KP_DEL",                      K_KP_DEL,                       "#str_07121"},
167         {"KP_SLASH",            K_KP_SLASH,             "#str_07122"},
168         {"KP_MINUS",            K_KP_MINUS,             "#str_07123"},
169         {"KP_PLUS",                     K_KP_PLUS,                      "#str_07124"},
170         {"KP_NUMLOCK",          K_KP_NUMLOCK,           "#str_07125"},
171         {"KP_STAR",                     K_KP_STAR,                      "#str_07126"},
172         {"KP_EQUALS",           K_KP_EQUALS,            "#str_07127"},
173
174         {"PAUSE",                       K_PAUSE,                        "#str_07128"},
175         
176         {"SEMICOLON",           ';',                            "#str_07129"},  // because a raw semicolon separates commands
177         {"APOSTROPHE",          '\'',                           "#str_07130"},  // because a raw apostrophe messes with parsing
178
179         {NULL,                          0,                                      NULL}
180 };
181
182
183
184 static const int        MAX_KEYS = 256;
185
186 class idKey {
187 public:
188                                         idKey( void ) { down = false; repeats = 0; usercmdAction = 0; }
189         bool                    down;
190         int                             repeats;                // if > 1, it is autorepeating
191         idStr                   binding;
192         int                             usercmdAction;  // for testing by the asyncronous usercmd generation
193 };
194
195 bool            key_overstrikeMode = false;
196 idKey *         keys = NULL;
197
198 #define ID_DOOM_LEGACY
199
200 #ifdef ID_DOOM_LEGACY
201
202 char *          cheatCodes[] = {
203         "iddqd",                // Invincibility
204         "idkfa",                // All weapons, keys, ammo, and 200% armor
205         "idfa",                 // Reset ammunition
206         "idspispopd",   // Walk through walls
207         "idclip",               // Walk through walls
208         "idchoppers",   // Chainsaw
209 /*
210         "idbeholds",    // Berserker strength
211         "idbeholdv",    // Temporary invincibility
212         "idbeholdi",    // Temporary invisibility
213         "idbeholda",    // Full automap
214         "idbeholdr",    // Anti-radiation suit
215         "idbeholdl",    // Light amplification visor
216         "idclev",               // Level select
217         "iddt",                 // Toggle full map; full map and objects; normal map
218         "idmypos",              // Display coordinates and heading
219         "idmus",                // Change music to indicated level
220         "fhhall",               // Kill all enemies in level
221         "fhshh",                // Invisible to enemies until attack
222 */
223         NULL
224 };
225 char            lastKeys[32];
226 int                     lastKeyIndex;
227
228 #endif
229
230 /*
231 ===================
232 idKeyInput::ArgCompletion_KeyName
233 ===================
234 */
235 void idKeyInput::ArgCompletion_KeyName( const idCmdArgs &args, void(*callback)( const char *s ) ) {
236         keyname_t *kn;
237         int i;
238
239         for( i = 0; i < sizeof( unnamedkeys ) - 1; i++ ) {
240                 callback( va( "%s %c", args.Argv( 0 ), unnamedkeys[ i ] ) );
241         }
242
243         for ( kn = keynames; kn->name; kn++ ) {
244                 callback( va( "%s %s", args.Argv( 0 ), kn->name ) );
245         }
246 }
247
248 /*
249 ===================
250 idKeyInput::GetOverstrikeMode
251 ===================
252 */
253 bool idKeyInput::GetOverstrikeMode( void ) {
254         return key_overstrikeMode;
255 }
256
257 /*
258 ===================
259 idKeyInput::SetOverstrikeMode
260 ===================
261 */
262 void idKeyInput::SetOverstrikeMode( bool state ) {
263         key_overstrikeMode = state;
264 }
265
266 /*
267 ===================
268 idKeyInput::IsDown
269 ===================
270 */
271 bool idKeyInput::IsDown( int keynum ) {
272         if ( keynum == -1 ) {
273                 return false;
274         }
275
276         return keys[keynum].down;
277 }
278
279 /*
280 ===================
281 idKeyInput::StringToKeyNum
282
283 Returns a key number to be used to index keys[] by looking at
284 the given string.  Single ascii characters return themselves, while
285 the K_* names are matched up.
286
287 0x11 will be interpreted as raw hex, which will allow new controlers
288 to be configured even if they don't have defined names.
289 ===================
290 */
291 int idKeyInput::StringToKeyNum( const char *str ) {
292         keyname_t       *kn;
293         
294         if ( !str || !str[0] ) {
295                 return -1;
296         }
297         if ( !str[1] ) {
298                 return (unsigned char)(str[0]);
299         }
300
301         // check for hex code
302         if ( str[0] == '0' && str[1] == 'x' && strlen( str ) == 4 ) {
303                 int             n1, n2;
304                 
305                 n1 = str[2];
306                 if ( n1 >= '0' && n1 <= '9' ) {
307                         n1 -= '0';
308                 } else if ( n1 >= 'a' && n1 <= 'f' ) {
309                         n1 = n1 - 'a' + 10;
310                 } else {
311                         n1 = 0;
312                 }
313
314                 n2 = str[3];
315                 if ( n2 >= '0' && n2 <= '9' ) {
316                         n2 -= '0';
317                 } else if ( n2 >= 'a' && n2 <= 'f' ) {
318                         n2 = n2 - 'a' + 10;
319                 } else {
320                         n2 = 0;
321                 }
322
323                 return n1 * 16 + n2;
324         }
325
326         // scan for a text match
327         for ( kn = keynames; kn->name; kn++ ) {
328                 if ( !idStr::Icmp( str, kn->name ) ) {
329                         return kn->keynum;
330                 }
331         }
332
333         return -1;
334 }
335
336 /*
337 ===================
338 idKeyInput::KeyNumToString
339
340 Returns a string (either a single ascii char, a K_* name, or a 0x11 hex string) for the
341 given keynum.
342 ===================
343 */
344 const char *idKeyInput::KeyNumToString( int keynum, bool localized ) {
345         keyname_t       *kn;    
346         static  char    tinystr[5];
347         int                     i, j;
348
349         if ( keynum == -1 ) {
350                 return "<KEY NOT FOUND>";
351         }
352
353         if ( keynum < 0 || keynum > 255 ) {
354                 return "<OUT OF RANGE>";
355         }
356
357         // check for printable ascii (don't use quote)
358         if ( keynum > 32 && keynum < 127 && keynum != '"' && keynum != ';' && keynum != '\'' ) {
359                 tinystr[0] = Sys_MapCharForKey( keynum );
360                 tinystr[1] = 0;
361                 return tinystr;
362         }
363
364         // check for a key string
365         for ( kn = keynames; kn->name; kn++ ) {
366                 if ( keynum == kn->keynum ) {
367                         if ( !localized || kn->strId[0] != '#' ) {
368                                 return kn->name;
369                         } else {
370 #if MACOS_X
371                                 
372                                 switch ( kn->keynum ) {
373                                         case K_ENTER:           
374                                         case K_BACKSPACE:       
375                                         case K_ALT:                     
376                                         case K_INS:
377                                         case K_PRINT_SCR:
378                                                 return OSX_GetLocalizedString( kn->name );
379                                                 break;
380                                         default :
381                                                 return common->GetLanguageDict()->GetString( kn->strId ); break;
382                                 }
383 #else
384                                 return common->GetLanguageDict()->GetString( kn->strId );
385 #endif
386                         }
387                 }
388         }
389
390         // check for European high-ASCII characters
391         if ( localized && keynum >= 161 && keynum <= 255 ) {
392                 tinystr[0] = keynum;
393                 tinystr[1] = 0;
394                 return tinystr;
395         }
396
397         // make a hex string
398         i = keynum >> 4;
399         j = keynum & 15;
400
401         tinystr[0] = '0';
402         tinystr[1] = 'x';
403         tinystr[2] = i > 9 ? i - 10 + 'a' : i + '0';
404         tinystr[3] = j > 9 ? j - 10 + 'a' : j + '0';
405         tinystr[4] = 0;
406
407         return tinystr;
408 }
409
410 /*
411 ===================
412 idKeyInput::SetBinding
413 ===================
414 */
415 void idKeyInput::SetBinding( int keynum, const char *binding ) {
416         if ( keynum == -1 ) {
417                 return;
418         }
419
420         // Clear out all button states so we aren't stuck forever thinking this key is held down
421         usercmdGen->Clear();
422
423         // allocate memory for new binding
424         keys[keynum].binding = binding;
425
426         // find the action for the async command generation
427         keys[keynum].usercmdAction = usercmdGen->CommandStringUsercmdData( binding );
428
429         // consider this like modifying an archived cvar, so the
430         // file write will be triggered at the next oportunity
431         cvarSystem->SetModifiedFlags( CVAR_ARCHIVE );
432 }
433
434
435 /*
436 ===================
437 idKeyInput::GetBinding
438 ===================
439 */
440 const char *idKeyInput::GetBinding( int keynum ) {
441         if ( keynum == -1 ) {
442                 return "";
443         }
444
445         return keys[ keynum ].binding;
446 }
447
448 /*
449 ===================
450 idKeyInput::GetUsercmdAction
451 ===================
452 */
453 int idKeyInput::GetUsercmdAction( int keynum ) {
454         return keys[ keynum ].usercmdAction;
455 }
456
457 /*
458 ===================
459 Key_Unbind_f
460 ===================
461 */
462 void Key_Unbind_f( const idCmdArgs &args ) {
463         int             b;
464
465         if ( args.Argc() != 2 ) {
466                 common->Printf( "unbind <key> : remove commands from a key\n" );
467                 return;
468         }
469         
470         b = idKeyInput::StringToKeyNum( args.Argv(1) );
471         if ( b == -1 ) {
472                 // If it wasn't a key, it could be a command
473                 if ( !idKeyInput::UnbindBinding( args.Argv(1) ) ) {
474                         common->Printf( "\"%s\" isn't a valid key\n", args.Argv(1) );
475                 }
476         } else {
477                 idKeyInput::SetBinding( b, "" );
478         }
479 }
480
481 /*
482 ===================
483 Key_Unbindall_f
484 ===================
485 */
486 void Key_Unbindall_f( const idCmdArgs &args ) {
487         int             i;
488         
489         for ( i = 0; i < MAX_KEYS; i++ ) {
490                 idKeyInput::SetBinding( i, "" );
491         }
492 }
493
494 /*
495 ===================
496 Key_Bind_f
497 ===================
498 */
499 void Key_Bind_f( const idCmdArgs &args ) {
500         int                     i, c, b;
501         char            cmd[MAX_STRING_CHARS];
502         
503         c = args.Argc();
504
505         if ( c < 2 ) {
506                 common->Printf( "bind <key> [command] : attach a command to a key\n" );
507                 return;
508         }
509         b = idKeyInput::StringToKeyNum( args.Argv(1) );
510         if ( b == -1 ) {
511                 common->Printf( "\"%s\" isn't a valid key\n", args.Argv(1) );
512                 return;
513         }
514
515         if ( c == 2 ) {
516                 if ( keys[b].binding.Length() ) {
517                         common->Printf( "\"%s\" = \"%s\"\n", args.Argv(1), keys[b].binding.c_str() );
518                 }
519                 else {
520                         common->Printf( "\"%s\" is not bound\n", args.Argv(1) );
521                 }
522                 return;
523         }
524         
525         // copy the rest of the command line
526         cmd[0] = 0;             // start out with a null string
527         for ( i = 2; i < c; i++ ) {
528                 strcat( cmd, args.Argv( i ) );
529                 if ( i != (c-1) ) {
530                         strcat( cmd, " " );
531                 }
532         }
533
534         idKeyInput::SetBinding( b, cmd );
535 }
536
537 /*
538 ============
539 Key_BindUnBindTwo_f
540
541 binds keynum to bindcommand and unbinds if there are already two binds on the key
542 ============
543 */
544 void Key_BindUnBindTwo_f( const idCmdArgs &args ) {
545         int c = args.Argc();
546         if ( c < 3 ) {
547                 common->Printf( "bindunbindtwo <keynum> [command]\n" );
548                 return;
549         }
550         int key = atoi( args.Argv( 1 ) );
551         idStr bind = args.Argv( 2 );
552         if ( idKeyInput::NumBinds( bind ) >= 2 && !idKeyInput::KeyIsBoundTo( key, bind ) ) {
553                 idKeyInput::UnbindBinding( bind );
554         }
555         idKeyInput::SetBinding( key, bind );
556 }
557
558
559
560 /*
561 ============
562 idKeyInput::WriteBindings
563
564 Writes lines containing "bind key value"
565 ============
566 */
567 void idKeyInput::WriteBindings( idFile *f ) {
568         int             i;
569
570         f->Printf( "unbindall\n" );
571
572         for ( i = 0; i < MAX_KEYS; i++ ) {
573                 if ( keys[i].binding.Length() ) {
574                         const char *name = KeyNumToString( i, false );
575
576                         // handle the escape character nicely
577                         if ( !strcmp( name, "\\" ) ) {
578                                 f->Printf( "bind \"\\\" \"%s\"\n", keys[i].binding.c_str() );
579                         } else {
580                                 f->Printf( "bind \"%s\" \"%s\"\n", KeyNumToString( i, false ), keys[i].binding.c_str() );
581                         }
582                 }
583         }
584 }
585
586 /*
587 ============
588 Key_ListBinds_f
589 ============
590 */
591 void Key_ListBinds_f( const idCmdArgs &args ) {
592         int             i;
593
594         for ( i = 0; i < MAX_KEYS; i++ ) {
595                 if ( keys[i].binding.Length() ) {
596                         common->Printf( "%s \"%s\"\n", idKeyInput::KeyNumToString( i, false ), keys[i].binding.c_str() );
597                 }
598         }
599 }
600
601 /*
602 ============
603 idKeyInput::KeysFromBinding
604 returns the localized name of the key for the binding
605 ============
606 */
607 const char *idKeyInput::KeysFromBinding( const char *bind ) {
608         int i;
609         static char keyName[MAX_STRING_CHARS];
610
611         keyName[0] = '\0';
612         if ( bind && *bind ) {
613                 for ( i = 0; i < MAX_KEYS; i++ ) {
614                         if ( keys[i].binding.Icmp( bind ) == 0 ) {
615                                 if ( keyName[0] != '\0' ) {
616                                         idStr::Append( keyName, sizeof( keyName ), common->GetLanguageDict()->GetString( "#str_07183" ) );
617                                 } 
618                                 idStr::Append( keyName, sizeof( keyName ), KeyNumToString( i, true ) );
619                         }
620                 }
621         }
622         if ( keyName[0] == '\0' ) {
623                 idStr::Copynz( keyName, common->GetLanguageDict()->GetString( "#str_07133" ), sizeof( keyName ) );
624         }
625         idStr::ToLower( keyName );
626         return keyName;
627 }
628
629 /*
630 ============
631 idKeyInput::BindingFromKey
632 returns the binding for the localized name of the key
633 ============
634 */
635 const char *idKeyInput::BindingFromKey( const char *key ) {
636         const int keyNum = idKeyInput::StringToKeyNum( key );
637         if ( keyNum<0 || keyNum >= MAX_KEYS ) {
638                 return NULL;
639         }
640         return keys[keyNum].binding.c_str();
641 }
642
643 /*
644 ============
645 idKeyInput::UnbindBinding
646 ============
647 */
648 bool idKeyInput::UnbindBinding( const char *binding ) {
649         bool unbound = false;
650         int i;
651
652         if ( binding && *binding ) {
653                 for ( i = 0; i < MAX_KEYS; i++ ) {
654                         if ( keys[i].binding.Icmp( binding ) == 0 ) {
655                                 SetBinding( i, "" );
656                                 unbound = true;
657                         }
658                 }
659         }
660         return unbound;
661 }
662
663 /*
664 ============
665 idKeyInput::NumBinds
666 ============
667 */
668 int idKeyInput::NumBinds( const char *binding ) {
669         int i, count = 0;
670
671         if ( binding && *binding ) {
672                 for ( i = 0; i < MAX_KEYS; i++ ) {
673                         if ( keys[i].binding.Icmp( binding ) == 0 ) {
674                                 count++;
675                         }
676                 }
677         }
678         return count;
679 }
680
681 /*
682 ============
683 idKeyInput::KeyIsBountTo
684 ============
685 */
686 bool idKeyInput::KeyIsBoundTo( int keynum, const char *binding ) {
687         if ( keynum >= 0 && keynum < MAX_KEYS ) {
688                 return ( keys[keynum].binding.Icmp( binding ) == 0 );
689         }
690         return false;
691 }
692
693 /*
694 ===================
695 idKeyInput::PreliminaryKeyEvent
696
697 Tracks global key up/down state
698 Called by the system for both key up and key down events
699 ===================
700 */
701 void idKeyInput::PreliminaryKeyEvent( int keynum, bool down ) {
702         keys[keynum].down = down;
703
704 #ifdef ID_DOOM_LEGACY
705         if ( down ) {
706                 lastKeys[ 0 + ( lastKeyIndex & 15 )] = keynum;
707                 lastKeys[16 + ( lastKeyIndex & 15 )] = keynum;
708                 lastKeyIndex = ( lastKeyIndex + 1 ) & 15;
709                 for ( int i = 0; cheatCodes[i] != NULL; i++ ) {
710                         int l = strlen( cheatCodes[i] );
711                         assert( l <= 16 );
712                         if ( idStr::Icmpn( lastKeys + 16 + ( lastKeyIndex & 15 ) - l, cheatCodes[i], l ) == 0 ) {
713                                 common->Printf( "your memory serves you well!\n" );
714                                 break;
715                         }
716                 }
717         }
718 #endif
719 }
720
721 /*
722 =================
723 idKeyInput::ExecKeyBinding
724 =================
725 */
726 bool idKeyInput::ExecKeyBinding( int keynum ) {
727         // commands that are used by the async thread
728         // don't add text
729         if ( keys[keynum].usercmdAction ) {
730                 return false;
731         }
732
733         // send the bound action
734         if ( keys[keynum].binding.Length() ) {
735                 cmdSystem->BufferCommandText( CMD_EXEC_APPEND, keys[keynum].binding.c_str() );
736                 cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "\n" );
737         }
738         return true;
739 }
740
741 /*
742 ===================
743 idKeyInput::ClearStates
744 ===================
745 */
746 void idKeyInput::ClearStates( void ) {
747         int i;
748
749         for ( i = 0; i < MAX_KEYS; i++ ) {
750                 if ( keys[i].down ) {
751                         PreliminaryKeyEvent( i, false );
752                 }
753                 keys[i].down = false;
754         }
755
756         // clear the usercommand states
757         usercmdGen->Clear();
758 }
759
760 /*
761 ===================
762 idKeyInput::Init
763 ===================
764 */
765 void idKeyInput::Init( void ) {
766
767         keys = new idKey[MAX_KEYS];
768
769         // register our functions
770         cmdSystem->AddCommand( "bind", Key_Bind_f, CMD_FL_SYSTEM, "binds a command to a key", idKeyInput::ArgCompletion_KeyName );
771         cmdSystem->AddCommand( "bindunbindtwo", Key_BindUnBindTwo_f, CMD_FL_SYSTEM, "binds a key but unbinds it first if there are more than two binds" );
772         cmdSystem->AddCommand( "unbind", Key_Unbind_f, CMD_FL_SYSTEM, "unbinds any command from a key", idKeyInput::ArgCompletion_KeyName );
773         cmdSystem->AddCommand( "unbindall", Key_Unbindall_f, CMD_FL_SYSTEM, "unbinds any commands from all keys" );
774         cmdSystem->AddCommand( "listBinds", Key_ListBinds_f, CMD_FL_SYSTEM, "lists key bindings" );
775 }
776
777 /*
778 ===================
779 idKeyInput::Shutdown
780 ===================
781 */
782 void idKeyInput::Shutdown( void ) {
783         delete [] keys;
784         keys = NULL;
785 }