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