1 /*
2 ** c_bind.cpp
3 ** Functions for using and maintaining key bindings
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 1998-2006 Randy Heit
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 ** notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 ** notice, this list of conditions and the following disclaimer in the
17 ** documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 ** derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 */
34
35 #include "doomtype.h"
36 #include "doomdef.h"
37 #include "cmdlib.h"
38 #include "c_dispatch.h"
39 #include "c_bind.h"
40 #include "g_level.h"
41 #include "hu_stuff.h"
42 #include "gi.h"
43 #include "configfile.h"
44 #include "i_system.h"
45 #include "d_event.h"
46 #include "w_wad.h"
47
48 #include <math.h>
49 #include <stdlib.h>
50
51 const char *KeyNames[NUM_KEYS] =
52 {
53 // This array is dependant on the particular keyboard input
54 // codes generated in i_input.c. If they change there, they
55 // also need to change here. In this case, we use the
56 // DirectInput codes and assume a qwerty keyboard layout.
57 // See <dinput.h> for the DIK_* codes
58
59 NULL, "escape", "1", "2", "3", "4", "5", "6", //00
60 "7", "8", "9", "0", "-", "=", "backspace","tab", //08
61 "q", "w", "e", "r", "t", "y", "u", "i", //10
62 "o", "p", "[", "]", "enter", "ctrl", "a", "s", //18
63 "d", "f", "g", "h", "j", "k", "l", ";", //20
64 "'", "`", "shift", "\\", "z", "x", "c", "v", //28
65 "b", "n", "m", ",", ".", "/", "rshift", "kp*", //30
66 "alt", "space", "capslock", "f1", "f2", "f3", "f4", "f5", //38
67 "f6", "f7", "f8", "f9", "f10", "numlock", "scroll", "kp7", //40
68 "kp8", "kp9", "kp-", "kp4", "kp5", "kp6", "kp+", "kp1", //48
69 "kp2", "kp3", "kp0", "kp.", NULL, NULL, "oem102", "f11", //50
70 "f12", NULL, NULL, NULL, NULL, NULL, NULL, NULL, //58
71 NULL, NULL, NULL, NULL, "f13", "f14", "f15", "f16", //60
72 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, //68
73 "kana", NULL, NULL, "abnt_c1", NULL, NULL, NULL, NULL, //70
74 NULL, "convert", NULL, "noconvert",NULL, "yen", "abnt_c2", NULL, //78
75 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, //80
76 NULL, NULL, NULL, NULL, NULL, "kp=", NULL, NULL, //88
77 "circumflex","@", ":", "_", "kanji", "stop", "ax", "unlabeled",//90
78 NULL, "prevtrack",NULL, NULL, "kp-enter", "rctrl", NULL, NULL, //98
79 "mute", "calculator","play", NULL, "stop", NULL, NULL, NULL, //A0
80 NULL, NULL, NULL, NULL, NULL, NULL, "voldown", NULL, //A8
81 "volup", NULL, "webhome", "kp,", NULL, "kp/", NULL, "sysrq", //B0
82 "ralt", NULL, NULL, NULL, NULL, NULL, NULL, NULL, //B8
83 NULL, NULL, NULL, NULL, NULL, "pause", NULL, "home", //C0
84 "uparrow", "pgup", NULL, "leftarrow",NULL, "rightarrow",NULL, "end", //C8
85 "downarrow","pgdn", "ins", "del", NULL, NULL, NULL, NULL, //D0
86 #ifdef __APPLE__
87 NULL, NULL, NULL, "command", NULL, "apps", "power", "sleep", //D8
88 #else // !__APPLE__
89 NULL, NULL, NULL, "lwin", "rwin", "apps", "power", "sleep", //D8
90 #endif // __APPLE__
91 NULL, NULL, NULL, "wake", NULL, "search", "favorites","refresh", //E0
92 "webstop", "webforward","webback", "mycomputer","mail", "mediaselect",NULL, NULL, //E8
93 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, //F0
94 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, //F8
95
96 // non-keyboard buttons that can be bound
97 "mouse1", "mouse2", "mouse3", "mouse4", // 8 mouse buttons
98 "mouse5", "mouse6", "mouse7", "mouse8",
99
100 "joy1", "joy2", "joy3", "joy4", // 128 joystick buttons!
101 "joy5", "joy6", "joy7", "joy8",
102 "joy9", "joy10", "joy11", "joy12",
103 "joy13", "joy14", "joy15", "joy16",
104 "joy17", "joy18", "joy19", "joy20",
105 "joy21", "joy22", "joy23", "joy24",
106 "joy25", "joy26", "joy27", "joy28",
107 "joy29", "joy30", "joy31", "joy32",
108 "joy33", "joy34", "joy35", "joy36",
109 "joy37", "joy38", "joy39", "joy40",
110 "joy41", "joy42", "joy43", "joy44",
111 "joy45", "joy46", "joy47", "joy48",
112 "joy49", "joy50", "joy51", "joy52",
113 "joy53", "joy54", "joy55", "joy56",
114 "joy57", "joy58", "joy59", "joy60",
115 "joy61", "joy62", "joy63", "joy64",
116 "joy65", "joy66", "joy67", "joy68",
117 "joy69", "joy70", "joy71", "joy72",
118 "joy73", "joy74", "joy75", "joy76",
119 "joy77", "joy78", "joy79", "joy80",
120 "joy81", "joy82", "joy83", "joy84",
121 "joy85", "joy86", "joy87", "joy88",
122 "joy89", "joy90", "joy91", "joy92",
123 "joy93", "joy94", "joy95", "joy96",
124 "joy97", "joy98", "joy99", "joy100",
125 "joy101", "joy102", "joy103", "joy104",
126 "joy105", "joy106", "joy107", "joy108",
127 "joy109", "joy110", "joy111", "joy112",
128 "joy113", "joy114", "joy115", "joy116",
129 "joy117", "joy118", "joy119", "joy120",
130 "joy121", "joy122", "joy123", "joy124",
131 "joy125", "joy126", "joy127", "joy128",
132
133 "pov1up", "pov1right","pov1down", "pov1left", // First POV hat
134 "pov2up", "pov2right","pov2down", "pov2left", // Second POV hat
135 "pov3up", "pov3right","pov3down", "pov3left", // Third POV hat
136 "pov4up", "pov4right","pov4down", "pov4left", // Fourth POV hat
137
138 "mwheelup", "mwheeldown", // the mouse wheel
139 "mwheelright", "mwheelleft",
140
141 "axis1plus","axis1minus","axis2plus","axis2minus", // joystick axes as buttons
142 "axis3plus","axis3minus","axis4plus","axis4minus",
143 "axis5plus","axis5minus","axis6plus","axis6minus",
144 "axis7plus","axis7minus","axis8plus","axis8minus",
145
146 "lstickright","lstickleft","lstickdown","lstickup", // Gamepad axis-based buttons
147 "rstickright","rstickleft","rstickdown","rstickup",
148
149 "dpadup","dpaddown","dpadleft","dpadright", // Gamepad buttons
150 "pad_start","pad_back","lthumb","rthumb",
151 "lshoulder","rshoulder","ltrigger","rtrigger",
152 "pad_a", "pad_b", "pad_x", "pad_y"
153 };
154
155 FKeyBindings Bindings;
156 FKeyBindings DoubleBindings;
157 FKeyBindings AutomapBindings;
158
159 static unsigned int DClickTime[NUM_KEYS];
160 static BYTE DClicked[(NUM_KEYS+7)/8];
161
162 //=============================================================================
163 //
164 //
165 //
166 //=============================================================================
167
GetKeyFromName(const char * name)168 static int GetKeyFromName (const char *name)
169 {
170 int i;
171
172 // Names of the form #xxx are translated to key xxx automatically
173 if (name[0] == '#' && name[1] != 0)
174 {
175 return atoi (name + 1);
176 }
177
178 // Otherwise, we scan the KeyNames[] array for a matching name
179 for (i = 0; i < NUM_KEYS; i++)
180 {
181 if (KeyNames[i] && !stricmp (KeyNames[i], name))
182 return i;
183 }
184 return 0;
185 }
186
187 //=============================================================================
188 //
189 //
190 //
191 //=============================================================================
192
GetConfigKeyFromName(const char * key)193 static int GetConfigKeyFromName (const char *key)
194 {
195 int keynum = GetKeyFromName(key);
196 if (keynum == 0)
197 {
198 if (stricmp (key, "LeftBracket") == 0)
199 {
200 keynum = GetKeyFromName ("[");
201 }
202 else if (stricmp (key, "RightBracket") == 0)
203 {
204 keynum = GetKeyFromName ("]");
205 }
206 else if (stricmp (key, "Equals") == 0)
207 {
208 keynum = GetKeyFromName ("=");
209 }
210 else if (stricmp (key, "KP-Equals") == 0)
211 {
212 keynum = GetKeyFromName ("kp=");
213 }
214 }
215 return keynum;
216 }
217
218 //=============================================================================
219 //
220 //
221 //
222 //=============================================================================
223
KeyName(int key)224 static const char *KeyName (int key)
225 {
226 static char name[5];
227
228 if (KeyNames[key])
229 return KeyNames[key];
230
231 mysnprintf (name, countof(name), "#%d", key);
232 return name;
233 }
234
235 //=============================================================================
236 //
237 //
238 //
239 //=============================================================================
240
ConfigKeyName(int keynum)241 static const char *ConfigKeyName(int keynum)
242 {
243 const char *name = KeyName(keynum);
244 if (name[1] == 0) // Make sure given name is config-safe
245 {
246 if (name[0] == '[')
247 return "LeftBracket";
248 else if (name[0] == ']')
249 return "RightBracket";
250 else if (name[0] == '=')
251 return "Equals";
252 else if (strcmp (name, "kp=") == 0)
253 return "KP-Equals";
254 }
255 return name;
256 }
257
258 //=============================================================================
259 //
260 //
261 //
262 //=============================================================================
263
C_NameKeys(char * str,int first,int second)264 void C_NameKeys (char *str, int first, int second)
265 {
266 int c = 0;
267
268 *str = 0;
269 if (first)
270 {
271 c++;
272 strcpy (str, KeyName (first));
273 if (second)
274 strcat (str, " or ");
275 }
276
277 if (second)
278 {
279 c++;
280 strcat (str, KeyName (second));
281 }
282
283 if (!c)
284 *str = '\0';
285 }
286
287 //=============================================================================
288 //
289 //
290 //
291 //=============================================================================
292
DoBind(const char * key,const char * bind)293 void FKeyBindings::DoBind (const char *key, const char *bind)
294 {
295 int keynum = GetConfigKeyFromName (key);
296 if (keynum != 0)
297 {
298 Binds[keynum] = bind;
299 }
300 }
301
302 //=============================================================================
303 //
304 //
305 //
306 //=============================================================================
307
UnbindAll()308 void FKeyBindings::UnbindAll ()
309 {
310 for (int i = 0; i < NUM_KEYS; ++i)
311 {
312 Binds[i] = "";
313 }
314 }
315
316 //=============================================================================
317 //
318 //
319 //
320 //=============================================================================
321
UnbindKey(const char * key)322 void FKeyBindings::UnbindKey(const char *key)
323 {
324 int i;
325
326 if ( (i = GetKeyFromName (key)) )
327 {
328 Binds[i] = "";
329 }
330 else
331 {
332 Printf ("Unknown key \"%s\"\n", key);
333 return;
334 }
335 }
336
337 //=============================================================================
338 //
339 //
340 //
341 //=============================================================================
342
PerformBind(FCommandLine & argv,const char * msg)343 void FKeyBindings::PerformBind(FCommandLine &argv, const char *msg)
344 {
345 int i;
346
347 if (argv.argc() > 1)
348 {
349 i = GetKeyFromName (argv[1]);
350 if (!i)
351 {
352 Printf ("Unknown key \"%s\"\n", argv[1]);
353 return;
354 }
355 if (argv.argc() == 2)
356 {
357 Printf ("\"%s\" = \"%s\"\n", argv[1], Binds[i].GetChars());
358 }
359 else
360 {
361 Binds[i] = argv[2];
362 }
363 }
364 else
365 {
366 Printf ("%s:\n", msg);
367
368 for (i = 0; i < NUM_KEYS; i++)
369 {
370 if (!Binds[i].IsEmpty())
371 Printf ("%s \"%s\"\n", KeyName (i), Binds[i].GetChars());
372 }
373 }
374 }
375
376
377 //=============================================================================
378 //
379 // This function is first called for functions in custom key sections.
380 // In this case, matchcmd is non-NULL, and only keys bound to that command
381 // are stored. If a match is found, its binding is set to "\1".
382 // After all custom key sections are saved, it is called one more for the
383 // normal Bindings and DoubleBindings sections for this game. In this case
384 // matchcmd is NULL and all keys will be stored. The config section was not
385 // previously cleared, so all old bindings are still in place. If the binding
386 // for a key is empty, the corresponding key in the config is removed as well.
387 // If a binding is "\1", then the binding itself is cleared, but nothing
388 // happens to the entry in the config.
389 //
390 //=============================================================================
391
ArchiveBindings(FConfigFile * f,const char * matchcmd)392 void FKeyBindings::ArchiveBindings(FConfigFile *f, const char *matchcmd)
393 {
394 int i;
395
396 for (i = 0; i < NUM_KEYS; i++)
397 {
398 if (Binds[i].IsEmpty())
399 {
400 if (matchcmd == NULL)
401 {
402 f->ClearKey(ConfigKeyName(i));
403 }
404 }
405 else if (matchcmd == NULL || stricmp(Binds[i], matchcmd) == 0)
406 {
407 if (Binds[i][0] == '\1')
408 {
409 Binds[i] = "";
410 continue;
411 }
412 f->SetValueForKey(ConfigKeyName(i), Binds[i]);
413 if (matchcmd != NULL)
414 { // If saving a specific command, set a marker so that
415 // it does not get saved in the general binding list.
416 Binds[i] = "\1";
417 }
418 }
419 }
420 }
421
422 //=============================================================================
423 //
424 //
425 //
426 //=============================================================================
427
GetKeysForCommand(const char * cmd,int * first,int * second)428 int FKeyBindings::GetKeysForCommand (const char *cmd, int *first, int *second)
429 {
430 int c, i;
431
432 *first = *second = c = i = 0;
433
434 while (i < NUM_KEYS && c < 2)
435 {
436 if (stricmp (cmd, Binds[i]) == 0)
437 {
438 if (c++ == 0)
439 *first = i;
440 else
441 *second = i;
442 }
443 i++;
444 }
445 return c;
446 }
447
448 //=============================================================================
449 //
450 //
451 //
452 //=============================================================================
453
UnbindACommand(const char * str)454 void FKeyBindings::UnbindACommand (const char *str)
455 {
456 int i;
457
458 for (i = 0; i < NUM_KEYS; i++)
459 {
460 if (!stricmp (str, Binds[i]))
461 {
462 Binds[i] = "";
463 }
464 }
465 }
466
467 //=============================================================================
468 //
469 //
470 //
471 //=============================================================================
472
DefaultBind(const char * keyname,const char * cmd)473 void FKeyBindings::DefaultBind(const char *keyname, const char *cmd)
474 {
475 int key = GetKeyFromName (keyname);
476 if (key == 0)
477 {
478 Printf ("Unknown key \"%s\"\n", keyname);
479 return;
480 }
481 if (!Binds[key].IsEmpty())
482 { // This key is already bound.
483 return;
484 }
485 for (int i = 0; i < NUM_KEYS; ++i)
486 {
487 if (!Binds[i].IsEmpty() && stricmp (Binds[i], cmd) == 0)
488 { // This command is already bound to a key.
489 return;
490 }
491 }
492 // It is safe to do the bind, so do it.
493 Binds[key] = cmd;
494 }
495
496 //=============================================================================
497 //
498 //
499 //
500 //=============================================================================
501
C_UnbindAll()502 void C_UnbindAll ()
503 {
504 Bindings.UnbindAll();
505 DoubleBindings.UnbindAll();
506 AutomapBindings.UnbindAll();
507 }
508
CCMD(unbindall)509 CCMD (unbindall)
510 {
511 C_UnbindAll ();
512 }
513
514 //=============================================================================
515 //
516 //
517 //
518 //=============================================================================
519
CCMD(unbind)520 CCMD (unbind)
521 {
522 if (argv.argc() > 1)
523 {
524 Bindings.UnbindKey(argv[1]);
525 }
526 }
527
CCMD(undoublebind)528 CCMD (undoublebind)
529 {
530 if (argv.argc() > 1)
531 {
532 DoubleBindings.UnbindKey(argv[1]);
533 }
534 }
535
CCMD(unmapbind)536 CCMD (unmapbind)
537 {
538 if (argv.argc() > 1)
539 {
540 AutomapBindings.UnbindKey(argv[1]);
541 }
542 }
543
544 //=============================================================================
545 //
546 //
547 //
548 //=============================================================================
549
CCMD(bind)550 CCMD (bind)
551 {
552 Bindings.PerformBind(argv, "Current key bindings");
553 }
554
CCMD(doublebind)555 CCMD (doublebind)
556 {
557 DoubleBindings.PerformBind(argv, "Current key doublebindings");
558 }
559
CCMD(mapbind)560 CCMD (mapbind)
561 {
562 AutomapBindings.PerformBind(argv, "Current automap key bindings");
563 }
564
565 //==========================================================================
566 //
567 // CCMD defaultbind
568 //
569 // Binds a command to a key if that key is not already bound and if
570 // that command is not already bound to another key.
571 //
572 //==========================================================================
573
CCMD(defaultbind)574 CCMD (defaultbind)
575 {
576 if (argv.argc() < 3)
577 {
578 Printf ("Usage: defaultbind <key> <command>\n");
579 }
580 else
581 {
582 Bindings.DefaultBind(argv[1], argv[2]);
583 }
584 }
585
586 //=============================================================================
587 //
588 //
589 //
590 //=============================================================================
591
CCMD(rebind)592 CCMD (rebind)
593 {
594 FKeyBindings *bindings;
595
596 if (key == 0)
597 {
598 Printf ("Rebind cannot be used from the console\n");
599 return;
600 }
601
602 if (key & KEY_DBLCLICKED)
603 {
604 bindings = &DoubleBindings;
605 key &= KEY_DBLCLICKED-1;
606 }
607 else
608 {
609 bindings = &Bindings;
610 }
611
612 if (argv.argc() > 1)
613 {
614 bindings->SetBind(key, argv[1]);
615 }
616 }
617
618 //=============================================================================
619 //
620 //
621 //
622 //=============================================================================
623
C_BindDefaults()624 void C_BindDefaults ()
625 {
626 int lump, lastlump = 0;
627
628 while ((lump = Wads.FindLump("DEFBINDS", &lastlump)) != -1)
629 {
630 FScanner sc(lump);
631
632 while (sc.GetString())
633 {
634 FKeyBindings *dest = &Bindings;
635 int key;
636
637 // bind destination is optional and is the same as the console command
638 if (sc.Compare("bind"))
639 {
640 sc.MustGetString();
641 }
642 else if (sc.Compare("doublebind"))
643 {
644 dest = &DoubleBindings;
645 sc.MustGetString();
646 }
647 else if (sc.Compare("mapbind"))
648 {
649 dest = &AutomapBindings;
650 sc.MustGetString();
651 }
652 key = GetConfigKeyFromName(sc.String);
653 sc.MustGetString();
654 dest->SetBind(key, sc.String);
655 }
656 }
657 }
658
CCMD(binddefaults)659 CCMD(binddefaults)
660 {
661 C_BindDefaults ();
662 }
663
C_SetDefaultBindings()664 void C_SetDefaultBindings ()
665 {
666 C_UnbindAll ();
667 C_BindDefaults ();
668 }
669
670 //=============================================================================
671 //
672 //
673 //
674 //=============================================================================
675
C_DoKey(event_t * ev,FKeyBindings * binds,FKeyBindings * doublebinds)676 bool C_DoKey (event_t *ev, FKeyBindings *binds, FKeyBindings *doublebinds)
677 {
678 FString binding;
679 bool dclick;
680 int dclickspot;
681 BYTE dclickmask;
682 unsigned int nowtime;
683
684 if (ev->type != EV_KeyDown && ev->type != EV_KeyUp)
685 return false;
686
687 if ((unsigned int)ev->data1 >= NUM_KEYS)
688 return false;
689
690 dclickspot = ev->data1 >> 3;
691 dclickmask = 1 << (ev->data1 & 7);
692 dclick = false;
693
694 // This used level.time which didn't work outside a level.
695 nowtime = I_MSTime();
696 if (doublebinds != NULL && DClickTime[ev->data1] > nowtime && ev->type == EV_KeyDown)
697 {
698 // Key pressed for a double click
699 binding = doublebinds->GetBinding(ev->data1);
700 DClicked[dclickspot] |= dclickmask;
701 dclick = true;
702 }
703 else
704 {
705 if (ev->type == EV_KeyDown)
706 { // Key pressed for a normal press
707 binding = binds->GetBinding(ev->data1);
708 DClickTime[ev->data1] = nowtime + 571;
709 }
710 else if (doublebinds != NULL && DClicked[dclickspot] & dclickmask)
711 { // Key released from a double click
712 binding = doublebinds->GetBinding(ev->data1);
713 DClicked[dclickspot] &= ~dclickmask;
714 DClickTime[ev->data1] = 0;
715 dclick = true;
716 }
717 else
718 { // Key released from a normal press
719 binding = binds->GetBinding(ev->data1);
720 }
721 }
722
723
724 if (binding.IsEmpty())
725 {
726 binding = binds->GetBinding(ev->data1);
727 dclick = false;
728 }
729
730 if (!binding.IsEmpty() && (chatmodeon == 0 || ev->data1 < 256))
731 {
732 if (ev->type == EV_KeyUp && binding[0] != '+')
733 {
734 return false;
735 }
736
737 char *copy = binding.LockBuffer();
738
739 if (ev->type == EV_KeyUp)
740 {
741 copy[0] = '-';
742 }
743
744 AddCommandString (copy, dclick ? ev->data1 | KEY_DBLCLICKED : ev->data1);
745 return true;
746 }
747 return false;
748 }
749
750