Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

cl_keys.c (25607B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 #include "client.h"
     23 
     24 /*
     25 
     26 key up events are sent even if in console mode
     27 
     28 */
     29 
     30 field_t	historyEditLines[COMMAND_HISTORY];
     31 
     32 int			nextHistoryLine;		// the last line in the history buffer, not masked
     33 int			historyLine;	// the line being displayed from history buffer
     34 							// will be <= nextHistoryLine
     35 
     36 field_t		g_consoleField;
     37 field_t		chatField;
     38 qboolean	chat_team;
     39 
     40 int			chat_playerNum;
     41 
     42 
     43 qboolean	key_overstrikeMode;
     44 
     45 qboolean	anykeydown;
     46 qkey_t		keys[MAX_KEYS];
     47 
     48 
     49 typedef struct {
     50 	char	*name;
     51 	int		keynum;
     52 } keyname_t;
     53 
     54 
     55 // names not in this list can either be lowercase ascii, or '0xnn' hex sequences
     56 keyname_t keynames[] =
     57 {
     58 	{"TAB", K_TAB},
     59 	{"ENTER", K_ENTER},
     60 	{"ESCAPE", K_ESCAPE},
     61 	{"SPACE", K_SPACE},
     62 	{"BACKSPACE", K_BACKSPACE},
     63 	{"UPARROW", K_UPARROW},
     64 	{"DOWNARROW", K_DOWNARROW},
     65 	{"LEFTARROW", K_LEFTARROW},
     66 	{"RIGHTARROW", K_RIGHTARROW},
     67 
     68 	{"ALT", K_ALT},
     69 	{"CTRL", K_CTRL},
     70 	{"SHIFT", K_SHIFT},
     71 
     72 	{"COMMAND", K_COMMAND},
     73 
     74 	{"CAPSLOCK", K_CAPSLOCK},
     75 
     76 	
     77 	{"F1", K_F1},
     78 	{"F2", K_F2},
     79 	{"F3", K_F3},
     80 	{"F4", K_F4},
     81 	{"F5", K_F5},
     82 	{"F6", K_F6},
     83 	{"F7", K_F7},
     84 	{"F8", K_F8},
     85 	{"F9", K_F9},
     86 	{"F10", K_F10},
     87 	{"F11", K_F11},
     88 	{"F12", K_F12},
     89 
     90 	{"INS", K_INS},
     91 	{"DEL", K_DEL},
     92 	{"PGDN", K_PGDN},
     93 	{"PGUP", K_PGUP},
     94 	{"HOME", K_HOME},
     95 	{"END", K_END},
     96 
     97 	{"MOUSE1", K_MOUSE1},
     98 	{"MOUSE2", K_MOUSE2},
     99 	{"MOUSE3", K_MOUSE3},
    100 	{"MOUSE4", K_MOUSE4},
    101 	{"MOUSE5", K_MOUSE5},
    102 
    103 	{"MWHEELUP",	K_MWHEELUP },
    104 	{"MWHEELDOWN",	K_MWHEELDOWN },
    105 
    106 	{"JOY1", K_JOY1},
    107 	{"JOY2", K_JOY2},
    108 	{"JOY3", K_JOY3},
    109 	{"JOY4", K_JOY4},
    110 	{"JOY5", K_JOY5},
    111 	{"JOY6", K_JOY6},
    112 	{"JOY7", K_JOY7},
    113 	{"JOY8", K_JOY8},
    114 	{"JOY9", K_JOY9},
    115 	{"JOY10", K_JOY10},
    116 	{"JOY11", K_JOY11},
    117 	{"JOY12", K_JOY12},
    118 	{"JOY13", K_JOY13},
    119 	{"JOY14", K_JOY14},
    120 	{"JOY15", K_JOY15},
    121 	{"JOY16", K_JOY16},
    122 	{"JOY17", K_JOY17},
    123 	{"JOY18", K_JOY18},
    124 	{"JOY19", K_JOY19},
    125 	{"JOY20", K_JOY20},
    126 	{"JOY21", K_JOY21},
    127 	{"JOY22", K_JOY22},
    128 	{"JOY23", K_JOY23},
    129 	{"JOY24", K_JOY24},
    130 	{"JOY25", K_JOY25},
    131 	{"JOY26", K_JOY26},
    132 	{"JOY27", K_JOY27},
    133 	{"JOY28", K_JOY28},
    134 	{"JOY29", K_JOY29},
    135 	{"JOY30", K_JOY30},
    136 	{"JOY31", K_JOY31},
    137 	{"JOY32", K_JOY32},
    138 
    139 	{"AUX1", K_AUX1},
    140 	{"AUX2", K_AUX2},
    141 	{"AUX3", K_AUX3},
    142 	{"AUX4", K_AUX4},
    143 	{"AUX5", K_AUX5},
    144 	{"AUX6", K_AUX6},
    145 	{"AUX7", K_AUX7},
    146 	{"AUX8", K_AUX8},
    147 	{"AUX9", K_AUX9},
    148 	{"AUX10", K_AUX10},
    149 	{"AUX11", K_AUX11},
    150 	{"AUX12", K_AUX12},
    151 	{"AUX13", K_AUX13},
    152 	{"AUX14", K_AUX14},
    153 	{"AUX15", K_AUX15},
    154 	{"AUX16", K_AUX16},
    155 
    156 	{"KP_HOME",			K_KP_HOME },
    157 	{"KP_UPARROW",		K_KP_UPARROW },
    158 	{"KP_PGUP",			K_KP_PGUP },
    159 	{"KP_LEFTARROW",	K_KP_LEFTARROW },
    160 	{"KP_5",			K_KP_5 },
    161 	{"KP_RIGHTARROW",	K_KP_RIGHTARROW },
    162 	{"KP_END",			K_KP_END },
    163 	{"KP_DOWNARROW",	K_KP_DOWNARROW },
    164 	{"KP_PGDN",			K_KP_PGDN },
    165 	{"KP_ENTER",		K_KP_ENTER },
    166 	{"KP_INS",			K_KP_INS },
    167 	{"KP_DEL",			K_KP_DEL },
    168 	{"KP_SLASH",		K_KP_SLASH },
    169 	{"KP_MINUS",		K_KP_MINUS },
    170 	{"KP_PLUS",			K_KP_PLUS },
    171 	{"KP_NUMLOCK",		K_KP_NUMLOCK },
    172 	{"KP_STAR",			K_KP_STAR },
    173 	{"KP_EQUALS",		K_KP_EQUALS },
    174 
    175 	{"PAUSE", K_PAUSE},
    176 	
    177 	{"SEMICOLON", ';'},	// because a raw semicolon seperates commands
    178 
    179 	{NULL,0}
    180 };
    181 
    182 /*
    183 =============================================================================
    184 
    185 EDIT FIELDS
    186 
    187 =============================================================================
    188 */
    189 
    190 
    191 /*
    192 ===================
    193 Field_Draw
    194 
    195 Handles horizontal scrolling and cursor blinking
    196 x, y, amd width are in pixels
    197 ===================
    198 */
    199 void Field_VariableSizeDraw( field_t *edit, int x, int y, int width, int size, qboolean showCursor ) {
    200 	int		len;
    201 	int		drawLen;
    202 	int		prestep;
    203 	int		cursorChar;
    204 	char	str[MAX_STRING_CHARS];
    205 	int		i;
    206 
    207 	drawLen = edit->widthInChars;
    208 	len = strlen( edit->buffer ) + 1;
    209 
    210 	// guarantee that cursor will be visible
    211 	if ( len <= drawLen ) {
    212 		prestep = 0;
    213 	} else {
    214 		if ( edit->scroll + drawLen > len ) {
    215 			edit->scroll = len - drawLen;
    216 			if ( edit->scroll < 0 ) {
    217 				edit->scroll = 0;
    218 			}
    219 		}
    220 		prestep = edit->scroll;
    221 
    222 /*
    223 		if ( edit->cursor < len - drawLen ) {
    224 			prestep = edit->cursor;	// cursor at start
    225 		} else {
    226 			prestep = len - drawLen;
    227 		}
    228 */
    229 	}
    230 
    231 	if ( prestep + drawLen > len ) {
    232 		drawLen = len - prestep;
    233 	}
    234 
    235 	// extract <drawLen> characters from the field at <prestep>
    236 	if ( drawLen >= MAX_STRING_CHARS ) {
    237 		Com_Error( ERR_DROP, "drawLen >= MAX_STRING_CHARS" );
    238 	}
    239 
    240 	Com_Memcpy( str, edit->buffer + prestep, drawLen );
    241 	str[ drawLen ] = 0;
    242 
    243 	// draw it
    244 	if ( size == SMALLCHAR_WIDTH ) {
    245 		float	color[4];
    246 
    247 		color[0] = color[1] = color[2] = color[3] = 1.0;
    248 		SCR_DrawSmallStringExt( x, y, str, color, qfalse );
    249 	} else {
    250 		// draw big string with drop shadow
    251 		SCR_DrawBigString( x, y, str, 1.0 );
    252 	}
    253 
    254 	// draw the cursor
    255 	if ( !showCursor ) {
    256 		return;
    257 	}
    258 
    259 	if ( (int)( cls.realtime >> 8 ) & 1 ) {
    260 		return;		// off blink
    261 	}
    262 
    263 	if ( key_overstrikeMode ) {
    264 		cursorChar = 11;
    265 	} else {
    266 		cursorChar = 10;
    267 	}
    268 
    269 	i = drawLen - ( Q_PrintStrlen( str ) + 1 );
    270 
    271 	if ( size == SMALLCHAR_WIDTH ) {
    272 		SCR_DrawSmallChar( x + ( edit->cursor - prestep - i ) * size, y, cursorChar );
    273 	} else {
    274 		str[0] = cursorChar;
    275 		str[1] = 0;
    276 		SCR_DrawBigString( x + ( edit->cursor - prestep - i ) * size, y, str, 1.0 );
    277 
    278 	}
    279 }
    280 
    281 void Field_Draw( field_t *edit, int x, int y, int width, qboolean showCursor ) 
    282 {
    283 	Field_VariableSizeDraw( edit, x, y, width, SMALLCHAR_WIDTH, showCursor );
    284 }
    285 
    286 void Field_BigDraw( field_t *edit, int x, int y, int width, qboolean showCursor ) 
    287 {
    288 	Field_VariableSizeDraw( edit, x, y, width, BIGCHAR_WIDTH, showCursor );
    289 }
    290 
    291 /*
    292 ================
    293 Field_Paste
    294 ================
    295 */
    296 void Field_Paste( field_t *edit ) {
    297 	char	*cbd;
    298 	int		pasteLen, i;
    299 
    300 	cbd = Sys_GetClipboardData();
    301 
    302 	if ( !cbd ) {
    303 		return;
    304 	}
    305 
    306 	// send as if typed, so insert / overstrike works properly
    307 	pasteLen = strlen( cbd );
    308 	for ( i = 0 ; i < pasteLen ; i++ ) {
    309 		Field_CharEvent( edit, cbd[i] );
    310 	}
    311 
    312 	Z_Free( cbd );
    313 }
    314 
    315 /*
    316 =================
    317 Field_KeyDownEvent
    318 
    319 Performs the basic line editing functions for the console,
    320 in-game talk, and menu fields
    321 
    322 Key events are used for non-printable characters, others are gotten from char events.
    323 =================
    324 */
    325 void Field_KeyDownEvent( field_t *edit, int key ) {
    326 	int		len;
    327 
    328 	// shift-insert is paste
    329 	if ( ( ( key == K_INS ) || ( key == K_KP_INS ) ) && keys[K_SHIFT].down ) {
    330 		Field_Paste( edit );
    331 		return;
    332 	}
    333 
    334 	len = strlen( edit->buffer );
    335 
    336 	if ( key == K_DEL ) {
    337 		if ( edit->cursor < len ) {
    338 			memmove( edit->buffer + edit->cursor, 
    339 				edit->buffer + edit->cursor + 1, len - edit->cursor );
    340 		}
    341 		return;
    342 	}
    343 
    344 	if ( key == K_RIGHTARROW ) 
    345 	{
    346 		if ( edit->cursor < len ) {
    347 			edit->cursor++;
    348 		}
    349 
    350 		if ( edit->cursor >= edit->scroll + edit->widthInChars && edit->cursor <= len )
    351 		{
    352 			edit->scroll++;
    353 		}
    354 		return;
    355 	}
    356 
    357 	if ( key == K_LEFTARROW ) 
    358 	{
    359 		if ( edit->cursor > 0 ) {
    360 			edit->cursor--;
    361 		}
    362 		if ( edit->cursor < edit->scroll )
    363 		{
    364 			edit->scroll--;
    365 		}
    366 		return;
    367 	}
    368 
    369 	if ( key == K_HOME || ( tolower(key) == 'a' && keys[K_CTRL].down ) ) {
    370 		edit->cursor = 0;
    371 		return;
    372 	}
    373 
    374 	if ( key == K_END || ( tolower(key) == 'e' && keys[K_CTRL].down ) ) {
    375 		edit->cursor = len;
    376 		return;
    377 	}
    378 
    379 	if ( key == K_INS ) {
    380 		key_overstrikeMode = !key_overstrikeMode;
    381 		return;
    382 	}
    383 }
    384 
    385 /*
    386 ==================
    387 Field_CharEvent
    388 ==================
    389 */
    390 void Field_CharEvent( field_t *edit, int ch ) {
    391 	int		len;
    392 
    393 	if ( ch == 'v' - 'a' + 1 ) {	// ctrl-v is paste
    394 		Field_Paste( edit );
    395 		return;
    396 	}
    397 
    398 	if ( ch == 'c' - 'a' + 1 ) {	// ctrl-c clears the field
    399 		Field_Clear( edit );
    400 		return;
    401 	}
    402 
    403 	len = strlen( edit->buffer );
    404 
    405 	if ( ch == 'h' - 'a' + 1 )	{	// ctrl-h is backspace
    406 		if ( edit->cursor > 0 ) {
    407 			memmove( edit->buffer + edit->cursor - 1, 
    408 				edit->buffer + edit->cursor, len + 1 - edit->cursor );
    409 			edit->cursor--;
    410 			if ( edit->cursor < edit->scroll )
    411 			{
    412 				edit->scroll--;
    413 			}
    414 		}
    415 		return;
    416 	}
    417 
    418 	if ( ch == 'a' - 'a' + 1 ) {	// ctrl-a is home
    419 		edit->cursor = 0;
    420 		edit->scroll = 0;
    421 		return;
    422 	}
    423 
    424 	if ( ch == 'e' - 'a' + 1 ) {	// ctrl-e is end
    425 		edit->cursor = len;
    426 		edit->scroll = edit->cursor - edit->widthInChars;
    427 		return;
    428 	}
    429 
    430 	//
    431 	// ignore any other non printable chars
    432 	//
    433 	if ( ch < 32 ) {
    434 		return;
    435 	}
    436 
    437 	if ( key_overstrikeMode ) {	
    438 		if ( edit->cursor == MAX_EDIT_LINE - 1 )
    439 			return;
    440 		edit->buffer[edit->cursor] = ch;
    441 		edit->cursor++;
    442 	} else {	// insert mode
    443 		if ( len == MAX_EDIT_LINE - 1 ) {
    444 			return; // all full
    445 		}
    446 		memmove( edit->buffer + edit->cursor + 1, 
    447 			edit->buffer + edit->cursor, len + 1 - edit->cursor );
    448 		edit->buffer[edit->cursor] = ch;
    449 		edit->cursor++;
    450 	}
    451 
    452 
    453 	if ( edit->cursor >= edit->widthInChars ) {
    454 		edit->scroll++;
    455 	}
    456 
    457 	if ( edit->cursor == len + 1) {
    458 		edit->buffer[edit->cursor] = 0;
    459 	}
    460 }
    461 
    462 /*
    463 =============================================================================
    464 
    465 CONSOLE LINE EDITING
    466 
    467 ==============================================================================
    468 */
    469 
    470 /*
    471 ====================
    472 Console_Key
    473 
    474 Handles history and console scrollback
    475 ====================
    476 */
    477 void Console_Key (int key) {
    478 	// ctrl-L clears screen
    479 	if ( key == 'l' && keys[K_CTRL].down ) {
    480 		Cbuf_AddText ("clear\n");
    481 		return;
    482 	}
    483 
    484 	// enter finishes the line
    485 	if ( key == K_ENTER || key == K_KP_ENTER ) {
    486 		// if not in the game explicitly prepent a slash if needed
    487 		if ( cls.state != CA_ACTIVE && g_consoleField.buffer[0] != '\\' 
    488 			&& g_consoleField.buffer[0] != '/' ) {
    489 			char	temp[MAX_STRING_CHARS];
    490 
    491 			Q_strncpyz( temp, g_consoleField.buffer, sizeof( temp ) );
    492 			Com_sprintf( g_consoleField.buffer, sizeof( g_consoleField.buffer ), "\\%s", temp );
    493 			g_consoleField.cursor++;
    494 		}
    495 
    496 		Com_Printf ( "]%s\n", g_consoleField.buffer );
    497 
    498 		// leading slash is an explicit command
    499 		if ( g_consoleField.buffer[0] == '\\' || g_consoleField.buffer[0] == '/' ) {
    500 			Cbuf_AddText( g_consoleField.buffer+1 );	// valid command
    501 			Cbuf_AddText ("\n");
    502 		} else {
    503 			// other text will be chat messages
    504 			if ( !g_consoleField.buffer[0] ) {
    505 				return;	// empty lines just scroll the console without adding to history
    506 			} else {
    507 				Cbuf_AddText ("cmd say ");
    508 				Cbuf_AddText( g_consoleField.buffer );
    509 				Cbuf_AddText ("\n");
    510 			}
    511 		}
    512 
    513 		// copy line to history buffer
    514 		historyEditLines[nextHistoryLine % COMMAND_HISTORY] = g_consoleField;
    515 		nextHistoryLine++;
    516 		historyLine = nextHistoryLine;
    517 
    518 		Field_Clear( &g_consoleField );
    519 
    520 		g_consoleField.widthInChars = g_console_field_width;
    521 
    522 		if ( cls.state == CA_DISCONNECTED ) {
    523 			SCR_UpdateScreen ();	// force an update, because the command
    524 		}							// may take some time
    525 		return;
    526 	}
    527 
    528 	// command completion
    529 
    530 	if (key == K_TAB) {
    531 		Field_CompleteCommand(&g_consoleField);
    532 		return;
    533 	}
    534 
    535 	// command history (ctrl-p ctrl-n for unix style)
    536 
    537 	if ( (key == K_MWHEELUP && keys[K_SHIFT].down) || ( key == K_UPARROW ) || ( key == K_KP_UPARROW ) ||
    538 		 ( ( tolower(key) == 'p' ) && keys[K_CTRL].down ) ) {
    539 		if ( nextHistoryLine - historyLine < COMMAND_HISTORY 
    540 			&& historyLine > 0 ) {
    541 			historyLine--;
    542 		}
    543 		g_consoleField = historyEditLines[ historyLine % COMMAND_HISTORY ];
    544 		return;
    545 	}
    546 
    547 	if ( (key == K_MWHEELDOWN && keys[K_SHIFT].down) || ( key == K_DOWNARROW ) || ( key == K_KP_DOWNARROW ) ||
    548 		 ( ( tolower(key) == 'n' ) && keys[K_CTRL].down ) ) {
    549 		if (historyLine == nextHistoryLine)
    550 			return;
    551 		historyLine++;
    552 		g_consoleField = historyEditLines[ historyLine % COMMAND_HISTORY ];
    553 		return;
    554 	}
    555 
    556 	// console scrolling
    557 	if ( key == K_PGUP ) {
    558 		Con_PageUp();
    559 		return;
    560 	}
    561 
    562 	if ( key == K_PGDN) {
    563 		Con_PageDown();
    564 		return;
    565 	}
    566 
    567 	if ( key == K_MWHEELUP) {	//----(SA)	added some mousewheel functionality to the console
    568 		Con_PageUp();
    569 		if(keys[K_CTRL].down) {	// hold <ctrl> to accelerate scrolling
    570 			Con_PageUp();
    571 			Con_PageUp();
    572 		}
    573 		return;
    574 	}
    575 
    576 	if ( key == K_MWHEELDOWN) {	//----(SA)	added some mousewheel functionality to the console
    577 		Con_PageDown();
    578 		if(keys[K_CTRL].down) {	// hold <ctrl> to accelerate scrolling
    579 			Con_PageDown();
    580 			Con_PageDown();
    581 		}
    582 		return;
    583 	}
    584 
    585 	// ctrl-home = top of console
    586 	if ( key == K_HOME && keys[K_CTRL].down ) {
    587 		Con_Top();
    588 		return;
    589 	}
    590 
    591 	// ctrl-end = bottom of console
    592 	if ( key == K_END && keys[K_CTRL].down ) {
    593 		Con_Bottom();
    594 		return;
    595 	}
    596 
    597 	// pass to the normal editline routine
    598 	Field_KeyDownEvent( &g_consoleField, key );
    599 }
    600 
    601 //============================================================================
    602 
    603 
    604 /*
    605 ================
    606 Message_Key
    607 
    608 In game talk message
    609 ================
    610 */
    611 void Message_Key( int key ) {
    612 
    613 	char	buffer[MAX_STRING_CHARS];
    614 
    615 
    616 	if (key == K_ESCAPE) {
    617 		cls.keyCatchers &= ~KEYCATCH_MESSAGE;
    618 		Field_Clear( &chatField );
    619 		return;
    620 	}
    621 
    622 	if ( key == K_ENTER || key == K_KP_ENTER )
    623 	{
    624 		if ( chatField.buffer[0] && cls.state == CA_ACTIVE ) {
    625 			if (chat_playerNum != -1 )
    626 
    627 				Com_sprintf( buffer, sizeof( buffer ), "tell %i \"%s\"\n", chat_playerNum, chatField.buffer );
    628 
    629 			else if (chat_team)
    630 
    631 				Com_sprintf( buffer, sizeof( buffer ), "say_team \"%s\"\n", chatField.buffer );
    632 			else
    633 				Com_sprintf( buffer, sizeof( buffer ), "say \"%s\"\n", chatField.buffer );
    634 
    635 
    636 
    637 			CL_AddReliableCommand( buffer );
    638 		}
    639 		cls.keyCatchers &= ~KEYCATCH_MESSAGE;
    640 		Field_Clear( &chatField );
    641 		return;
    642 	}
    643 
    644 	Field_KeyDownEvent( &chatField, key );
    645 }
    646 
    647 //============================================================================
    648 
    649 
    650 qboolean Key_GetOverstrikeMode( void ) {
    651 	return key_overstrikeMode;
    652 }
    653 
    654 
    655 void Key_SetOverstrikeMode( qboolean state ) {
    656 	key_overstrikeMode = state;
    657 }
    658 
    659 
    660 /*
    661 ===================
    662 Key_IsDown
    663 ===================
    664 */
    665 qboolean Key_IsDown( int keynum ) {
    666 	if ( keynum == -1 ) {
    667 		return qfalse;
    668 	}
    669 
    670 	return keys[keynum].down;
    671 }
    672 
    673 
    674 /*
    675 ===================
    676 Key_StringToKeynum
    677 
    678 Returns a key number to be used to index keys[] by looking at
    679 the given string.  Single ascii characters return themselves, while
    680 the K_* names are matched up.
    681 
    682 0x11 will be interpreted as raw hex, which will allow new controlers
    683 
    684 to be configured even if they don't have defined names.
    685 ===================
    686 */
    687 int Key_StringToKeynum( char *str ) {
    688 	keyname_t	*kn;
    689 	
    690 	if ( !str || !str[0] ) {
    691 		return -1;
    692 	}
    693 	if ( !str[1] ) {
    694 		return str[0];
    695 	}
    696 
    697 	// check for hex code
    698 	if ( str[0] == '0' && str[1] == 'x' && strlen( str ) == 4) {
    699 		int		n1, n2;
    700 		
    701 		n1 = str[2];
    702 		if ( n1 >= '0' && n1 <= '9' ) {
    703 			n1 -= '0';
    704 		} else if ( n1 >= 'a' && n1 <= 'f' ) {
    705 			n1 = n1 - 'a' + 10;
    706 		} else {
    707 			n1 = 0;
    708 		}
    709 
    710 		n2 = str[3];
    711 		if ( n2 >= '0' && n2 <= '9' ) {
    712 			n2 -= '0';
    713 		} else if ( n2 >= 'a' && n2 <= 'f' ) {
    714 			n2 = n2 - 'a' + 10;
    715 		} else {
    716 			n2 = 0;
    717 		}
    718 
    719 		return n1 * 16 + n2;
    720 	}
    721 
    722 	// scan for a text match
    723 	for ( kn=keynames ; kn->name ; kn++ ) {
    724 		if ( !Q_stricmp( str,kn->name ) )
    725 			return kn->keynum;
    726 	}
    727 
    728 	return -1;
    729 }
    730 
    731 /*
    732 ===================
    733 Key_KeynumToString
    734 
    735 Returns a string (either a single ascii char, a K_* name, or a 0x11 hex string) for the
    736 given keynum.
    737 ===================
    738 */
    739 char *Key_KeynumToString( int keynum ) {
    740 	keyname_t	*kn;	
    741 	static	char	tinystr[5];
    742 	int			i, j;
    743 
    744 	if ( keynum == -1 ) {
    745 		return "<KEY NOT FOUND>";
    746 	}
    747 
    748 	if ( keynum < 0 || keynum > 255 ) {
    749 		return "<OUT OF RANGE>";
    750 	}
    751 
    752 	// check for printable ascii (don't use quote)
    753 	if ( keynum > 32 && keynum < 127 && keynum != '"' && keynum != ';' ) {
    754 		tinystr[0] = keynum;
    755 		tinystr[1] = 0;
    756 		return tinystr;
    757 	}
    758 
    759 	// check for a key string
    760 	for ( kn=keynames ; kn->name ; kn++ ) {
    761 		if (keynum == kn->keynum) {
    762 			return kn->name;
    763 		}
    764 	}
    765 
    766 	// make a hex string
    767 	i = keynum >> 4;
    768 	j = keynum & 15;
    769 
    770 	tinystr[0] = '0';
    771 	tinystr[1] = 'x';
    772 	tinystr[2] = i > 9 ? i - 10 + 'a' : i + '0';
    773 	tinystr[3] = j > 9 ? j - 10 + 'a' : j + '0';
    774 	tinystr[4] = 0;
    775 
    776 	return tinystr;
    777 }
    778 
    779 
    780 /*
    781 ===================
    782 Key_SetBinding
    783 ===================
    784 */
    785 void Key_SetBinding( int keynum, const char *binding ) {
    786 	if ( keynum == -1 ) {
    787 		return;
    788 	}
    789 
    790 	// free old bindings
    791 	if ( keys[ keynum ].binding ) {
    792 		Z_Free( keys[ keynum ].binding );
    793 	}
    794 		
    795 	// allocate memory for new binding
    796 	keys[keynum].binding = CopyString( binding );
    797 
    798 	// consider this like modifying an archived cvar, so the
    799 	// file write will be triggered at the next oportunity
    800 	cvar_modifiedFlags |= CVAR_ARCHIVE;
    801 }
    802 
    803 
    804 /*
    805 ===================
    806 Key_GetBinding
    807 ===================
    808 */
    809 char *Key_GetBinding( int keynum ) {
    810 	if ( keynum == -1 ) {
    811 		return "";
    812 	}
    813 
    814 	return keys[ keynum ].binding;
    815 }
    816 
    817 /* 
    818 ===================
    819 Key_GetKey
    820 ===================
    821 */
    822 
    823 int Key_GetKey(const char *binding) {
    824   int i;
    825 
    826   if (binding) {
    827   	for (i=0 ; i<256 ; i++) {
    828       if (keys[i].binding && Q_stricmp(binding, keys[i].binding) == 0) {
    829         return i;
    830       }
    831     }
    832   }
    833   return -1;
    834 }
    835 
    836 /*
    837 ===================
    838 Key_Unbind_f
    839 ===================
    840 */
    841 void Key_Unbind_f (void)
    842 {
    843 	int		b;
    844 
    845 	if (Cmd_Argc() != 2)
    846 	{
    847 		Com_Printf ("unbind <key> : remove commands from a key\n");
    848 		return;
    849 	}
    850 	
    851 	b = Key_StringToKeynum (Cmd_Argv(1));
    852 	if (b==-1)
    853 	{
    854 		Com_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1));
    855 		return;
    856 	}
    857 
    858 	Key_SetBinding (b, "");
    859 }
    860 
    861 /*
    862 ===================
    863 Key_Unbindall_f
    864 ===================
    865 */
    866 void Key_Unbindall_f (void)
    867 {
    868 	int		i;
    869 	
    870 	for (i=0 ; i<256 ; i++)
    871 		if (keys[i].binding)
    872 			Key_SetBinding (i, "");
    873 }
    874 
    875 
    876 /*
    877 ===================
    878 Key_Bind_f
    879 ===================
    880 */
    881 void Key_Bind_f (void)
    882 {
    883 	int			i, c, b;
    884 	char		cmd[1024];
    885 	
    886 	c = Cmd_Argc();
    887 
    888 	if (c < 2)
    889 	{
    890 		Com_Printf ("bind <key> [command] : attach a command to a key\n");
    891 		return;
    892 	}
    893 	b = Key_StringToKeynum (Cmd_Argv(1));
    894 	if (b==-1)
    895 	{
    896 		Com_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1));
    897 		return;
    898 	}
    899 
    900 	if (c == 2)
    901 	{
    902 		if (keys[b].binding)
    903 			Com_Printf ("\"%s\" = \"%s\"\n", Cmd_Argv(1), keys[b].binding );
    904 		else
    905 			Com_Printf ("\"%s\" is not bound\n", Cmd_Argv(1) );
    906 		return;
    907 	}
    908 	
    909 // copy the rest of the command line
    910 	cmd[0] = 0;		// start out with a null string
    911 	for (i=2 ; i< c ; i++)
    912 	{
    913 		strcat (cmd, Cmd_Argv(i));
    914 		if (i != (c-1))
    915 			strcat (cmd, " ");
    916 	}
    917 
    918 	Key_SetBinding (b, cmd);
    919 }
    920 
    921 /*
    922 ============
    923 Key_WriteBindings
    924 
    925 Writes lines containing "bind key value"
    926 ============
    927 */
    928 void Key_WriteBindings( fileHandle_t f ) {
    929 	int		i;
    930 
    931 	FS_Printf (f, "unbindall\n" );
    932 
    933 	for (i=0 ; i<256 ; i++) {
    934 		if (keys[i].binding && keys[i].binding[0] ) {
    935 			FS_Printf (f, "bind %s \"%s\"\n", Key_KeynumToString(i), keys[i].binding);
    936 
    937 		}
    938 
    939 	}
    940 }
    941 
    942 
    943 /*
    944 ============
    945 Key_Bindlist_f
    946 
    947 ============
    948 */
    949 void Key_Bindlist_f( void ) {
    950 	int		i;
    951 
    952 	for ( i = 0 ; i < 256 ; i++ ) {
    953 		if ( keys[i].binding && keys[i].binding[0] ) {
    954 			Com_Printf( "%s \"%s\"\n", Key_KeynumToString(i), keys[i].binding );
    955 		}
    956 	}
    957 }
    958 
    959 /*
    960 ===================
    961 CL_InitKeyCommands
    962 ===================
    963 */
    964 void CL_InitKeyCommands( void ) {
    965 	// register our functions
    966 	Cmd_AddCommand ("bind",Key_Bind_f);
    967 	Cmd_AddCommand ("unbind",Key_Unbind_f);
    968 	Cmd_AddCommand ("unbindall",Key_Unbindall_f);
    969 	Cmd_AddCommand ("bindlist",Key_Bindlist_f);
    970 }
    971 
    972 /*
    973 ===================
    974 CL_AddKeyUpCommands
    975 ===================
    976 */
    977 void CL_AddKeyUpCommands( int key, char *kb ) {
    978 	int i;
    979 	char button[1024], *buttonPtr;
    980 	char	cmd[1024];
    981 	qboolean keyevent;
    982 
    983 	if ( !kb ) {
    984 		return;
    985 	}
    986 	keyevent = qfalse;
    987 	buttonPtr = button;
    988 	for ( i = 0; ; i++ ) {
    989 		if ( kb[i] == ';' || !kb[i] ) {
    990 			*buttonPtr = '\0';
    991 			if ( button[0] == '+') {
    992 				// button commands add keynum and time as parms so that multiple
    993 				// sources can be discriminated and subframe corrected
    994 				Com_sprintf (cmd, sizeof(cmd), "-%s %i %i\n", button+1, key, time);
    995 				Cbuf_AddText (cmd);
    996 				keyevent = qtrue;
    997 			} else {
    998 				if (keyevent) {
    999 					// down-only command
   1000 					Cbuf_AddText (button);
   1001 					Cbuf_AddText ("\n");
   1002 				}
   1003 			}
   1004 			buttonPtr = button;
   1005 			while ( (kb[i] <= ' ' || kb[i] == ';') && kb[i] != 0 ) {
   1006 				i++;
   1007 			}
   1008 		}
   1009 		*buttonPtr++ = kb[i];
   1010 		if ( !kb[i] ) {
   1011 			break;
   1012 		}
   1013 	}
   1014 }
   1015 
   1016 /*
   1017 ===================
   1018 CL_KeyEvent
   1019 
   1020 Called by the system for both key up and key down events
   1021 ===================
   1022 */
   1023 void CL_KeyEvent (int key, qboolean down, unsigned time) {
   1024 	char	*kb;
   1025 	char	cmd[1024];
   1026 
   1027 	// update auto-repeat status and BUTTON_ANY status
   1028 	keys[key].down = down;
   1029 
   1030 	if (down) {
   1031 		keys[key].repeats++;
   1032 		if ( keys[key].repeats == 1) {
   1033 			anykeydown++;
   1034 		}
   1035 	} else {
   1036 		keys[key].repeats = 0;
   1037 		anykeydown--;
   1038 		if (anykeydown < 0) {
   1039 			anykeydown = 0;
   1040 		}
   1041 	}
   1042 
   1043 #ifdef __linux__
   1044   if (key == K_ENTER)
   1045   {
   1046     if (down)
   1047     {
   1048       if (keys[K_ALT].down)
   1049       {
   1050         Key_ClearStates();
   1051         if (Cvar_VariableValue("r_fullscreen") == 0)
   1052         {
   1053           Com_Printf("Switching to fullscreen rendering\n");
   1054           Cvar_Set("r_fullscreen", "1");
   1055         }
   1056         else
   1057         {
   1058           Com_Printf("Switching to windowed rendering\n");
   1059           Cvar_Set("r_fullscreen", "0");
   1060         }
   1061         Cbuf_ExecuteText( EXEC_APPEND, "vid_restart\n");
   1062         return;
   1063       }
   1064     }
   1065   }
   1066 #endif
   1067 
   1068 	// console key is hardcoded, so the user can never unbind it
   1069 	if (key == '`' || key == '~') {
   1070 		if (!down) {
   1071 			return;
   1072 		}
   1073     Con_ToggleConsole_f ();
   1074 		return;
   1075 	}
   1076 
   1077 
   1078 	// keys can still be used for bound actions
   1079 	if ( down && ( key < 128 || key == K_MOUSE1 ) && ( clc.demoplaying || cls.state == CA_CINEMATIC ) && !cls.keyCatchers) {
   1080 
   1081 		if (Cvar_VariableValue ("com_cameraMode") == 0) {
   1082 			Cvar_Set ("nextdemo","");
   1083 			key = K_ESCAPE;
   1084 		}
   1085 	}
   1086 
   1087 
   1088 	// escape is always handled special
   1089 	if ( key == K_ESCAPE && down ) {
   1090 		if ( cls.keyCatchers & KEYCATCH_MESSAGE ) {
   1091 			// clear message mode
   1092 			Message_Key( key );
   1093 			return;
   1094 		}
   1095 
   1096 		// escape always gets out of CGAME stuff
   1097 		if (cls.keyCatchers & KEYCATCH_CGAME) {
   1098 			cls.keyCatchers &= ~KEYCATCH_CGAME;
   1099 			VM_Call (cgvm, CG_EVENT_HANDLING, CGAME_EVENT_NONE);
   1100 			return;
   1101 		}
   1102 
   1103 		if ( !( cls.keyCatchers & KEYCATCH_UI ) ) {
   1104 			if ( cls.state == CA_ACTIVE && !clc.demoplaying ) {
   1105 				VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_INGAME );
   1106 			}
   1107 			else {
   1108 				CL_Disconnect_f();
   1109 				S_StopAllSounds();
   1110 				VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_MAIN );
   1111 			}
   1112 			return;
   1113 		}
   1114 
   1115 		VM_Call( uivm, UI_KEY_EVENT, key, down );
   1116 		return;
   1117 	}
   1118 
   1119 	//
   1120 	// key up events only perform actions if the game key binding is
   1121 	// a button command (leading + sign).  These will be processed even in
   1122 	// console mode and menu mode, to keep the character from continuing 
   1123 	// an action started before a mode switch.
   1124 	//
   1125 	if (!down) {
   1126 		kb = keys[key].binding;
   1127 
   1128 		CL_AddKeyUpCommands( key, kb );
   1129 
   1130 		if ( cls.keyCatchers & KEYCATCH_UI && uivm ) {
   1131 			VM_Call( uivm, UI_KEY_EVENT, key, down );
   1132 		} else if ( cls.keyCatchers & KEYCATCH_CGAME && cgvm ) {
   1133 			VM_Call( cgvm, CG_KEY_EVENT, key, down );
   1134 		} 
   1135 
   1136 		return;
   1137 	}
   1138 
   1139 
   1140 	// distribute the key down event to the apropriate handler
   1141 	if ( cls.keyCatchers & KEYCATCH_CONSOLE ) {
   1142 		Console_Key( key );
   1143 	} else if ( cls.keyCatchers & KEYCATCH_UI ) {
   1144 		if ( uivm ) {
   1145 			VM_Call( uivm, UI_KEY_EVENT, key, down );
   1146 		} 
   1147 	} else if ( cls.keyCatchers & KEYCATCH_CGAME ) {
   1148 		if ( cgvm ) {
   1149 			VM_Call( cgvm, CG_KEY_EVENT, key, down );
   1150 		} 
   1151 	} else if ( cls.keyCatchers & KEYCATCH_MESSAGE ) {
   1152 		Message_Key( key );
   1153 	} else if ( cls.state == CA_DISCONNECTED ) {
   1154 		Console_Key( key );
   1155 	} else {
   1156 		// send the bound action
   1157 		kb = keys[key].binding;
   1158 		if ( !kb ) {
   1159 			if (key >= 200) {
   1160 				Com_Printf ("%s is unbound, use controls menu to set.\n"
   1161 					, Key_KeynumToString( key ) );
   1162 			}
   1163 		} else if (kb[0] == '+') {	
   1164 			int i;
   1165 			char button[1024], *buttonPtr;
   1166 			buttonPtr = button;
   1167 			for ( i = 0; ; i++ ) {
   1168 				if ( kb[i] == ';' || !kb[i] ) {
   1169 					*buttonPtr = '\0';
   1170 					if ( button[0] == '+') {
   1171 						// button commands add keynum and time as parms so that multiple
   1172 						// sources can be discriminated and subframe corrected
   1173 						Com_sprintf (cmd, sizeof(cmd), "%s %i %i\n", button, key, time);
   1174 						Cbuf_AddText (cmd);
   1175 					} else {
   1176 						// down-only command
   1177 						Cbuf_AddText (button);
   1178 						Cbuf_AddText ("\n");
   1179 					}
   1180 					buttonPtr = button;
   1181 					while ( (kb[i] <= ' ' || kb[i] == ';') && kb[i] != 0 ) {
   1182 						i++;
   1183 					}
   1184 				}
   1185 				*buttonPtr++ = kb[i];
   1186 				if ( !kb[i] ) {
   1187 					break;
   1188 				}
   1189 			}
   1190 		} else {
   1191 			// down-only command
   1192 			Cbuf_AddText (kb);
   1193 			Cbuf_AddText ("\n");
   1194 		}
   1195 	}
   1196 }
   1197 
   1198 
   1199 /*
   1200 ===================
   1201 CL_CharEvent
   1202 
   1203 Normal keyboard characters, already shifted / capslocked / etc
   1204 ===================
   1205 */
   1206 void CL_CharEvent( int key ) {
   1207 	// the console key should never be used as a char
   1208 	if ( key == '`' || key == '~' ) {
   1209 		return;
   1210 	}
   1211 
   1212 	// distribute the key down event to the apropriate handler
   1213 	if ( cls.keyCatchers & KEYCATCH_CONSOLE )
   1214 	{
   1215 		Field_CharEvent( &g_consoleField, key );
   1216 	}
   1217 	else if ( cls.keyCatchers & KEYCATCH_UI )
   1218 	{
   1219 		VM_Call( uivm, UI_KEY_EVENT, key | K_CHAR_FLAG, qtrue );
   1220 	}
   1221 	else if ( cls.keyCatchers & KEYCATCH_MESSAGE ) 
   1222 	{
   1223 		Field_CharEvent( &chatField, key );
   1224 	}
   1225 	else if ( cls.state == CA_DISCONNECTED )
   1226 	{
   1227 		Field_CharEvent( &g_consoleField, key );
   1228 	}
   1229 }
   1230 
   1231 
   1232 /*
   1233 ===================
   1234 Key_ClearStates
   1235 ===================
   1236 */
   1237 void Key_ClearStates (void)
   1238 {
   1239 	int		i;
   1240 
   1241 	anykeydown = qfalse;
   1242 
   1243 	for ( i=0 ; i < MAX_KEYS ; i++ ) {
   1244 		if ( keys[i].down ) {
   1245 			CL_KeyEvent( i, qfalse, 0 );
   1246 
   1247 		}
   1248 		keys[i].down = 0;
   1249 		keys[i].repeats = 0;
   1250 	}
   1251 }
   1252