improve abstraction of the console buffers; console drawing still needs to be changed...
[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 #include "cl_video.h"
25
26 cvar_t con_closeontoggleconsole = {CVAR_SAVE, "con_closeontoggleconsole","1", "allows toggleconsole binds to close the console as well"};
27
28 /*
29 key up events are sent even if in console mode
30 */
31
32 char            key_line[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 int         history_line;
39 char            history_savedline[MAX_INPUTLINE];
40 conbuffer_t history;
41 #define HIST_TEXTSIZE 262144
42 #define HIST_MAXLINES 4096
43
44 static void Key_History_Init()
45 {
46         qfile_t *historyfile;
47         ConBuffer_Init(&history, HIST_TEXTSIZE, HIST_MAXLINES, zonemempool);
48
49         historyfile = FS_OpenRealFile("darkplaces_history.txt", "rb", false); // rb to handle unix line endings on windows too
50         if(historyfile)
51         {
52                 char buf[MAX_INPUTLINE];
53                 int bufpos;
54                 int c;
55
56                 bufpos = 0;
57                 for(;;)
58                 {
59                         c = FS_Getc(historyfile);
60                         if(c < 0 || c == 0 || c == '\r' || c == '\n')
61                         {
62                                 if(bufpos > 0)
63                                 {
64                                         buf[bufpos] = 0;
65                                         ConBuffer_AddLine(&history, buf, bufpos, 0);
66                                         bufpos = 0;
67                                 }
68                                 if(c < 0)
69                                         break;
70                         }
71                         else
72                         {
73                                 if(bufpos < MAX_INPUTLINE - 1)
74                                         buf[bufpos++] = c;
75                         }
76                 }
77
78                 FS_Close(historyfile);
79         }
80
81         history_line = -1;
82 }
83
84 static void Key_History_Shutdown()
85 {
86         // TODO write history to a file
87
88         qfile_t *historyfile = FS_OpenRealFile("darkplaces_history.txt", "w", false);
89         if(historyfile)
90         {
91                 int i;
92                 for(i = 0; i < CONBUFFER_LINES_COUNT(&history); ++i)
93                         FS_Printf(historyfile, "%s\n", ConBuffer_GetLine(&history, i));
94                 FS_Close(historyfile);
95         }
96
97         ConBuffer_Shutdown(&history);
98 }
99
100 static void Key_History_Push()
101 {
102         if(key_line[1]) // empty?
103         if(strcmp(key_line, "]quit")) // putting these into the history just sucks
104         if(strncmp(key_line, "]quit ", 6)) // putting these into the history just sucks
105                 ConBuffer_AddLine(&history, key_line + 1, strlen(key_line) - 1, 0);
106         Con_Printf("%s\n", key_line); // don't mark empty lines as history
107         history_line = -1;
108 }
109
110 static void Key_History_Up()
111 {
112         if(history_line == -1) // editing the "new" line
113                 strlcpy(history_savedline, key_line + 1, sizeof(history_savedline));
114
115         if(history_line == -1)
116         {
117                 history_line = CONBUFFER_LINES_COUNT(&history) - 1;
118                 if(history_line != -1)
119                 {
120                         strlcpy(key_line + 1, ConBuffer_GetLine(&history, history_line), sizeof(key_line) - 1);
121                         key_linepos = strlen(key_line);
122                 }
123         }
124         else if(history_line > 0)
125         {
126                 --history_line; // this also does -1 -> 0, so it is good
127                 strlcpy(key_line + 1, ConBuffer_GetLine(&history, history_line), sizeof(key_line) - 1);
128                 key_linepos = strlen(key_line);
129         }
130 }
131
132 static void Key_History_Down()
133 {
134         if(history_line == -1) // editing the "new" line
135                 return;
136
137         if(history_line < CONBUFFER_LINES_COUNT(&history) - 1)
138         {
139                 ++history_line;
140                 strlcpy(key_line + 1, ConBuffer_GetLine(&history, history_line), sizeof(key_line) - 1);
141         }
142         else
143         {
144                 history_line = -1;
145                 strlcpy(key_line + 1, history_savedline, sizeof(key_line) - 1);
146         }
147
148         key_linepos = strlen(key_line);
149 }
150
151 static int      key_bmap, key_bmap2;
152 static unsigned char keydown[MAX_KEYS]; // 0 = up, 1 = down, 2 = repeating
153
154 typedef struct keyname_s
155 {
156         const char      *name;
157         int                     keynum;
158 }
159 keyname_t;
160
161 static const keyname_t   keynames[] = {
162         {"TAB", K_TAB},
163         {"ENTER", K_ENTER},
164         {"ESCAPE", K_ESCAPE},
165         {"SPACE", K_SPACE},
166
167         // spacer so it lines up with keys.h
168
169         {"BACKSPACE", K_BACKSPACE},
170         {"UPARROW", K_UPARROW},
171         {"DOWNARROW", K_DOWNARROW},
172         {"LEFTARROW", K_LEFTARROW},
173         {"RIGHTARROW", K_RIGHTARROW},
174
175         {"ALT", K_ALT},
176         {"CTRL", K_CTRL},
177         {"SHIFT", K_SHIFT},
178
179         {"F1", K_F1},
180         {"F2", K_F2},
181         {"F3", K_F3},
182         {"F4", K_F4},
183         {"F5", K_F5},
184         {"F6", K_F6},
185         {"F7", K_F7},
186         {"F8", K_F8},
187         {"F9", K_F9},
188         {"F10", K_F10},
189         {"F11", K_F11},
190         {"F12", K_F12},
191
192         {"INS", K_INS},
193         {"DEL", K_DEL},
194         {"PGDN", K_PGDN},
195         {"PGUP", K_PGUP},
196         {"HOME", K_HOME},
197         {"END", K_END},
198
199         {"PAUSE", K_PAUSE},
200
201         {"NUMLOCK", K_NUMLOCK},
202         {"CAPSLOCK", K_CAPSLOCK},
203         {"SCROLLOCK", K_SCROLLOCK},
204
205         {"KP_INS",                      K_KP_INS },
206         {"KP_0", K_KP_0},
207         {"KP_END",                      K_KP_END },
208         {"KP_1", K_KP_1},
209         {"KP_DOWNARROW",        K_KP_DOWNARROW },
210         {"KP_2", K_KP_2},
211         {"KP_PGDN",                     K_KP_PGDN },
212         {"KP_3", K_KP_3},
213         {"KP_LEFTARROW",        K_KP_LEFTARROW },
214         {"KP_4", K_KP_4},
215         {"KP_5", K_KP_5},
216         {"KP_RIGHTARROW",       K_KP_RIGHTARROW },
217         {"KP_6", K_KP_6},
218         {"KP_HOME",                     K_KP_HOME },
219         {"KP_7", K_KP_7},
220         {"KP_UPARROW",          K_KP_UPARROW },
221         {"KP_8", K_KP_8},
222         {"KP_PGUP",                     K_KP_PGUP },
223         {"KP_9", K_KP_9},
224         {"KP_DEL",                      K_KP_DEL },
225         {"KP_PERIOD", K_KP_PERIOD},
226         {"KP_SLASH",            K_KP_SLASH },
227         {"KP_DIVIDE", K_KP_DIVIDE},
228         {"KP_MULTIPLY", K_KP_MULTIPLY},
229         {"KP_MINUS", K_KP_MINUS},
230         {"KP_PLUS", K_KP_PLUS},
231         {"KP_ENTER", K_KP_ENTER},
232         {"KP_EQUALS", K_KP_EQUALS},
233
234
235
236         {"MOUSE1", K_MOUSE1},
237
238         {"MOUSE2", K_MOUSE2},
239         {"MOUSE3", K_MOUSE3},
240         {"MWHEELUP", K_MWHEELUP},
241         {"MWHEELDOWN", K_MWHEELDOWN},
242         {"MOUSE4", K_MOUSE4},
243         {"MOUSE5", K_MOUSE5},
244         {"MOUSE6", K_MOUSE6},
245         {"MOUSE7", K_MOUSE7},
246         {"MOUSE8", K_MOUSE8},
247         {"MOUSE9", K_MOUSE9},
248         {"MOUSE10", K_MOUSE10},
249         {"MOUSE11", K_MOUSE11},
250         {"MOUSE12", K_MOUSE12},
251         {"MOUSE13", K_MOUSE13},
252         {"MOUSE14", K_MOUSE14},
253         {"MOUSE15", K_MOUSE15},
254         {"MOUSE16", K_MOUSE16},
255
256
257
258
259         {"JOY1",  K_JOY1},
260         {"JOY2",  K_JOY2},
261         {"JOY3",  K_JOY3},
262         {"JOY4",  K_JOY4},
263         {"JOY5",  K_JOY5},
264         {"JOY6",  K_JOY6},
265         {"JOY7",  K_JOY7},
266         {"JOY8",  K_JOY8},
267         {"JOY9",  K_JOY9},
268         {"JOY10", K_JOY10},
269         {"JOY11", K_JOY11},
270         {"JOY12", K_JOY12},
271         {"JOY13", K_JOY13},
272         {"JOY14", K_JOY14},
273         {"JOY15", K_JOY15},
274         {"JOY16", K_JOY16},
275
276
277
278
279
280
281         {"AUX1", K_AUX1},
282         {"AUX2", K_AUX2},
283         {"AUX3", K_AUX3},
284         {"AUX4", K_AUX4},
285         {"AUX5", K_AUX5},
286         {"AUX6", K_AUX6},
287         {"AUX7", K_AUX7},
288         {"AUX8", K_AUX8},
289         {"AUX9", K_AUX9},
290         {"AUX10", K_AUX10},
291         {"AUX11", K_AUX11},
292         {"AUX12", K_AUX12},
293         {"AUX13", K_AUX13},
294         {"AUX14", K_AUX14},
295         {"AUX15", K_AUX15},
296         {"AUX16", K_AUX16},
297         {"AUX17", K_AUX17},
298         {"AUX18", K_AUX18},
299         {"AUX19", K_AUX19},
300         {"AUX20", K_AUX20},
301         {"AUX21", K_AUX21},
302         {"AUX22", K_AUX22},
303         {"AUX23", K_AUX23},
304         {"AUX24", K_AUX24},
305         {"AUX25", K_AUX25},
306         {"AUX26", K_AUX26},
307         {"AUX27", K_AUX27},
308         {"AUX28", K_AUX28},
309         {"AUX29", K_AUX29},
310         {"AUX30", K_AUX30},
311         {"AUX31", K_AUX31},
312         {"AUX32", K_AUX32},
313
314         {"SEMICOLON", ';'},                     // because a raw semicolon separates commands
315         {"TILDE", '~'},
316         {"BACKQUOTE", '`'},
317         {"QUOTE", '"'},
318         {"APOSTROPHE", '\''},
319         {"BACKSLASH", '\\'},            // because a raw backslash is used for special characters
320
321         {NULL, 0}
322 };
323
324 /*
325 ==============================================================================
326
327                         LINE TYPING INTO THE CONSOLE
328
329 ==============================================================================
330 */
331
332 void
333 Key_ClearEditLine (int edit_line)
334 {
335         memset (key_line, '\0', sizeof(key_line));
336         key_line[0] = ']';
337         key_linepos = 1;
338 }
339
340 /*
341 ====================
342 Interactive line editing and console scrollback
343 ====================
344 */
345 static void
346 Key_Console (int key, int ascii)
347 {
348         // LordHavoc: copied most of this from Q2 to improve keyboard handling
349         switch (key)
350         {
351         case K_KP_SLASH:
352                 key = '/';
353                 break;
354         case K_KP_MINUS:
355                 key = '-';
356                 break;
357         case K_KP_PLUS:
358                 key = '+';
359                 break;
360         case K_KP_HOME:
361                 key = '7';
362                 break;
363         case K_KP_UPARROW:
364                 key = '8';
365                 break;
366         case K_KP_PGUP:
367                 key = '9';
368                 break;
369         case K_KP_LEFTARROW:
370                 key = '4';
371                 break;
372         case K_KP_5:
373                 key = '5';
374                 break;
375         case K_KP_RIGHTARROW:
376                 key = '6';
377                 break;
378         case K_KP_END:
379                 key = '1';
380                 break;
381         case K_KP_DOWNARROW:
382                 key = '2';
383                 break;
384         case K_KP_PGDN:
385                 key = '3';
386                 break;
387         case K_KP_INS:
388                 key = '0';
389                 break;
390         case K_KP_DEL:
391                 key = '.';
392                 break;
393         }
394
395         if ((toupper(key) == 'V' && keydown[K_CTRL]) || ((key == K_INS || key == K_KP_INS) && keydown[K_SHIFT]))
396         {
397                 char *cbd, *p;
398                 if ((cbd = Sys_GetClipboardData()) != 0)
399                 {
400                         int i;
401 #if 1
402                         p = cbd;
403                         while (*p)
404                         {
405                                 if (*p == '\n' || *p == '\r' || *p == '\b')
406                                 {
407                                         *p++ = 0;
408                                         break;
409                                 }
410                                 p++;
411                         }
412 #else
413                         strtok(cbd, "\n\r\b");
414 #endif
415                         i = (int)strlen(cbd);
416                         if (i + key_linepos >= MAX_INPUTLINE)
417                                 i= MAX_INPUTLINE - key_linepos - 1;
418                         if (i > 0)
419                         {
420                                 // terencehill: insert the clipboard text between the characters of the line
421                                 char *temp = Z_Malloc(MAX_INPUTLINE);
422                                 cbd[i]=0;
423                                 temp[0]=0;
424                                 if ( key_linepos < (int)strlen(key_line) )
425                                         strlcpy(temp, key_line + key_linepos, (int)strlen(key_line) - key_linepos +1);
426                                 key_line[key_linepos] = 0;
427                                 strlcat(key_line, cbd, sizeof(key_line));
428                                 if (temp[0])
429                                         strlcat(key_line, temp, sizeof(key_line));
430                                 Z_Free(temp);
431                                 key_linepos += i;
432                         }
433                         Z_Free(cbd);
434                 }
435                 return;
436         }
437
438         if (key == 'l')
439         {
440                 if (keydown[K_CTRL])
441                 {
442                         Cbuf_AddText ("clear\n");
443                         return;
444                 }
445         }
446
447         if (key == K_ENTER || key == K_KP_ENTER)
448         {
449                 Cbuf_AddText (key_line+1);      // skip the ]
450                 Cbuf_AddText ("\n");
451                 Key_History_Push();
452                 key_line[0] = ']';
453                 key_line[1] = 0;        // EvilTypeGuy: null terminate
454                 key_linepos = 1;
455                 // force an update, because the command may take some time
456                 if (cls.state == ca_disconnected)
457                         CL_UpdateScreen ();
458                 return;
459         }
460
461         if (key == K_TAB)
462         {
463                 // Enhanced command completion
464                 // by EvilTypeGuy eviltypeguy@qeradiant.com
465                 // Thanks to Fett, Taniwha
466                 Con_CompleteCommandLine();
467                 return;
468         }
469
470         // Advanced Console Editing by Radix radix@planetquake.com
471         // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
472         // Enhanced by [515]
473         // Enhanced by terencehill
474
475         // move cursor to the previous character
476         if (key == K_LEFTARROW || key == K_KP_LEFTARROW)
477         {
478                 if (key_linepos < 2)
479                         return;
480                 if(keydown[K_CTRL]) // move cursor to the previous word
481                 {
482                         int             pos;
483                         char    k;
484                         pos = key_linepos-1;
485                         if(pos)
486                                 while(--pos)
487                                 {
488                                         k = key_line[pos];
489                                         if(k == '\"' || k == ';' || k == ' ' || k == '\'')
490                                                 break;
491                                 }
492                         key_linepos = pos + 1;
493                 }
494                 else if(keydown[K_SHIFT]) // move cursor to the previous character ignoring colors
495                 {
496                         int             pos;
497                         pos = key_linepos-1;
498                         while (pos)
499                                 if(pos-1 > 0 && key_line[pos-1] == STRING_COLOR_TAG && isdigit(key_line[pos]))
500                                         pos-=2;
501                                 else if(pos-4 > 0 && key_line[pos-4] == STRING_COLOR_TAG && key_line[pos-3] == STRING_COLOR_RGB_TAG_CHAR
502                                                 && isxdigit(key_line[pos-2]) && isxdigit(key_line[pos-1]) && isxdigit(key_line[pos]))
503                                         pos-=5;
504                                 else
505                                 {
506                                         pos--;
507                                         break;
508                                 }
509                         key_linepos = pos + 1;
510                 }
511                 else
512                         key_linepos--;
513                 return;
514         }
515
516         // delete char before cursor
517         if (key == K_BACKSPACE || (key == 'h' && keydown[K_CTRL]))
518         {
519                 if (key_linepos > 1)
520                 {
521                         strlcpy(key_line + key_linepos - 1, key_line + key_linepos, sizeof(key_line) + 1 - key_linepos);
522                         key_linepos--;
523                 }
524                 return;
525         }
526
527         // delete char on cursor
528         if (key == K_DEL || key == K_KP_DEL)
529         {
530                 size_t linelen;
531                 linelen = strlen(key_line);
532                 if (key_linepos < (int)linelen)
533                         memmove(key_line + key_linepos, key_line + key_linepos + 1, linelen - key_linepos);
534                 return;
535         }
536
537
538         // move cursor to the next character
539         if (key == K_RIGHTARROW || key == K_KP_RIGHTARROW)
540         {
541                 if (key_linepos >= (int)strlen(key_line))
542                         return;
543                 if(keydown[K_CTRL]) // move cursor to the next word
544                 {
545                         int             pos, len;
546                         char    k;
547                         len = (int)strlen(key_line);
548                         pos = key_linepos;
549                         while(++pos < len)
550                         {
551                                 k = key_line[pos];
552                                 if(k == '\"' || k == ';' || k == ' ' || k == '\'')
553                                         break;
554                         }
555                         key_linepos = pos;
556                 }
557                 else if(keydown[K_SHIFT]) // move cursor to the next character ignoring colors
558                 {
559                         int             pos, len;
560                         len = (int)strlen(key_line);
561                         pos = key_linepos;
562                         // check if there is a color tag right after the cursor
563                         if (key_line[pos] == STRING_COLOR_TAG)
564                         {
565                                 if(isdigit(key_line[pos+1]))
566                                         pos+=1;
567                                 else if(key_line[pos+1] == STRING_COLOR_RGB_TAG_CHAR && isxdigit(key_line[pos+2]) && isxdigit(key_line[pos+3]) && isxdigit(key_line[pos+4]))
568                                         pos+=4;
569                         }
570                         pos++;
571                         
572                         // now go beyond all next consecutive color tags, if any
573                         if(pos < len)
574                                 while (key_line[pos] == STRING_COLOR_TAG)
575                                 {
576                                         if(isdigit(key_line[pos+1]))
577                                                 pos+=2;
578                                         else if(key_line[pos+1] == STRING_COLOR_RGB_TAG_CHAR && isxdigit(key_line[pos+2]) && isxdigit(key_line[pos+3]) && isxdigit(key_line[pos+4]))
579                                                 pos+=5;
580                                         else
581                                                 break;
582                                 }
583                         key_linepos = pos;
584                 }
585                 else
586                         key_linepos++;
587                 return;
588         }
589
590         if (key == K_INS || key == K_KP_INS) // toggle insert mode
591         {
592                 key_insert ^= 1;
593                 return;
594         }
595
596         // End Advanced Console Editing
597
598         if (key == K_UPARROW || key == K_KP_UPARROW || (key == 'p' && keydown[K_CTRL]))
599         {
600                 Key_History_Up();
601                 return;
602         }
603
604         if (key == K_DOWNARROW || key == K_KP_DOWNARROW || (key == 'n' && keydown[K_CTRL]))
605         {
606                 Key_History_Down();
607                 return;
608         }
609
610         if (key == K_PGUP || key == K_KP_PGUP || key == K_MWHEELUP)
611         {
612                 if(keydown[K_CTRL])
613                 {
614                         con_backscroll += 3;
615                 }
616                 else
617                         con_backscroll += ((int) vid_conheight.integer >> 5);
618                 return;
619         }
620
621         if (key == K_PGDN || key == K_KP_PGDN || key == K_MWHEELDOWN)
622         {
623                 if(keydown[K_CTRL])
624                 {
625                         con_backscroll -= 3;
626                 }
627                 else
628                         con_backscroll -= ((int) vid_conheight.integer >> 5);
629                 return;
630         }
631
632         if (key == K_HOME || key == K_KP_HOME)
633         {
634                 if (keydown[K_CTRL])
635                         con_backscroll = INT_MAX;
636                 else
637                         key_linepos = 1;
638                 return;
639         }
640
641         if (key == K_END || key == K_KP_END)
642         {
643                 if (keydown[K_CTRL])
644                         con_backscroll = 0;
645                 else
646                         key_linepos = (int)strlen(key_line);
647                 return;
648         }
649
650         // non printable
651         if (ascii < 32)
652                 return;
653
654         if (key_linepos < MAX_INPUTLINE-1)
655         {
656                 int len;
657                 len = (int)strlen(&key_line[key_linepos]);
658                 // check insert mode, or always insert if at end of line
659                 if (key_insert || len == 0)
660                 {
661                         // can't use strcpy to move string to right
662                         len++;
663                         memmove(&key_line[key_linepos + 1], &key_line[key_linepos], len);
664                 }
665                 key_line[key_linepos] = ascii;
666                 key_linepos++;
667         }
668 }
669
670 //============================================================================
671
672 int chat_mode;
673 char            chat_buffer[MAX_INPUTLINE];
674 unsigned int    chat_bufferlen = 0;
675
676 extern int Nicks_CompleteChatLine(char *buffer, size_t size, unsigned int pos);
677
678 static void
679 Key_Message (int key, int ascii)
680 {
681
682         if (key == K_ENTER || ascii == 10 || ascii == 13)
683         {
684                 if(chat_mode < 0)
685                         Cmd_ExecuteString(chat_buffer, src_command); // not Cbuf_AddText to allow semiclons in args; however, this allows no variables then. Use aliases!
686                 else
687                         Cmd_ForwardStringToServer(va("%s %s", chat_mode ? "say_team" : "say ", chat_buffer));
688
689                 key_dest = key_game;
690                 chat_bufferlen = 0;
691                 chat_buffer[0] = 0;
692                 return;
693         }
694
695         // TODO add support for arrow keys and simple editing
696
697         if (key == K_ESCAPE) {
698                 key_dest = key_game;
699                 chat_bufferlen = 0;
700                 chat_buffer[0] = 0;
701                 return;
702         }
703
704         if (key == K_BACKSPACE) {
705                 if (chat_bufferlen) {
706                         chat_bufferlen--;
707                         chat_buffer[chat_bufferlen] = 0;
708                 }
709                 return;
710         }
711
712         if(key == K_TAB) {
713                 chat_bufferlen = Nicks_CompleteChatLine(chat_buffer, sizeof(chat_buffer), chat_bufferlen);
714                 return;
715         }
716
717         if (chat_bufferlen == sizeof (chat_buffer) - 1)
718                 return;                                                 // all full
719
720         if (!ascii)
721                 return;                                                 // non printable
722
723         chat_buffer[chat_bufferlen++] = ascii;
724         chat_buffer[chat_bufferlen] = 0;
725 }
726
727 //============================================================================
728
729
730 /*
731 ===================
732 Returns a key number to be used to index keybindings[] by looking at
733 the given string.  Single ascii characters return themselves, while
734 the K_* names are matched up.
735 ===================
736 */
737 int
738 Key_StringToKeynum (const char *str)
739 {
740         const keyname_t  *kn;
741
742         if (!str || !str[0])
743                 return -1;
744         if (!str[1])
745                 return tolower(str[0]);
746
747         for (kn = keynames; kn->name; kn++) {
748                 if (!strcasecmp (str, kn->name))
749                         return kn->keynum;
750         }
751         return -1;
752 }
753
754 /*
755 ===================
756 Returns a string (either a single ascii char, or a K_* name) for the
757 given keynum.
758 FIXME: handle quote special (general escape sequence?)
759 ===================
760 */
761 const char *
762 Key_KeynumToString (int keynum)
763 {
764         const keyname_t  *kn;
765         static char tinystr[2];
766
767         // -1 is an invalid code
768         if (keynum < 0)
769                 return "<KEY NOT FOUND>";
770
771         // search overrides first, because some characters are special
772         for (kn = keynames; kn->name; kn++)
773                 if (keynum == kn->keynum)
774                         return kn->name;
775
776         // if it is printable, output it as a single character
777         if (keynum > 32 && keynum < 256)
778         {
779                 tinystr[0] = keynum;
780                 tinystr[1] = 0;
781                 return tinystr;
782         }
783
784         // if it is not overridden and not printable, we don't know what to do with it
785         return "<UNKNOWN KEYNUM>";
786 }
787
788
789 void
790 Key_SetBinding (int keynum, int bindmap, const char *binding)
791 {
792         char *newbinding;
793         size_t l;
794
795         if (keynum == -1 || keynum >= MAX_KEYS)
796                 return;
797
798 // free old bindings
799         if (keybindings[bindmap][keynum]) {
800                 Z_Free (keybindings[bindmap][keynum]);
801                 keybindings[bindmap][keynum] = NULL;
802         }
803         if(!binding[0]) // make "" binds be removed --blub
804                 return;
805 // allocate memory for new binding
806         l = strlen (binding);
807         newbinding = (char *)Z_Malloc (l + 1);
808         memcpy (newbinding, binding, l + 1);
809         newbinding[l] = 0;
810         keybindings[bindmap][keynum] = newbinding;
811 }
812
813 static void
814 Key_In_Unbind_f (void)
815 {
816         int         b, m;
817         char *errchar = NULL;
818
819         if (Cmd_Argc () != 3) {
820                 Con_Print("in_unbind <bindmap> <key> : remove commands from a key\n");
821                 return;
822         }
823
824         m = strtol(Cmd_Argv (1), &errchar, 0);
825         if ((m < 0) || (m >= 8) || (errchar && *errchar)) {
826                 Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(1));
827                 return;
828         }
829
830         b = Key_StringToKeynum (Cmd_Argv (2));
831         if (b == -1) {
832                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (2));
833                 return;
834         }
835
836         Key_SetBinding (b, m, "");
837 }
838
839 static void
840 Key_In_Bind_f (void)
841 {
842         int         i, c, b, m;
843         char        cmd[MAX_INPUTLINE];
844         char *errchar = NULL;
845
846         c = Cmd_Argc ();
847
848         if (c != 3 && c != 4) {
849                 Con_Print("in_bind <bindmap> <key> [command] : attach a command to a key\n");
850                 return;
851         }
852
853         m = strtol(Cmd_Argv (1), &errchar, 0);
854         if ((m < 0) || (m >= 8) || (errchar && *errchar)) {
855                 Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(1));
856                 return;
857         }
858
859         b = Key_StringToKeynum (Cmd_Argv (2));
860         if (b == -1 || b >= MAX_KEYS) {
861                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (2));
862                 return;
863         }
864
865         if (c == 3) {
866                 if (keybindings[m][b])
867                         Con_Printf("\"%s\" = \"%s\"\n", Cmd_Argv (2), keybindings[m][b]);
868                 else
869                         Con_Printf("\"%s\" is not bound\n", Cmd_Argv (2));
870                 return;
871         }
872 // copy the rest of the command line
873         cmd[0] = 0;                                                     // start out with a null string
874         for (i = 3; i < c; i++) {
875                 strlcat (cmd, Cmd_Argv (i), sizeof (cmd));
876                 if (i != (c - 1))
877                         strlcat (cmd, " ", sizeof (cmd));
878         }
879
880         Key_SetBinding (b, m, cmd);
881 }
882
883 static void
884 Key_In_Bindmap_f (void)
885 {
886         int         m1, m2, c;
887         char *errchar = NULL;
888
889         c = Cmd_Argc ();
890
891         if (c != 3) {
892                 Con_Print("in_bindmap <bindmap> <fallback>: set current bindmap and fallback\n");
893                 return;
894         }
895
896         m1 = strtol(Cmd_Argv (1), &errchar, 0);
897         if ((m1 < 0) || (m1 >= 8) || (errchar && *errchar)) {
898                 Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(1));
899                 return;
900         }
901
902         m2 = strtol(Cmd_Argv (2), &errchar, 0);
903         if ((m2 < 0) || (m2 >= 8) || (errchar && *errchar)) {
904                 Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(2));
905                 return;
906         }
907
908         key_bmap = m1;
909         key_bmap2 = m2;
910 }
911
912 static void
913 Key_Unbind_f (void)
914 {
915         int         b;
916
917         if (Cmd_Argc () != 2) {
918                 Con_Print("unbind <key> : remove commands from a key\n");
919                 return;
920         }
921
922         b = Key_StringToKeynum (Cmd_Argv (1));
923         if (b == -1) {
924                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (1));
925                 return;
926         }
927
928         Key_SetBinding (b, 0, "");
929 }
930
931 static void
932 Key_Unbindall_f (void)
933 {
934         int         i, j;
935
936         for (j = 0; j < 8; j++)
937                 for (i = 0; i < (int)(sizeof(keybindings[0])/sizeof(keybindings[0][0])); i++)
938                         if (keybindings[j][i])
939                                 Key_SetBinding (i, j, "");
940 }
941
942 static void
943 Key_PrintBindList(int j)
944 {
945         char bindbuf[MAX_INPUTLINE];
946         const char *p;
947         int i;
948
949         for (i = 0; i < (int)(sizeof(keybindings[0])/sizeof(keybindings[0][0])); i++)
950         {
951                 p = keybindings[j][i];
952                 if (p)
953                 {
954                         Cmd_QuoteString(bindbuf, sizeof(bindbuf), p, "\"\\");
955                         if (j == 0)
956                                 Con_Printf("^2%s ^7= \"%s\"\n", Key_KeynumToString (i), bindbuf);
957                         else
958                                 Con_Printf("^3bindmap %d: ^2%s ^7= \"%s\"\n", j, Key_KeynumToString (i), bindbuf);
959                 }
960         }
961 }
962
963 static void
964 Key_In_BindList_f (void)
965 {
966         int m;
967         char *errchar = NULL;
968
969         if(Cmd_Argc() >= 2)
970         {
971                 m = strtol(Cmd_Argv(1), &errchar, 0);
972                 if ((m < 0) || (m >= 8) || (errchar && *errchar)) {
973                         Con_Printf("%s isn't a valid bindmap\n", Cmd_Argv(1));
974                         return;
975                 }
976                 Key_PrintBindList(m);
977         }
978         else
979         {
980                 for (m = 0; m < MAX_BINDMAPS; m++)
981                         Key_PrintBindList(m);
982         }
983 }
984
985 static void
986 Key_BindList_f (void)
987 {
988         Key_PrintBindList(0);
989 }
990
991 static void
992 Key_Bind_f (void)
993 {
994         int         i, c, b;
995         char        cmd[MAX_INPUTLINE];
996
997         c = Cmd_Argc ();
998
999         if (c != 2 && c != 3) {
1000                 Con_Print("bind <key> [command] : attach a command to a key\n");
1001                 return;
1002         }
1003         b = Key_StringToKeynum (Cmd_Argv (1));
1004         if (b == -1 || b >= MAX_KEYS) {
1005                 Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv (1));
1006                 return;
1007         }
1008
1009         if (c == 2) {
1010                 if (keybindings[0][b])
1011                         Con_Printf("\"%s\" = \"%s\"\n", Cmd_Argv (1), keybindings[0][b]);
1012                 else
1013                         Con_Printf("\"%s\" is not bound\n", Cmd_Argv (1));
1014                 return;
1015         }
1016 // copy the rest of the command line
1017         cmd[0] = 0;                                                     // start out with a null string
1018         for (i = 2; i < c; i++) {
1019                 strlcat (cmd, Cmd_Argv (i), sizeof (cmd));
1020                 if (i != (c - 1))
1021                         strlcat (cmd, " ", sizeof (cmd));
1022         }
1023
1024         Key_SetBinding (b, 0, cmd);
1025 }
1026
1027 /*
1028 ============
1029 Writes lines containing "bind key value"
1030 ============
1031 */
1032 void
1033 Key_WriteBindings (qfile_t *f)
1034 {
1035         int         i, j;
1036         char bindbuf[MAX_INPUTLINE];
1037         const char *p;
1038
1039         for (j = 0; j < MAX_BINDMAPS; j++)
1040         {
1041                 for (i = 0; i < (int)(sizeof(keybindings[0])/sizeof(keybindings[0][0])); i++)
1042                 {
1043                         p = keybindings[j][i];
1044                         if (p)
1045                         {
1046                                 Cmd_QuoteString(bindbuf, sizeof(bindbuf), p, "\"\\");
1047                                 if (j == 0)
1048                                         FS_Printf(f, "bind %s \"%s\"\n", Key_KeynumToString (i), bindbuf);
1049                                 else
1050                                         FS_Printf(f, "in_bind %d %s \"%s\"\n", j, Key_KeynumToString (i), bindbuf);
1051                         }
1052                 }
1053         }
1054 }
1055
1056
1057 void
1058 Key_Init (void)
1059 {
1060         Key_History_Init();
1061         key_line[0] = ']';
1062         key_line[1] = 0;
1063         key_linepos = 1;
1064
1065 //
1066 // register our functions
1067 //
1068         Cmd_AddCommand ("in_bind", Key_In_Bind_f, "binds a command to the specified key in the selected bindmap");
1069         Cmd_AddCommand ("in_unbind", Key_In_Unbind_f, "removes command on the specified key in the selected bindmap");
1070         Cmd_AddCommand ("in_bindlist", Key_In_BindList_f, "bindlist: displays bound keys for all bindmaps, or the given bindmap");
1071         Cmd_AddCommand ("in_bindmap", Key_In_Bindmap_f, "selects active foreground and background (used only if a key is not bound in the foreground) bindmaps for typing");
1072
1073         Cmd_AddCommand ("bind", Key_Bind_f, "binds a command to the specified key in bindmap 0");
1074         Cmd_AddCommand ("unbind", Key_Unbind_f, "removes a command on the specified key in bindmap 0");
1075         Cmd_AddCommand ("bindlist", Key_BindList_f, "bindlist: displays bound keys for bindmap 0 bindmaps");
1076         Cmd_AddCommand ("unbindall", Key_Unbindall_f, "removes all commands from all keys in all bindmaps (leaving only shift-escape and escape)");
1077
1078         Cvar_RegisterVariable (&con_closeontoggleconsole);
1079 }
1080
1081 void
1082 Key_Shutdown (void)
1083 {
1084         Key_History_Shutdown();
1085 }
1086
1087 const char *Key_GetBind (int key)
1088 {
1089         const char *bind;
1090         if (key < 0 || key >= MAX_KEYS)
1091                 return NULL;
1092         bind = keybindings[key_bmap][key];
1093         if (!bind)
1094                 bind = keybindings[key_bmap2][key];
1095         return bind;
1096 }
1097
1098 qboolean CL_VM_InputEvent (qboolean down, int key, int ascii);
1099
1100 /*
1101 ===================
1102 Called by the system between frames for both key up and key down events
1103 Should NOT be called during an interrupt!
1104 ===================
1105 */
1106 static char tbl_keyascii[MAX_KEYS];
1107 static keydest_t tbl_keydest[MAX_KEYS];
1108
1109 void
1110 Key_Event (int key, int ascii, qboolean down)
1111 {
1112         const char *bind;
1113         qboolean q;
1114         keydest_t keydest = key_dest;
1115
1116         if (key < 0 || key >= MAX_KEYS)
1117                 return;
1118
1119         // get key binding
1120         bind = keybindings[key_bmap][key];
1121         if (!bind)
1122                 bind = keybindings[key_bmap2][key];
1123
1124         if (developer.integer >= 1000)
1125                 Con_Printf("Key_Event(%i, '%c', %s) keydown %i bind \"%s\"\n", key, ascii, down ? "down" : "up", keydown[key], bind ? bind : "");
1126
1127         if(key_consoleactive)
1128                 keydest = key_console;
1129         
1130         if (down)
1131         {
1132                 // increment key repeat count each time a down is received so that things
1133                 // which want to ignore key repeat can ignore it
1134                 keydown[key] = min(keydown[key] + 1, 2);
1135                 if(keydown[key] == 1) {
1136                         tbl_keyascii[key] = ascii;
1137                         tbl_keydest[key] = keydest;
1138                 } else {
1139                         ascii = tbl_keyascii[key];
1140                         keydest = tbl_keydest[key];
1141                 }
1142         }
1143         else
1144         {
1145                 // clear repeat count now that the key is released
1146                 keydown[key] = 0;
1147                 keydest = tbl_keydest[key];
1148                 ascii = tbl_keyascii[key];
1149         }
1150
1151         if(keydest == key_void)
1152                 return;
1153         
1154         // key_consoleactive is a flag not a key_dest because the console is a
1155         // high priority overlay ontop of the normal screen (designed as a safety
1156         // feature so that developers and users can rescue themselves from a bad
1157         // situation).
1158         //
1159         // this also means that toggling the console on/off does not lose the old
1160         // key_dest state
1161
1162         // specially handle escape (togglemenu) and shift-escape (toggleconsole)
1163         // engine bindings, these are not handled as normal binds so that the user
1164         // can recover from a completely empty bindmap
1165         if (key == K_ESCAPE)
1166         {
1167                 // ignore key repeats on escape
1168                 if (keydown[key] > 1)
1169                         return;
1170
1171                 // escape does these things:
1172                 // key_consoleactive - close console
1173                 // key_message - abort messagemode
1174                 // key_menu - go to parent menu (or key_game)
1175                 // key_game - open menu
1176
1177                 // in all modes shift-escape toggles console
1178                 if (keydown[K_SHIFT])
1179                 {
1180                         if(down)
1181                         {
1182                                 Con_ToggleConsole_f ();
1183                                 tbl_keydest[key] = key_void; // esc release should go nowhere (especially not to key_menu or key_game)
1184                         }
1185                         return;
1186                 }
1187
1188                 switch (keydest)
1189                 {
1190                         case key_console:
1191                                 if(down)
1192                                 {
1193                                         if(key_consoleactive & KEY_CONSOLEACTIVE_FORCED)
1194                                         {
1195                                                 key_consoleactive &= ~KEY_CONSOLEACTIVE_USER;
1196                                                 MR_ToggleMenu_f ();
1197                                         }
1198                                         else
1199                                                 Con_ToggleConsole_f();
1200                                 }
1201                                 break;
1202
1203                         case key_message:
1204                                 if (down)
1205                                         Key_Message (key, ascii); // that'll close the message input
1206                                 break;
1207
1208                         case key_menu:
1209                         case key_menu_grabbed:
1210                                 MR_KeyEvent (key, ascii, down);
1211                                 break;
1212
1213                         case key_game:
1214                                 // csqc has priority over toggle menu if it wants to (e.g. handling escape for UI stuff in-game.. :sick:)
1215                                 q = CL_VM_InputEvent(down, key, ascii);
1216                                 if (!q && down)
1217                                         MR_ToggleMenu_f ();
1218                                 break;
1219
1220                         default:
1221                                 Con_Printf ("Key_Event: Bad key_dest\n");
1222                 }
1223                 return;
1224         }
1225
1226         // send function keydowns to interpreter no matter what mode is (unless the menu has specifically grabbed the keyboard, for rebinding keys)
1227         if (keydest != key_menu_grabbed)
1228         if (key >= K_F1 && key <= K_F12)
1229         {
1230                 if (bind)
1231                 {
1232                         if(keydown[key] == 1 && down)
1233                         {
1234                                 // button commands add keynum as a parm
1235                                 if (bind[0] == '+')
1236                                         Cbuf_AddText (va("%s %i\n", bind, key));
1237                                 else
1238                                 {
1239                                         Cbuf_AddText (bind);
1240                                         Cbuf_AddText ("\n");
1241                                 }
1242                         } else if(bind[0] == '+' && !down && keydown[key] == 0)
1243                                 Cbuf_AddText(va("-%s %i\n", bind + 1, key));
1244                 }
1245                 return;
1246         }
1247
1248         // send input to console if it wants it
1249         if (keydest == key_console)
1250         {
1251                 if (!down)
1252                         return;
1253                 // con_closeontoggleconsole enables toggleconsole keys to close the
1254                 // console, as long as they are not the color prefix character
1255                 // (special exemption for german keyboard layouts)
1256                 if (con_closeontoggleconsole.integer && bind && !strncmp(bind, "toggleconsole", strlen("toggleconsole")) && (key_consoleactive & KEY_CONSOLEACTIVE_USER) && ascii != STRING_COLOR_TAG)
1257                 {
1258                         Con_ToggleConsole_f ();
1259                         return;
1260                 }
1261                 Key_Console (key, ascii);
1262                 return;
1263         }
1264
1265         // handle toggleconsole in menu too
1266         if (keydest == key_menu)
1267         {
1268                 if (down && con_closeontoggleconsole.integer && bind && !strncmp(bind, "toggleconsole", strlen("toggleconsole")) && ascii != STRING_COLOR_TAG)
1269                 {
1270                         Con_ToggleConsole_f ();
1271                         tbl_keydest[key] = key_void; // key release should go nowhere (especially not to key_menu or key_game)
1272                         return;
1273                 }
1274         }
1275
1276         // ignore binds while a video is played, let the video system handle the key event
1277         if (cl_videoplaying)
1278         {
1279                 CL_Video_KeyEvent (key, ascii, keydown[key] != 0);
1280                 return;
1281         }
1282
1283         // anything else is a key press into the game, chat line, or menu
1284         switch (keydest)
1285         {
1286                 case key_message:
1287                         if (down)
1288                                 Key_Message (key, ascii);
1289                         break;
1290                 case key_menu:
1291                 case key_menu_grabbed:
1292                         MR_KeyEvent (key, ascii, down);
1293                         break;
1294                 case key_game:
1295                         q = CL_VM_InputEvent(down, key, ascii);
1296                         // ignore key repeats on binds and only send the bind if the event hasnt been already processed by csqc
1297                         if (!q && bind)
1298                         {
1299                                 if(keydown[key] == 1 && down)
1300                                 {
1301                                         // button commands add keynum as a parm
1302                                         if (bind[0] == '+')
1303                                                 Cbuf_AddText (va("%s %i\n", bind, key));
1304                                         else
1305                                         {
1306                                                 Cbuf_AddText (bind);
1307                                                 Cbuf_AddText ("\n");
1308                                         }
1309                                 } else if(bind[0] == '+' && !down && keydown[key] == 0)
1310                                         Cbuf_AddText(va("-%s %i\n", bind + 1, key));
1311                         }
1312                         break;
1313                 default:
1314                         Con_Printf ("Key_Event: Bad key_dest\n");
1315         }
1316 }
1317
1318 /*
1319 ===================
1320 Key_ClearStates
1321 ===================
1322 */
1323 void
1324 Key_ClearStates (void)
1325 {
1326         memset(keydown, 0, sizeof(keydown));
1327 }