Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

keys.c (17931B)


      1 /*
      2 Copyright (C) 1997-2001 Id Software, Inc.
      3 
      4 This program is free software; you can redistribute it and/or
      5 modify it under the terms of the GNU General Public License
      6 as published by the Free Software Foundation; either version 2
      7 of the License, or (at your option) any later version.
      8 
      9 This program is distributed in the hope that it will be useful,
     10 but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
     12 
     13 See the GNU General Public License for more details.
     14 
     15 You should have received a copy of the GNU General Public License
     16 along with this program; if not, write to the Free Software
     17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
     18 
     19 */
     20 #include "client.h"
     21 
     22 /*
     23 
     24 key up events are sent even if in console mode
     25 
     26 */
     27 
     28 
     29 #define		MAXCMDLINE	256
     30 char	key_lines[32][MAXCMDLINE];
     31 int		key_linepos;
     32 int		shift_down=false;
     33 int	anykeydown;
     34 
     35 int		edit_line=0;
     36 int		history_line=0;
     37 
     38 int		key_waiting;
     39 char	*keybindings[256];
     40 qboolean	consolekeys[256];	// if true, can't be rebound while in console
     41 qboolean	menubound[256];	// if true, can't be rebound while in menu
     42 int		keyshift[256];		// key to map to if shift held down in console
     43 int		key_repeats[256];	// if > 1, it is autorepeating
     44 qboolean	keydown[256];
     45 
     46 typedef struct
     47 {
     48 	char	*name;
     49 	int		keynum;
     50 } keyname_t;
     51 
     52 keyname_t keynames[] =
     53 {
     54 	{"TAB", K_TAB},
     55 	{"ENTER", K_ENTER},
     56 	{"ESCAPE", K_ESCAPE},
     57 	{"SPACE", K_SPACE},
     58 	{"BACKSPACE", K_BACKSPACE},
     59 	{"UPARROW", K_UPARROW},
     60 	{"DOWNARROW", K_DOWNARROW},
     61 	{"LEFTARROW", K_LEFTARROW},
     62 	{"RIGHTARROW", K_RIGHTARROW},
     63 
     64 	{"ALT", K_ALT},
     65 	{"CTRL", K_CTRL},
     66 	{"SHIFT", K_SHIFT},
     67 	
     68 	{"F1", K_F1},
     69 	{"F2", K_F2},
     70 	{"F3", K_F3},
     71 	{"F4", K_F4},
     72 	{"F5", K_F5},
     73 	{"F6", K_F6},
     74 	{"F7", K_F7},
     75 	{"F8", K_F8},
     76 	{"F9", K_F9},
     77 	{"F10", K_F10},
     78 	{"F11", K_F11},
     79 	{"F12", K_F12},
     80 
     81 	{"INS", K_INS},
     82 	{"DEL", K_DEL},
     83 	{"PGDN", K_PGDN},
     84 	{"PGUP", K_PGUP},
     85 	{"HOME", K_HOME},
     86 	{"END", K_END},
     87 
     88 	{"MOUSE1", K_MOUSE1},
     89 	{"MOUSE2", K_MOUSE2},
     90 	{"MOUSE3", K_MOUSE3},
     91 
     92 	{"JOY1", K_JOY1},
     93 	{"JOY2", K_JOY2},
     94 	{"JOY3", K_JOY3},
     95 	{"JOY4", K_JOY4},
     96 
     97 	{"AUX1", K_AUX1},
     98 	{"AUX2", K_AUX2},
     99 	{"AUX3", K_AUX3},
    100 	{"AUX4", K_AUX4},
    101 	{"AUX5", K_AUX5},
    102 	{"AUX6", K_AUX6},
    103 	{"AUX7", K_AUX7},
    104 	{"AUX8", K_AUX8},
    105 	{"AUX9", K_AUX9},
    106 	{"AUX10", K_AUX10},
    107 	{"AUX11", K_AUX11},
    108 	{"AUX12", K_AUX12},
    109 	{"AUX13", K_AUX13},
    110 	{"AUX14", K_AUX14},
    111 	{"AUX15", K_AUX15},
    112 	{"AUX16", K_AUX16},
    113 	{"AUX17", K_AUX17},
    114 	{"AUX18", K_AUX18},
    115 	{"AUX19", K_AUX19},
    116 	{"AUX20", K_AUX20},
    117 	{"AUX21", K_AUX21},
    118 	{"AUX22", K_AUX22},
    119 	{"AUX23", K_AUX23},
    120 	{"AUX24", K_AUX24},
    121 	{"AUX25", K_AUX25},
    122 	{"AUX26", K_AUX26},
    123 	{"AUX27", K_AUX27},
    124 	{"AUX28", K_AUX28},
    125 	{"AUX29", K_AUX29},
    126 	{"AUX30", K_AUX30},
    127 	{"AUX31", K_AUX31},
    128 	{"AUX32", K_AUX32},
    129 
    130 	{"KP_HOME",			K_KP_HOME },
    131 	{"KP_UPARROW",		K_KP_UPARROW },
    132 	{"KP_PGUP",			K_KP_PGUP },
    133 	{"KP_LEFTARROW",	K_KP_LEFTARROW },
    134 	{"KP_5",			K_KP_5 },
    135 	{"KP_RIGHTARROW",	K_KP_RIGHTARROW },
    136 	{"KP_END",			K_KP_END },
    137 	{"KP_DOWNARROW",	K_KP_DOWNARROW },
    138 	{"KP_PGDN",			K_KP_PGDN },
    139 	{"KP_ENTER",		K_KP_ENTER },
    140 	{"KP_INS",			K_KP_INS },
    141 	{"KP_DEL",			K_KP_DEL },
    142 	{"KP_SLASH",		K_KP_SLASH },
    143 	{"KP_MINUS",		K_KP_MINUS },
    144 	{"KP_PLUS",			K_KP_PLUS },
    145 
    146 	{"MWHEELUP", K_MWHEELUP },
    147 	{"MWHEELDOWN", K_MWHEELDOWN },
    148 
    149 	{"PAUSE", K_PAUSE},
    150 
    151 	{"SEMICOLON", ';'},	// because a raw semicolon seperates commands
    152 
    153 	{NULL,0}
    154 };
    155 
    156 /*
    157 ==============================================================================
    158 
    159 			LINE TYPING INTO THE CONSOLE
    160 
    161 ==============================================================================
    162 */
    163 
    164 void CompleteCommand (void)
    165 {
    166 	char	*cmd, *s;
    167 
    168 	s = key_lines[edit_line]+1;
    169 	if (*s == '\\' || *s == '/')
    170 		s++;
    171 
    172 	cmd = Cmd_CompleteCommand (s);
    173 	if (!cmd)
    174 		cmd = Cvar_CompleteVariable (s);
    175 	if (cmd)
    176 	{
    177 		key_lines[edit_line][1] = '/';
    178 		strcpy (key_lines[edit_line]+2, cmd);
    179 		key_linepos = strlen(cmd)+2;
    180 		key_lines[edit_line][key_linepos] = ' ';
    181 		key_linepos++;
    182 		key_lines[edit_line][key_linepos] = 0;
    183 		return;
    184 	}
    185 }
    186 
    187 /*
    188 ====================
    189 Key_Console
    190 
    191 Interactive line editing and console scrollback
    192 ====================
    193 */
    194 void Key_Console (int key)
    195 {
    196 
    197 	switch ( key )
    198 	{
    199 	case K_KP_SLASH:
    200 		key = '/';
    201 		break;
    202 	case K_KP_MINUS:
    203 		key = '-';
    204 		break;
    205 	case K_KP_PLUS:
    206 		key = '+';
    207 		break;
    208 	case K_KP_HOME:
    209 		key = '7';
    210 		break;
    211 	case K_KP_UPARROW:
    212 		key = '8';
    213 		break;
    214 	case K_KP_PGUP:
    215 		key = '9';
    216 		break;
    217 	case K_KP_LEFTARROW:
    218 		key = '4';
    219 		break;
    220 	case K_KP_5:
    221 		key = '5';
    222 		break;
    223 	case K_KP_RIGHTARROW:
    224 		key = '6';
    225 		break;
    226 	case K_KP_END:
    227 		key = '1';
    228 		break;
    229 	case K_KP_DOWNARROW:
    230 		key = '2';
    231 		break;
    232 	case K_KP_PGDN:
    233 		key = '3';
    234 		break;
    235 	case K_KP_INS:
    236 		key = '0';
    237 		break;
    238 	case K_KP_DEL:
    239 		key = '.';
    240 		break;
    241 	}
    242 
    243 	if ( ( toupper( key ) == 'V' && keydown[K_CTRL] ) ||
    244 		 ( ( ( key == K_INS ) || ( key == K_KP_INS ) ) && keydown[K_SHIFT] ) )
    245 	{
    246 		char *cbd;
    247 		
    248 		if ( ( cbd = Sys_GetClipboardData() ) != 0 )
    249 		{
    250 			int i;
    251 
    252 			strtok( cbd, "\n\r\b" );
    253 
    254 			i = strlen( cbd );
    255 			if ( i + key_linepos >= MAXCMDLINE)
    256 				i= MAXCMDLINE - key_linepos;
    257 
    258 			if ( i > 0 )
    259 			{
    260 				cbd[i]=0;
    261 				strcat( key_lines[edit_line], cbd );
    262 				key_linepos += i;
    263 			}
    264 			free( cbd );
    265 		}
    266 
    267 		return;
    268 	}
    269 
    270 	if ( key == 'l' ) 
    271 	{
    272 		if ( keydown[K_CTRL] )
    273 		{
    274 			Cbuf_AddText ("clear\n");
    275 			return;
    276 		}
    277 	}
    278 
    279 	if ( key == K_ENTER || key == K_KP_ENTER )
    280 	{	// backslash text are commands, else chat
    281 		if (key_lines[edit_line][1] == '\\' || key_lines[edit_line][1] == '/')
    282 			Cbuf_AddText (key_lines[edit_line]+2);	// skip the >
    283 		else
    284 			Cbuf_AddText (key_lines[edit_line]+1);	// valid command
    285 
    286 		Cbuf_AddText ("\n");
    287 		Com_Printf ("%s\n",key_lines[edit_line]);
    288 		edit_line = (edit_line + 1) & 31;
    289 		history_line = edit_line;
    290 		key_lines[edit_line][0] = ']';
    291 		key_linepos = 1;
    292 		if (cls.state == ca_disconnected)
    293 			SCR_UpdateScreen ();	// force an update, because the command
    294 									// may take some time
    295 		return;
    296 	}
    297 
    298 	if (key == K_TAB)
    299 	{	// command completion
    300 		CompleteCommand ();
    301 		return;
    302 	}
    303 	
    304 	if ( ( key == K_BACKSPACE ) || ( key == K_LEFTARROW ) || ( key == K_KP_LEFTARROW ) || ( ( key == 'h' ) && ( keydown[K_CTRL] ) ) )
    305 	{
    306 		if (key_linepos > 1)
    307 			key_linepos--;
    308 		return;
    309 	}
    310 
    311 	if ( ( key == K_UPARROW ) || ( key == K_KP_UPARROW ) ||
    312 		 ( ( key == 'p' ) && keydown[K_CTRL] ) )
    313 	{
    314 		do
    315 		{
    316 			history_line = (history_line - 1) & 31;
    317 		} while (history_line != edit_line
    318 				&& !key_lines[history_line][1]);
    319 		if (history_line == edit_line)
    320 			history_line = (edit_line+1)&31;
    321 		strcpy(key_lines[edit_line], key_lines[history_line]);
    322 		key_linepos = strlen(key_lines[edit_line]);
    323 		return;
    324 	}
    325 
    326 	if ( ( key == K_DOWNARROW ) || ( key == K_KP_DOWNARROW ) ||
    327 		 ( ( key == 'n' ) && keydown[K_CTRL] ) )
    328 	{
    329 		if (history_line == edit_line) return;
    330 		do
    331 		{
    332 			history_line = (history_line + 1) & 31;
    333 		}
    334 		while (history_line != edit_line
    335 			&& !key_lines[history_line][1]);
    336 		if (history_line == edit_line)
    337 		{
    338 			key_lines[edit_line][0] = ']';
    339 			key_linepos = 1;
    340 		}
    341 		else
    342 		{
    343 			strcpy(key_lines[edit_line], key_lines[history_line]);
    344 			key_linepos = strlen(key_lines[edit_line]);
    345 		}
    346 		return;
    347 	}
    348 
    349 	if (key == K_PGUP || key == K_KP_PGUP )
    350 	{
    351 		con.display -= 2;
    352 		return;
    353 	}
    354 
    355 	if (key == K_PGDN || key == K_KP_PGDN ) 
    356 	{
    357 		con.display += 2;
    358 		if (con.display > con.current)
    359 			con.display = con.current;
    360 		return;
    361 	}
    362 
    363 	if (key == K_HOME || key == K_KP_HOME )
    364 	{
    365 		con.display = con.current - con.totallines + 10;
    366 		return;
    367 	}
    368 
    369 	if (key == K_END || key == K_KP_END )
    370 	{
    371 		con.display = con.current;
    372 		return;
    373 	}
    374 	
    375 	if (key < 32 || key > 127)
    376 		return;	// non printable
    377 		
    378 	if (key_linepos < MAXCMDLINE-1)
    379 	{
    380 		key_lines[edit_line][key_linepos] = key;
    381 		key_linepos++;
    382 		key_lines[edit_line][key_linepos] = 0;
    383 	}
    384 
    385 }
    386 
    387 //============================================================================
    388 
    389 qboolean	chat_team;
    390 char		chat_buffer[MAXCMDLINE];
    391 int			chat_bufferlen = 0;
    392 
    393 void Key_Message (int key)
    394 {
    395 
    396 	if ( key == K_ENTER || key == K_KP_ENTER )
    397 	{
    398 		if (chat_team)
    399 			Cbuf_AddText ("say_team \"");
    400 		else
    401 			Cbuf_AddText ("say \"");
    402 		Cbuf_AddText(chat_buffer);
    403 		Cbuf_AddText("\"\n");
    404 
    405 		cls.key_dest = key_game;
    406 		chat_bufferlen = 0;
    407 		chat_buffer[0] = 0;
    408 		return;
    409 	}
    410 
    411 	if (key == K_ESCAPE)
    412 	{
    413 		cls.key_dest = key_game;
    414 		chat_bufferlen = 0;
    415 		chat_buffer[0] = 0;
    416 		return;
    417 	}
    418 
    419 	if (key < 32 || key > 127)
    420 		return;	// non printable
    421 
    422 	if (key == K_BACKSPACE)
    423 	{
    424 		if (chat_bufferlen)
    425 		{
    426 			chat_bufferlen--;
    427 			chat_buffer[chat_bufferlen] = 0;
    428 		}
    429 		return;
    430 	}
    431 
    432 	if (chat_bufferlen == sizeof(chat_buffer)-1)
    433 		return; // all full
    434 
    435 	chat_buffer[chat_bufferlen++] = key;
    436 	chat_buffer[chat_bufferlen] = 0;
    437 }
    438 
    439 //============================================================================
    440 
    441 
    442 /*
    443 ===================
    444 Key_StringToKeynum
    445 
    446 Returns a key number to be used to index keybindings[] by looking at
    447 the given string.  Single ascii characters return themselves, while
    448 the K_* names are matched up.
    449 ===================
    450 */
    451 int Key_StringToKeynum (char *str)
    452 {
    453 	keyname_t	*kn;
    454 	
    455 	if (!str || !str[0])
    456 		return -1;
    457 	if (!str[1])
    458 		return str[0];
    459 
    460 	for (kn=keynames ; kn->name ; kn++)
    461 	{
    462 		if (!Q_strcasecmp(str,kn->name))
    463 			return kn->keynum;
    464 	}
    465 	return -1;
    466 }
    467 
    468 /*
    469 ===================
    470 Key_KeynumToString
    471 
    472 Returns a string (either a single ascii char, or a K_* name) for the
    473 given keynum.
    474 FIXME: handle quote special (general escape sequence?)
    475 ===================
    476 */
    477 char *Key_KeynumToString (int keynum)
    478 {
    479 	keyname_t	*kn;	
    480 	static	char	tinystr[2];
    481 	
    482 	if (keynum == -1)
    483 		return "<KEY NOT FOUND>";
    484 	if (keynum > 32 && keynum < 127)
    485 	{	// printable ascii
    486 		tinystr[0] = keynum;
    487 		tinystr[1] = 0;
    488 		return tinystr;
    489 	}
    490 	
    491 	for (kn=keynames ; kn->name ; kn++)
    492 		if (keynum == kn->keynum)
    493 			return kn->name;
    494 
    495 	return "<UNKNOWN KEYNUM>";
    496 }
    497 
    498 
    499 /*
    500 ===================
    501 Key_SetBinding
    502 ===================
    503 */
    504 void Key_SetBinding (int keynum, char *binding)
    505 {
    506 	char	*new;
    507 	int		l;
    508 			
    509 	if (keynum == -1)
    510 		return;
    511 
    512 // free old bindings
    513 	if (keybindings[keynum])
    514 	{
    515 		Z_Free (keybindings[keynum]);
    516 		keybindings[keynum] = NULL;
    517 	}
    518 			
    519 // allocate memory for new binding
    520 	l = strlen (binding);	
    521 	new = Z_Malloc (l+1);
    522 	strcpy (new, binding);
    523 	new[l] = 0;
    524 	keybindings[keynum] = new;	
    525 }
    526 
    527 /*
    528 ===================
    529 Key_Unbind_f
    530 ===================
    531 */
    532 void Key_Unbind_f (void)
    533 {
    534 	int		b;
    535 
    536 	if (Cmd_Argc() != 2)
    537 	{
    538 		Com_Printf ("unbind <key> : remove commands from a key\n");
    539 		return;
    540 	}
    541 	
    542 	b = Key_StringToKeynum (Cmd_Argv(1));
    543 	if (b==-1)
    544 	{
    545 		Com_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1));
    546 		return;
    547 	}
    548 
    549 	Key_SetBinding (b, "");
    550 }
    551 
    552 void Key_Unbindall_f (void)
    553 {
    554 	int		i;
    555 	
    556 	for (i=0 ; i<256 ; i++)
    557 		if (keybindings[i])
    558 			Key_SetBinding (i, "");
    559 }
    560 
    561 
    562 /*
    563 ===================
    564 Key_Bind_f
    565 ===================
    566 */
    567 void Key_Bind_f (void)
    568 {
    569 	int			i, c, b;
    570 	char		cmd[1024];
    571 	
    572 	c = Cmd_Argc();
    573 
    574 	if (c < 2)
    575 	{
    576 		Com_Printf ("bind <key> [command] : attach a command to a key\n");
    577 		return;
    578 	}
    579 	b = Key_StringToKeynum (Cmd_Argv(1));
    580 	if (b==-1)
    581 	{
    582 		Com_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1));
    583 		return;
    584 	}
    585 
    586 	if (c == 2)
    587 	{
    588 		if (keybindings[b])
    589 			Com_Printf ("\"%s\" = \"%s\"\n", Cmd_Argv(1), keybindings[b] );
    590 		else
    591 			Com_Printf ("\"%s\" is not bound\n", Cmd_Argv(1) );
    592 		return;
    593 	}
    594 	
    595 // copy the rest of the command line
    596 	cmd[0] = 0;		// start out with a null string
    597 	for (i=2 ; i< c ; i++)
    598 	{
    599 		strcat (cmd, Cmd_Argv(i));
    600 		if (i != (c-1))
    601 			strcat (cmd, " ");
    602 	}
    603 
    604 	Key_SetBinding (b, cmd);
    605 }
    606 
    607 /*
    608 ============
    609 Key_WriteBindings
    610 
    611 Writes lines containing "bind key value"
    612 ============
    613 */
    614 void Key_WriteBindings (FILE *f)
    615 {
    616 	int		i;
    617 
    618 	for (i=0 ; i<256 ; i++)
    619 		if (keybindings[i] && keybindings[i][0])
    620 			fprintf (f, "bind %s \"%s\"\n", Key_KeynumToString(i), keybindings[i]);
    621 }
    622 
    623 
    624 /*
    625 ============
    626 Key_Bindlist_f
    627 
    628 ============
    629 */
    630 void Key_Bindlist_f (void)
    631 {
    632 	int		i;
    633 
    634 	for (i=0 ; i<256 ; i++)
    635 		if (keybindings[i] && keybindings[i][0])
    636 			Com_Printf ("%s \"%s\"\n", Key_KeynumToString(i), keybindings[i]);
    637 }
    638 
    639 
    640 /*
    641 ===================
    642 Key_Init
    643 ===================
    644 */
    645 void Key_Init (void)
    646 {
    647 	int		i;
    648 
    649 	for (i=0 ; i<32 ; i++)
    650 	{
    651 		key_lines[i][0] = ']';
    652 		key_lines[i][1] = 0;
    653 	}
    654 	key_linepos = 1;
    655 	
    656 //
    657 // init ascii characters in console mode
    658 //
    659 	for (i=32 ; i<128 ; i++)
    660 		consolekeys[i] = true;
    661 	consolekeys[K_ENTER] = true;
    662 	consolekeys[K_KP_ENTER] = true;
    663 	consolekeys[K_TAB] = true;
    664 	consolekeys[K_LEFTARROW] = true;
    665 	consolekeys[K_KP_LEFTARROW] = true;
    666 	consolekeys[K_RIGHTARROW] = true;
    667 	consolekeys[K_KP_RIGHTARROW] = true;
    668 	consolekeys[K_UPARROW] = true;
    669 	consolekeys[K_KP_UPARROW] = true;
    670 	consolekeys[K_DOWNARROW] = true;
    671 	consolekeys[K_KP_DOWNARROW] = true;
    672 	consolekeys[K_BACKSPACE] = true;
    673 	consolekeys[K_HOME] = true;
    674 	consolekeys[K_KP_HOME] = true;
    675 	consolekeys[K_END] = true;
    676 	consolekeys[K_KP_END] = true;
    677 	consolekeys[K_PGUP] = true;
    678 	consolekeys[K_KP_PGUP] = true;
    679 	consolekeys[K_PGDN] = true;
    680 	consolekeys[K_KP_PGDN] = true;
    681 	consolekeys[K_SHIFT] = true;
    682 	consolekeys[K_INS] = true;
    683 	consolekeys[K_KP_INS] = true;
    684 	consolekeys[K_KP_DEL] = true;
    685 	consolekeys[K_KP_SLASH] = true;
    686 	consolekeys[K_KP_PLUS] = true;
    687 	consolekeys[K_KP_MINUS] = true;
    688 	consolekeys[K_KP_5] = true;
    689 
    690 	consolekeys['`'] = false;
    691 	consolekeys['~'] = false;
    692 
    693 	for (i=0 ; i<256 ; i++)
    694 		keyshift[i] = i;
    695 	for (i='a' ; i<='z' ; i++)
    696 		keyshift[i] = i - 'a' + 'A';
    697 	keyshift['1'] = '!';
    698 	keyshift['2'] = '@';
    699 	keyshift['3'] = '#';
    700 	keyshift['4'] = '$';
    701 	keyshift['5'] = '%';
    702 	keyshift['6'] = '^';
    703 	keyshift['7'] = '&';
    704 	keyshift['8'] = '*';
    705 	keyshift['9'] = '(';
    706 	keyshift['0'] = ')';
    707 	keyshift['-'] = '_';
    708 	keyshift['='] = '+';
    709 	keyshift[','] = '<';
    710 	keyshift['.'] = '>';
    711 	keyshift['/'] = '?';
    712 	keyshift[';'] = ':';
    713 	keyshift['\''] = '"';
    714 	keyshift['['] = '{';
    715 	keyshift[']'] = '}';
    716 	keyshift['`'] = '~';
    717 	keyshift['\\'] = '|';
    718 
    719 	menubound[K_ESCAPE] = true;
    720 	for (i=0 ; i<12 ; i++)
    721 		menubound[K_F1+i] = true;
    722 
    723 //
    724 // register our functions
    725 //
    726 	Cmd_AddCommand ("bind",Key_Bind_f);
    727 	Cmd_AddCommand ("unbind",Key_Unbind_f);
    728 	Cmd_AddCommand ("unbindall",Key_Unbindall_f);
    729 	Cmd_AddCommand ("bindlist",Key_Bindlist_f);
    730 }
    731 
    732 /*
    733 ===================
    734 Key_Event
    735 
    736 Called by the system between frames for both key up and key down events
    737 Should NOT be called during an interrupt!
    738 ===================
    739 */
    740 void Key_Event (int key, qboolean down, unsigned time)
    741 {
    742 	char	*kb;
    743 	char	cmd[1024];
    744 
    745 	// hack for modal presses
    746 	if (key_waiting == -1)
    747 	{
    748 		if (down)
    749 			key_waiting = key;
    750 		return;
    751 	}
    752 
    753 	// update auto-repeat status
    754 	if (down)
    755 	{
    756 		key_repeats[key]++;
    757 		if (key != K_BACKSPACE 
    758 			&& key != K_PAUSE 
    759 			&& key != K_PGUP 
    760 			&& key != K_KP_PGUP 
    761 			&& key != K_PGDN
    762 			&& key != K_KP_PGDN
    763 			&& key_repeats[key] > 1)
    764 			return;	// ignore most autorepeats
    765 			
    766 		if (key >= 200 && !keybindings[key])
    767 			Com_Printf ("%s is unbound, hit F4 to set.\n", Key_KeynumToString (key) );
    768 	}
    769 	else
    770 	{
    771 		key_repeats[key] = 0;
    772 	}
    773 
    774 	if (key == K_SHIFT)
    775 		shift_down = down;
    776 
    777 	// console key is hardcoded, so the user can never unbind it
    778 	if (key == '`' || key == '~')
    779 	{
    780 		if (!down)
    781 			return;
    782 		Con_ToggleConsole_f ();
    783 		return;
    784 	}
    785 
    786 	// any key during the attract mode will bring up the menu
    787 	if (cl.attractloop && cls.key_dest != key_menu)
    788 		key = K_ESCAPE;
    789 
    790 	// menu key is hardcoded, so the user can never unbind it
    791 	if (key == K_ESCAPE)
    792 	{
    793 		if (!down)
    794 			return;
    795 
    796 		if (cl.frame.playerstate.stats[STAT_LAYOUTS] && cls.key_dest == key_game)
    797 		{	// put away help computer / inventory
    798 			Cbuf_AddText ("cmd putaway\n");
    799 			return;
    800 		}
    801 		switch (cls.key_dest)
    802 		{
    803 		case key_message:
    804 			Key_Message (key);
    805 			break;
    806 		case key_menu:
    807 			M_Keydown (key);
    808 			break;
    809 		case key_game:
    810 		case key_console:
    811 			M_Menu_Main_f ();
    812 			break;
    813 		default:
    814 			Com_Error (ERR_FATAL, "Bad cls.key_dest");
    815 		}
    816 		return;
    817 	}
    818 
    819 	// track if any key is down for BUTTON_ANY
    820 	keydown[key] = down;
    821 	if (down)
    822 	{
    823 		if (key_repeats[key] == 1)
    824 			anykeydown++;
    825 	}
    826 	else
    827 	{
    828 		anykeydown--;
    829 		if (anykeydown < 0)
    830 			anykeydown = 0;
    831 	}
    832 
    833 //
    834 // key up events only generate commands if the game key binding is
    835 // a button command (leading + sign).  These will occur even in console mode,
    836 // to keep the character from continuing an action started before a console
    837 // switch.  Button commands include the kenum as a parameter, so multiple
    838 // downs can be matched with ups
    839 //
    840 	if (!down)
    841 	{
    842 		kb = keybindings[key];
    843 		if (kb && kb[0] == '+')
    844 		{
    845 			Com_sprintf (cmd, sizeof(cmd), "-%s %i %i\n", kb+1, key, time);
    846 			Cbuf_AddText (cmd);
    847 		}
    848 		if (keyshift[key] != key)
    849 		{
    850 			kb = keybindings[keyshift[key]];
    851 			if (kb && kb[0] == '+')
    852 			{
    853 				Com_sprintf (cmd, sizeof(cmd), "-%s %i %i\n", kb+1, key, time);
    854 				Cbuf_AddText (cmd);
    855 			}
    856 		}
    857 		return;
    858 	}
    859 
    860 //
    861 // if not a consolekey, send to the interpreter no matter what mode is
    862 //
    863 	if ( (cls.key_dest == key_menu && menubound[key])
    864 	|| (cls.key_dest == key_console && !consolekeys[key])
    865 	|| (cls.key_dest == key_game && ( cls.state == ca_active || !consolekeys[key] ) ) )
    866 	{
    867 		kb = keybindings[key];
    868 		if (kb)
    869 		{
    870 			if (kb[0] == '+')
    871 			{	// button commands add keynum and time as a parm
    872 				Com_sprintf (cmd, sizeof(cmd), "%s %i %i\n", kb, key, time);
    873 				Cbuf_AddText (cmd);
    874 			}
    875 			else
    876 			{
    877 				Cbuf_AddText (kb);
    878 				Cbuf_AddText ("\n");
    879 			}
    880 		}
    881 		return;
    882 	}
    883 
    884 	if (!down)
    885 		return;		// other systems only care about key down events
    886 
    887 	if (shift_down)
    888 		key = keyshift[key];
    889 
    890 	switch (cls.key_dest)
    891 	{
    892 	case key_message:
    893 		Key_Message (key);
    894 		break;
    895 	case key_menu:
    896 		M_Keydown (key);
    897 		break;
    898 
    899 	case key_game:
    900 	case key_console:
    901 		Key_Console (key);
    902 		break;
    903 	default:
    904 		Com_Error (ERR_FATAL, "Bad cls.key_dest");
    905 	}
    906 }
    907 
    908 /*
    909 ===================
    910 Key_ClearStates
    911 ===================
    912 */
    913 void Key_ClearStates (void)
    914 {
    915 	int		i;
    916 
    917 	anykeydown = false;
    918 
    919 	for (i=0 ; i<256 ; i++)
    920 	{
    921 		if ( keydown[i] || key_repeats[i] )
    922 			Key_Event( i, false, 0 );
    923 		keydown[i] = 0;
    924 		key_repeats[i] = 0;
    925 	}
    926 }
    927 
    928 
    929 /*
    930 ===================
    931 Key_GetKey
    932 ===================
    933 */
    934 int Key_GetKey (void)
    935 {
    936 	key_waiting = -1;
    937 
    938 	while (key_waiting == -1)
    939 		Sys_SendKeyEvents ();
    940 
    941 	return key_waiting;
    942 }
    943