made darkplaces compile successfully with g++ to test for errors C doesn't care about...
[xonotic/darkplaces.git] / keys.c
1 /*
2         Copyright (C) 1996-1997  Id Software, Inc.
3
4         This program is free software; you can redistribute it and/or
5         modify it under the terms of the GNU General Public License
6         as published by the Free Software Foundation; either version 2
7         of the License, or (at your option) any later version.
8
9         This program is distributed in the hope that it will be useful,
10         but WITHOUT ANY WARRANTY; without even the implied warranty of
11         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13         See the GNU General Public License for more details.
14
15         You should have received a copy of the GNU General Public License
16         along with this program; if not, write to:
17
18                 Free Software Foundation, Inc.
19                 59 Temple Place - Suite 330
20                 Boston, MA  02111-1307, USA
21 */
22
23 #include "quakedef.h"
24
25
26 /*
27 key up events are sent even if in console mode
28 */
29
30 int                     edit_line = MAX_INPUTLINES-1;
31 int                     history_line = MAX_INPUTLINES-1;
32 char            key_lines[MAX_INPUTLINES][MAX_INPUTLINE];
33 int                     key_linepos;
34 qboolean        key_insert = true;      // insert key toggle (for editing)
35 keydest_t       key_dest;
36 int                     key_consoleactive;
37 char            *keybindings[MAX_BINDMAPS][MAX_KEYS];
38
39 static int      key_bmap, key_bmap2;
40 static qbyte keydown[MAX_KEYS]; // 0 = up, 1 = down, 2 = repeating
41
42 typedef struct {
43         const char      *name;
44         int                     keynum;
45 } keyname_t;
46
47 static const keyname_t   keynames[] = {
48         {"TAB", K_TAB},
49         {"ENTER", K_ENTER},
50         {"ESCAPE", K_ESCAPE},
51         {"SPACE", K_SPACE},
52         {"BACKSPACE", K_BACKSPACE},
53         {"UPARROW", K_UPARROW},
54         {"DOWNARROW", K_DOWNARROW},
55         {"LEFTARROW", K_LEFTARROW},
56         {"RIGHTARROW", K_RIGHTARROW},
57
58         {"ALT", K_ALT},
59         {"CTRL", K_CTRL},
60         {"SHIFT", K_SHIFT},
61
62         {"F1", K_F1},
63         {"F2", K_F2},
64         {"F3", K_F3},
65         {"F4", K_F4},
66         {"F5", K_F5},
67         {"F6", K_F6},
68         {"F7", K_F7},
69         {"F8", K_F8},
70         {"F9", K_F9},
71         {"F10", K_F10},
72         {"F11", K_F11},
73         {"F12", K_F12},
74
75         {"INS", K_INS},
76         {"DEL", K_DEL},
77         {"PGDN", K_PGDN},
78         {"PGUP", K_PGUP},
79         {"HOME", K_HOME},
80         {"END", K_END},
81
82         {"PAUSE", K_PAUSE},
83
84         {"MWHEELUP", K_MWHEELUP},
85         {"MWHEELDOWN", K_MWHEELDOWN},
86
87         {"MOUSE1", K_MOUSE1},
88         {"MOUSE2", K_MOUSE2},
89         {"MOUSE3", K_MOUSE3},
90         {"MOUSE4", K_MOUSE4},
91         {"MOUSE5", K_MOUSE5},
92         {"MOUSE6", K_MOUSE6},
93         {"MOUSE7", K_MOUSE7},
94         {"MOUSE8", K_MOUSE8},
95         {"MOUSE9", K_MOUSE9},
96         {"MOUSE10", K_MOUSE10},
97         {"MOUSE11", K_MOUSE11},
98         {"MOUSE12", K_MOUSE12},
99         {"MOUSE13", K_MOUSE13},
100         {"MOUSE14", K_MOUSE14},
101         {"MOUSE15", K_MOUSE15},
102         {"MOUSE16", K_MOUSE16},
103
104         {"NUMLOCK", K_NUMLOCK},
105         {"CAPSLOCK", K_CAPSLOCK},
106         {"SCROLLOCK", K_SCROLLOCK},
107
108         {"KP_HOME",                     K_KP_HOME },
109         {"KP_UPARROW",          K_KP_UPARROW },
110         {"KP_PGUP",                     K_KP_PGUP },
111         {"KP_LEFTARROW",        K_KP_LEFTARROW },
112         {"KP_RIGHTARROW",       K_KP_RIGHTARROW },
113         {"KP_END",                      K_KP_END },
114         {"KP_DOWNARROW",        K_KP_DOWNARROW },
115         {"KP_PGDN",                     K_KP_PGDN },
116         {"KP_INS",                      K_KP_INS },
117         {"KP_DEL",                      K_KP_DEL },
118         {"KP_SLASH",            K_KP_SLASH },
119
120         {"KP_0", K_KP_0},
121         {"KP_1", K_KP_1},
122         {"KP_2", K_KP_2},
123         {"KP_3", K_KP_3},
124         {"KP_4", K_KP_4},
125         {"KP_5", K_KP_5},
126         {"KP_6", K_KP_6},
127         {"KP_7", K_KP_7},
128         {"KP_8", K_KP_8},
129         {"KP_9", K_KP_9},
130         {"KP_PERIOD", K_KP_PERIOD},
131         {"KP_DIVIDE", K_KP_DIVIDE},
132         {"KP_MULTIPLY", K_KP_MULTIPLY},
133         {"KP_MINUS", K_KP_MINUS},
134         {"KP_PLUS", K_KP_PLUS},
135         {"KP_ENTER", K_KP_ENTER},
136         {"KP_EQUALS", K_KP_EQUALS},
137
138         {"JOY1",  K_JOY1},
139         {"JOY2",  K_JOY2},
140         {"JOY3",  K_JOY3},
141         {"JOY4",  K_JOY4},
142         {"JOY5",  K_JOY5},
143         {"JOY6",  K_JOY6},
144         {"JOY7",  K_JOY7},
145         {"JOY8",  K_JOY8},
146         {"JOY9",  K_JOY9},
147         {"JOY10", K_JOY10},
148         {"JOY11", K_JOY11},
149         {"JOY12", K_JOY12},
150         {"JOY13", K_JOY13},
151         {"JOY14", K_JOY14},
152         {"JOY15", K_JOY15},
153         {"JOY16", K_JOY16},
154
155         {"AUX1", K_AUX1},
156         {"AUX2", K_AUX2},
157         {"AUX3", K_AUX3},
158         {"AUX4", K_AUX4},
159         {"AUX5", K_AUX5},
160         {"AUX6", K_AUX6},
161         {"AUX7", K_AUX7},
162         {"AUX8", K_AUX8},
163         {"AUX9", K_AUX9},
164         {"AUX10", K_AUX10},
165         {"AUX11", K_AUX11},
166         {"AUX12", K_AUX12},
167         {"AUX13", K_AUX13},
168         {"AUX14", K_AUX14},
169         {"AUX15", K_AUX15},
170         {"AUX16", K_AUX16},
171         {"AUX17", K_AUX17},
172         {"AUX18", K_AUX18},
173         {"AUX19", K_AUX19},
174         {"AUX20", K_AUX20},
175         {"AUX21", K_AUX21},
176         {"AUX22", K_AUX22},
177         {"AUX23", K_AUX23},
178         {"AUX24", K_AUX24},
179         {"AUX25", K_AUX25},
180         {"AUX26", K_AUX26},
181         {"AUX27", K_AUX27},
182         {"AUX28", K_AUX28},
183         {"AUX29", K_AUX29},
184         {"AUX30", K_AUX30},
185         {"AUX31", K_AUX31},
186         {"AUX32", K_AUX32},
187
188         {"SEMICOLON", ';'},                     // because a raw semicolon seperates commands
189         {"TILDE", '~'},
190         {"BACKQUOTE", '`'},
191         {"QUOTE", '"'},
192         {"APOSTROPHE", '\''},
193
194         {NULL, 0}
195 };
196
197 /*
198 ==============================================================================
199
200                         LINE TYPING INTO THE CONSOLE
201
202 ==============================================================================
203 */
204
205 void
206 Key_ClearEditLine (int edit_line)
207 {
208         memset (key_lines[edit_line], '\0', sizeof(key_lines[edit_line]));
209         key_lines[edit_line][0] = ']';
210         key_linepos = 1;
211 }
212
213 /*
214 ====================
215 Interactive line editing and console scrollback
216 ====================
217 */
218 static void
219 Key_Console (int key, char ascii)
220 {
221         // LordHavoc: copied most of this from Q2 to improve keyboard handling
222         switch (key)
223         {
224         case K_KP_SLASH:
225                 key = '/';
226                 break;
227         case K_KP_MINUS:
228                 key = '-';
229                 break;
230         case K_KP_PLUS:
231                 key = '+';
232                 break;
233         case K_KP_HOME:
234                 key = '7';
235                 break;
236         case K_KP_UPARROW:
237                 key = '8';
238                 break;
239         case K_KP_PGUP:
240                 key = '9';
241                 break;
242         case K_KP_LEFTARROW:
243                 key = '4';
244                 break;
245         case K_KP_5:
246                 key = '5';
247                 break;
248         case K_KP_RIGHTARROW:
249                 key = '6';
250                 break;
251         case K_KP_END:
252                 key = '1';
253                 break;
254         case K_KP_DOWNARROW:
255                 key = '2';
256                 break;
257         case K_KP_PGDN:
258                 key = '3';
259                 break;
260         case K_KP_INS:
261                 key = '0';
262                 break;
263         case K_KP_DEL:
264                 key = '.';
265                 break;
266         }
267
268         if ((toupper(key) == 'V' && keydown[K_CTRL]) || ((key == K_INS || key == K_KP_INS) && keydown[K_SHIFT]))
269         {
270                 char *cbd;
271                 if ((cbd = Sys_GetClipboardData()) != 0)
272                 {
273                         int i;
274                         strtok(cbd, "\n\r\b");
275                         i = (int)strlen(cbd);
276                         if (i + key_linepos >= MAX_INPUTLINE)
277                                 i= MAX_INPUTLINE - key_linepos;
278                         if (i > 0)
279                         {
280                                 cbd[i]=0;
281                                 strcat(key_lines[edit_line], cbd);
282                                 key_linepos += i;
283                         }
284                         free(cbd);
285                 }
286                 return;
287         }
288
289         if (key == 'l')
290         {
291                 if (keydown[K_CTRL])
292                 {
293                         Cbuf_AddText ("clear\n");
294                         return;
295                 }
296         }
297
298         if (key == K_ENTER || key == K_KP_ENTER)
299         {
300                 Cbuf_AddText (key_lines[edit_line]+1);  // skip the ]
301                 Cbuf_AddText ("\n");
302                 Con_Printf("%s\n",key_lines[edit_line]);
303                 // LordHavoc: redesigned edit_line/history_line
304                 edit_line = 31;
305                 history_line = edit_line;
306                 memmove(key_lines[0], key_lines[1], sizeof(key_lines[0]) * edit_line);
307                 key_lines[edit_line][0] = ']';
308                 key_lines[edit_line][1] = 0;    // EvilTypeGuy: null terminate
309                 key_linepos = 1;
310                 // force an update, because the command may take some time
311                 if (cls.state == ca_disconnected)
312                         CL_UpdateScreen ();
313                 return;
314         }
315
316         if (key == K_TAB)
317         {
318                 // Enhanced command completion
319                 // by EvilTypeGuy eviltypeguy@qeradiant.com
320                 // Thanks to Fett, Taniwha
321                 Con_CompleteCommandLine();
322                 return;
323         }
324
325         // Advanced Console Editing by Radix radix@planetquake.com
326         // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
327
328         // left arrow will just move left one without erasing, backspace will
329         // actually erase charcter
330         if (key == K_LEFTARROW || key == K_KP_LEFTARROW)
331         {
332                 if (key_linepos > 1)
333                         key_linepos--;
334                 return;
335         }
336
337         // delete char before cursor
338         if (key == K_BACKSPACE || (key == 'h' && keydown[K_CTRL]))
339         {
340                 if (key_linepos > 1)
341                 {
342                         strcpy(key_lines[edit_line] + key_linepos - 1, key_lines[edit_line] + key_linepos);
343                         key_linepos--;
344                 }
345                 return;
346         }
347
348         // delete char on cursor
349         if (key == K_DEL || key == K_KP_DEL)
350         {
351                 if (key_linepos < (int)strlen(key_lines[edit_line]))
352                         strcpy(key_lines[edit_line] + key_linepos, key_lines[edit_line] + key_linepos + 1);
353                 return;
354         }
355
356
357         // if we're at the end, get one character from previous line,
358         // otherwise just go right one
359         if (key == K_RIGHTARROW || key == K_KP_RIGHTARROW)
360         {
361                 if (key_linepos < (int)strlen(key_lines[edit_line]))
362                         key_linepos++;
363
364                 return;
365         }
366
367         if (key == K_INS || key == K_KP_INS) // toggle insert mode
368         {
369                 key_insert ^= 1;
370                 return;
371         }
372
373         // End Advanced Console Editing
374
375         if (key == K_UPARROW || key == K_KP_UPARROW || (key == 'p' && keydown[K_CTRL]))
376         {
377                 if (history_line > 0 && key_lines[history_line-1][1])
378                 {
379                         history_line--;
380                         strcpy(key_lines[edit_line], key_lines[history_line]);
381                         key_linepos = (int)strlen(key_lines[edit_line]);
382                 }
383                 return;
384         }
385
386         if (key == K_DOWNARROW || key == K_KP_DOWNARROW || (key == 'n' && keydown[K_CTRL]))
387         {
388                 history_line++;
389                 if (history_line >= edit_line)
390                 {
391                         history_line = edit_line;
392                         key_lines[edit_line][0] = ']';
393                         key_lines[edit_line][1] = 0;
394                         key_linepos = 1;
395                 }
396                 else
397                 {
398                         strcpy(key_lines[edit_line], key_lines[history_line]);
399                         key_linepos = (int)strlen(key_lines[edit_line]);
400                 }
401                 return;
402         }
403
404         if (key == K_PGUP || key == K_KP_PGUP || key == K_MWHEELUP)
405         {
406                 con_backscroll += ((int) vid_conheight.integer >> 5);
407                 if (con_backscroll > con_totallines - (vid_conheight.integer>>3) - 1)
408                         con_backscroll = con_totallines - (vid_conheight.integer>>3) - 1;
409                 return;
410         }
411
412         if (key == K_PGDN || key == K_KP_PGDN || key == K_MWHEELDOWN)
413         {
414                 con_backscroll -= ((int) vid_conheight.integer >> 5);
415                 if (con_backscroll < 0)
416                         con_backscroll = 0;
417                 return;
418         }
419
420         if (key == K_HOME || key == K_KP_HOME)
421         {
422                 con_backscroll = con_totallines - (vid_conheight.integer>>3) - 1;
423                 return;
424         }
425
426         if (key == K_END || key == K_KP_END)
427         {
428                 con_backscroll = 0;
429                 return;
430         }
431
432         // non printable
433         if (ascii < 32)
434                 return;
435
436         if (key_linepos < MAX_INPUTLINE-1)
437         {
438                 int len;
439                 len = (int)strlen(&key_lines[edit_line][key_linepos]);
440                 // check insert mode, or always insert if at end of line
441                 if (key_insert || len == 0)
442                 {
443                         // can't use strcpy to move string to right
444                         len++;
445                         memmove(&key_lines[edit_line][key_linepos + 1], &key_lines[edit_line][key_linepos], len);
446                 }
447                 key_lines[edit_line][key_linepos] = ascii;
448                 key_linepos++;
449         }
450 }
451
452 //============================================================================
453
454 qboolean        chat_team;
455 char            chat_buffer[MAX_INPUTLINE];
456 unsigned int    chat_bufferlen = 0;
457
458 static void
459 Key_Message (int key, char ascii)
460 {
461
462         if (key == K_ENTER)
463         {
464                 Cmd_ForwardStringToServer(va("%s %s", chat_team ? "say_team" : "say ", chat_buffer));
465
466                 key_dest = key_game;
467                 chat_bufferlen = 0;
468                 chat_buffer[0] = 0;
469                 return;
470         }
471
472         if (key == K_ESCAPE) {
473                 key_dest = key_game;
474                 chat_bufferlen = 0;
475                 chat_buffer[0] = 0;
476                 return;
477         }
478
479         if (key == K_BACKSPACE) {
480                 if (chat_bufferlen) {
481                         chat_bufferlen--;
482                         chat_buffer[chat_bufferlen] = 0;
483                 }
484                 return;
485         }
486
487         if (chat_bufferlen == sizeof (chat_buffer) - 1)
488                 return;                                                 // all full
489
490         if (!ascii)
491                 return;                                                 // non printable
492
493         chat_buffer[chat_bufferlen++] = ascii;
494         chat_buffer[chat_bufferlen] = 0;
495 }
496
497 //============================================================================
498
499
500 /*
501 ===================
502 Returns a key number to be used to index keybindings[] by looking at
503 the given string.  Single ascii characters return themselves, while
504 the K_* names are matched up.
505 ===================
506 */
507 int
508 Key_StringToKeynum (const char *str)
509 {
510         const keyname_t  *kn;
511
512         if (!str || !str[0])
513                 return -1;
514         if (!str[1])
515                 return tolower(str[0]);
516
517         for (kn = keynames; kn->name; kn++) {
518                 if (!strcasecmp (str, kn->name))
519                         return kn->keynum;
520         }
521         return -1;
522 }
523
524 /*
525 ===================
526 Returns a string (either a single ascii char, or a K_* name) for the
527 given keynum.
528 FIXME: handle quote special (general escape sequence?)
529 ===================
530 */
531 const char *
532 Key_KeynumToString (int keynum)
533 {
534         const keyname_t  *kn;
535         static char tinystr[2];
536
537         if (keynum == -1)
538                 return "<KEY NOT FOUND>";
539         if (keynum > 32 && keynum < 127) {      // printable ascii
540                 tinystr[0] = keynum;
541                 tinystr[1] = 0;
542                 return tinystr;
543         }
544
545         for (kn = keynames; kn->name; kn++)
546                 if (keynum == kn->keynum)
547                         return kn->name;
548
549         return "<UNKNOWN KEYNUM>";
550 }
551
552
553 void
554 Key_SetBinding (int keynum, int bindmap, const char *binding)
555 {
556         char *newbinding;
557         size_t l;
558
559         if (keynum == -1)
560                 return;
561
562 // free old bindings
563         if (keybindings[bindmap][keynum]) {
564                 Z_Free (keybindings[bindmap][keynum]);
565                 keybindings[bindmap][keynum] = NULL;
566         }
567 // allocate memory for new binding
568         l = strlen (binding);
569         newbinding = (char *)Z_Malloc (l + 1);
570         strcpy (newbinding, binding);
571         newbinding[l] = 0;
572         keybindings[bindmap][keynum] = newbinding;
573 }
574
575 static void
576 Key_In_Unbind_f (void)
577 {
578         int         b, m;
579
580         if (Cmd_Argc () != 3) {
581                 Con_Print("in_unbind <bindmap> <key> : remove commands from a key\n");
582                 return;
583         }
584
585         m = strtol(Cmd_Argv (1), NULL, 0);
586         if ((m < 0) || (m >= 8)) {
587                 Con_Printf("%d isn't a valid bindmap\n", m);
588                 return;
589         }
590
591         b = Key_StringToKeynum (Cmd_Argv (2));
592         if (b == -1) {
593                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (2));
594                 return;
595         }
596
597         Key_SetBinding (b, m, "");
598 }
599
600 static void
601 Key_In_Bind_f (void)
602 {
603         int         i, c, b, m;
604         char        cmd[1024];
605
606         c = Cmd_Argc ();
607
608         if (c != 3 && c != 4) {
609                 Con_Print("in_bind <bindmap> <key> [command] : attach a command to a key\n");
610                 return;
611         }
612
613         m = strtol(Cmd_Argv (1), NULL, 0);
614         if ((m < 0) || (m >= 8)) {
615                 Con_Printf("%d isn't a valid bindmap\n", m);
616                 return;
617         }
618
619         b = Key_StringToKeynum (Cmd_Argv (2));
620         if (b == -1) {
621                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (2));
622                 return;
623         }
624
625         if (c == 3) {
626                 if (keybindings[m][b])
627                         Con_Printf("\"%s\" = \"%s\"\n", Cmd_Argv (2), keybindings[m][b]);
628                 else
629                         Con_Printf("\"%s\" is not bound\n", Cmd_Argv (2));
630                 return;
631         }
632 // copy the rest of the command line
633         cmd[0] = 0;                                                     // start out with a null string
634         for (i = 3; i < c; i++) {
635                 strlcat (cmd, Cmd_Argv (i), sizeof (cmd));
636                 if (i != (c - 1))
637                         strlcat (cmd, " ", sizeof (cmd));
638         }
639
640         Key_SetBinding (b, m, cmd);
641 }
642
643 static void
644 Key_In_Bindmap_f (void)
645 {
646         int         m1, m2, c;
647
648         c = Cmd_Argc ();
649
650         if (c != 3) {
651                 Con_Print("in_bindmap <bindmap> <fallback>: set current bindmap and fallback\n");
652                 return;
653         }
654
655         m1 = strtol(Cmd_Argv (1), NULL, 0);
656         if ((m1 < 0) || (m1 >= 8)) {
657                 Con_Printf("%d isn't a valid bindmap\n", m1);
658                 return;
659         }
660
661         m2 = strtol(Cmd_Argv (2), NULL, 0);
662         if ((m2 < 0) || (m2 >= 8)) {
663                 Con_Printf("%d isn't a valid bindmap\n", m2);
664                 return;
665         }
666
667         key_bmap = m1;
668         key_bmap2 = m2;
669 }
670
671 static void
672 Key_Unbind_f (void)
673 {
674         int         b;
675
676         if (Cmd_Argc () != 2) {
677                 Con_Print("unbind <key> : remove commands from a key\n");
678                 return;
679         }
680
681         b = Key_StringToKeynum (Cmd_Argv (1));
682         if (b == -1) {
683                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (1));
684                 return;
685         }
686
687         Key_SetBinding (b, 0, "");
688 }
689
690 static void
691 Key_Unbindall_f (void)
692 {
693         int         i, j;
694
695         for (j = 0; j < 8; j++)
696                 for (i = 0; i < (int)(sizeof(keybindings[0])/sizeof(keybindings[0][0])); i++)
697                         if (keybindings[j][i])
698                                 Key_SetBinding (i, j, "");
699 }
700
701
702 static void
703 Key_Bind_f (void)
704 {
705         int         i, c, b;
706         char        cmd[1024];
707
708         c = Cmd_Argc ();
709
710         if (c != 2 && c != 3) {
711                 Con_Print("bind <key> [command] : attach a command to a key\n");
712                 return;
713         }
714         b = Key_StringToKeynum (Cmd_Argv (1));
715         if (b == -1) {
716                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (1));
717                 return;
718         }
719
720         if (c == 2) {
721                 if (keybindings[0][b])
722                         Con_Printf("\"%s\" = \"%s\"\n", Cmd_Argv (1), keybindings[0][b]);
723                 else
724                         Con_Printf("\"%s\" is not bound\n", Cmd_Argv (1));
725                 return;
726         }
727 // copy the rest of the command line
728         cmd[0] = 0;                                                     // start out with a null string
729         for (i = 2; i < c; i++) {
730                 strlcat (cmd, Cmd_Argv (i), sizeof (cmd));
731                 if (i != (c - 1))
732                         strlcat (cmd, " ", sizeof (cmd));
733         }
734
735         Key_SetBinding (b, 0, cmd);
736 }
737
738 /*
739 ============
740 Writes lines containing "bind key value"
741 ============
742 */
743 void
744 Key_WriteBindings (qfile_t *f)
745 {
746         int         i, j;
747
748         for (i = 0; i < (int)(sizeof(keybindings[0])/sizeof(keybindings[0][0])); i++)
749                 if (keybindings[0][i])
750                         FS_Printf(f, "bind \"%s\" \"%s\"\n",
751                                         Key_KeynumToString (i), keybindings[0][i]);
752         for (j = 1; j < 8; j++)
753                 for (i = 0; i < (int)(sizeof(keybindings[0])/sizeof(keybindings[0][0])); i++)
754                         if (keybindings[j][i])
755                                 FS_Printf(f, "in_bind %d \"%s\" \"%s\"\n",
756                                                 j, Key_KeynumToString (i), keybindings[j][i]);
757 }
758
759
760 void
761 Key_Init (void)
762 {
763         int         i;
764
765         for (i = 0; i < 32; i++) {
766                 key_lines[i][0] = ']';
767                 key_lines[i][1] = 0;
768         }
769         key_linepos = 1;
770
771 //
772 // register our functions
773 //
774         Cmd_AddCommand ("in_bind", Key_In_Bind_f);
775         Cmd_AddCommand ("in_unbind", Key_In_Unbind_f);
776         Cmd_AddCommand ("in_bindmap", Key_In_Bindmap_f);
777
778         Cmd_AddCommand ("bind", Key_Bind_f);
779         Cmd_AddCommand ("unbind", Key_Unbind_f);
780         Cmd_AddCommand ("unbindall", Key_Unbindall_f);
781 }
782
783
784 /*
785 ===================
786 Called by the system between frames for both key up and key down events
787 Should NOT be called during an interrupt!
788 ===================
789 */
790 void
791 Key_Event (int key, char ascii, qboolean down)
792 {
793         const char *bind;
794
795         // get key binding
796         bind = keybindings[key_bmap][key];
797         if (!bind)
798                 bind = keybindings[key_bmap2][key];
799
800         if (!down)
801         {
802                 // clear repeat count now that the key is released
803                 keydown[key] = 0;
804                 // key up events only generate commands if the game key binding is a button
805                 // command (leading + sign).  These will occur even in console mode, to
806                 // keep the character from continuing an action started before a console
807                 // switch.  Button commands include the kenum as a parameter, so multiple
808                 // downs can be matched with ups
809                 if (bind && bind[0] == '+')
810                         Cbuf_AddText(va("-%s %i\n", bind + 1, key));
811                 return;
812         }
813
814         // from here on we know this is a down event
815
816         // increment key repeat count each time a down is received so that things
817         // which want to ignore key repeat can ignore it
818         keydown[key] = min(keydown[key] + 1, 2);
819
820         // key_consoleactive is a flag not a key_dest because the console is a
821         // high priority overlay ontop of the normal screen (designed as a safety
822         // feature so that developers and users can rescue themselves from a bad
823         // situation).
824         //
825         // this also means that toggling the console on/off does not lose the old
826         // key_dest state
827
828         // specially handle escape (togglemenu) and shift-escape (toggleconsole)
829         // engine bindings, these are not handled as normal binds so that the user
830         // can recover from a completely empty bindmap
831         if (key == K_ESCAPE)
832         {
833                 // ignore key repeats on escape
834                 if (keydown[key] > 1)
835                         return;
836                 // escape does these things:
837                 // key_consoleactive - close console
838                 // key_message - abort messagemode
839                 // key_menu - go to parent menu (or key_game)
840                 // key_game - open menu
841                 // in all modes shift-escape toggles console
842                 if ((key_consoleactive & KEY_CONSOLEACTIVE_USER) || keydown[K_SHIFT])
843                 {
844                         Con_ToggleConsole_f ();
845                         return;
846                 }
847                 switch (key_dest)
848                 {
849                         case key_message:
850                                 Key_Message (key, ascii);
851                                 break;
852                         case key_menu:
853                                 MR_Keydown (key, ascii);
854                                 break;
855                         case key_game:
856                                 MR_ToggleMenu_f ();
857                                 break;
858                         default:
859                                 if(UI_Callback_IsSlotUsed(key_dest - 3))
860                                         UI_Callback_KeyDown (key, ascii);
861                                 else
862                                         Con_Printf ("Key_Event: Bad key_dest");
863                 }
864                 return;
865         }
866
867         // send function keydowns to interpreter no matter what mode is
868         if (key >= K_F1 && key <= K_F12)
869         {
870                 // ignore key repeats on F1-F12 binds
871                 if (keydown[key] > 1)
872                         return;
873                 if (bind)
874                 {
875                         // button commands add keynum as a parm
876                         if (bind[0] == '+')
877                                 Cbuf_AddText (va("%s %i\n", bind, key));
878                         else
879                         {
880                                 Cbuf_AddText (bind);
881                                 Cbuf_AddText ("\n");
882                         }
883                 }
884                 return;
885         }
886
887 #if 1
888         // ignore binds (other than the above escape/F1-F12 keys) while in console
889         if (key_consoleactive)
890 #else
891         // respond to toggleconsole binds while in console unless the pressed key
892         // happens to be the color prefix character (such as on German keyboards)
893         if (key_consoleactive && (strncmp(bind, "toggleconsole", strlen("toggleconsole")) || ascii == STRING_COLOR_TAG))
894 #endif
895         {
896                 Key_Console (key, ascii);
897                 return;
898         }
899
900         // anything else is a key press into the game, chat line, or menu
901         switch (key_dest)
902         {
903                 case key_message:
904                         Key_Message (key, ascii);
905                         break;
906                 case key_menu:
907                         MR_Keydown (key, ascii);
908                         break;
909                 case key_game:
910                         // ignore key repeats on binds
911                         if (bind && keydown[key] == 1)
912                         {
913                                 // button commands add keynum as a parm
914                                 if (bind[0] == '+')
915                                         Cbuf_AddText (va("%s %i\n", bind, key));
916                                 else
917                                 {
918                                         Cbuf_AddText (bind);
919                                         Cbuf_AddText ("\n");
920                                 }
921                         }
922                         break;
923                 default:
924                         if(UI_Callback_IsSlotUsed(key_dest - 3))
925                                 UI_Callback_KeyDown (key, ascii);
926                         else
927                                 Con_Printf ("Key_Event: Bad key_dest");
928         }
929 }
930
931 /*
932 ===================
933 Key_ClearStates
934 ===================
935 */
936 void
937 Key_ClearStates (void)
938 {
939         memset(keydown, 0, sizeof(keydown));
940 }