cl_keys.c

Go to the documentation of this file.
00001 
00008 /*
00009 All original material Copyright (C) 2002-2010 UFO: Alien Invasion.
00010 
00011 Original file from Quake 2 v3.21: quake2-2.31/client/keys.c
00012 Copyright (C) 1997-2001 Id Software, Inc.
00013 
00014 This program is free software; you can redistribute it and/or
00015 modify it under the terms of the GNU General Public License
00016 as published by the Free Software Foundation; either version 2
00017 of the License, or (at your option) any later version.
00018 
00019 This program is distributed in the hope that it will be useful,
00020 but WITHOUT ANY WARRANTY; without even the implied warranty of
00021 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00022 
00023 See the GNU General Public License for more details.
00024 
00025 You should have received a copy of the GNU General Public License
00026 along with this program; if not, write to the Free Software
00027 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00028 
00029 */
00030 
00031 #include "../client.h"
00032 #include "../cl_screen.h"
00033 #include "../cl_console.h"
00034 #include "../ui/ui_input.h"
00035 #include "../ui/ui_nodes.h"
00036 #include "../../shared/utf8.h"
00037 
00038 char keyLines[MAXKEYLINES][MAXCMDLINE];
00039 int keyLinePos;
00040 
00041 static int keyInsert = 1;
00042 
00043 int editLine = 0;
00044 int historyLine = 0;
00045 
00046 int msgMode;
00047 char msgBuffer[MAXCMDLINE];
00048 size_t msgBufferLen = 0;
00049 
00064 char *keyBindings[K_KEY_SIZE];
00065 char *menuKeyBindings[K_KEY_SIZE];
00066 char *battleKeyBindings[K_KEY_SIZE];
00067 
00068 static qboolean keyDown[K_KEY_SIZE];
00069 
00070 typedef struct {
00071     const char *name;
00072     int keynum;
00073 } keyName_t;
00074 
00075 #define M(x) {#x, K_##x}
00076 static const keyName_t keyNames[] = {
00077     M(TAB),
00078     M(ENTER),
00079     M(ESCAPE),
00080     M(SPACE),
00081     M(BACKSPACE),
00082     M(UPARROW),
00083     M(DOWNARROW),
00084     M(LEFTARROW),
00085     M(RIGHTARROW),
00086 
00087     M(ALT),
00088     M(CTRL),
00089     M(SHIFT),
00090 
00091     M(F1),
00092     M(F2),
00093     M(F3),
00094     M(F4),
00095     M(F5),
00096     M(F6),
00097     M(F7),
00098     M(F8),
00099     M(F9),
00100     M(F10),
00101     M(F11),
00102     M(F12),
00103 
00104     M(INS),
00105     M(DEL),
00106     M(PGDN),
00107     M(PGUP),
00108     M(HOME),
00109     M(END),
00110 
00111     M(MOUSE1),
00112     M(MOUSE2),
00113     M(MOUSE3),
00114     M(MOUSE4),
00115     M(MOUSE5),
00116 
00117     M(AUX1),
00118     M(AUX2),
00119     M(AUX3),
00120     M(AUX4),
00121     M(AUX5),
00122     M(AUX6),
00123     M(AUX7),
00124     M(AUX8),
00125     M(AUX9),
00126     M(AUX10),
00127     M(AUX11),
00128     M(AUX12),
00129     M(AUX13),
00130     M(AUX14),
00131     M(AUX15),
00132     M(AUX16),
00133 
00134     M(APPS),
00135 
00136     M(KP_HOME),
00137     M(KP_UPARROW),
00138     M(KP_PGUP),
00139     M(KP_LEFTARROW),
00140     M(KP_5),
00141     M(KP_RIGHTARROW),
00142     M(KP_END),
00143     M(KP_DOWNARROW),
00144     M(KP_PGDN),
00145     M(KP_ENTER),
00146     M(KP_INS),
00147     M(KP_DEL),
00148     M(KP_SLASH),
00149     M(KP_MINUS),
00150     M(KP_PLUS),
00151 
00152     M(MWHEELUP),
00153     M(MWHEELDOWN),
00154 
00155     M(JOY1),
00156     M(JOY2),
00157     M(JOY3),
00158     M(JOY4),
00159     M(JOY5),
00160     M(JOY6),
00161     M(JOY7),
00162     M(JOY8),
00163     M(JOY9),
00164     M(JOY10),
00165     M(JOY11),
00166     M(JOY12),
00167     M(JOY13),
00168     M(JOY14),
00169     M(JOY15),
00170     M(JOY16),
00171     M(JOY17),
00172     M(JOY18),
00173     M(JOY19),
00174     M(JOY20),
00175     M(JOY21),
00176     M(JOY22),
00177     M(JOY23),
00178     M(JOY24),
00179     M(JOY25),
00180     M(JOY26),
00181     M(JOY27),
00182     M(JOY28),
00183     M(JOY29),
00184     M(JOY30),
00185     M(JOY31),
00186     M(JOY32),
00187 
00188     M(PAUSE),
00189 
00190     {"SEMICOLON", ';'},         /* because a raw semicolon seperates commands */
00191 
00192     M(SUPER),
00193     M(COMPOSE),
00194     M(MODE),
00195     M(HELP),
00196     M(PRINT),
00197     M(SYSREQ),
00198     M(SCROLLOCK),
00199     M(BREAK),
00200     M(MENU),
00201     M(POWER),
00202     M(EURO),
00203     M(UNDO),
00204 
00205     {NULL, 0}
00206 };
00207 
00208 /*
00209 ==============================================================================
00210 LINE TYPING INTO THE CONSOLE
00211 ==============================================================================
00212 */
00213 
00219 static void Key_Console (int key, int unicode)
00220 {
00221     int i;
00222 
00223     if (keyDown[K_CTRL]) {
00224         switch (toupper(key)) {
00225         /* ctrl-L clears screen */
00226         case 'L':
00227             Cbuf_AddText("clear\n");
00228             return;
00229         /* jump to beginning of line */
00230         case 'A':
00231             keyLinePos = 1;
00232             return;
00233         /* end of line */
00234         case 'E':
00235             keyLinePos = strlen(keyLines[editLine]);
00236             return;
00237         }
00238     }
00239 
00240     if (key == K_ENTER || key == K_KP_ENTER) {  /* backslash text are commands, else chat */
00241         if (keyLines[editLine][1] == '\\' || keyLines[editLine][1] == '/')
00242             Cbuf_AddText(keyLines[editLine] + 2);   /* skip the > */
00243         /* no command - just enter */
00244         else if (!keyLines[editLine][1])
00245             return;
00246         else
00247             Cbuf_AddText(keyLines[editLine] + 1);   /* valid command */
00248 
00249         Cbuf_AddText("\n");
00250         Com_Printf("%s\n", keyLines[editLine] + 1);
00251         editLine = (editLine + 1) & (MAXKEYLINES - 1);
00252         historyLine = editLine;
00253         keyLines[editLine][0] = CONSOLE_PROMPT_CHAR | CONSOLE_COLORED_TEXT_MASK;
00254         /* maybe MAXKEYLINES was reached - we don't want to spawn 'random' strings
00255          * from history buffer in our console */
00256         keyLines[editLine][1] = '\0';
00257         keyLinePos = 1;
00258 
00259         /* force an update, because the command may take some time */
00260         if (cls.state == ca_disconnected)
00261             SCR_UpdateScreen();
00262 
00263         return;
00264     }
00265 
00266     /* command completion */
00267     if (key == K_TAB) {
00268         Com_ConsoleCompleteCommand(&keyLines[editLine][1], keyLines[editLine], MAXCMDLINE, &keyLinePos, 1);
00269         return;
00270     }
00271 
00272     if (key == K_BACKSPACE || (key == 'h' && keyDown[K_CTRL])) {
00273         if (keyLinePos > 1) {
00274             strcpy(keyLines[editLine] + keyLinePos - 1, keyLines[editLine] + keyLinePos);
00275             keyLinePos--;
00276         }
00277         return;
00278     }
00279     /* delete char on cursor */
00280     if (key == K_DEL) {
00281         if (keyLinePos < strlen(keyLines[editLine]))
00282             strcpy(keyLines[editLine] + keyLinePos, keyLines[editLine] + keyLinePos + 1);
00283         return;
00284     }
00285 
00286     if (key == K_UPARROW || key == K_KP_UPARROW || (tolower(key) == 'p' && keyDown[K_CTRL])) {
00287         do {
00288             historyLine = (historyLine - 1) & (MAXKEYLINES - 1);
00289         } while (historyLine != editLine && !keyLines[historyLine][1]);
00290 
00291         if (historyLine == editLine)
00292             historyLine = (editLine + 1) & (MAXKEYLINES - 1);
00293 
00294         Q_strncpyz(keyLines[editLine], keyLines[historyLine], MAXCMDLINE);
00295         keyLinePos = strlen(keyLines[editLine]);
00296         return;
00297     } else if (key == K_DOWNARROW || key == K_KP_DOWNARROW || (tolower(key) == 'n' && keyDown[K_CTRL])) {
00298         if (historyLine == editLine)
00299             return;
00300         do {
00301             historyLine = (historyLine + 1) & (MAXKEYLINES - 1);
00302         } while (historyLine != editLine && !keyLines[historyLine][1]);
00303 
00304         if (historyLine == editLine) {
00305             keyLines[editLine][0] = CONSOLE_PROMPT_CHAR | CONSOLE_COLORED_TEXT_MASK;
00306             /* fresh edit line */
00307             keyLines[editLine][1] = '\0';
00308             keyLinePos = 1;
00309         } else {
00310             Q_strncpyz(keyLines[editLine], keyLines[historyLine], MAXCMDLINE);
00311             keyLinePos = strlen(keyLines[editLine]);
00312         }
00313         return;
00314     }
00315 
00316     if (key == K_LEFTARROW) {  /* move cursor left */
00317         if (keyDown[K_CTRL]) { /* by a whole word */
00318             while (keyLinePos > 1 && keyLines[editLine][keyLinePos - 1] == ' ')
00319                 keyLinePos--;  /* get off current word */
00320             while (keyLinePos > 1 && keyLines[editLine][keyLinePos - 1] != ' ')
00321                 keyLinePos--;  /* and behind previous word */
00322             return;
00323         }
00324 
00325         if (keyLinePos > 1)  /* or just a char */
00326             keyLinePos--;
00327         return;
00328     } else if (key == K_RIGHTARROW) {  /* move cursor right */
00329         if ((i = strlen(keyLines[editLine])) == keyLinePos)
00330             return; /* no character to get */
00331         if (keyDown[K_CTRL]) {  /* by a whole word */
00332             while (keyLinePos < i && keyLines[editLine][keyLinePos + 1] == ' ')
00333                 keyLinePos++;  /* get off current word */
00334             while (keyLinePos < i && keyLines[editLine][keyLinePos + 1] != ' ')
00335                 keyLinePos++;  /* and in front of next word */
00336             if (keyLinePos < i)  /* all the way in front */
00337                 keyLinePos++;
00338             return;
00339         }
00340         keyLinePos++;  /* or just a char */
00341         return;
00342     }
00343 
00344     /* toggle insert mode */
00345     if (key == K_INS) {
00346         keyInsert ^= 1;
00347         return;
00348     }
00349 
00350     if (key == K_PGUP || key == K_KP_PGUP || key == K_MWHEELUP) {
00351         Con_Scroll(-2);
00352         return;
00353     }
00354 
00355     if (key == K_PGDN || key == K_KP_PGDN || key == K_MWHEELDOWN) {
00356         Con_Scroll(2);
00357         return;
00358     }
00359 
00360     if (key == K_HOME || key == K_KP_HOME) {
00361         keyLinePos = 1;
00362         return;
00363     }
00364 
00365     if (key == K_END || key == K_KP_END) {
00366         keyLinePos = strlen(keyLines[editLine]);
00367         return;
00368     }
00369 
00370     switch (key) {
00371     case K_KP_SLASH:
00372         key = '/';
00373         break;
00374     case K_KP_MINUS:
00375         key = '-';
00376         break;
00377     case K_KP_PLUS:
00378         key = '+';
00379         break;
00380     case K_KP_HOME:
00381         key = '7';
00382         break;
00383     case K_KP_UPARROW:
00384         key = '8';
00385         break;
00386     case K_KP_PGUP:
00387         key = '9';
00388         break;
00389     case K_KP_LEFTARROW:
00390         key = '4';
00391         break;
00392     case K_KP_5:
00393         key = '5';
00394         break;
00395     case K_KP_RIGHTARROW:
00396         key = '6';
00397         break;
00398     case K_KP_END:
00399         key = '1';
00400         break;
00401     case K_KP_DOWNARROW:
00402         key = '2';
00403         break;
00404     case K_KP_PGDN:
00405         key = '3';
00406         break;
00407     case K_KP_INS:
00408         key = '0';
00409         break;
00410     case K_KP_DEL:
00411         key = '.';
00412         break;
00413     default:
00414         key = unicode;
00415         break;
00416     }
00417 
00419     if (key < 32 || key > 127)
00420         return;                 /* non printable */
00421 
00422     if (keyLinePos < MAXCMDLINE - 1) {
00423         if (keyInsert) {  /* can't do strcpy to move string to right */
00424             i = strlen(keyLines[editLine]) - 1;
00425 
00426             if (i == MAXCMDLINE - 2)
00427                 i--;
00428             for (; i >= keyLinePos; i--)
00429                 keyLines[editLine][i + 1] = keyLines[editLine][i];
00430         }
00431         i = keyLines[editLine][keyLinePos];
00432         keyLines[editLine][keyLinePos] = key;
00433         keyLinePos++;
00434         if (!i)  /* only null terminate if at the end */
00435             keyLines[editLine][keyLinePos] = 0;
00436     }
00437 }
00438 
00445 static void Key_Message (int key)
00446 {
00447     int utf8len;
00448 
00449     if (key == K_ENTER || key == K_KP_ENTER) {
00450         qboolean send = qtrue;
00451 
00452         switch (msgMode) {
00453         case MSG_SAY:
00454             if (msgBuffer[0])
00455                 Cbuf_AddText("say \"");
00456             else
00457                 send = qfalse;
00458             break;
00459         case MSG_SAY_TEAM:
00460             if (msgBuffer[0])
00461                 Cbuf_AddText("say_team \"");
00462             else
00463                 send = qfalse;
00464             break;
00465         default:
00466             Com_Printf("Invalid msg_mode\n");
00467             return;
00468         }
00469         if (send) {
00470             Com_DPrintf(DEBUG_CLIENT, "msg_buffer: %s\n", msgBuffer);
00471             Cbuf_AddText(msgBuffer);
00472             Cbuf_AddText("\"\n");
00473         }
00474 
00475         Key_SetDest(key_game);
00476         msgBufferLen = 0;
00477         msgBuffer[0] = 0;
00478         return;
00479     }
00480 
00481     if (key == K_ESCAPE) {
00482         if (cls.state != ca_active) {
00483             /* If connecting or loading a level, disconnect */
00484             if (cls.state != ca_disconnected)
00485                 Com_Error(ERR_DROP, "Disconnected from server");
00486         }
00487 
00488         Key_SetDest(key_game);
00489         msgBufferLen = 0;
00490         msgBuffer[0] = 0;
00491         return;
00492     }
00493 
00494     if (key == K_BACKSPACE) {
00495         if (msgBufferLen)
00496             msgBufferLen = UTF8_delete_char(msgBuffer, msgBufferLen - 1);
00497         return;
00498     }
00499 
00500     utf8len = UTF8_encoded_len(key);
00501 
00503     if (utf8len == 0 || key < 32 || (key >= 127 && key < 192))
00504         return;                 /* non printable */
00505 
00506     if (msgBufferLen + utf8len >= sizeof(msgBuffer))
00507         return;                 /* all full */
00508 
00509     msgBufferLen += UTF8_insert_char(msgBuffer, sizeof(msgBuffer),  msgBufferLen, key);
00510 }
00511 
00512 
00521 static int Key_StringToKeynum (const char *str)
00522 {
00523     const keyName_t *kn;
00524 
00525     if (!str || str[0] == '\0')
00526         return -1;
00527 
00528     /* single char? */
00529     if (str[1] == '\0')
00530         return str[0];
00531 
00532     for (kn = keyNames; kn->name; kn++) {
00533         if (!Q_strcasecmp(str, kn->name))
00534             return kn->keynum;
00535     }
00536     return -1;
00537 }
00538 
00546 const char *Key_KeynumToString (int keynum)
00547 {
00548     const keyName_t *kn;
00549     static char tinystr[2];
00550 
00551     if (keynum == -1)
00552         return "<KEY NOT FOUND>";
00554     if (keynum > 32 && keynum < 127) {  /* printable ascii */
00555         tinystr[0] = keynum;
00556         tinystr[1] = 0;
00557         return tinystr;
00558     }
00559 
00560     for (kn = keyNames; kn->name; kn++)
00561         if (keynum == kn->keynum)
00562             return kn->name;
00563 
00564     return "<UNKNOWN KEYNUM>";
00565 }
00566 
00573 const char* Key_GetBinding (const char *binding, keyBindSpace_t space)
00574 {
00575     int i;
00576     char **keySpace = NULL;
00577 
00578     switch (space) {
00579     case KEYSPACE_UI:
00580         keySpace = menuKeyBindings;
00581         break;
00582     case KEYSPACE_GAME:
00583         keySpace = keyBindings;
00584         break;
00585     case KEYSPACE_BATTLE:
00586         keySpace = battleKeyBindings;
00587         break;
00588     default:
00589         Sys_Error("Unknown key space (%i) given", space);
00590     }
00591 
00592     for (i = K_FIRST_KEY; i < K_LAST_KEY; i++)
00593         if (keySpace[i] && *keySpace[i] && !strncmp(keySpace[i], binding, strlen(binding))) {
00594             return Key_KeynumToString(i);
00595         }
00596 
00597     /* not found */
00598     return "";
00599 }
00600 
00610 void Key_SetBinding (int keynum, const char *binding, keyBindSpace_t space)
00611 {
00612     char **keySpace = NULL;
00613 
00614     if (keynum == -1 || keynum >= K_KEY_SIZE)
00615         return;
00616 
00617     Com_DPrintf(DEBUG_CLIENT, "Binding for '%s' for space ", binding);
00618     switch (space) {
00619     case KEYSPACE_UI:
00620         keySpace = &menuKeyBindings[keynum];
00621         Com_DPrintf(DEBUG_CLIENT, "menu\n");
00622         break;
00623     case KEYSPACE_GAME:
00624         keySpace = &keyBindings[keynum];
00625         Com_DPrintf(DEBUG_CLIENT, "game\n");
00626         break;
00627     case KEYSPACE_BATTLE:
00628         keySpace = &battleKeyBindings[keynum];
00629         Com_DPrintf(DEBUG_CLIENT, "battle\n");
00630         break;
00631     default:
00632         Com_DPrintf(DEBUG_CLIENT, "failure\n");
00633         return;
00634     }
00635 
00636     /* free old bindings */
00637     if (*keySpace) {
00638         Mem_Free(*keySpace);
00639         *keySpace = NULL;
00640     }
00641 
00642     /* allocate memory for new binding, but don't set empty commands*/
00643     if (binding)
00644         *keySpace = Mem_PoolStrDup(binding, com_genericPool, 0);
00645 }
00646 
00651 static void Key_Unbind_f (void)
00652 {
00653     int b;
00654 
00655     if (Cmd_Argc() != 2) {
00656         Com_Printf("Usage: %s <key> : remove commands from a key\n", Cmd_Argv(0));
00657         return;
00658     }
00659 
00660     b = Key_StringToKeynum(Cmd_Argv(1));
00661     if (b == -1) {
00662         Com_Printf("\"%s\" isn't a valid key\n", Cmd_Argv(1));
00663         return;
00664     }
00665 
00666     if (!strcmp(Cmd_Argv(0), "unbindmenu"))
00667         Key_SetBinding(b, "", KEYSPACE_UI);
00668     else if (!strcmp(Cmd_Argv(0), "unbindbattle"))
00669         Key_SetBinding(b, "", KEYSPACE_BATTLE);
00670     else
00671         Key_SetBinding(b, "", KEYSPACE_GAME);
00672 }
00673 
00678 static void Key_Unbindall_f (void)
00679 {
00680     int i;
00681 
00682     for (i = K_FIRST_KEY; i < K_LAST_KEY; i++)
00683         if (keyBindings[i]) {
00684             if (!strcmp(Cmd_Argv(0), "unbindallmenu"))
00685                 Key_SetBinding(i, "", KEYSPACE_UI);
00686             else
00687                 Key_SetBinding(i, "", KEYSPACE_GAME);
00688         }
00689 }
00690 
00695 static void Key_Bind_f (void)
00696 {
00697     int i, c, b;
00698     char cmd[1024];
00699 
00700     c = Cmd_Argc();
00701 
00702     if (c < 2) {
00703         Com_Printf("Usage: %s <key> [command] : attach a command to a key\n", Cmd_Argv(0));
00704         return;
00705     }
00706     b = Key_StringToKeynum(Cmd_Argv(1));
00707     if (b == -1) {
00708         Com_Printf("\"%s\" isn't a valid key\n", Cmd_Argv(1));
00709         return;
00710     }
00711 
00712     if (c == 2) {
00713         if (keyBindings[b])
00714             Com_Printf("\"%s\" = \"%s\"\n", Cmd_Argv(1), keyBindings[b]);
00715         else
00716             Com_Printf("\"%s\" is not bound\n", Cmd_Argv(1));
00717         return;
00718     }
00719 
00720     /* copy the rest of the command line */
00721     cmd[0] = '\0';                  /* start out with a null string */
00722     for (i = 2; i < c; i++) {
00723         Q_strcat(cmd, Cmd_Argv(i), sizeof(cmd));
00724         if (i != (c - 1))
00725             Q_strcat(cmd, " ", sizeof(cmd));
00726     }
00727 
00728     if (!strcmp(Cmd_Argv(0), "bindui"))
00729         UI_SetKeyBinding(cmd, b);
00730     else if (!strcmp(Cmd_Argv(0), "bindmenu"))
00731         Key_SetBinding(b, cmd, KEYSPACE_UI);
00732     else if (!strcmp(Cmd_Argv(0), "bindbattle"))
00733         Key_SetBinding(b, cmd, KEYSPACE_BATTLE);
00734     else
00735         Key_SetBinding(b, cmd, KEYSPACE_GAME);
00736 }
00737 
00743 void Key_WriteBindings (const char* filename)
00744 {
00745     int i;
00746     /* this gets true in case of an error */
00747     qboolean deleteFile = qfalse;
00748     qFILE f;
00749     int cnt = 0;
00750 
00751     memset(&f, 0, sizeof(f));
00752     FS_OpenFile(filename, &f, FILE_WRITE);
00753     if (!f.f) {
00754         Com_Printf("Couldn't write %s.\n", filename);
00755         return;
00756     }
00757 
00758     FS_Printf(&f, "// generated by ufo, do not modify\n");
00759     FS_Printf(&f, "// If you want to know the keyname of a specific key - set in_debug cvar to 1 and press the key\n");
00760     FS_Printf(&f, "unbindallmenu\n");
00761     FS_Printf(&f, "unbindall\n");
00762     FS_Printf(&f, "unbindallbattle\n");
00763     /* failfast, stops loop for first occurred error in fprintf */
00764     for (i = 0; i < K_LAST_KEY && !deleteFile; i++)
00765         if (menuKeyBindings[i] && menuKeyBindings[i][0]) {
00766             if (FS_Printf(&f, "bindmenu %s \"%s\"\n", Key_KeynumToString(i), menuKeyBindings[i]) < 0)
00767                 deleteFile = qtrue;
00768             cnt++;
00769         }
00770     for (i = 0; i < K_LAST_KEY && !deleteFile; i++)
00771         if (keyBindings[i] && keyBindings[i][0]) {
00772             if (FS_Printf(&f, "bind %s \"%s\"\n", Key_KeynumToString(i), keyBindings[i]) < 0)
00773                 deleteFile = qtrue;
00774             cnt++;
00775         }
00776     for (i = 0; i < K_LAST_KEY && !deleteFile; i++)
00777         if (battleKeyBindings[i] && battleKeyBindings[i][0]) {
00778             if (FS_Printf(&f, "bindbattle %s \"%s\"\n", Key_KeynumToString(i), battleKeyBindings[i]) < 0)
00779                 deleteFile = qtrue;
00780             cnt++;
00781         }
00782 
00783     for (i = 0; i < UI_GetKeyBindingCount(); i++) {
00784         const char *path;
00785         uiKeyBinding_t*binding = UI_GetKeyBindingByIndex (i);
00786         if (binding->node == NULL)
00787             continue;
00788         if (binding->property == NULL)
00789             path = va("%s", UI_GetPath(binding->node));
00790         else
00791             path = va("%s@%s", UI_GetPath(binding->node), binding->property->string);
00792 
00793         if (FS_Printf(&f, "bindui %s \"%s\"\n", Key_KeynumToString(binding->key), path) < 0)
00794             deleteFile = qtrue;
00795     }
00796 
00797     FS_CloseFile(&f);
00798     if (!deleteFile && cnt)
00799         Com_Printf("Wrote %s\n", filename);
00800     else
00801         /* error in writing the keys.cfg - remove the file again */
00802         FS_RemoveFile(va("%s/%s", FS_Gamedir(), filename));
00803 }
00804 
00808 static void Key_WriteBindings_f (void)
00809 {
00810     char filename[MAX_QPATH];
00811 
00812     if (Cmd_Argc() != 2) {
00813         Com_Printf("Usage: %s <filename>\n", Cmd_Argv(0));
00814         return;
00815     }
00816 
00817     Q_strncpyz(filename, Cmd_Argv(1), sizeof(filename));
00818     Com_DefaultExtension(filename, sizeof(filename), ".cfg");
00819     Key_WriteBindings(filename);
00820 }
00821 
00825 static void Key_Bindlist_f (void)
00826 {
00827     int i;
00828 
00829     Com_Printf("key space: game\n");
00830     for (i = K_FIRST_KEY; i < K_LAST_KEY; i++)
00831         if (keyBindings[i] && keyBindings[i][0])
00832             Com_Printf("- %s \"%s\"\n", Key_KeynumToString(i), keyBindings[i]);
00833     Com_Printf("key space: menu\n");
00834     for (i = K_FIRST_KEY; i < K_LAST_KEY; i++)
00835         if (menuKeyBindings[i] && menuKeyBindings[i][0])
00836             Com_Printf("- %s \"%s\"\n", Key_KeynumToString(i), menuKeyBindings[i]);
00837     Com_Printf("key space: battle\n");
00838     for (i = 0; i < K_LAST_KEY; i++)
00839         if (battleKeyBindings[i] && battleKeyBindings[i][0])
00840             Com_Printf("- %s \"%s\"\n", Key_KeynumToString(i), battleKeyBindings[i]);
00841 
00842 }
00843 
00844 static int Key_CompleteKeyName (const char *partial, const char **match)
00845 {
00846     int matches = 0;
00847     const char *localMatch[MAX_COMPLETE];
00848     size_t len;
00849     const keyName_t *kn;
00850 
00851     len = strlen(partial);
00852     if (!len) {
00853         for (kn = keyNames; kn->name; kn++) {
00854             Com_Printf("%s\n", kn->name);
00855         }
00856         return 0;
00857     }
00858 
00859     /* check for partial matches */
00860     for (kn = keyNames; kn->name; kn++) {
00861         if (!strncmp(partial, kn->name, len)) {
00862             Com_Printf("%s\n", kn->name);
00863             localMatch[matches++] = kn->name;
00864             if (matches >= MAX_COMPLETE)
00865                 break;
00866         }
00867     }
00868 
00869     return Cmd_GenericCompleteFunction(len, match, matches, localMatch);
00870 }
00871 
00872 void Key_Init (void)
00873 {
00874     int i;
00875 
00876     for (i = 0; i < MAXKEYLINES; i++) {
00877         keyLines[i][0] = CONSOLE_PROMPT_CHAR | CONSOLE_COLORED_TEXT_MASK;
00878         keyLines[i][1] = 0;
00879     }
00880     keyLinePos = 1;
00881 
00882     /* register our functions */
00883     Cmd_AddCommand("bindui", Key_Bind_f, "Bind a key to a ui node");
00884     Cmd_AddCommand("bindmenu", Key_Bind_f, "Bind a key to a console command - only executed when hovering a menu");
00885     Cmd_AddCommand("bind", Key_Bind_f, "Bind a key to a console command");
00886     Cmd_AddCommand("bindbattle", Key_Bind_f, "Bind a key to a console command - only executed when in battlescape");
00887     Cmd_AddCommand("unbindmenu", Key_Unbind_f, "Unbind a key");
00888     Cmd_AddCommand("unbind", Key_Unbind_f, "Unbind a key");
00889     Cmd_AddCommand("unbindbattle", Key_Unbind_f, "Unbind a key");
00890     Cmd_AddParamCompleteFunction("bind", Key_CompleteKeyName);
00891     Cmd_AddParamCompleteFunction("unbind", Key_CompleteKeyName);
00892     Cmd_AddParamCompleteFunction("bindmenu", Key_CompleteKeyName);
00893     Cmd_AddParamCompleteFunction("unbindmenu", Key_CompleteKeyName);
00894     Cmd_AddParamCompleteFunction("bindbattle", Key_CompleteKeyName);
00895     Cmd_AddParamCompleteFunction("unbindbattle", Key_CompleteKeyName);
00896     Cmd_AddCommand("unbindallmenu", Key_Unbindall_f, "Delete all key bindings for the menu");
00897     Cmd_AddCommand("unbindall", Key_Unbindall_f, "Delete all key bindings");
00898     Cmd_AddCommand("unbindallbattle", Key_Unbindall_f, "Delete all key bindings for battlescape");
00899     Cmd_AddCommand("bindlist", Key_Bindlist_f, "Show all bindings on the game console");
00900     Cmd_AddCommand("savebind", Key_WriteBindings_f, "Saves key bindings to keys.cfg");
00901 }
00902 
00907 void Key_SetDest (int keyDest)
00908 {
00909     cls.keyDest = keyDest;
00910     if (cls.keyDest == key_console) {
00911         /* make sure the menu no more capture inputs */
00912         UI_ReleaseInput();
00913     }
00914 }
00915 
00921 void Key_Event (unsigned int key, unsigned short unicode, qboolean down, unsigned time)
00922 {
00923     char cmd[MAX_STRING_CHARS];
00924 
00925     /* unbindable key */
00926     if (key >= K_KEY_SIZE)
00927         return;
00928 
00929     /* any key (except F1-F12) during the sequence mode will bring up the menu */
00930 
00931     if (cls.keyDest == key_game && down) {
00932         if (UI_KeyPressed(key, unicode))
00933             return;
00934     }
00935 
00936     /* menu key is hardcoded, so the user can never unbind it */
00937     if (key == K_ESCAPE) {
00938         if (!down)
00939             return;
00940 
00941         switch (cls.keyDest) {
00942         case key_message:
00943             Key_Message(unicode);
00944             break;
00945         case key_console:
00946             Con_ToggleConsole_f();
00947             break;
00948         default:
00949             Com_Error(ERR_FATAL, "Bad cls.key_dest");
00950         }
00951         return;
00952     }
00953 
00954     /* track if any key is down for BUTTON_ANY */
00955     keyDown[key] = down;
00956     if (!down) {
00957         int i;
00958         /* key up events only generate commands if the game key binding is
00959          * a button command (leading + sign).  These will occur even in console mode,
00960          * to keep the character from continuing an action started before a console
00961          * switch.  Button commands include the kenum as a parameter, so multiple
00962          * downs can be matched with ups */
00963         const char *kb = menuKeyBindings[key];
00964         /* this loop ensures, that every down event reaches it's proper kbutton_t */
00965         for (i = 0; i < 3; i++) {
00966             if (kb && kb[0] == '+') {
00967                 /* '-' means we have released the key
00968                  * the key number is used to determine whether the kbutton_t is really
00969                  * released or whether any other bound key will still ensure that the
00970                  * kbutton_t is pressed
00971                  * the time is the msec value when the key was released */
00972                 Com_sprintf(cmd, sizeof(cmd), "-%s %i %i\n", kb + 1, key, time);
00973                 Cbuf_AddText(cmd);
00974             }
00975             if (i == 0)
00976                 kb = keyBindings[key];
00977             else
00978                 kb = battleKeyBindings[key];
00979         }
00980         return;
00981     }
00982 
00983     /* if not a consolekey, send to the interpreter no matter what mode is */
00984     if (cls.keyDest == key_game || (key >= K_MOUSE1 && key <= K_MWHEELUP)) {
00985         /* Some keyboards need modifiers to access key values that are
00986          * present as bare keys on other keyboards. Smooth over the difference
00987          * here by using the translated value if there is a binding for it. */
00988         const char *kb = NULL;
00989         if (mouseSpace == MS_UI && unicode >= 32 && unicode < 127)
00990             kb = menuKeyBindings[unicode];
00991         if (!kb && mouseSpace == MS_UI)
00992             kb = menuKeyBindings[key];
00993         if (!kb && unicode >= 32 && unicode < 127)
00994             kb = keyBindings[unicode];
00995         if (!kb)
00996             kb = keyBindings[key];
00997         if (!kb && CL_OnBattlescape())
00998             kb = battleKeyBindings[key];
00999         if (kb) {
01000             if (kb[0] == '+') { /* button commands add keynum and time as a parm */
01001                 /* '+' means we have pressed the key
01002                  * the key number is used because the kbutton_t can be 'pressed' by several keys
01003                  * the time is the msec value when the key was pressed */
01004                 Com_sprintf(cmd, sizeof(cmd), "%s %i %i\n", kb, key, time);
01005                 Cbuf_AddText(cmd);
01006             } else {
01007                 Cbuf_AddText(kb);
01008                 Cbuf_AddText("\n");
01009             }
01010             if (cls.keyDest == key_game)
01011                 return;
01012         }
01013     }
01014 
01015     if (!down)
01016         return; /* other systems only care about key down events */
01017 
01018     switch (cls.keyDest) {
01019     case key_message:
01020         Key_Message(unicode);
01021         break;
01022     case key_game:
01023     case key_console:
01024         Key_Console(key, unicode);
01025         break;
01026     default:
01027         Com_Error(ERR_FATAL, "Bad cls.key_dest");
01028     }
01029 }

Generated by  doxygen 1.6.2