2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
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
10 * $Logfile: /Freespace2/code/Io/Key.cpp $
15 * <insert description of file here>
18 * Revision 1.12 2004/06/11 01:14:12 tigital
19 * OSX: switched to __APPLE__
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
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)
27 * Revision 1.8 2002/06/17 23:11:39 relnev
28 * enable sdl key repeating.
32 * Revision 1.7 2002/06/09 04:41:21 relnev
33 * added copyright header
35 * Revision 1.6 2002/06/05 04:03:32 relnev
36 * finished cfilesystem.
38 * removed some old code.
40 * fixed mouse save off-by-one.
44 * Revision 1.5 2002/05/31 03:34:02 theoddone33
48 * Revision 1.4 2002/05/30 23:46:29 theoddone33
49 * some minor key changes (not necessarily fixes)
51 * Revision 1.3 2002/05/30 16:50:24 theoddone33
52 * Keyboard partially fixed
54 * Revision 1.2 2002/05/29 23:17:50 theoddone33
55 * Non working text code and fixed keys
57 * Revision 1.1.1.1 2002/05/03 03:28:09 root
61 * 6 10/29/99 6:10p Jefff
62 * squashed the damned y/z german issues once and for all
64 * 5 6/07/99 1:21p Dave
65 * Fixed debug console scrolling problem. Thread related.
67 * 4 6/02/99 6:18p Dave
68 * Fixed TNT lockup problems! Wheeeee!
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
75 * 2 10/07/98 10:53a Dave
78 * 1 10/07/98 10:49a Dave
80 * 37 6/19/98 3:50p Lawrance
81 * change GERMAN to GR_BUILD
83 * 36 6/17/98 11:05a Lawrance
84 * translate french and german keys
86 * 35 6/12/98 4:49p Hoffoss
87 * Added code to remap scancodes for german and french keyboards.
89 * 34 5/20/98 12:10a Mike
92 * 33 5/19/98 12:19p Mike
95 * 32 5/19/98 12:28a Mike
98 * 31 5/18/98 11:01p Mike
99 * Adding support for cheat system.
101 * 30 5/11/98 12:09a Lawrance
102 * Put in code to turn on/off NumLock key when running under 95
104 * 29 5/01/98 4:23p Lawrance
105 * Remap the scancode for the UK "\" key
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.
111 * 27 4/13/98 10:16a John
112 * Switched gettime back to timer_get_milliseconds, which is now thread
115 * 26 4/12/98 11:08p Lawrance
116 * switch back to using gettime() in separate threads
118 * 25 4/12/98 5:31p Lawrance
119 * use timer_get_milliseconds() instead of gettime()
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.
125 * 23 1/23/98 3:49p Mike
126 * Fix bug in negative time-down due to latency.
128 * 22 1/07/98 6:41p Lawrance
129 * Pass message latency to the keyboard lib.
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.
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".
140 * 19 9/13/97 9:30a Lawrance
141 * added ability to block certain keys from the keyboard
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.
147 * 17 9/09/97 11:08a Sandeep
148 * fixed warning level 4
150 * 16 7/29/97 5:30p Lawrance
151 * move gettime() to timer module
153 * 15 4/22/97 10:56a John
154 * fixed some resource leaks.
156 * 14 2/03/97 4:23p Allender
157 * use F11 as debug key now
159 * 13 1/10/97 5:15p Mike
160 * Moved ship-specific parameters from obj_subsystem to ship_subsys.
162 * Added turret code to AI system.
167 #include <ctype.h> // for toupper
174 #include "localize.h"
176 #define KEY_BUFFER_SIZE 16
178 //-------- Variable accessed by outside functions ---------
179 ubyte keyd_buffer_type; // 0=No buffer, 1=buffer ASCII, 2=buffer scans
181 uint keyd_last_pressed;
182 uint keyd_last_released;
183 int keyd_time_when_last_pressed;
185 static bool keyd_pressed[SDL_NUM_SCANCODES];
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;
203 CRITICAL_SECTION key_lock;
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
210 // used to limit the keypresses that are accepted from the keyboard
211 #define MAX_FILTER_KEYS 64
213 int Key_filter[MAX_FILTER_KEYS];
215 static int Key_numlock_was_on = 0; // Flag to indicate whether NumLock is on at start
217 int Cheats_enabled = 0;
218 int Key_normal_game = 0;
220 static std::deque<int> key_text_input;
223 void key_set_text_input(int ch)
225 key_text_input.push_back(ch);
228 int key_get_text_input()
230 if ( key_text_input.empty() ) {
234 int ch = key_text_input.front();
236 key_text_input.pop_front();
241 bool key_pressed(int keycode)
243 SDL_Scancode scancode = SDL_GetScancodeFromKey(keycode & KEY_MASK);
245 return keyd_pressed[scancode];
248 int key_numlock_is_on()
251 state = SDL_GetKeyboardState(NULL);
252 if ( state[SDL_SCANCODE_NUMLOCKCLEAR] ) {
259 void key_turn_off_numlock()
263 void key_turn_on_numlock()
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)
271 // bail on non-printable keycodes
272 if (keycode & SDLK_SCANCODE_MASK) {
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)) )
288 // Flush the keyboard buffer.
289 // Clear the keyboard array (keyd_pressed).
295 if ( !key_inited ) return;
297 ENTER_CRITICAL_SECTION(&key_lock);
299 key_data.keyhead = key_data.keytail = 0;
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;
307 //Clear the keyboard array
309 CurTime = timer_get_milliseconds();
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;
321 key_text_input.clear();
323 LEAVE_CRITICAL_SECTION(&key_lock);
326 // A nifty function which performs the function:
327 // n = (n+1) % KEY_BUFFER_SIZE
328 // (assuming positive values of n).
332 if ( n >= KEY_BUFFER_SIZE ) n=0;
336 // Returns 1 if character waiting... 0 otherwise
339 int is_one_waiting = 0;
341 if ( !key_inited ) return 0;
343 ENTER_CRITICAL_SECTION(&key_lock);
345 if (key_data.keytail != key_data.keyhead){
349 LEAVE_CRITICAL_SECTION(&key_lock);
351 return is_one_waiting;
354 // Return keycode if a key has been pressed,
356 // Reads keys out of the key buffer and updates keyhead.
359 SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN;
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);
372 // need to strip key mod state for keycode lookup
373 mod = (scancode & 0xf900);
374 keycode = SDL_GetKeyFromScancode((SDL_Scancode)(scancode & KEY_MASK));
376 return (keycode | mod);
379 // If not installed, uses BIOS and returns getch();
380 // Else returns pending key (or waits for one if none waiting).
386 if ( !key_inited ) return 0;
388 while (!key_checkch()){
398 // Set global shift_status with modifier results (shift, ctrl, alt).
399 uint key_get_shift_status()
401 unsigned int shift_status = 0;
406 SDL_Keymod kmod = SDL_GetModState();
408 if (kmod & KMOD_SHIFT)
409 shift_status |= KEY_SHIFTED;
412 shift_status |= KEY_ALTED;
414 if (kmod & KMOD_CTRL)
415 shift_status |= KEY_CTRLED;
418 if (key_pressed(KEY_DEBUG_KEY))
419 shift_status |= KEY_DEBUGGED;
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;
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)
437 uint time_down, time;
439 SDL_Scancode scancode;
444 scancode = SDL_GetScancodeFromKey(keycode & KEY_MASK);
446 if (scancode == SDL_SCANCODE_UNKNOWN)
449 ENTER_CRITICAL_SECTION(&key_lock);
451 time = timer_get_milliseconds();
452 delta_time = time - key_data.TimeKeyDownChecked[scancode];
453 key_data.TimeKeyDownChecked[scancode] = time;
455 if ( delta_time <= 1 ) {
456 key_data.TimeKeyWentDown[scancode] = time;
457 if (keyd_pressed[scancode]) {
458 LEAVE_CRITICAL_SECTION(&key_lock);
461 LEAVE_CRITICAL_SECTION(&key_lock);
466 if ( !keyd_pressed[scancode] ) {
467 time_down = key_data.TimeKeyHeldDown[scancode];
468 key_data.TimeKeyHeldDown[scancode] = 0;
470 time_down = time - key_data.TimeKeyWentDown[scancode];
471 key_data.TimeKeyWentDown[scancode] = time;
474 LEAVE_CRITICAL_SECTION(&key_lock);
476 return i2fl(time_down) / i2fl(delta_time);
479 // Returns number of times key has went from up to down since last call.
480 int key_down_count(int keycode)
483 SDL_Scancode scancode;
488 scancode = SDL_GetScancodeFromKey(keycode & KEY_MASK);
490 if (scancode == SDL_SCANCODE_UNKNOWN)
493 ENTER_CRITICAL_SECTION(&key_lock);
495 n = key_data.NumDowns[scancode];
496 key_data.NumDowns[scancode] = 0;
498 LEAVE_CRITICAL_SECTION(&key_lock);
504 // Returns number of times key has went from down to up since last call.
505 int key_up_count(int keycode)
508 SDL_Scancode scancode;
513 scancode = SDL_GetScancodeFromKey(keycode & KEY_MASK);
515 if (scancode == SDL_SCANCODE_UNKNOWN)
518 ENTER_CRITICAL_SECTION(&key_lock);
520 n = key_data.NumUps[scancode];
521 key_data.NumUps[scancode] = 0;
523 LEAVE_CRITICAL_SECTION(&key_lock);
528 int key_check(int keycode)
530 SDL_Scancode scancode = SDL_GetScancodeFromKey(keycode & KEY_MASK);
532 return key_data.down_check[scancode];
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 )
540 uint breakbit, temp, event_time;
543 if ( !key_inited ) return;
545 ENTER_CRITICAL_SECTION(&key_lock);
547 Assert( scancode < SDL_NUM_SCANCODES );
549 // ignore GUI key, we use it for specials commands
550 if ( (scancode == SDL_SCANCODE_LGUI) || (scancode == SDL_SCANCODE_RGUI) ) {
554 // for Mac keyboard, make F13 be printscreen
555 if (scancode == SDL_SCANCODE_F13) {
556 scancode = SDL_SCANCODE_PRINTSCREEN;
559 event_time = timer_get_milliseconds() - latency;
560 // event_time = timeGetTime() - latency;
566 keyd_last_released = scancode;
567 keyd_pressed[scancode] = false;
568 key_data.NumUps[scancode]++;
570 if (event_time < key_data.TimeKeyWentDown[scancode])
571 key_data.TimeKeyHeldDown[scancode] = 0;
573 key_data.TimeKeyHeldDown[scancode] += event_time - key_data.TimeKeyWentDown[scancode];
576 keyd_last_pressed = scancode;
577 keyd_time_when_last_pressed = event_time;
578 if (!keyd_pressed[scancode]) {
580 key_data.TimeKeyWentDown[scancode] = event_time;
581 keyd_pressed[scancode] = true;
582 key_data.NumDowns[scancode]++;
583 key_data.down_check[scancode]++;
585 // mprintf(( "Scancode = %x\n", scancode ));
587 // if ( scancode == KEY_BREAK )
591 keycode = (unsigned short)scancode;
593 if (kmod & KMOD_SHIFT)
594 keycode |= KEY_SHIFTED;
597 keycode |= KEY_ALTED;
599 if (kmod & KMOD_CTRL)
600 keycode |= KEY_CTRLED;
603 if ( key_pressed(KEY_DEBUG_KEY) )
604 keycode |= KEY_DEBUGGED;
606 // if ( keycode == (KEY_BACKSP + KEY_DEBUGGED) ) {
608 // keyd_pressed[KEY_DEBUG_KEY] = 0;
609 // keyd_pressed[KEY_BACKSP] = 0;
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;
623 temp = key_data.keytail+1;
624 if ( temp >= KEY_BUFFER_SIZE ) temp=0;
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++ ) {
632 if ( Key_filter[i] == keycode ) {
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;
647 LEAVE_CRITICAL_SECTION(&key_lock);
655 if ( Key_numlock_was_on ) {
656 key_turn_on_numlock();
657 Key_numlock_was_on = 0;
671 keyd_time_when_last_pressed = timer_get_milliseconds();
672 keyd_buffer_type = 1;
674 // Clear the keyboard array
683 void key_level_init()
687 for (i=0; i<SDL_NUM_SCANCODES; i++)
688 key_data.down_check[i] = 0;
691 void key_lost_focus()
707 // Restricts the keys that are accepted from the keyboard
709 // filter_array => array of keys to act as a filter
710 // num => number of keys in filter_array
712 void key_set_filter(int *filter_array, int num)
716 if ( num >= MAX_FILTER_KEYS ) {
718 num = MAX_FILTER_KEYS;
721 Num_filter_keys = num;
723 for ( i = 0; i < num; i++ ) {
724 Key_filter[i] = filter_array[i];
728 // Clear the key filter, so all keypresses are accepted from keyboard
730 void key_clear_filter()
735 for ( i = 0; i < MAX_FILTER_KEYS; i++ ) {