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