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