]> icculus.org git repositories - taylor/freespace2.git/blob - src/io/key.cpp
SDL2 port - stage 4
[taylor/freespace2.git] / src / io / key.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/Io/Key.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * <insert description of file here>
16  *
17  * $Log$
18  * Revision 1.12  2004/06/11 01:14:12  tigital
19  * OSX: switched to __APPLE__
20  *
21  * Revision 1.11  2003/05/18 03:57:08  taylor
22  * do not swap German z and y keys if they are already swapped
23  *
24  * Revision 1.9  2003/01/30 19:55:01  relnev
25  * add German keys (this is mostly a patch already sent in by someone else that hasn't made it into cvs yet) (Taylor Richards)
26  *
27  * Revision 1.8  2002/06/17 23:11:39  relnev
28  * enable sdl key repeating.
29  *
30  * swap '/` keys.
31  *
32  * Revision 1.7  2002/06/09 04:41:21  relnev
33  * added copyright header
34  *
35  * Revision 1.6  2002/06/05 04:03:32  relnev
36  * finished cfilesystem.
37  *
38  * removed some old code.
39  *
40  * fixed mouse save off-by-one.
41  *
42  * sound cleanups.
43  *
44  * Revision 1.5  2002/05/31 03:34:02  theoddone33
45  * Fix Keyboard
46  * Add titlebar
47  *
48  * Revision 1.4  2002/05/30 23:46:29  theoddone33
49  * some minor key changes (not necessarily fixes)
50  *
51  * Revision 1.3  2002/05/30 16:50:24  theoddone33
52  * Keyboard partially fixed
53  *
54  * Revision 1.2  2002/05/29 23:17:50  theoddone33
55  * Non working text code and fixed keys
56  *
57  * Revision 1.1.1.1  2002/05/03 03:28:09  root
58  * Initial import.
59  *
60  * 
61  * 6     10/29/99 6:10p Jefff
62  * squashed the damned y/z german issues once and for all
63  * 
64  * 5     6/07/99 1:21p Dave
65  * Fixed debug console scrolling problem. Thread related.
66  * 
67  * 4     6/02/99 6:18p Dave
68  * Fixed TNT lockup problems! Wheeeee!
69  * 
70  * 3     11/05/98 4:18p Dave
71  * First run nebula support. Beefed up localization a bit. Removed all
72  * conditional compiles for foreign versions. Modified mission file
73  * format.
74  * 
75  * 2     10/07/98 10:53a Dave
76  * Initial checkin.
77  * 
78  * 1     10/07/98 10:49a Dave
79  * 
80  * 37    6/19/98 3:50p Lawrance
81  * change GERMAN to GR_BUILD
82  * 
83  * 36    6/17/98 11:05a Lawrance
84  * translate french and german keys
85  * 
86  * 35    6/12/98 4:49p Hoffoss
87  * Added code to remap scancodes for german and french keyboards.
88  * 
89  * 34    5/20/98 12:10a Mike
90  * Remove mprintfs.
91  * 
92  * 33    5/19/98 12:19p Mike
93  * Cheat codes!
94  * 
95  * 32    5/19/98 12:28a Mike
96  * Cheat stuff.
97  * 
98  * 31    5/18/98 11:01p Mike
99  * Adding support for cheat system.
100  * 
101  * 30    5/11/98 12:09a Lawrance
102  * Put in code to turn on/off NumLock key when running under 95
103  * 
104  * 29    5/01/98 4:23p Lawrance
105  * Remap the scancode for the UK "\" key
106  * 
107  * 28    4/18/98 12:42p John
108  * Added code to use DirectInput to read keyboard. Took out because it
109  * didn't differentiate btwn Pause and Numlock and sometimes Ctrl.
110  * 
111  * 27    4/13/98 10:16a John
112  * Switched gettime back to timer_get_milliseconds, which is now thread
113  * safe.
114  * 
115  * 26    4/12/98 11:08p Lawrance
116  * switch back to using gettime() in separate threads
117  * 
118  * 25    4/12/98 5:31p Lawrance
119  * use timer_get_milliseconds() instead of gettime()
120  * 
121  * 24    3/25/98 8:08p John
122  * Restructured software rendering into two modules; One for windowed
123  * debug mode and one for DirectX fullscreen.   
124  * 
125  * 23    1/23/98 3:49p Mike
126  * Fix bug in negative time-down due to latency.
127  * 
128  * 22    1/07/98 6:41p Lawrance
129  * Pass message latency to the keyboard lib.
130  * 
131  * 21    11/17/97 10:42a John
132  * On Debug+Backsp, cleared out keys so that it looks like nothing ever
133  * happened, so they're not stuck down.
134  * 
135  * 20    11/14/97 4:33p Mike
136  * Change Debug key to backquote (from F11).
137  * Balance a ton of subsystems in ships.tbl.
138  * Change "Heavy Laser" to "Disruptor".
139  * 
140  * 19    9/13/97 9:30a Lawrance
141  * added ability to block certain keys from the keyboard
142  * 
143  * 18    9/10/97 6:02p Hoffoss
144  * Added code to check for key-pressed sexp operator in FreeSpace as part
145  * of training mission stuff.
146  * 
147  * 17    9/09/97 11:08a Sandeep
148  * fixed warning level 4
149  * 
150  * 16    7/29/97 5:30p Lawrance
151  * move gettime() to timer module
152  * 
153  * 15    4/22/97 10:56a John
154  * fixed some resource leaks.
155  * 
156  * 14    2/03/97 4:23p Allender
157  * use F11 as debug key now
158  * 
159  * 13    1/10/97 5:15p Mike
160  * Moved ship-specific parameters from obj_subsystem to ship_subsys.
161  * 
162  * Added turret code to AI system.
163  *
164  * $NoKeywords: $
165  */
166
167 #include <ctype.h>      // for toupper
168 #include <deque>
169 #include "pstypes.h"
170 #include "key.h"
171 #include "fix.h"
172 #include "timer.h"
173 #include "osapi.h"
174 #include "localize.h"
175
176 #define KEY_BUFFER_SIZE 16
177
178 //-------- Variable accessed by outside functions ---------
179 ubyte                           keyd_buffer_type;               // 0=No buffer, 1=buffer ASCII, 2=buffer scans
180 ubyte                           keyd_repeat;
181 uint                            keyd_last_pressed;
182 uint                            keyd_last_released;
183 int                             keyd_time_when_last_pressed;
184
185 static bool             keyd_pressed[SDL_NUM_SCANCODES];
186
187 typedef struct keyboard {
188         ushort                  keybuffer[KEY_BUFFER_SIZE];
189         uint                            time_pressed[KEY_BUFFER_SIZE];
190         uint                            TimeKeyWentDown[SDL_NUM_SCANCODES];
191         uint                            TimeKeyHeldDown[SDL_NUM_SCANCODES];
192         uint                            TimeKeyDownChecked[SDL_NUM_SCANCODES];
193         uint                            NumDowns[SDL_NUM_SCANCODES];
194         uint                            NumUps[SDL_NUM_SCANCODES];
195         int                             down_check[SDL_NUM_SCANCODES];  // nonzero if has been pressed yet this mission
196         uint                            keyhead, keytail;
197 } keyboard;
198
199 keyboard key_data;
200
201 int key_inited = 0;
202
203 CRITICAL_SECTION key_lock;
204
205 //int Backspace_debug=1;        // global flag that will enable/disable the backspace key from stopping execution
206                                                                 // This flag was created since the backspace key is also used to correct mistakes
207                                                                 // when typing in your pilots callsign.  This global flag is checked before execution
208                                                                 // is stopped.
209
210 // used to limit the keypresses that are accepted from the keyboard
211 #define MAX_FILTER_KEYS 64
212 int Num_filter_keys;
213 int Key_filter[MAX_FILTER_KEYS];
214
215 static int Key_numlock_was_on = 0;      // Flag to indicate whether NumLock is on at start
216
217 int Cheats_enabled = 0;
218 int Key_normal_game = 0;
219
220 static std::deque<int> key_text_input;
221
222
223 void key_set_text_input(int ch)
224 {
225         key_text_input.push_back(ch);
226 }
227
228 int key_get_text_input()
229 {
230         if ( key_text_input.empty() ) {
231                 return -1;
232         }
233
234         int ch = key_text_input.front();
235
236         key_text_input.pop_front();
237
238         return ch;
239 }
240
241 bool key_pressed(int keycode)
242 {
243         SDL_Scancode scancode = SDL_GetScancodeFromKey(keycode & KEY_MASK);
244
245         return keyd_pressed[scancode];
246 }
247
248 int key_numlock_is_on()
249 {
250         const Uint8 *state;
251         state = SDL_GetKeyboardState(NULL);
252         if ( state[SDL_SCANCODE_NUMLOCKCLEAR] ) {
253                 return 1;
254         }
255
256         return 0;
257 }
258
259 void key_turn_off_numlock()
260 {
261 }
262
263 void key_turn_on_numlock()
264 {
265 }
266
267 // checks if a keycode is ASCII printable or not
268 // returns true if ASCII, false if not
269 bool key_is_ascii(int keycode)
270 {
271         // bail on non-printable keycodes
272         if (keycode & SDLK_SCANCODE_MASK) {
273                 return false;
274         }
275
276         keycode &= KEY_MASK;
277
278         // this is definitely never come back to bite me in the ass
279         if ( ((keycode >= SDLK_SPACE) && (keycode <= SDLK_AT))
280                         || ((keycode >= SDLK_LEFTBRACKET) && (keycode <= SDLK_z)) )
281         {
282                 return true;
283         }
284
285         return false;
286 }
287
288 //      Flush the keyboard buffer.
289 //      Clear the keyboard array (keyd_pressed).
290 void key_flush()
291 {
292         int i;
293         uint CurTime;
294
295         if ( !key_inited ) return;
296
297         ENTER_CRITICAL_SECTION(&key_lock);      
298
299         key_data.keyhead = key_data.keytail = 0;
300
301         //Clear the keyboard buffer
302         for (i=0; i<KEY_BUFFER_SIZE; i++ )      {
303                 key_data.keybuffer[i] = 0;
304                 key_data.time_pressed[i] = 0;
305         }
306         
307         //Clear the keyboard array
308
309         CurTime = timer_get_milliseconds();
310
311
312         for (i=0; i<SDL_NUM_SCANCODES; i++ )    {
313                 keyd_pressed[i] = false;
314                 key_data.TimeKeyDownChecked[i] = CurTime;
315                 key_data.TimeKeyWentDown[i] = CurTime;
316                 key_data.TimeKeyHeldDown[i] = 0;
317                 key_data.NumDowns[i]=0;
318                 key_data.NumUps[i]=0;
319         }
320
321         key_text_input.clear();
322
323         LEAVE_CRITICAL_SECTION(&key_lock);      
324 }
325
326 //      A nifty function which performs the function:
327 //              n = (n+1) % KEY_BUFFER_SIZE
328 //      (assuming positive values of n).
329 int add_one( int n )
330 {
331         n++;
332         if ( n >= KEY_BUFFER_SIZE ) n=0;
333         return n;
334 }
335
336 // Returns 1 if character waiting... 0 otherwise
337 int key_checkch()
338 {
339         int is_one_waiting = 0;
340
341         if ( !key_inited ) return 0;
342
343         ENTER_CRITICAL_SECTION(&key_lock);      
344
345         if (key_data.keytail != key_data.keyhead){
346                 is_one_waiting = 1;
347         }
348
349         LEAVE_CRITICAL_SECTION(&key_lock);              
350
351         return is_one_waiting;
352 }
353
354 //      Return keycode if a key has been pressed,
355 //      else return 0.
356 //      Reads keys out of the key buffer and updates keyhead.
357 int key_inkey()
358 {
359         SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN;
360         int mod, keycode;
361
362         if ( !key_inited )
363                 return 0;
364
365         if (key_data.keytail != key_data.keyhead) {
366                 scancode = (SDL_Scancode)key_data.keybuffer[key_data.keyhead];
367                 key_data.keyhead = add_one(key_data.keyhead);
368         } else {
369                 return 0;
370         }
371
372         // need to strip key mod state for keycode lookup
373         mod = (scancode & 0xf900);
374         keycode = SDL_GetKeyFromScancode((SDL_Scancode)(scancode & KEY_MASK));
375
376         return (keycode | mod);
377 }
378
379 // If not installed, uses BIOS and returns getch();
380 //      Else returns pending key (or waits for one if none waiting).
381 int key_getch()
382 {
383         int dummy=0;
384         int in;
385
386         if ( !key_inited ) return 0;
387         
388         while (!key_checkch()){
389                 os_poll();
390
391                 dummy++;
392         }
393         in = key_inkey();
394
395         return in;
396 }
397
398 //      Set global shift_status with modifier results (shift, ctrl, alt).
399 uint key_get_shift_status()
400 {
401         unsigned int shift_status = 0;
402
403         if ( !key_inited )
404                 return 0;
405
406         SDL_Keymod kmod = SDL_GetModState();
407
408         if (kmod & KMOD_SHIFT)
409                 shift_status |= KEY_SHIFTED;
410
411         if (kmod & KMOD_ALT)
412                 shift_status |= KEY_ALTED;
413
414         if (kmod & KMOD_CTRL)
415                 shift_status |= KEY_CTRLED;
416
417 #ifndef NDEBUG
418         if (key_pressed(KEY_DEBUG_KEY))
419                 shift_status |= KEY_DEBUGGED;
420 #else
421         if (key_pressed(KEY_DEBUG_KEY)) {
422                 mprintf(("Cheats_enabled = %i, Key_normal_game = %i\n", Cheats_enabled, Key_normal_game));
423                 if ((Cheats_enabled) && Key_normal_game) {
424                         mprintf(("Debug key\n"));
425                         shift_status |= KEY_DEBUGGED1;
426                 }
427         }
428 #endif
429
430         return shift_status;
431 }
432
433 //      Returns amount of time key (specified by "keycode") has been down since last call.
434 //      Returns float, unlike key_down_time() which returns a fix.
435 float key_down_timef(int keycode)
436 {
437         uint time_down, time;
438         uint delta_time;
439         SDL_Scancode scancode;
440
441         if ( !key_inited )
442                 return 0.0f;
443
444         scancode = SDL_GetScancodeFromKey(keycode & KEY_MASK);
445
446         if (scancode == SDL_SCANCODE_UNKNOWN)
447                 return 0.0f;
448
449         ENTER_CRITICAL_SECTION(&key_lock);              
450
451         time = timer_get_milliseconds();
452         delta_time = time - key_data.TimeKeyDownChecked[scancode];
453         key_data.TimeKeyDownChecked[scancode] = time;
454
455         if ( delta_time <= 1 ) {
456                 key_data.TimeKeyWentDown[scancode] = time;
457                 if (keyd_pressed[scancode])     {
458                         LEAVE_CRITICAL_SECTION(&key_lock);              
459                         return 1.0f;
460                 } else  {
461                         LEAVE_CRITICAL_SECTION(&key_lock);              
462                         return 0.0f;
463                 }
464         }
465
466         if ( !keyd_pressed[scancode] )  {
467                 time_down = key_data.TimeKeyHeldDown[scancode];
468                 key_data.TimeKeyHeldDown[scancode] = 0;
469         } else  {
470                 time_down =  time - key_data.TimeKeyWentDown[scancode];
471                 key_data.TimeKeyWentDown[scancode] = time;
472         }
473
474         LEAVE_CRITICAL_SECTION(&key_lock);              
475
476         return i2fl(time_down) / i2fl(delta_time);
477 }
478
479 // Returns number of times key has went from up to down since last call.
480 int key_down_count(int keycode)
481 {
482         int n;
483         SDL_Scancode scancode;
484
485         if ( !key_inited )
486                 return 0;
487
488         scancode = SDL_GetScancodeFromKey(keycode & KEY_MASK);
489
490         if (scancode == SDL_SCANCODE_UNKNOWN)
491                 return 0;
492
493         ENTER_CRITICAL_SECTION(&key_lock);              
494
495         n = key_data.NumDowns[scancode];
496         key_data.NumDowns[scancode] = 0;
497
498         LEAVE_CRITICAL_SECTION(&key_lock);              
499
500         return n;
501 }
502
503
504 // Returns number of times key has went from down to up since last call.
505 int key_up_count(int keycode)
506 {
507         int n;
508         SDL_Scancode scancode;
509
510         if ( !key_inited )
511                 return 0;
512
513         scancode = SDL_GetScancodeFromKey(keycode & KEY_MASK);
514
515         if (scancode == SDL_SCANCODE_UNKNOWN)
516                 return 0;
517
518         ENTER_CRITICAL_SECTION(&key_lock);              
519
520         n = key_data.NumUps[scancode];
521         key_data.NumUps[scancode] = 0;
522
523         LEAVE_CRITICAL_SECTION(&key_lock);              
524
525         return n;
526 }
527
528 int key_check(int keycode)
529 {
530         SDL_Scancode scancode = SDL_GetScancodeFromKey(keycode & KEY_MASK);
531
532         return key_data.down_check[scancode];
533 }
534
535 //      Add a key up or down code to the key buffer.  state=1 -> down, state=0 -> up
536 // latency => time difference in ms between when key was actually pressed and now
537 //void key_mark( uint code, int state )
538 void key_mark(SDL_Scancode scancode, int state, ushort kmod, uint latency )
539 {
540         uint breakbit, temp, event_time;
541         ushort keycode;
542
543         if ( !key_inited ) return;
544
545         ENTER_CRITICAL_SECTION(&key_lock);              
546
547         Assert( scancode < SDL_NUM_SCANCODES );
548
549         // ignore GUI key, we use it for specials commands
550         if ( (scancode == SDL_SCANCODE_LGUI) || (scancode == SDL_SCANCODE_RGUI) ) {
551                 return;
552         }
553
554         // for Mac keyboard, make F13 be printscreen
555         if (scancode == SDL_SCANCODE_F13) {
556                 scancode = SDL_SCANCODE_PRINTSCREEN;
557         }
558
559         event_time = timer_get_milliseconds() - latency;
560         // event_time = timeGetTime() - latency;
561
562         breakbit = !state;
563         
564         if (breakbit)   {
565                 // Key going up
566                 keyd_last_released = scancode;
567                 keyd_pressed[scancode] = false;
568                 key_data.NumUps[scancode]++;
569
570                 if (event_time < key_data.TimeKeyWentDown[scancode])
571                         key_data.TimeKeyHeldDown[scancode] = 0;
572                 else
573                         key_data.TimeKeyHeldDown[scancode] += event_time - key_data.TimeKeyWentDown[scancode];
574         } else {
575                 // Key going down
576                 keyd_last_pressed = scancode;
577                 keyd_time_when_last_pressed = event_time;
578                 if (!keyd_pressed[scancode])    {
579                         // First time down
580                         key_data.TimeKeyWentDown[scancode] = event_time;
581                         keyd_pressed[scancode] = true;
582                         key_data.NumDowns[scancode]++;
583                         key_data.down_check[scancode]++;
584
585 //                      mprintf(( "Scancode = %x\n", scancode ));
586
587 //                      if ( scancode == KEY_BREAK )
588 //                              Int3();
589                 } 
590
591                 keycode = (unsigned short)scancode;
592
593                 if (kmod & KMOD_SHIFT)
594                         keycode |= KEY_SHIFTED;
595
596                 if (kmod & KMOD_ALT)
597                         keycode |= KEY_ALTED;
598
599                 if (kmod & KMOD_CTRL)
600                         keycode |= KEY_CTRLED;
601
602 #ifndef NDEBUG
603                 if ( key_pressed(KEY_DEBUG_KEY) )
604                         keycode |= KEY_DEBUGGED;
605
606 //                      if ( keycode == (KEY_BACKSP + KEY_DEBUGGED) )   {
607 //                              keycode = 0;
608 //                              keyd_pressed[KEY_DEBUG_KEY] = 0;
609 //                              keyd_pressed[KEY_BACKSP] = 0;
610 //                              Int3();
611 //                      }
612 #else
613                 if ( keyd_pressed(KEY_DEBUG_KEY) ) {
614                         mprintf(("Cheats_enabled = %i, Key_normal_game = %i\n", Cheats_enabled, Key_normal_game));
615                         if (Cheats_enabled && Key_normal_game) {
616                                 keycode |= KEY_DEBUGGED1;
617                         }
618                 }
619
620 #endif
621
622                 if ( keycode )  {
623                         temp = key_data.keytail+1;
624                         if ( temp >= KEY_BUFFER_SIZE ) temp=0;
625
626                         if (temp!=key_data.keyhead)     {
627                                 int i, accept_key = 1;
628                                 // Num_filter_keys will only be non-zero when a key filter has
629                                 // been explicity set up via key_set_filter()
630                                 for ( i = 0; i < Num_filter_keys; i++ ) {
631                                         accept_key = 0;
632                                         if ( Key_filter[i] == keycode ) {
633                                                 accept_key = 1;
634                                                 break;
635                                         }
636                                 }
637
638                                 if ( accept_key ) {
639                                         key_data.keybuffer[key_data.keytail] = keycode;
640                                         key_data.time_pressed[key_data.keytail] = keyd_time_when_last_pressed;
641                                         key_data.keytail = temp;
642                                 }
643                         }
644                 }
645         }
646
647         LEAVE_CRITICAL_SECTION(&key_lock);              
648 }
649
650 void key_close()
651 {
652         if ( !key_inited )
653                 return;
654
655         if ( Key_numlock_was_on ) {
656                 key_turn_on_numlock();
657                 Key_numlock_was_on = 0;
658         }
659
660         key_inited = 0;
661 }
662
663 void key_init()
664 {
665         // Initialize queue
666         if (key_inited)
667                 return;
668
669         key_inited = 1;
670
671         keyd_time_when_last_pressed = timer_get_milliseconds();
672         keyd_buffer_type = 1;
673
674         // Clear the keyboard array
675         key_flush();
676
677         // Clear key filter
678         key_clear_filter();
679
680         atexit(key_close);
681 }
682
683 void key_level_init()
684 {
685         int i;
686
687         for (i=0; i<SDL_NUM_SCANCODES; i++)
688                 key_data.down_check[i] = 0;
689 }
690
691 void key_lost_focus()
692 {
693         if ( !key_inited )
694                 return;
695
696         key_flush();    
697 }
698
699 void key_got_focus()
700 {
701         if ( !key_inited )
702                 return;
703         
704         key_flush();    
705 }
706
707 // Restricts the keys that are accepted from the keyboard
708 //
709 //      filter_array    =>              array of keys to act as a filter
710 //      num                             =>              number of keys in filter_array
711 //
712 void key_set_filter(int *filter_array, int num)
713 {
714         int i;
715
716         if ( num >= MAX_FILTER_KEYS ) {
717                 Int3();
718                 num = MAX_FILTER_KEYS;
719         }
720
721         Num_filter_keys = num;
722
723         for ( i = 0; i < num; i++ ) {
724                 Key_filter[i] = filter_array[i];
725         }
726 }
727
728 // Clear the key filter, so all keypresses are accepted from keyboard 
729 //
730 void key_clear_filter()
731 {
732         int i;
733
734         Num_filter_keys = 0;
735         for ( i = 0; i < MAX_FILTER_KEYS; i++ ) {
736                 Key_filter[i] = -1;
737         }
738 }