/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "client.h" #include "cl_ui.h" #include "../uilib/ui_public.h" /* key up events are sent even if in console mode */ int nextHistoryLine; // the last line in the history buffer, not masked int historyLine; // the line being displayed from history buffer // will be <= nextHistoryLine qboolean chat_team; int chat_playerNum; qboolean key_overstrikeMode; int anykeydown; qkey_t keys[MAX_KEYS]; qkey_t altkeys[MAX_KEYS]; qkey_t ctrlkeys[MAX_KEYS]; qboolean menubound[MAX_KEYS]; qboolean alt_down; qboolean ctrl_down; typedef struct { const char *name; int keynum; } keyname_t; // names not in this list can either be lowercase ascii, or '0xnn' hex sequences keyname_t keynames[] = { {"TAB", K_TAB }, {"ENTER", K_ENTER }, {"ESCAPE", K_ESCAPE }, {"SPACE", K_SPACE }, {"BACKSPACE", K_BACKSPACE }, {"UPARROW", K_UPARROW }, {"DOWNARROW", K_DOWNARROW }, {"LEFTARROW", K_LEFTARROW }, {"RIGHTARROW", K_RIGHTARROW }, {"ALT", K_ALT }, {"CTRL", K_CTRL }, {"SHIFT", K_SHIFT }, {"COMMAND", K_COMMAND }, {"CAPSLOCK", K_CAPSLOCK }, {"F1", K_F1 }, {"F2", K_F2 }, {"F3", K_F3 }, {"F4", K_F4 }, {"F5", K_F5 }, {"F6", K_F6 }, {"F7", K_F7 }, {"F8", K_F8 }, {"F9", K_F9 }, {"F10", K_F10 }, {"F11", K_F11 }, {"F12", K_F12 }, {"F13", K_F13 }, {"F14", K_F14 }, {"F15", K_F15 }, {"INS", K_INS }, {"DEL", K_DEL }, {"PGDN", K_PGDN }, {"PGUP", K_PGUP }, {"HOME", K_HOME }, {"END", K_END }, {"MOUSE1", K_MOUSE1 }, {"MOUSE2", K_MOUSE2 }, {"MOUSE3", K_MOUSE3 }, {"MOUSE4", K_MOUSE4 }, {"MOUSE5", K_MOUSE5 }, {"MWHEELUP", K_MWHEELUP }, {"MWHEELDOWN", K_MWHEELDOWN }, {"JOY1", K_JOY1 }, {"JOY2", K_JOY2 }, {"JOY3", K_JOY3 }, {"JOY4", K_JOY4 }, {"JOY5", K_JOY5 }, {"JOY6", K_JOY6 }, {"JOY7", K_JOY7 }, {"JOY8", K_JOY8 }, {"JOY9", K_JOY9 }, {"JOY10", K_JOY10 }, {"JOY11", K_JOY11 }, {"JOY12", K_JOY12 }, {"JOY13", K_JOY13 }, {"JOY14", K_JOY14 }, {"JOY15", K_JOY15 }, {"JOY16", K_JOY16 }, {"JOY17", K_JOY17 }, {"JOY18", K_JOY18 }, {"JOY19", K_JOY19 }, {"JOY20", K_JOY20 }, {"JOY21", K_JOY21 }, {"JOY22", K_JOY22 }, {"JOY23", K_JOY23 }, {"JOY24", K_JOY24 }, {"JOY25", K_JOY25 }, {"JOY26", K_JOY26 }, {"JOY27", K_JOY27 }, {"JOY28", K_JOY28 }, {"JOY29", K_JOY29 }, {"JOY30", K_JOY30 }, {"JOY31", K_JOY31 }, {"JOY32", K_JOY32 }, {"AUX1", K_AUX1 }, {"AUX2", K_AUX2 }, {"AUX3", K_AUX3 }, {"AUX4", K_AUX4 }, {"AUX5", K_AUX5 }, {"AUX6", K_AUX6 }, {"AUX7", K_AUX7 }, {"AUX8", K_AUX8 }, {"AUX9", K_AUX9 }, {"AUX10", K_AUX10 }, {"AUX11", K_AUX11 }, {"AUX12", K_AUX12 }, {"AUX13", K_AUX13 }, {"AUX14", K_AUX14 }, {"AUX15", K_AUX15 }, {"AUX16", K_AUX16 }, {"KP_HOME", K_KP_HOME }, {"KP_UPARROW", K_KP_UPARROW }, {"KP_PGUP", K_KP_PGUP }, {"KP_LEFTARROW", K_KP_LEFTARROW }, {"KP_5", K_KP_5 }, {"KP_RIGHTARROW", K_KP_RIGHTARROW }, {"KP_END", K_KP_END }, {"KP_DOWNARROW", K_KP_DOWNARROW }, {"KP_PGDN", K_KP_PGDN }, {"KP_ENTER", K_KP_ENTER }, {"KP_INS", K_KP_INS }, {"KP_DEL", K_KP_DEL }, {"KP_SLASH", K_KP_SLASH }, {"KP_MINUS", K_KP_MINUS }, {"KP_PLUS", K_KP_PLUS }, {"KP_NUMLOCK", K_KP_NUMLOCK }, {"KP_STAR", K_KP_STAR }, {"KP_EQUALS", K_KP_EQUALS }, {"PAUSE", K_PAUSE }, {"SEMICOLON", ';' }, // because a raw semicolon separates commands {"WORLD_0", K_WORLD_0 }, {"WORLD_1", K_WORLD_1 }, {"WORLD_2", K_WORLD_2 }, {"WORLD_3", K_WORLD_3 }, {"WORLD_4", K_WORLD_4 }, {"WORLD_5", K_WORLD_5 }, {"WORLD_6", K_WORLD_6 }, {"WORLD_7", K_WORLD_7 }, {"WORLD_8", K_WORLD_8 }, {"WORLD_9", K_WORLD_9 }, {"WORLD_10", K_WORLD_10 }, {"WORLD_11", K_WORLD_11 }, {"WORLD_12", K_WORLD_12 }, {"WORLD_13", K_WORLD_13 }, {"WORLD_14", K_WORLD_14 }, {"WORLD_15", K_WORLD_15 }, {"WORLD_16", K_WORLD_16 }, {"WORLD_17", K_WORLD_17 }, {"WORLD_18", K_WORLD_18 }, {"WORLD_19", K_WORLD_19 }, {"WORLD_20", K_WORLD_20 }, {"WORLD_21", K_WORLD_21 }, {"WORLD_22", K_WORLD_22 }, {"WORLD_23", K_WORLD_23 }, {"WORLD_24", K_WORLD_24 }, {"WORLD_25", K_WORLD_25 }, {"WORLD_26", K_WORLD_26 }, {"WORLD_27", K_WORLD_27 }, {"WORLD_28", K_WORLD_28 }, {"WORLD_29", K_WORLD_29 }, {"WORLD_30", K_WORLD_30 }, {"WORLD_31", K_WORLD_31 }, {"WORLD_32", K_WORLD_32 }, {"WORLD_33", K_WORLD_33 }, {"WORLD_34", K_WORLD_34 }, {"WORLD_35", K_WORLD_35 }, {"WORLD_36", K_WORLD_36 }, {"WORLD_37", K_WORLD_37 }, {"WORLD_38", K_WORLD_38 }, {"WORLD_39", K_WORLD_39 }, {"WORLD_40", K_WORLD_40 }, {"WORLD_41", K_WORLD_41 }, {"WORLD_42", K_WORLD_42 }, {"WORLD_43", K_WORLD_43 }, {"WORLD_44", K_WORLD_44 }, {"WORLD_45", K_WORLD_45 }, {"WORLD_46", K_WORLD_46 }, {"WORLD_47", K_WORLD_47 }, {"WORLD_48", K_WORLD_48 }, {"WORLD_49", K_WORLD_49 }, {"WORLD_50", K_WORLD_50 }, {"WORLD_51", K_WORLD_51 }, {"WORLD_52", K_WORLD_52 }, {"WORLD_53", K_WORLD_53 }, {"WORLD_54", K_WORLD_54 }, {"WORLD_55", K_WORLD_55 }, {"WORLD_56", K_WORLD_56 }, {"WORLD_57", K_WORLD_57 }, {"WORLD_58", K_WORLD_58 }, {"WORLD_59", K_WORLD_59 }, {"WORLD_60", K_WORLD_60 }, {"WORLD_61", K_WORLD_61 }, {"WORLD_62", K_WORLD_62 }, {"WORLD_63", K_WORLD_63 }, {"WORLD_64", K_WORLD_64 }, {"WORLD_65", K_WORLD_65 }, {"WORLD_66", K_WORLD_66 }, {"WORLD_67", K_WORLD_67 }, {"WORLD_68", K_WORLD_68 }, {"WORLD_69", K_WORLD_69 }, {"WORLD_70", K_WORLD_70 }, {"WORLD_71", K_WORLD_71 }, {"WORLD_72", K_WORLD_72 }, {"WORLD_73", K_WORLD_73 }, {"WORLD_74", K_WORLD_74 }, {"WORLD_75", K_WORLD_75 }, {"WORLD_76", K_WORLD_76 }, {"WORLD_77", K_WORLD_77 }, {"WORLD_78", K_WORLD_78 }, {"WORLD_79", K_WORLD_79 }, {"WORLD_80", K_WORLD_80 }, {"WORLD_81", K_WORLD_81 }, {"WORLD_82", K_WORLD_82 }, {"WORLD_83", K_WORLD_83 }, {"WORLD_84", K_WORLD_84 }, {"WORLD_85", K_WORLD_85 }, {"WORLD_86", K_WORLD_86 }, {"WORLD_87", K_WORLD_87 }, {"WORLD_88", K_WORLD_88 }, {"WORLD_89", K_WORLD_89 }, {"WORLD_90", K_WORLD_90 }, {"WORLD_91", K_WORLD_91 }, {"WORLD_92", K_WORLD_92 }, {"WORLD_93", K_WORLD_93 }, {"WORLD_94", K_WORLD_94 }, {"WORLD_95", K_WORLD_95 }, {"WINDOWS", K_SUPER }, {"COMPOSE", K_COMPOSE }, {"MODE", K_MODE }, {"HELP", K_HELP }, {"PRINT", K_PRINT }, {"SYSREQ", K_SYSREQ }, {"SCROLLOCK", K_SCROLLOCK }, {"BREAK", K_BREAK }, {"MENU", K_MENU }, {"POWER", K_POWER }, {"EURO", K_EURO }, {"UNDO", K_UNDO }, {"PAD0_A", K_PAD0_A }, {"PAD0_B", K_PAD0_B }, {"PAD0_X", K_PAD0_X }, {"PAD0_Y", K_PAD0_Y }, {"PAD0_BACK", K_PAD0_BACK }, {"PAD0_GUIDE", K_PAD0_GUIDE }, {"PAD0_START", K_PAD0_START }, {"PAD0_LEFTSTICK_CLICK", K_PAD0_LEFTSTICK_CLICK }, {"PAD0_RIGHTSTICK_CLICK", K_PAD0_RIGHTSTICK_CLICK}, {"PAD0_LEFTSHOULDER", K_PAD0_LEFTSHOULDER }, {"PAD0_RIGHTSHOULDER", K_PAD0_RIGHTSHOULDER }, {"PAD0_DPAD_UP", K_PAD0_DPAD_UP }, {"PAD0_DPAD_DOWN", K_PAD0_DPAD_DOWN }, {"PAD0_DPAD_LEFT", K_PAD0_DPAD_LEFT }, {"PAD0_DPAD_RIGHT", K_PAD0_DPAD_RIGHT }, {"PAD0_LEFTSTICK_LEFT", K_PAD0_LEFTSTICK_LEFT }, {"PAD0_LEFTSTICK_RIGHT", K_PAD0_LEFTSTICK_RIGHT }, {"PAD0_LEFTSTICK_UP", K_PAD0_LEFTSTICK_UP }, {"PAD0_LEFTSTICK_DOWN", K_PAD0_LEFTSTICK_DOWN }, {"PAD0_RIGHTSTICK_LEFT", K_PAD0_RIGHTSTICK_LEFT }, {"PAD0_RIGHTSTICK_RIGHT", K_PAD0_RIGHTSTICK_RIGHT}, {"PAD0_RIGHTSTICK_UP", K_PAD0_RIGHTSTICK_UP }, {"PAD0_RIGHTSTICK_DOWN", K_PAD0_RIGHTSTICK_DOWN }, {"PAD0_LEFTTRIGGER", K_PAD0_LEFTTRIGGER }, {"PAD0_RIGHTTRIGGER", K_PAD0_RIGHTTRIGGER }, {"PAD0_MISC1", K_PAD0_MISC1 }, {"PAD0_PADDLE1", K_PAD0_PADDLE1 }, {"PAD0_PADDLE2", K_PAD0_PADDLE2 }, {"PAD0_PADDLE3", K_PAD0_PADDLE3 }, {"PAD0_PADDLE4", K_PAD0_PADDLE4 }, {"PAD0_TOUCHPAD", K_PAD0_TOUCHPAD }, // // non-ioq3 // {"LALT", K_ALT }, {"LCTRL", K_CTRL }, {"LSHIFT", K_SHIFT }, {NULL, 0 } }; /* ================ Message_Key In game talk message ================ */ void Message_Key(int key) { if (key == K_ESCAPE) { Key_SetCatcher(Key_GetCatcher() & ~KEYCATCH_MESSAGE); return; } if (key == K_ENTER || key == K_KP_ENTER) { Key_SetCatcher(Key_GetCatcher() & ~KEYCATCH_MESSAGE); return; } } //============================================================================ qboolean Key_GetOverstrikeMode(void) { return key_overstrikeMode; } void Key_SetOverstrikeMode(qboolean state) { key_overstrikeMode = state; } /* =================== Key_IsDown =================== */ qboolean Key_IsDown(int keynum) { if (keynum < 0 || keynum >= MAX_KEYS) { return qfalse; } return keys[keynum].down; } /* =================== Key_StringToKeynum Returns a key number to be used to index keys[] by looking at the given string. Single ascii characters return themselves, while the K_* names are matched up. 0x11 will be interpreted as raw hex, which will allow new controlers to be configured even if they don't have defined names. =================== */ int Key_StringToKeynum(const char *str) { keyname_t *kn; if (!str || !str[0]) { return -1; } if (!str[1]) { return str[0]; } // check for hex code if (str[0] == '0' && str[1] == 'x' && strlen(str) == 4) { int n1, n2; n1 = str[2]; if (n1 >= '0' && n1 <= '9') { n1 -= '0'; } else if (n1 >= 'a' && n1 <= 'f') { n1 = n1 - 'a' + 10; } else { n1 = 0; } n2 = str[3]; if (n2 >= '0' && n2 <= '9') { n2 -= '0'; } else if (n2 >= 'a' && n2 <= 'f') { n2 = n2 - 'a' + 10; } else { n2 = 0; } return n1 * 16 + n2; } // scan for a text match for (kn = keynames; kn->name; kn++) { if (!Q_stricmp(str, kn->name)) { return kn->keynum; } } return -1; } /* =================== Key_KeynumToString Returns a string (either a single ascii char, a K_* name, or a 0x11 hex string) for the given keynum. =================== */ const char *Key_KeynumToString(int keynum) { keyname_t *kn; static char tinystr[5]; int i, j; if (keynum == -1) { return ""; } if (keynum < 0 || keynum >= MAX_KEYS) { return ""; } // check for printable ascii (don't use quote) if (keynum > 32 && keynum < 127 && keynum != '"' && keynum != ';') { tinystr[0] = keynum; tinystr[1] = 0; return tinystr; } // check for a key string for (kn = keynames; kn->name; kn++) { if (keynum == kn->keynum) { return kn->name; } } // make a hex string i = keynum >> 4; j = keynum & 15; tinystr[0] = '0'; tinystr[1] = 'x'; tinystr[2] = i > 9 ? i - 10 + 'a' : i + '0'; tinystr[3] = j > 9 ? j - 10 + 'a' : j + '0'; tinystr[4] = 0; return tinystr; } /* =================== Key_KeynumToString Returns a string (either a single ascii char, a K_* name, or a 0x11 hex string) for the given keynum. =================== */ const char *Key_KeynumToBindString(int keynum) { keyname_t *kn; static char tinystr[5]; if (keynum == -1) { return "Not Bound"; } if (keynum < 0 || keynum >= MAX_KEYS) { return "Out of Range"; } // check for printable ascii (don't use quote) if (keynum > 32 && keynum < 127 && keynum != '"' && keynum != ';') { tinystr[0] = keynum; tinystr[1] = 0; return tinystr; } // check for a key string for (kn = keynames; kn->name; kn++) { if (keynum == kn->keynum) { return kn->name; } } return "Unknown Key"; } /* =================== Key_SetBinding =================== */ void Key_SetBinding(int keynum, const char *binding) { if (keynum < 0 || keynum >= MAX_KEYS) { return; } // free old bindings if (keys[keynum].binding) { Z_Free(keys[keynum].binding); } // allocate memory for new binding keys[keynum].binding = CopyString(binding); // consider this like modifying an archived cvar, so the // file write will be triggered at the next oportunity cvar_modifiedFlags |= CVAR_ARCHIVE; } /* =================== Key_SetAltBinding =================== */ void Key_SetAltBinding(int keynum, const char *binding) { if (keynum < 0 || keynum >= MAX_KEYS) { return; } // free old bindings if (altkeys[keynum].binding) { Z_Free(altkeys[keynum].binding); } // allocate memory for new binding altkeys[keynum].binding = CopyString(binding); // consider this like modifying an archived cvar, so the // file write will be triggered at the next oportunity cvar_modifiedFlags |= CVAR_ARCHIVE; } /* =================== Key_SetCtrlBinding =================== */ void Key_SetCtrlBinding(int keynum, const char *binding) { if (keynum < 0 || keynum >= MAX_KEYS) { return; } // free old bindings if (ctrlkeys[keynum].binding) { Z_Free(ctrlkeys[keynum].binding); } // allocate memory for new binding ctrlkeys[keynum].binding = CopyString(binding); // consider this like modifying an archived cvar, so the // file write will be triggered at the next oportunity cvar_modifiedFlags |= CVAR_ARCHIVE; } /* =================== Key_GetBinding =================== */ const char *Key_GetBinding(int keynum) { if (keynum < 0 || keynum >= MAX_KEYS) { return ""; } return keys[keynum].binding; } /* =================== Key_GetKey =================== */ int Key_GetKey(const char *binding) { int i; if (binding) { for (i = 0; i < MAX_KEYS; i++) { if (keys[i].binding && Q_stricmp(binding, keys[i].binding) == 0) { return i; } } } return -1; } /* =================== Key_GetKeynameForCommand =================== */ const char *Key_GetKeynameForCommand(const char *command) { int i; for (i = 0; i < MAX_KEYS; i++) { if (!keys[i].binding) { continue; } if (keys[i].down && !stricmp(command, keys[i].binding)) { return Key_KeynumToString(i); } } return "Unknown Command"; } /* =================== Key_GetKeysForCommand =================== */ void Key_GetKeysForCommand(const char *command, int *key1, int *key2) { int i; *key1 = *key2 = -1; for (i = 0; i < MAX_KEYS; i++) { if (!keys[i].binding) { continue; } if (!stricmp(command, keys[i].binding)) { if (*key1 == -1) { *key1 = i; } else { *key2 = i; return; } } } } /* =================== Key_Unbind_f =================== */ void Key_Unbind_f(void) { int b; if (Cmd_Argc() != 2) { Com_Printf("unbind : remove commands from a key\n"); return; } b = Key_StringToKeynum(Cmd_Argv(1)); if (b == -1) { Com_Printf("\"%s\" isn't a valid key\n", Cmd_Argv(1)); return; } Key_SetBinding(b, ""); } /* =================== Key_Unaltbind_f =================== */ void Key_Unaltbind_f(void) { int b; if (Cmd_Argc() != 2) { Com_Printf("unaltbind : remove commands from a key\n"); return; } b = Key_StringToKeynum(Cmd_Argv(1)); if (b == -1) { Com_Printf("\"%s\" isn't a valid key\n", Cmd_Argv(1)); return; } Key_SetAltBinding(b, ""); } /* =================== Key_Unctrlbind_f =================== */ void Key_Unctrlbind_f(void) { int b; if (Cmd_Argc() != 2) { Com_Printf("unctrlbind : remove commands from a key\n"); return; } b = Key_StringToKeynum(Cmd_Argv(1)); if (b == -1) { Com_Printf("\"%s\" isn't a valid key\n", Cmd_Argv(1)); return; } Key_SetCtrlBinding(b, ""); } /* =================== Key_Unbindall_f =================== */ void Key_Unbindall_f(void) { int i; for (i = 0; i < MAX_KEYS; i++) { if (keys[i].binding) { Key_SetBinding(i, ""); } } } /* =================== Key_Bind_f =================== */ void Key_Bind_f(void) { int i, c, b; char cmd[1024]; c = Cmd_Argc(); if (c < 2) { Com_Printf("bind [command] : attach a command to a key\n"); return; } b = Key_StringToKeynum(Cmd_Argv(1)); if (b == -1) { Com_Printf("\"%s\" isn't a valid key\n", Cmd_Argv(1)); return; } if (c == 2) { if (keys[b].binding) { Com_Printf("\"%s\" = \"%s\"\n", Cmd_Argv(1), keys[b].binding); } else { Com_Printf("\"%s\" is not bound\n", Cmd_Argv(1)); } return; } // copy the rest of the command line cmd[0] = 0; // start out with a null string for (i = 2; i < c; i++) { strcat(cmd, Cmd_Argv(i)); if (i != (c - 1)) { strcat(cmd, " "); } } Key_SetBinding(b, cmd); } /* =================== Key_AltBind_f =================== */ void Key_AltBind_f(void) { int i, c, b; char cmd[1024]; c = Cmd_Argc(); if (c < 2) { Com_Printf("altbind [command] : attach a command to a key\n"); return; } b = Key_StringToKeynum(Cmd_Argv(1)); if (b == -1) { Com_Printf("\"%s\" isn't a valid key\n", Cmd_Argv(1)); return; } if (c == 2) { if (altkeys[b].binding) { Com_Printf("\"%s\" = \"%s\"\n", Cmd_Argv(1), altkeys[b].binding); } else { Com_Printf("\"%s\" is not bound\n", Cmd_Argv(1)); } return; } // copy the rest of the command line cmd[0] = 0; // start out with a null string for (i = 2; i < c; i++) { strcat(cmd, Cmd_Argv(i)); if (i != (c - 1)) { strcat(cmd, " "); } } Key_SetAltBinding(b, cmd); } /* =================== Key_AltBind_f =================== */ void Key_CtrlBind_f(void) { int i, c, b; char cmd[1024]; c = Cmd_Argc(); if (c < 2) { Com_Printf("ctrlbind [command] : attach a command to a key\n"); return; } b = Key_StringToKeynum(Cmd_Argv(1)); if (b == -1) { Com_Printf("\"%s\" isn't a valid key\n", Cmd_Argv(1)); return; } if (c == 2) { if (ctrlkeys[b].binding) { Com_Printf("\"%s\" = \"%s\"\n", Cmd_Argv(1), ctrlkeys[b].binding); } else { Com_Printf("\"%s\" is not bound\n", Cmd_Argv(1)); } return; } // copy the rest of the command line cmd[0] = 0; // start out with a null string for (i = 2; i < c; i++) { strcat(cmd, Cmd_Argv(i)); if (i != (c - 1)) { strcat(cmd, " "); } } Key_SetCtrlBinding(b, cmd); } /* ============ Key_WriteBindings Writes lines containing "bind key value" ============ */ void Key_WriteBindings(fileHandle_t f) { int i; FS_Printf(f, "unbindall\n"); for (i = 0; i < MAX_KEYS; i++) { if (keys[i].binding && keys[i].binding[0]) { FS_Printf(f, "bind %s \"%s\"\n", Key_KeynumToString(i), keys[i].binding); } } } /* ============ Key_Bindlist_f ============ */ void Key_Bindlist_f(void) { int i; for (i = 0; i < MAX_KEYS; i++) { if (keys[i].binding && keys[i].binding[0]) { Com_Printf("%s \"%s\"\n", Key_KeynumToString(i), keys[i].binding); } } } /* ============ Key_AltBindlist_f ============ */ void Key_AltBindlist_f(void) { int i; for (i = 0; i < MAX_KEYS; i++) { if (altkeys[i].binding && altkeys[i].binding[0]) { Com_Printf("%s \"%s\"\n", Key_KeynumToString(i), altkeys[i].binding); } } } /* ============ Key_CtrlBindlist_f ============ */ void Key_CtrlBindlist_f(void) { int i; for (i = 0; i < MAX_KEYS; i++) { if (ctrlkeys[i].binding && ctrlkeys[i].binding[0]) { Com_Printf("%s \"%s\"\n", Key_KeynumToString(i), ctrlkeys[i].binding); } } } /* ============ Key_KeynameCompletion ============ */ void Key_KeynameCompletion(void (*callback)(const char *s)) { int i; for (i = 0; keynames[i].name != NULL; i++) { callback(keynames[i].name); } } /* =================== CL_InitKeyCommands =================== */ void CL_InitKeyCommands(void) { int i; // register our functions Cmd_AddCommand("bind", Key_Bind_f); Cmd_AddCommand("unbind", Key_Unbind_f); Cmd_AddCommand("altbind", Key_AltBind_f); Cmd_AddCommand("unaltbind", Key_Unaltbind_f); Cmd_AddCommand("ctrlbind", Key_CtrlBind_f); Cmd_AddCommand("unctrlbind", Key_Unctrlbind_f); Cmd_AddCommand("unbindall", Key_Unbindall_f); Cmd_AddCommand("bindlist", Key_Bindlist_f); Cmd_AddCommand("altbindlist", Key_AltBindlist_f); Cmd_AddCommand("ctrlbindlist", Key_AltBindlist_f); memset(menubound, 0, sizeof(menubound)); for (i = K_F1; i <= K_F15; i++) { menubound[i] = qtrue; } } /* =================== CL_AddKeyUpCommands =================== */ void CL_AddKeyUpCommands(int key, char *kb, unsigned time) { int i; char button[1024], *buttonPtr; char cmd[1024]; qboolean keyevent; if (!kb) { return; } keyevent = qfalse; buttonPtr = button; for (i = 0;; i++) { if (kb[i] == ';' || !kb[i]) { *buttonPtr = '\0'; if (button[0] == '+') { // button commands add keynum and time as parms so that multiple // sources can be discriminated and subframe corrected Com_sprintf(cmd, sizeof(cmd), "-%s %i %i\n", button + 1, key, time); Cbuf_AddText(cmd); keyevent = qtrue; } else { if (keyevent) { // down-only command Cbuf_AddText(button); Cbuf_AddText("\n"); } } buttonPtr = button; while ((kb[i] <= ' ' || kb[i] == ';') && kb[i] != 0) { i++; } } *buttonPtr++ = kb[i]; if (!kb[i]) { break; } } } static int last_escape_time; /* =================== CL_KeyEvent Called by the system for both key up and key down events =================== */ void CL_KeyEvent(int key, qboolean down, unsigned time) { char *kb; char cmd[1024]; // update auto-repeat status and BUTTON_ANY status keys[key].down = down; if (down) { keys[key].repeats++; if (keys[key].repeats == 1) { anykeydown++; } } else { keys[key].repeats = 0; anykeydown--; if (anykeydown < 0) { anykeydown = 0; } } if ((key == K_ALT || key == K_INS) && cl_altbindings->integer) { alt_down = down; } if ((key == K_CTRL || key == K_DEL) && cl_ctrlbindings->integer) { ctrl_down = down; } if (down && !CL_FinishedIntro()) { UI_StartStageKeyEvent(); return; } if (clc.state > CA_DISCONNECTED && cge && !Key_GetCatcher() && cge->CG_CheckCaptureKey(key, down, time) && key != K_ESCAPE) { if (key != K_CONSOLE && (!keys[K_SHIFT].down || key != K_ESCAPE)) { return; } } if (key == K_CONSOLE || (keys[K_SHIFT].down && key == K_ESCAPE)) { if (!down) { return; } if (Cvar_VariableIntegerValue("ui_console")) { UI_ToggleConsole(); } else if (clc.state == CA_ACTIVE) { if (cg_gametype->integer) { UI_ToggleDMConsole(300); } } return; } if (down) { if (alt_down && key != K_ALT && key != K_INS) { altkeys[key].down = down; } if (down && ctrl_down && key != K_CTRL) { ctrlkeys[key].down = down; } } if (key != K_DEL) { ctrlkeys[key].down = down; } if (key == K_ENTER) { if (down) { if (keys[K_ALT].down) { Key_ClearStates(); Cvar_SetValue("r_fullscreen", !Cvar_VariableIntegerValue("r_fullscreen")); return; } } } if (key >= K_MOUSE1 && key <= K_MOUSE5) { if (down) { cl.mouseButtons |= (1 << (key - K_MOUSE1)); } else { cl.mouseButtons &= ~(1 << (key - K_MOUSE1)); } if (in_guimouse) { return; } } // keys can still be used for bound actions if (down && (key <= K_BACKSPACE || key == K_MOUSE4) && (clc.demoplaying || clc.state == CA_CINEMATIC) && Key_GetCatcher() == 0) { Cvar_Set("nextdemo", ""); key = K_ESCAPE; } // escape is always handled special if (key == K_ESCAPE) { if (down) { qboolean wasup = UI_MenuUp(); UI_DeactiveFloatingWindows(); if (clc.state == CA_CINEMATIC) { SCR_StopCinematic(); return; } if (cls.realFrametime - last_escape_time <= 1000) { UI_MenuEscape("main"); return; } if (wasup) { UI_MenuEscape("main"); return; } if (cl.snap.ps.stats[STAT_CINEMATIC] & 1 && clc.state == CA_ACTIVE) { Cbuf_AddText("skipcinematic\n"); return; } if (clc.state <= CA_DISCONNECTED || clc.state == CA_ACTIVE) { UI_MenuEscape("main"); } return; } } else if (down) { if ((Key_GetCatcher() & KEYCATCH_UI && !menubound[key]) || UI_BindActive()) { UI_KeyEvent(key, time); } else if (cls.loading & KEYCATCH_MESSAGE) { Message_Key(key); } else if (clc.state != CA_DISCONNECTED || menubound[key]) { // send the bound action kb = altkeys[key].binding; if (!kb || !altkeys[key].down) { kb = ctrlkeys[key].binding; if (!kb || !ctrlkeys[key].down) { kb = keys[key].binding; } } if (!kb) { if (key >= 200) { Com_Printf("%s is unbound, use controls menu to set.\n", Key_KeynumToString(key)); } } else if (kb[0] == '+') { int i; char button[1024], *buttonPtr; buttonPtr = button; for (i = 0;; i++) { if (kb[i] == ';' || !kb[i]) { *buttonPtr = '\0'; if (button[0] == '+') { // button commands add keynum and time as parms so that multiple // sources can be discriminated and subframe corrected Com_sprintf(cmd, sizeof(cmd), "%s %i %i\n", button, key, time); Cbuf_AddText(cmd); } else { // down-only command Cbuf_AddText(button); Cbuf_AddText("\n"); } buttonPtr = button; while ((kb[i] <= ' ' || kb[i] == ';') && kb[i] != 0) { i++; } } *buttonPtr++ = kb[i]; if (!kb[i]) { break; } } } else { // down-only command Cbuf_AddText(kb); Cbuf_AddText("\n"); } } return; } if (altkeys[key].down) { kb = altkeys[key].binding; altkeys[key].down = false; CL_AddKeyUpCommands(key, kb, time); } if (ctrlkeys[key].down) { kb = ctrlkeys[key].binding; ctrlkeys[key].down = false; CL_AddKeyUpCommands(key, kb, time); } CL_AddKeyUpCommands(key, keys[key].binding, time); } /* =================== CL_Key_ForceCommand =================== */ void CL_Key_ForceCommand(int key, unsigned int time) { const char *kb; char cmd[1024]; if (clc.state == CA_DISCONNECTED) { return; } kb = keys[key].binding; if (kb) { if (*kb == '+') { Com_sprintf(cmd, sizeof(cmd), "%s %i %i\n", kb, key, time); } else { Cbuf_AddText(keys[key].binding); Cbuf_AddText("\n"); } } else if (key > K_JOY12) { Com_Printf("%s is unbound, use controls menu to set.\n", Key_KeynumToString(key)); } } /* =================== CL_CharEvent Normal keyboard characters, already shifted / capslocked / etc =================== */ void CL_CharEvent(int key) { // the console key should never be used as a char if (key == '`' || key == '~') { return; } // delete is not a printable character and is // otherwise handled by Field_KeyDownEvent if (key == 127) { return; } // distribute the key down event to the apropriate handler if (Key_GetCatcher() & KEYCATCH_CONSOLE) { return; } if (Key_GetCatcher() & KEYCATCH_UI) { UI_CharEvent(key); } } /* =================== Key_ClearStates =================== */ void Key_ClearStates(void) { int i; anykeydown = 0; for (i = 0; i < MAX_KEYS; i++) { if (keys[i].down) { CL_KeyEvent(i, qfalse, 0); } keys[i].down = 0; keys[i].repeats = 0; } } static int keyCatchers = 0; /* ==================== Key_GetCatcher ==================== */ int Key_GetCatcher(void) { return cls.keyCatchers; } /* ==================== Key_SetCatcher ==================== */ void Key_SetCatcher(int catcher) { cls.keyCatchers = catcher; }