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