1 /*
2  * keys.c -- key up events are sent even if in console mode
3  * $Id: keys.c 5181 2013-04-02 21:25:23Z sezero $
4  *
5  * Copyright (C) 1996-1997  Id Software, Inc.
6  * Copyright (C) 2006-2012  O.Sezer
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or (at
11  * your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16  *
17  * See the GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22  */
23 
24 #include "quakedef.h"
25 
26 char	key_lines[32][MAXCMDLINE];
27 int		key_linepos;
28 static qboolean	shift_down = false;
29 int		key_lastpress;
30 int		key_insert = 1;		// insert/overwrite mode toggle
31 
32 int		edit_line = 0;
33 static int	history_line = 0;
34 
35 static keydest_t	key_dest;
36 static qboolean		key_gamekey, prev_gamekey;
37 
38 int		key_count;		// incremented every key event
39 
40 char		*keybindings[256];
41 static qboolean	consolekeys[256];	// if true, can't be rebound while in console
42 static qboolean	menubound[256];		// if true, can't be rebound while in menu
43 static int	keyshift[256];		// key to map to if shift held down in console
44 static int	key_repeats[256];	// if > 1, it is autorepeating
45 static qboolean	keyreserved[256];	// hardcoded, can't be rebound by the user
46 static qboolean	keydown[256];
47 
48 typedef struct
49 {
50 	const char	*name;
51 	int		keynum;
52 } keyname_t;
53 
54 static keyname_t keynames[] =
55 {
56 	{"TAB", K_TAB},
57 	{"ENTER", K_ENTER},
58 	{"ESCAPE", K_ESCAPE},
59 	{"SPACE", K_SPACE},
60 	{"BACKSPACE", K_BACKSPACE},
61 	{"UPARROW", K_UPARROW},
62 	{"DOWNARROW", K_DOWNARROW},
63 	{"LEFTARROW", K_LEFTARROW},
64 	{"RIGHTARROW", K_RIGHTARROW},
65 
66 	{"ALT", K_ALT},
67 	{"CTRL", K_CTRL},
68 	{"SHIFT", K_SHIFT},
69 
70 	{"F1", K_F1},
71 	{"F2", K_F2},
72 	{"F3", K_F3},
73 	{"F4", K_F4},
74 	{"F5", K_F5},
75 	{"F6", K_F6},
76 	{"F7", K_F7},
77 	{"F8", K_F8},
78 	{"F9", K_F9},
79 	{"F10", K_F10},
80 	{"F11", K_F11},
81 	{"F12", K_F12},
82 
83 	{"INS", K_INS},
84 	{"DEL", K_DEL},
85 	{"PGDN", K_PGDN},
86 	{"PGUP", K_PGUP},
87 	{"HOME", K_HOME},
88 	{"END", K_END},
89 
90 //	{"KP_NUMLOCK", K_KP_NUMLOCK},
91 	{"KP_SLASH", K_KP_SLASH},
92 	{"KP_STAR", K_KP_STAR},
93 	{"KP_MINUS", K_KP_MINUS},
94 	{"KP_HOME", K_KP_HOME},
95 	{"KP_UPARROW", K_KP_UPARROW},
96 	{"KP_PGUP", K_KP_PGUP},
97 	{"KP_PLUS", K_KP_PLUS},
98 	{"KP_LEFTARROW", K_KP_LEFTARROW},
99 	{"KP_5", K_KP_5},
100 	{"KP_RIGHTARROW", K_KP_RIGHTARROW},
101 	{"KP_END", K_KP_END},
102 	{"KP_DOWNARROW", K_KP_DOWNARROW},
103 	{"KP_PGDN", K_KP_PGDN},
104 	{"KP_ENTER", K_KP_ENTER},
105 	{"KP_INS", K_KP_INS},
106 	{"KP_DEL", K_KP_DEL},
107 
108 	{"COMMAND", K_COMMAND},
109 
110 	{"MOUSE1", K_MOUSE1},
111 	{"MOUSE2", K_MOUSE2},
112 	{"MOUSE3", K_MOUSE3},
113 	{"MWHEELUP", K_MWHEELUP},
114 	{"MWHEELDOWN", K_MWHEELDOWN},
115 	{"MOUSE4", K_MOUSE4},
116 	{"MOUSE5", K_MOUSE5},
117 
118 	{"JOY1", K_JOY1},
119 	{"JOY2", K_JOY2},
120 	{"JOY3", K_JOY3},
121 	{"JOY4", K_JOY4},
122 
123 	{"AUX1", K_AUX1},
124 	{"AUX2", K_AUX2},
125 	{"AUX3", K_AUX3},
126 	{"AUX4", K_AUX4},
127 	{"AUX5", K_AUX5},
128 	{"AUX6", K_AUX6},
129 	{"AUX7", K_AUX7},
130 	{"AUX8", K_AUX8},
131 	{"AUX9", K_AUX9},
132 	{"AUX10", K_AUX10},
133 	{"AUX11", K_AUX11},
134 	{"AUX12", K_AUX12},
135 	{"AUX13", K_AUX13},
136 	{"AUX14", K_AUX14},
137 	{"AUX15", K_AUX15},
138 	{"AUX16", K_AUX16},
139 	{"AUX17", K_AUX17},
140 	{"AUX18", K_AUX18},
141 	{"AUX19", K_AUX19},
142 	{"AUX20", K_AUX20},
143 	{"AUX21", K_AUX21},
144 	{"AUX22", K_AUX22},
145 	{"AUX23", K_AUX23},
146 	{"AUX24", K_AUX24},
147 	{"AUX25", K_AUX25},
148 	{"AUX26", K_AUX26},
149 	{"AUX27", K_AUX27},
150 	{"AUX28", K_AUX28},
151 	{"AUX29", K_AUX29},
152 	{"AUX30", K_AUX30},
153 	{"AUX31", K_AUX31},
154 	{"AUX32", K_AUX32},
155 
156 	{"PAUSE", K_PAUSE},
157 
158 	{"SEMICOLON", ';'},	// because a raw semicolon seperates commands
159 
160 	{NULL,		0}
161 };
162 
163 /*
164 ==============================================================================
165 
166 			LINE TYPING INTO THE CONSOLE
167 
168 ==============================================================================
169 */
170 
CheckForCommand(void)171 static qboolean CheckForCommand (void)
172 {
173 	char	command[128], *s;
174 	int	i;
175 
176 	s = key_lines[edit_line] + 1;
177 
178 	command[0] = '\0';
179 	for (i = 0; i < 127; i++)
180 	{
181 		if (s[i] <= ' ')
182 			break;
183 		command[i] = s[i];
184 	}
185 
186 	command[i] = 0;
187 
188 	return Cmd_CheckCommand (command);
189 }
190 
CompleteCommand(void)191 static void CompleteCommand (void)
192 {
193 	char	*matches[MAX_MATCHES];
194 	char		backup[MAXCMDLINE];
195 	char		c, *prefix, *workline;
196 	qboolean	editing;
197 	int		count, i;
198 	size_t		len1, len2;
199 
200 	if (key_linepos < 2)
201 		return;
202 
203 	workline = key_lines[edit_line];
204 	c = workline[key_linepos];
205 	editing = (c != 0);
206 
207 	if (editing)
208 	{
209 		// make a copy of the text starting from the
210 		// cursor position (see below)
211 		q_strlcpy(backup, workline + key_linepos, sizeof(backup));
212 	}
213 
214 	// complete the text only up to the cursor position:
215 	// bash style. cut off the rest for now.
216 	workline[key_linepos] = 0;
217 	prefix = workline + 1;
218 
219 	// skip the leading whitespace and command markers
220 	while (*prefix)
221 	{
222 		if (*prefix != '\\' && *prefix != '/' && *prefix > ' ')
223 			break;
224 		++prefix;
225 	}
226 
227 	// if the remainder line has no length or has
228 	// spaces in it, don't bother
229 	if (!*prefix || strstr(prefix," "))
230 	{
231 		workline[key_linepos] = c;
232 		return;
233 	}
234 
235 	// store the length of the relevant partial
236 	len1 = len2 = strlen(prefix);
237 
238 	// start checking for matches, finally...
239 	count = 0;
240 	count += ListCommands(prefix, (const char**)matches, count);
241 	count += ListCvars   (prefix, (const char**)matches, count);
242 	count += ListAlias   (prefix, (const char**)matches, count);
243 
244 	if (count)
245 	{
246 		// do not do a full auto-complete
247 		// unless there is only one match
248 		if (count == 1)
249 		{
250 			workline[1] = '/';
251 			q_strlcpy (workline + 2, matches[0], MAXCMDLINE-2);
252 			key_linepos = 2 + strlen(matches[0]);
253 		//	q_strlcpy (workline + 1, matches[0], MAXCMDLINE-1);
254 		//	key_linepos = 1 + strlen(matches[0]);
255 			workline[key_linepos] = ' ';
256 			key_linepos++;
257 		}
258 		else
259 		{
260 			// more than one match, sort and list all of them
261 			qsort (matches, count, sizeof(char *), COM_StrCompare);
262 			Con_Printf("\n");
263 #if 0
264 			// plain listing
265 			for (i = 0; i < count && i < MAX_MATCHES; i++)
266 				Con_Printf ("%s\n", matches[i]);
267 			Con_Printf("\n%d matches found\n\n", count);
268 #else
269 			// S.A.: columnize the listing.
270 			Con_Printf("%d possible completions:\n\n", count);
271 			Con_ShowList (count, (const char**) matches);
272 			Con_Printf("\n");
273 #endif
274 
275 			// cycle throgh all matches and see
276 			// if there is a partial completion
277 		_search:
278 			for (i = 1; i < count && i < MAX_MATCHES; i++)
279 			{
280 				if (matches[0][len2] != matches[i][len2])
281 					goto _check;
282 			}
283 			++len2;
284 			goto _search;
285 		_check:
286 			if (len2 > len1)	// found a partial match
287 			{
288 				workline[1] = '/';
289 				strncpy (workline + 2, matches[0], len2);
290 				key_linepos = len2 + 2;
291 			//	strncpy (workline + 1, matches[0], len2);
292 			//	key_linepos = len2 + 1;
293 			}
294 		}
295 
296 		workline[key_linepos] = 0;
297 	}
298 
299 	// put back the remainder of the original text
300 	// which was lost after the trimming
301 	if (editing)
302 		q_strlcpy (workline + key_linepos, backup, MAXCMDLINE-key_linepos);
303 }
304 
PasteToConsole(void)305 static void PasteToConsole (void)
306 {
307 	char *cbd, *p, *workline;
308 	int mvlen, inslen;
309 
310 	if (key_linepos == MAXCMDLINE - 1)
311 		return;
312 
313 	if ((cbd = Sys_GetClipboardData()) == NULL)
314 		return;
315 
316 	p = cbd;
317 	while (*p)
318 	{
319 		if (*p == '\n' || *p == '\r' || *p == '\b')
320 		{
321 			*p = 0;
322 			break;
323 		}
324 		p++;
325 	}
326 
327 	inslen = (int) (p - cbd);
328 	if (inslen + key_linepos > MAXCMDLINE - 1)
329 		inslen = MAXCMDLINE - 1 - key_linepos;
330 	if (inslen <= 0) goto done;
331 
332 	workline = key_lines[edit_line];
333 	workline += key_linepos;
334 	mvlen = (int) strlen(workline);
335 	if (mvlen + inslen + key_linepos > MAXCMDLINE - 1)
336 	{
337 		mvlen = MAXCMDLINE - 1 - key_linepos - inslen;
338 		if (mvlen < 0) mvlen = 0;
339 	}
340 
341 	// insert the string
342 	if (mvlen != 0)
343 		memmove (workline + inslen, workline, mvlen);
344 	memcpy (workline, cbd, inslen);
345 	key_linepos += inslen;
346 	workline[mvlen + inslen] = '\0';
347   done:
348 	Z_Free(cbd);
349 }
350 
351 /*
352 ====================
353 Key_Console
354 
355 Interactive line editing and console scrollback
356 ====================
357 */
Key_Console(int key)358 static void Key_Console (int key)
359 {
360 	int	history_line_last;
361 	size_t		len;
362 	char	*workline = key_lines[edit_line];
363 
364 	switch (key)
365 	{
366 	case K_ENTER:
367 	// backslash text are commands, else chat
368 		if (workline[1] == '\\' || workline[1] == '/')
369 			Cbuf_AddText (workline + 2);	// skip the >
370 		else if (CheckForCommand())
371 			Cbuf_AddText (workline + 1);	// valid command
372 		else
373 		{	// convert to a chat message
374 			if (cls.state >= ca_connected)
375 				Cbuf_AddText ("say ");
376 			Cbuf_AddText (workline + 1);	// skip the >
377 		}
378 
379 		Cbuf_AddText ("\n");
380 		Con_Printf ("%s\n", workline);
381 		edit_line = (edit_line + 1) & 31;
382 		history_line = edit_line;
383 		key_lines[edit_line][0] = ']';
384 		key_lines[edit_line][1] = 0;
385 		key_linepos = 1;
386 		if (cls.state == ca_disconnected)
387 			SCR_UpdateScreen ();	// force an update, because the command
388 								// may take some time
389 		return;
390 
391 	case K_TAB:
392 		CompleteCommand ();
393 		return;
394 
395 	case K_LEFTARROW:
396 		if (key_linepos < 2)
397 			return;
398 		if (keydown[K_CTRL])
399 		{
400 		/* ctrl - left, word processor style: first,
401 		 * move to the ending of previous word, then
402 		 * move to its beginning
403 		 */
404 			char *p = workline + key_linepos - 1;
405 			while (p != workline && *p == ' ')
406 				--p;
407 			while (p != workline)
408 			{
409 				if (*--p == ' ')
410 					break;
411 			}
412 			key_linepos = (int)(p - workline) + 1;
413 		}
414 		else	/* simple cursor-to-left, only. */
415 		{
416 			--key_linepos;
417 		}
418 		return;
419 
420 	case K_RIGHTARROW:
421 		if (!workline[key_linepos])
422 			return;
423 		if (keydown[K_CTRL])
424 		{
425 		/* ctrl - right, word processor style: if
426 		 * we are on a text move to its end, then
427 		 * move to the beginning of the next word
428 		 */
429 			char *p = workline + key_linepos;
430 			while (*p && *p != ' ')
431 				++p;
432 			while (*p && *p == ' ')
433 				++p;
434 			key_linepos = (int)(p - workline);
435 		}
436 		else	/* simple cursor-to-right only. */
437 		{
438 			++key_linepos;
439 		}
440 		return;
441 
442 	case K_BACKSPACE:
443 		if (key_linepos > 1)
444 		{
445 			workline += key_linepos - 1;
446 			if (workline[1])
447 			{
448 				len = strlen(workline);
449 				memmove (workline, workline + 1, len);
450 			}
451 			else	*workline = 0;
452 
453 			key_linepos--;
454 		}
455 		return;
456 
457 	case K_DEL:
458 		workline += key_linepos;
459 		if (*workline)
460 		{
461 			if (workline[1])
462 			{
463 				len = strlen(workline);
464 				memmove (workline, workline + 1, len);
465 			}
466 			else	*workline = 0;
467 		}
468 		return;
469 
470 	case K_UPARROW:
471 		history_line_last = history_line;
472 		do
473 		{
474 			history_line = (history_line - 1) & 31;
475 		} while (history_line != edit_line && !key_lines[history_line][1]);
476 
477 		if (history_line == edit_line)
478 			history_line = history_line_last;
479 
480 		strcpy(workline, key_lines[history_line]);
481 		key_linepos = strlen(workline);
482 		return;
483 
484 	case K_DOWNARROW:
485 		if (history_line == edit_line)
486 			return;
487 		do
488 		{
489 			history_line = (history_line + 1) & 31;
490 		} while (history_line != edit_line && !key_lines[history_line][1]);
491 
492 		if (history_line == edit_line)
493 		{
494 			workline[0] = ']';
495 			workline[1] = 0;
496 			key_linepos = 1;
497 		}
498 		else
499 		{
500 			strcpy(workline, key_lines[history_line]);
501 			key_linepos = strlen(workline);
502 		}
503 		return;
504 
505 	case K_PGUP:
506 	case K_MWHEELUP:
507 		con->display -= 2;
508 		return;
509 
510 	case K_PGDN:
511 	case K_MWHEELDOWN:
512 		con->display += 2;
513 		if (con->display > con->current)
514 			con->display = con->current;
515 		return;
516 
517 	case K_HOME:
518 		if (keydown[K_CTRL])
519 			con->display = con->current - con_totallines + 10;
520 		else	key_linepos = 1;
521 		return;
522 
523 	case K_END:
524 		if (keydown[K_CTRL])
525 			con->display = con->current;
526 		else	key_linepos = strlen(workline);
527 		return;
528 
529 	case K_INS:
530 		if (keydown[K_SHIFT])		/* Shift+Ins paste */
531 			PasteToConsole();
532 		else	key_insert ^= 1;
533 		return;
534 
535 	case 'v':
536 	case 'V':
537 #if defined(PLATFORM_OSX) || defined(PLATFORM_MAC)
538 		if (keydown[K_COMMAND]) {	/* Cmd+V paste (Mac-only) */
539 			PasteToConsole();
540 			return;
541 		}
542 #endif
543 		if (keydown[K_CTRL]) {		/* Ctrl+v paste */
544 			PasteToConsole();
545 			return;
546 		}
547 		break;
548 
549 	case 'c':
550 	case 'C':
551 		if (keydown[K_CTRL]) {		/* Ctrl+C: abort the line -- S.A */
552 			Con_Printf ("%s\n", workline);
553 			workline[0] = ']';
554 			workline[1] = 0;
555 			key_linepos = 1;
556 			history_line= edit_line;
557 			return;
558 		}
559 		break;
560 	}
561 
562 	if (key < 32 || key > 127)
563 		return;	// non printable
564 
565 	if (key_linepos < MAXCMDLINE - 1)
566 	{
567 		qboolean endpos = !workline[key_linepos];
568 		// if inserting, move the text to the right
569 		if (key_insert && !endpos)
570 		{
571 			workline[MAXCMDLINE - 2] = 0;
572 			workline += key_linepos;
573 			len = strlen(workline) + 1;
574 			memmove (workline + 1, workline, len);
575 			*workline = key;
576 		}
577 		else
578 		{
579 			workline += key_linepos;
580 			*workline = key;
581 			// null terminate if at the end
582 			if (endpos)
583 				workline[1] = 0;
584 		}
585 		key_linepos++;
586 	}
587 }
588 
589 //============================================================================
590 
591 qboolean	chat_team = false;
592 static char	chat_buffer[MAXCMDLINE];
593 static int	chat_bufferlen = 0;
594 
Key_GetChatBuffer(void)595 const char *Key_GetChatBuffer (void)
596 {
597 	return chat_buffer;
598 }
599 
Key_GetChatMsgLen(void)600 int Key_GetChatMsgLen (void)
601 {
602 	return chat_bufferlen;
603 }
604 
Key_EndChat(void)605 void Key_EndChat (void)
606 {
607 	Key_SetDest (key_game);
608 	chat_bufferlen = 0;
609 	chat_buffer[0] = 0;
610 }
611 
Key_Message(int key)612 static void Key_Message (int key)
613 {
614 	if (key == K_ENTER)
615 	{
616 		if (chat_team)
617 			Cbuf_AddText ("say_team \"");
618 		else
619 			Cbuf_AddText ("say \"");
620 		Cbuf_AddText(chat_buffer);
621 		Cbuf_AddText("\"\n");
622 
623 		Key_EndChat ();
624 		return;
625 	}
626 
627 	if (key == K_ESCAPE)
628 	{
629 		Key_EndChat ();
630 		return;
631 	}
632 
633 	if (key == K_BACKSPACE)
634 	{
635 		if (chat_bufferlen)
636 			chat_buffer[--chat_bufferlen] = 0;
637 		return;
638 	}
639 
640 	if (key < 32 || key > 127)
641 		return; // non printable
642 
643 	if (chat_bufferlen == sizeof(chat_buffer) - 1)
644 		return; // all full
645 
646 	chat_buffer[chat_bufferlen++] = key;
647 	chat_buffer[chat_bufferlen] = 0;
648 }
649 
650 //============================================================================
651 
652 
653 /*
654 ===================
655 Key_StringToKeynum
656 
657 Returns a key number to be used to index keybindings[] by looking at
658 the given string.  Single ascii characters return themselves, while
659 the K_* names are matched up.
660 ===================
661 */
Key_StringToKeynum(const char * str)662 static int Key_StringToKeynum (const char *str)
663 {
664 	keyname_t	*kn;
665 
666 	if (!str || !str[0])
667 		return -1;
668 	if (!str[1])
669 		return str[0];
670 
671 	for (kn = keynames; kn->name; kn++)
672 	{
673 		if (!q_strcasecmp(str,kn->name))
674 			return kn->keynum;
675 	}
676 	return -1;
677 }
678 
679 /*
680 ===================
681 Key_KeynumToString
682 
683 Returns a string (either a single ascii char, or a K_* name) for the
684 given keynum.
685 FIXME: handle quote special (general escape sequence?)
686 ===================
687 */
Key_KeynumToString(int keynum)688 const char *Key_KeynumToString (int keynum)
689 {
690 	static	char	tinystr[2];
691 	keyname_t	*kn;
692 
693 	if (keynum == -1)
694 		return "<KEY NOT FOUND>";
695 	if (keynum > 32 && keynum < 127)
696 	{	// printable ascii
697 		tinystr[0] = keynum;
698 		tinystr[1] = 0;
699 		return tinystr;
700 	}
701 
702 	for (kn = keynames; kn->name; kn++)
703 	{
704 		if (keynum == kn->keynum)
705 			return kn->name;
706 	}
707 
708 	return "<UNKNOWN KEYNUM>";
709 }
710 
711 
712 /*
713 ===================
714 Key_SetBinding
715 ===================
716 */
Key_SetBinding(int keynum,const char * binding)717 void Key_SetBinding (int keynum, const char *binding)
718 {
719 	if (keynum == -1)
720 		return;
721 	if (keyreserved[keynum])
722 		return;
723 
724 // free old bindings
725 	if (keybindings[keynum])
726 	{
727 		Z_Free (keybindings[keynum]);
728 		keybindings[keynum] = NULL;
729 	}
730 
731 // allocate memory for new binding
732 	if (binding)
733 		keybindings[keynum] = Z_Strdup(binding);
734 }
735 
736 /*
737 ===================
738 Key_Unbind_f
739 ===================
740 */
Key_Unbind_f(void)741 static void Key_Unbind_f (void)
742 {
743 	int	b;
744 
745 	if (Cmd_Argc() != 2)
746 	{
747 		Con_Printf ("unbind <key> : remove commands from a key\n");
748 		return;
749 	}
750 
751 	b = Key_StringToKeynum (Cmd_Argv(1));
752 	if (b == -1)
753 	{
754 		Con_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1));
755 		return;
756 	}
757 
758 	Key_SetBinding (b, NULL);
759 }
760 
Key_Unbindall_f(void)761 static void Key_Unbindall_f (void)
762 {
763 	int	i;
764 
765 	for (i = 0; i < 256; i++)
766 		Key_SetBinding(i, NULL);
767 }
768 
769 
770 /*
771 ===================
772 Key_Bind_f
773 ===================
774 */
Key_Bind_f(void)775 static void Key_Bind_f (void)
776 {
777 	int	i, c, b;
778 	char	cmd[1024];
779 
780 	c = Cmd_Argc();
781 
782 	if (c != 2 && c != 3)
783 	{
784 		Con_Printf ("bind <key> [command] : attach a command to a key\n");
785 		return;
786 	}
787 	b = Key_StringToKeynum (Cmd_Argv(1));
788 	if (b == -1)
789 	{
790 		Con_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1));
791 		return;
792 	}
793 
794 	if (c == 2)
795 	{
796 		if (keybindings[b])
797 			Con_Printf ("\"%s\" = \"%s\"\n", Cmd_Argv(1), keybindings[b] );
798 		else
799 			Con_Printf ("\"%s\" is not bound\n", Cmd_Argv(1) );
800 		return;
801 	}
802 
803 // copy the rest of the command line
804 	cmd[0] = 0;
805 	for (i = 2; i < c; i++)
806 	{
807 		q_strlcat (cmd, Cmd_Argv(i), sizeof(cmd));
808 		if (i != (c-1))
809 			q_strlcat (cmd, " ", sizeof(cmd));
810 	}
811 
812 	Key_SetBinding (b, cmd);
813 }
814 
815 /*
816 ============
817 Key_WriteBindings
818 
819 Writes lines containing "bind key value"
820 ============
821 */
Key_WriteBindings(FILE * f)822 void Key_WriteBindings (FILE *f)
823 {
824 	int	i;
825 
826 	// unbindall before loading stored bindings:
827 	if (cfg_unbindall.integer)
828 		fprintf (f, "unbindall\n");
829 	for (i = 0; i < 256; i++)
830 	{
831 		if (keybindings[i] && *keybindings[i])
832 			fprintf (f, "bind \"%s\" \"%s\"\n", Key_KeynumToString(i), keybindings[i]);
833 	}
834 }
835 
836 
837 /*
838 ===================
839 Key_Init
840 ===================
841 */
Key_Init(void)842 void Key_Init (void)
843 {
844 	int	i;
845 
846 	for (i = 0; i < 32; i++)
847 	{
848 		key_lines[i][0] = ']';
849 		key_lines[i][1] = 0;
850 	}
851 	key_linepos = 1;
852 
853 	memset (consolekeys, 0, sizeof(consolekeys));
854 	memset (menubound, 0, sizeof(menubound));
855 	memset (keyreserved, 0, sizeof(keyreserved));
856 
857 // init ascii characters in console mode
858 	for (i = 32; i < 128; i++)
859 		consolekeys[i] = true;
860 	consolekeys[K_ENTER] = true;
861 	consolekeys[K_TAB] = true;
862 	consolekeys[K_LEFTARROW] = true;
863 	consolekeys[K_RIGHTARROW] = true;
864 	consolekeys[K_UPARROW] = true;
865 	consolekeys[K_DOWNARROW] = true;
866 	consolekeys[K_BACKSPACE] = true;
867 	consolekeys[K_DEL] = true;
868 	consolekeys[K_INS] = true;
869 	consolekeys[K_HOME] = true;
870 	consolekeys[K_END] = true;
871 	consolekeys[K_PGUP] = true;
872 	consolekeys[K_PGDN] = true;
873 	consolekeys[K_SHIFT] = true;
874 	consolekeys[K_MWHEELUP] = true;
875 	consolekeys[K_MWHEELDOWN] = true;
876 	consolekeys['`'] = false;
877 	consolekeys['~'] = false;
878 
879 	for (i = 0; i < 256; i++)
880 		keyshift[i] = i;
881 	for (i = 'a'; i <= 'z'; i++)
882 		keyshift[i] = i - 'a' + 'A';
883 	keyshift['1'] = '!';
884 	keyshift['2'] = '@';
885 	keyshift['3'] = '#';
886 	keyshift['4'] = '$';
887 	keyshift['5'] = '%';
888 	keyshift['6'] = '^';
889 	keyshift['7'] = '&';
890 	keyshift['8'] = '*';
891 	keyshift['9'] = '(';
892 	keyshift['0'] = ')';
893 	keyshift['-'] = '_';
894 	keyshift['='] = '+';
895 	keyshift[','] = '<';
896 	keyshift['.'] = '>';
897 	keyshift['/'] = '?';
898 	keyshift[';'] = ':';
899 	keyshift['\''] = '"';
900 	keyshift['['] = '{';
901 	keyshift[']'] = '}';
902 	keyshift['`'] = '~';
903 	keyshift['\\'] = '|';
904 
905 	menubound[K_ESCAPE] = true;
906 	for (i = 0; i < 12; i++)
907 		menubound[K_F1+i] = true;
908 
909 	memset (key_repeats, 0, sizeof(key_repeats));
910 
911 // bind our reserved keys
912 	Key_SetBinding ('`', "toggleconsole");
913 	Key_SetBinding ('~', "toggleconsole");
914 	Key_SetBinding (K_PAUSE, "pause");
915 	keyreserved['`'] = true;
916 	keyreserved['~'] = true;
917 	keyreserved[K_KP_NUMLOCK] = true;
918 	keyreserved[K_PAUSE] = true;
919 
920 // register our functions
921 	Cmd_AddCommand ("bind",Key_Bind_f);
922 	Cmd_AddCommand ("unbind",Key_Unbind_f);
923 	Cmd_AddCommand ("unbindall",Key_Unbindall_f);
924 }
925 
926 /*
927 ===================
928 Key_Event
929 
930 Called by the system between frames for both key up and key down events
931 Should NOT be called during an interrupt!
932 ===================
933 */
Key_Event(int key,qboolean down)934 void Key_Event (int key, qboolean down)
935 {
936 	char	*kb;
937 	char	cmd[1024];
938 
939 	keydown[key] = down;
940 
941 	if (!down)
942 		key_repeats[key] = 0;
943 
944 	key_lastpress = key;
945 	key_count++;
946 	if (key_count <= 0)
947 		return;		// just catching keys for Con_NotifyBox
948 
949 // update auto-repeat status
950 	if (down)
951 	{
952 		/* Pause key doesn't generate a scancode when released,
953 		 * never increment its auto-repeat status.
954 		 */
955 		if (key != K_PAUSE && key != K_KP_NUMLOCK)
956 			key_repeats[key]++;
957 #if 0
958 		if (key != K_BACKSPACE && key != K_PGUP && key != K_PGDN &&
959 					key_repeats[key] > 1)
960 			return;	// ignore most autorepeats
961 #endif
962 		if (key_repeats[key] > 1)
963 		{
964 			if (key_dest == key_game && !con_forcedup)
965 				return;	// ignore autorepeats in game mode
966 		}
967 		else if (key >= 200 && !keybindings[key])
968 			Con_Printf ("%s is unbound, hit F4 to set.\n", Key_KeynumToString(key));
969 	}
970 
971 	if (key == K_SHIFT)
972 		shift_down = down;
973 
974 // handle escape specialy, so the user can never unbind it
975 	if (key == K_ESCAPE)
976 	{
977 		if (!down)
978 			return;
979 		switch (key_dest)
980 		{
981 		case key_message:
982 			Key_Message (key);
983 			break;
984 		case key_menu:
985 			M_Keydown (key);
986 			break;
987 		case key_menubind:
988 			M_Keybind (key);
989 			break;
990 		case key_game:
991 		case key_console:
992 			M_ToggleMenu_f ();
993 			break;
994 		default:
995 			Sys_Error ("Bad key_dest");
996 		}
997 		return;
998 	}
999 
1000 // key up events only generate commands if the game key binding is
1001 // a button command (leading + sign).  These will occur even in console mode,
1002 // to keep the character from continuing an action started before a console
1003 // switch.  Button commands include the kenum as a parameter, so multiple
1004 // downs can be matched with ups
1005 	if (!down)
1006 	{
1007 		kb = keybindings[key];
1008 		if (kb && kb[0] == '+')
1009 		{
1010 			sprintf (cmd, "-%s %i\n", kb+1, key);
1011 			Cbuf_AddText (cmd);
1012 		}
1013 		if (keyshift[key] != key)
1014 		{
1015 			kb = keybindings[keyshift[key]];
1016 			if (kb && kb[0] == '+')
1017 			{
1018 				sprintf (cmd, "-%s %i\n", kb+1, key);
1019 				Cbuf_AddText (cmd);
1020 			}
1021 		}
1022 		return;
1023 	}
1024 
1025 // during demo playback, most keys bring up the main menu
1026 	if (cls.demoplayback && down && consolekeys[key] && key_dest == key_game)
1027 	{
1028 		M_ToggleMenu_f ();
1029 		return;
1030 	}
1031 
1032 // if not a consolekey, send to the interpreter no matter what mode is
1033 	if (((key_dest & key_menu) && menubound[key]) ||
1034 	    (key_dest == key_console && !consolekeys[key]) ||
1035 	    (key_dest == key_game && (!con_forcedup || !consolekeys[key])))
1036 	{
1037 		kb = keybindings[key];
1038 		if (kb)
1039 		{
1040 			if (kb[0] == '+')
1041 			{	// button commands add keynum as a parm
1042 				sprintf (cmd, "%s %i\n", kb, key);
1043 				Cbuf_AddText (cmd);
1044 			}
1045 			else
1046 			{
1047 				Cbuf_AddText (kb);
1048 				Cbuf_AddText ("\n");
1049 			}
1050 		}
1051 		return;
1052 	}
1053 
1054 	if (!down)
1055 		return;		// other systems only care about key down events
1056 
1057 	if (shift_down)
1058 		key = keyshift[key];
1059 
1060 	switch (key_dest)
1061 	{
1062 	case key_message:
1063 		Key_Message (key);
1064 		break;
1065 	case key_menu:
1066 		M_Keydown (key);
1067 		break;
1068 	case key_menubind:
1069 		M_Keybind (key);
1070 		break;
1071 	case key_game:
1072 	case key_console:
1073 		Key_Console (key);
1074 		break;
1075 	default:
1076 		Sys_Error ("Bad key_dest");
1077 	}
1078 }
1079 
1080 /*
1081 ===================
1082 Key_ClearStates
1083 ===================
1084 */
Key_ClearStates(void)1085 void Key_ClearStates (void)
1086 {
1087 	int	i;
1088 
1089 	for (i = 0; i < 256; i++)
1090 	{
1091 		if (keydown[i])
1092 			Key_Event (i, false);
1093 	}
1094 }
1095 
Key_IsGameKey(void)1096 qboolean Key_IsGameKey (void)
1097 {
1098 	return ((key_dest == key_game && !con_forcedup) || (key_dest == key_menubind));
1099 }
1100 
Key_GetDest(void)1101 keydest_t Key_GetDest (void)
1102 {
1103 	return key_dest;
1104 }
1105 
Key_SetDest(keydest_t dest)1106 void Key_SetDest (keydest_t dest)
1107 {
1108 	key_dest = dest;
1109 	if ((key_gamekey = Key_IsGameKey()) != prev_gamekey)
1110 	{
1111 		prev_gamekey = key_gamekey;
1112 		Key_ClearStates();
1113 	}
1114 }
1115 
1116