]> icculus.org git repositories - taylor/freespace2.git/blob - src/io/key.cpp
Oops.
[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.11  2003/05/18 03:57:08  taylor
19  * do not swap German z and y keys if they are already swapped
20  *
21  * Revision 1.9  2003/01/30 19:55:01  relnev
22  * add German keys (this is mostly a patch already sent in by someone else that hasn't made it into cvs yet) (Taylor Richards)
23  *
24  * Revision 1.8  2002/06/17 23:11:39  relnev
25  * enable sdl key repeating.
26  *
27  * swap '/` keys.
28  *
29  * Revision 1.7  2002/06/09 04:41:21  relnev
30  * added copyright header
31  *
32  * Revision 1.6  2002/06/05 04:03:32  relnev
33  * finished cfilesystem.
34  *
35  * removed some old code.
36  *
37  * fixed mouse save off-by-one.
38  *
39  * sound cleanups.
40  *
41  * Revision 1.5  2002/05/31 03:34:02  theoddone33
42  * Fix Keyboard
43  * Add titlebar
44  *
45  * Revision 1.4  2002/05/30 23:46:29  theoddone33
46  * some minor key changes (not necessarily fixes)
47  *
48  * Revision 1.3  2002/05/30 16:50:24  theoddone33
49  * Keyboard partially fixed
50  *
51  * Revision 1.2  2002/05/29 23:17:50  theoddone33
52  * Non working text code and fixed keys
53  *
54  * Revision 1.1.1.1  2002/05/03 03:28:09  root
55  * Initial import.
56  *
57  * 
58  * 6     10/29/99 6:10p Jefff
59  * squashed the damned y/z german issues once and for all
60  * 
61  * 5     6/07/99 1:21p Dave
62  * Fixed debug console scrolling problem. Thread related.
63  * 
64  * 4     6/02/99 6:18p Dave
65  * Fixed TNT lockup problems! Wheeeee!
66  * 
67  * 3     11/05/98 4:18p Dave
68  * First run nebula support. Beefed up localization a bit. Removed all
69  * conditional compiles for foreign versions. Modified mission file
70  * format.
71  * 
72  * 2     10/07/98 10:53a Dave
73  * Initial checkin.
74  * 
75  * 1     10/07/98 10:49a Dave
76  * 
77  * 37    6/19/98 3:50p Lawrance
78  * change GERMAN to GR_BUILD
79  * 
80  * 36    6/17/98 11:05a Lawrance
81  * translate french and german keys
82  * 
83  * 35    6/12/98 4:49p Hoffoss
84  * Added code to remap scancodes for german and french keyboards.
85  * 
86  * 34    5/20/98 12:10a Mike
87  * Remove mprintfs.
88  * 
89  * 33    5/19/98 12:19p Mike
90  * Cheat codes!
91  * 
92  * 32    5/19/98 12:28a Mike
93  * Cheat stuff.
94  * 
95  * 31    5/18/98 11:01p Mike
96  * Adding support for cheat system.
97  * 
98  * 30    5/11/98 12:09a Lawrance
99  * Put in code to turn on/off NumLock key when running under 95
100  * 
101  * 29    5/01/98 4:23p Lawrance
102  * Remap the scancode for the UK "\" key
103  * 
104  * 28    4/18/98 12:42p John
105  * Added code to use DirectInput to read keyboard. Took out because it
106  * didn't differentiate btwn Pause and Numlock and sometimes Ctrl.
107  * 
108  * 27    4/13/98 10:16a John
109  * Switched gettime back to timer_get_milliseconds, which is now thread
110  * safe.
111  * 
112  * 26    4/12/98 11:08p Lawrance
113  * switch back to using gettime() in separate threads
114  * 
115  * 25    4/12/98 5:31p Lawrance
116  * use timer_get_milliseconds() instead of gettime()
117  * 
118  * 24    3/25/98 8:08p John
119  * Restructured software rendering into two modules; One for windowed
120  * debug mode and one for DirectX fullscreen.   
121  * 
122  * 23    1/23/98 3:49p Mike
123  * Fix bug in negative time-down due to latency.
124  * 
125  * 22    1/07/98 6:41p Lawrance
126  * Pass message latency to the keyboard lib.
127  * 
128  * 21    11/17/97 10:42a John
129  * On Debug+Backsp, cleared out keys so that it looks like nothing ever
130  * happened, so they're not stuck down.
131  * 
132  * 20    11/14/97 4:33p Mike
133  * Change Debug key to backquote (from F11).
134  * Balance a ton of subsystems in ships.tbl.
135  * Change "Heavy Laser" to "Disruptor".
136  * 
137  * 19    9/13/97 9:30a Lawrance
138  * added ability to block certain keys from the keyboard
139  * 
140  * 18    9/10/97 6:02p Hoffoss
141  * Added code to check for key-pressed sexp operator in FreeSpace as part
142  * of training mission stuff.
143  * 
144  * 17    9/09/97 11:08a Sandeep
145  * fixed warning level 4
146  * 
147  * 16    7/29/97 5:30p Lawrance
148  * move gettime() to timer module
149  * 
150  * 15    4/22/97 10:56a John
151  * fixed some resource leaks.
152  * 
153  * 14    2/03/97 4:23p Allender
154  * use F11 as debug key now
155  * 
156  * 13    1/10/97 5:15p Mike
157  * Moved ship-specific parameters from obj_subsystem to ship_subsys.
158  * 
159  * Added turret code to AI system.
160  *
161  * $NoKeywords: $
162  */
163
164 //#define USE_DIRECTINPUT
165
166 #ifndef PLAT_UNIX
167 #include <windows.h>
168 #include <windowsx.h>
169 #endif
170
171 #include <ctype.h>      // for toupper
172 #include "pstypes.h"
173 #include "key.h"
174 #include "fix.h"
175 #include "timer.h"
176 #include "osapi.h"
177 #include "localize.h"
178
179 #define KEY_BUFFER_SIZE 16
180
181 //-------- Variable accessed by outside functions ---------
182 ubyte                           keyd_buffer_type;               // 0=No buffer, 1=buffer ASCII, 2=buffer scans
183 ubyte                           keyd_repeat;
184 uint                            keyd_last_pressed;
185 uint                            keyd_last_released;
186 ubyte                           keyd_pressed[NUM_KEYS];
187 int                             keyd_time_when_last_pressed;
188
189 typedef struct keyboard {
190         ushort                  keybuffer[KEY_BUFFER_SIZE];
191         uint                            time_pressed[KEY_BUFFER_SIZE];
192         uint                            TimeKeyWentDown[NUM_KEYS];
193         uint                            TimeKeyHeldDown[NUM_KEYS];
194         uint                            TimeKeyDownChecked[NUM_KEYS];
195         uint                            NumDowns[NUM_KEYS];
196         uint                            NumUps[NUM_KEYS];
197         int                             down_check[NUM_KEYS];  // nonzero if has been pressed yet this mission
198         uint                            keyhead, keytail;
199 } keyboard;
200
201 keyboard key_data;
202
203 int key_inited = 0;
204
205 CRITICAL_SECTION key_lock;
206
207 //int Backspace_debug=1;        // global flag that will enable/disable the backspace key from stopping execution
208                                                                 // This flag was created since the backspace key is also used to correct mistakes
209                                                                 // when typing in your pilots callsign.  This global flag is checked before execution
210                                                                 // is stopped.
211
212 #ifdef PLAT_UNIX
213 int SDLtoFS2[SDLK_LAST];
214 #endif
215
216 int ascii_table[128] = 
217 { 255, 255, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=',255,255,
218   'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', 255, 255,
219   'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', 39, '`',
220   255, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 255,'*',
221   255, ' ', 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,255,255,
222   255, 255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
223   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
224   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
225   255,255,255,255,255,255,255,255 };
226
227 int shifted_ascii_table[128] = 
228 { 255, 255, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+',255,255,
229   'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', 255, 255,
230   'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', 
231   255, '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', 255,255,
232   255, ' ', 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,255,255,
233   255, 255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
234   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
235   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
236   255,255,255,255,255,255,255,255 };
237
238 // used to limit the keypresses that are accepted from the keyboard
239 #define MAX_FILTER_KEYS 64
240 int Num_filter_keys;
241 int Key_filter[MAX_FILTER_KEYS];
242
243 static int Key_numlock_was_on = 0;      // Flag to indicate whether NumLock is on at start
244 static int Key_running_NT = 0;          // NT is the OS
245
246 int Cheats_enabled = 0;
247 int Key_normal_game = 0;
248
249 #ifdef PLAT_UNIX
250 void FillSDLArray ()
251 {
252         SDLtoFS2[SDLK_0] = KEY_0;
253         SDLtoFS2[SDLK_1] = KEY_1;
254         SDLtoFS2[SDLK_2] = KEY_2;
255         SDLtoFS2[SDLK_3] = KEY_3;
256         SDLtoFS2[SDLK_4] = KEY_4;
257         SDLtoFS2[SDLK_5] = KEY_5;
258         SDLtoFS2[SDLK_6] = KEY_6;
259         SDLtoFS2[SDLK_7] = KEY_7;
260         SDLtoFS2[SDLK_8] = KEY_8;
261         SDLtoFS2[SDLK_9] = KEY_9;
262
263         SDLtoFS2[SDLK_a] = KEY_A;
264         SDLtoFS2[SDLK_b] = KEY_B;
265         SDLtoFS2[SDLK_c] = KEY_C;
266         SDLtoFS2[SDLK_d] = KEY_D;
267         SDLtoFS2[SDLK_e] = KEY_E;
268         SDLtoFS2[SDLK_f] = KEY_F;
269         SDLtoFS2[SDLK_g] = KEY_G;
270         SDLtoFS2[SDLK_h] = KEY_H;
271         SDLtoFS2[SDLK_i] = KEY_I;
272         SDLtoFS2[SDLK_j] = KEY_J;
273         SDLtoFS2[SDLK_k] = KEY_K;
274         SDLtoFS2[SDLK_l] = KEY_L;
275         SDLtoFS2[SDLK_m] = KEY_M;
276         SDLtoFS2[SDLK_n] = KEY_N;
277         SDLtoFS2[SDLK_o] = KEY_O;
278         SDLtoFS2[SDLK_p] = KEY_P;
279         SDLtoFS2[SDLK_q] = KEY_Q;
280         SDLtoFS2[SDLK_r] = KEY_R;
281         SDLtoFS2[SDLK_s] = KEY_S;
282         SDLtoFS2[SDLK_t] = KEY_T;
283         SDLtoFS2[SDLK_u] = KEY_U;
284         SDLtoFS2[SDLK_v] = KEY_V;
285         SDLtoFS2[SDLK_w] = KEY_W;
286         SDLtoFS2[SDLK_x] = KEY_X;
287         SDLtoFS2[SDLK_y] = KEY_Y;
288         SDLtoFS2[SDLK_z] = KEY_Z;
289
290         if (Lcl_gr) {
291                 SDLtoFS2[SDLK_WORLD_63] = KEY_MINUS;
292                 SDLtoFS2[SDLK_WORLD_20] = KEY_EQUAL;
293                 SDLtoFS2[SDLK_MINUS] = KEY_DIVIDE;
294                 SDLtoFS2[SDLK_HASH] = KEY_SLASH;
295                 SDLtoFS2[SDLK_COMMA] = KEY_COMMA;
296                 SDLtoFS2[SDLK_PERIOD] = KEY_PERIOD;
297                 SDLtoFS2[SDLK_WORLD_86] = KEY_SEMICOL;
298
299                 SDLtoFS2[SDLK_WORLD_92] = KEY_LBRACKET;
300                 SDLtoFS2[SDLK_PLUS] = KEY_RBRACKET;
301
302                 SDLtoFS2[SDLK_CARET] = KEY_LAPOSTRO;
303                 SDLtoFS2[SDLK_WORLD_68] = KEY_RAPOSTRO;
304         } else {
305                 SDLtoFS2[SDLK_MINUS] = KEY_MINUS;
306                 SDLtoFS2[SDLK_EQUALS] = KEY_EQUAL;
307                 SDLtoFS2[SDLK_SLASH] = KEY_DIVIDE; // No idea - DDOI
308                 SDLtoFS2[SDLK_BACKSLASH] = KEY_SLASH;
309                 //SDLtoFS2[SDLK_BACKSLASH] = KEY_SLASH_UK; // ?
310                 SDLtoFS2[SDLK_COMMA] = KEY_COMMA;
311                 SDLtoFS2[SDLK_PERIOD] = KEY_PERIOD;
312                 SDLtoFS2[SDLK_SEMICOLON] = KEY_SEMICOL;
313
314                 SDLtoFS2[SDLK_LEFTBRACKET] = KEY_LBRACKET;
315                 SDLtoFS2[SDLK_RIGHTBRACKET] = KEY_RBRACKET;
316
317                 SDLtoFS2[SDLK_BACKQUOTE] = KEY_LAPOSTRO;
318                 SDLtoFS2[SDLK_QUOTE] = KEY_RAPOSTRO;
319         }
320
321         SDLtoFS2[SDLK_ESCAPE] = KEY_ESC;
322         SDLtoFS2[SDLK_RETURN] = KEY_ENTER;
323         SDLtoFS2[SDLK_BACKSPACE] = KEY_BACKSP;
324         SDLtoFS2[SDLK_TAB] = KEY_TAB;
325         SDLtoFS2[SDLK_SPACE] = KEY_SPACEBAR;
326
327         SDLtoFS2[SDLK_NUMLOCK] = KEY_NUMLOCK;
328         SDLtoFS2[SDLK_SCROLLOCK] = KEY_SCROLLOCK;
329         SDLtoFS2[SDLK_CAPSLOCK] = KEY_CAPSLOCK;
330
331         SDLtoFS2[SDLK_LSHIFT] = KEY_LSHIFT;
332         SDLtoFS2[SDLK_RSHIFT] = KEY_RSHIFT;
333
334         SDLtoFS2[SDLK_LALT] = KEY_LALT;
335         SDLtoFS2[SDLK_RALT] = KEY_RALT;
336
337         SDLtoFS2[SDLK_LCTRL] = KEY_LCTRL;
338         SDLtoFS2[SDLK_RCTRL] = KEY_RCTRL;
339
340         SDLtoFS2[SDLK_F1] = KEY_F1;
341         SDLtoFS2[SDLK_F2] = KEY_F2;
342         SDLtoFS2[SDLK_F3] = KEY_F3;
343         SDLtoFS2[SDLK_F4] = KEY_F4;
344         SDLtoFS2[SDLK_F5] = KEY_F5;
345         SDLtoFS2[SDLK_F6] = KEY_F6;
346         SDLtoFS2[SDLK_F7] = KEY_F7;
347         SDLtoFS2[SDLK_F8] = KEY_F8;
348         SDLtoFS2[SDLK_F9] = KEY_F9;
349         SDLtoFS2[SDLK_F10] = KEY_F10;
350         SDLtoFS2[SDLK_F11] = KEY_F11;
351         SDLtoFS2[SDLK_F12] = KEY_F12;
352
353         SDLtoFS2[SDLK_KP0] = KEY_PAD0;
354         SDLtoFS2[SDLK_KP1] = KEY_PAD1;
355         SDLtoFS2[SDLK_KP2] = KEY_PAD2;
356         SDLtoFS2[SDLK_KP3] = KEY_PAD3;
357         SDLtoFS2[SDLK_KP4] = KEY_PAD4;
358         SDLtoFS2[SDLK_KP5] = KEY_PAD5;
359         SDLtoFS2[SDLK_KP6] = KEY_PAD6;
360         SDLtoFS2[SDLK_KP7] = KEY_PAD7;
361         SDLtoFS2[SDLK_KP8] = KEY_PAD8;
362         SDLtoFS2[SDLK_KP9] = KEY_PAD9;
363         SDLtoFS2[SDLK_KP_MINUS] = KEY_PADMINUS;
364         SDLtoFS2[SDLK_KP_PLUS] = KEY_PADPLUS;
365         SDLtoFS2[SDLK_KP_PERIOD] = KEY_PADPERIOD;
366         SDLtoFS2[SDLK_KP_DIVIDE] = KEY_PADDIVIDE;
367         SDLtoFS2[SDLK_KP_MULTIPLY] = KEY_PADMULTIPLY;
368         SDLtoFS2[SDLK_KP_ENTER] = KEY_PADENTER;
369
370         SDLtoFS2[SDLK_INSERT] = KEY_INSERT;
371         SDLtoFS2[SDLK_HOME] = KEY_HOME;
372         SDLtoFS2[SDLK_PAGEUP] = KEY_PAGEUP;
373         SDLtoFS2[SDLK_DELETE] = KEY_DELETE;
374         SDLtoFS2[SDLK_END] = KEY_END;
375         SDLtoFS2[SDLK_PAGEDOWN] = KEY_PAGEDOWN;
376         SDLtoFS2[SDLK_UP] = KEY_UP;
377         SDLtoFS2[SDLK_DOWN] = KEY_DOWN;
378         SDLtoFS2[SDLK_LEFT] = KEY_LEFT;
379         SDLtoFS2[SDLK_RIGHT] = KEY_RIGHT;
380
381         SDLtoFS2[SDLK_PRINT] = KEY_PRINT_SCRN;
382         SDLtoFS2[SDLK_PAUSE] = KEY_PAUSE;
383         SDLtoFS2[SDLK_BREAK] = KEY_BREAK;
384 }
385 #endif
386
387 int key_numlock_is_on()
388 {
389 #ifdef PLAT_UNIX
390         int keys[SDLK_LAST];
391         SDL_GetKeyState(keys);
392         if ( keys[SDLK_NUMLOCK] ) {
393                 return 1;
394         }
395 #else
396         unsigned char keys[256];
397         GetKeyboardState(keys);
398         if ( keys[VK_NUMLOCK]  ) {
399                 return 1;
400         }
401 #endif
402         return 0;
403 }
404
405 void key_turn_off_numlock()
406 {
407 #ifdef PLAT_UNIX
408 //      STUB_FUNCTION; /* sdl doesn't support this */
409 #else
410         unsigned char keys[256];
411         GetKeyboardState(keys);
412         keys[VK_NUMLOCK] = 0;
413         SetKeyboardState(keys);
414 #endif
415 }
416
417 void key_turn_on_numlock()
418 {
419 #ifdef PLAT_UNIX
420 //      STUB_FUNCTION; /* sdl doesn't support this */
421 #else
422         unsigned char keys[256];
423         GetKeyboardState(keys);
424         keys[VK_NUMLOCK] = 1;
425         SetKeyboardState(keys);
426 #endif
427 }
428
429 //      Convert a BIOS scancode to ASCII.
430 //      If scancode >= 127, returns 255, meaning there is no corresponding ASCII code.
431 //      Uses ascii_table and shifted_ascii_table to translate scancode to ASCII.
432 int key_to_ascii(int keycode )
433 {
434         int shifted;
435
436         if ( !key_inited ) return 255;
437
438         shifted = keycode & KEY_SHIFTED;
439         keycode &= KEY_MASK;
440
441         if ( keycode>=127 )
442                 return 255;
443
444         if (shifted)
445                 return shifted_ascii_table[keycode];
446         else
447                 return ascii_table[keycode];
448 }
449
450 //      Flush the keyboard buffer.
451 //      Clear the keyboard array (keyd_pressed).
452 void key_flush()
453 {
454         int i;
455         uint CurTime;
456
457         if ( !key_inited ) return;
458
459         ENTER_CRITICAL_SECTION(&key_lock);      
460
461         key_data.keyhead = key_data.keytail = 0;
462
463         //Clear the keyboard buffer
464         for (i=0; i<KEY_BUFFER_SIZE; i++ )      {
465                 key_data.keybuffer[i] = 0;
466                 key_data.time_pressed[i] = 0;
467         }
468         
469         //Clear the keyboard array
470
471         CurTime = timer_get_milliseconds();
472
473
474         for (i=0; i<NUM_KEYS; i++ )     {
475                 keyd_pressed[i] = 0;
476                 key_data.TimeKeyDownChecked[i] = CurTime;
477                 key_data.TimeKeyWentDown[i] = CurTime;
478                 key_data.TimeKeyHeldDown[i] = 0;
479                 key_data.NumDowns[i]=0;
480                 key_data.NumUps[i]=0;
481         }
482
483         LEAVE_CRITICAL_SECTION(&key_lock);      
484 }
485
486 //      A nifty function which performs the function:
487 //              n = (n+1) % KEY_BUFFER_SIZE
488 //      (assuming positive values of n).
489 int add_one( int n )
490 {
491         n++;
492         if ( n >= KEY_BUFFER_SIZE ) n=0;
493         return n;
494 }
495
496 // Returns 1 if character waiting... 0 otherwise
497 int key_checkch()
498 {
499         int is_one_waiting = 0;
500
501         if ( !key_inited ) return 0;
502
503         ENTER_CRITICAL_SECTION(&key_lock);      
504
505         if (key_data.keytail != key_data.keyhead){
506                 is_one_waiting = 1;
507         }
508
509         LEAVE_CRITICAL_SECTION(&key_lock);              
510
511         return is_one_waiting;
512 }
513
514 //      Return key scancode if a key has been pressed,
515 //      else return 0.
516 //      Reads keys out of the key buffer and updates keyhead.
517 int key_inkey()
518 {
519         int key = 0;
520
521         if ( !key_inited ) return 0;
522
523         ENTER_CRITICAL_SECTION(&key_lock);      
524
525         if (key_data.keytail!=key_data.keyhead) {
526                 key = key_data.keybuffer[key_data.keyhead];
527                 key_data.keyhead = add_one(key_data.keyhead);
528         }
529
530         LEAVE_CRITICAL_SECTION(&key_lock);      
531
532         return key;
533 }
534
535 //      Unget a key.  Puts it back in the input queue.
536 void key_outkey(int key)
537 {
538         int     bufp;
539
540         if ( !key_inited ) return;
541
542         ENTER_CRITICAL_SECTION(&key_lock);              
543
544         bufp = key_data.keytail+1;
545
546         if (bufp >= KEY_BUFFER_SIZE){
547                 bufp = 0;
548         }
549
550         key_data.keybuffer[key_data.keytail] = (unsigned short)key;
551
552         key_data.keytail = bufp;
553
554         LEAVE_CRITICAL_SECTION(&key_lock);              
555 }
556
557
558
559 //      Return amount of time last key was held down.
560 //      This is currently (July 17, 1996) bogus because our timing is
561 //      not accurate.
562 int key_inkey_time(uint * time)
563 {
564         int key = 0;
565
566         if ( !key_inited ) {
567                 *time = 0;
568                 return 0;
569         }
570         
571         ENTER_CRITICAL_SECTION(&key_lock);              
572
573         if (key_data.keytail!=key_data.keyhead) {
574                 key = key_data.keybuffer[key_data.keyhead];
575                 *time = key_data.time_pressed[key_data.keyhead];
576                 key_data.keyhead = add_one(key_data.keyhead);
577         }
578
579         LEAVE_CRITICAL_SECTION(&key_lock);              
580
581         return key;
582 }
583
584
585 //      Returns scancode of last key pressed, if any (returns 0 if no key pressed)
586 //      but does not update keyhead pointer.
587 int key_peekkey()
588 {
589         int key = 0;
590
591         if ( !key_inited ) return 0;
592
593         ENTER_CRITICAL_SECTION(&key_lock);              
594
595         if (key_data.keytail!=key_data.keyhead) {
596                 key = key_data.keybuffer[key_data.keyhead];
597         }
598         LEAVE_CRITICAL_SECTION(&key_lock);              
599
600         return key;
601 }
602
603 // If not installed, uses BIOS and returns getch();
604 //      Else returns pending key (or waits for one if none waiting).
605 int key_getch()
606 {
607         int dummy=0;
608         int in;
609
610         if ( !key_inited ) return 0;
611         
612         while (!key_checkch()){
613                 os_poll();
614
615                 dummy++;
616         }
617         in = key_inkey();
618
619         return in;
620 }
621
622 //      Set global shift_status with modifier results (shift, ctrl, alt).
623 uint key_get_shift_status()
624 {
625         unsigned int shift_status = 0;
626
627         if ( !key_inited ) return 0;
628
629         ENTER_CRITICAL_SECTION(&key_lock);              
630
631         if ( keyd_pressed[KEY_LSHIFT] || keyd_pressed[KEY_RSHIFT] )
632                 shift_status |= KEY_SHIFTED;
633
634         if ( keyd_pressed[KEY_LALT] || keyd_pressed[KEY_RALT] )
635                 shift_status |= KEY_ALTED;
636
637         if ( keyd_pressed[KEY_LCTRL] || keyd_pressed[KEY_RCTRL] )
638                 shift_status |= KEY_CTRLED;
639
640 #ifndef NDEBUG
641         if (keyd_pressed[KEY_DEBUG_KEY])
642                 shift_status |= KEY_DEBUGGED;
643 #else
644         if (keyd_pressed[KEY_DEBUG_KEY]) {
645                 mprintf(("Cheats_enabled = %i, Key_normal_game = %i\n", Cheats_enabled, Key_normal_game));
646                 if ((Cheats_enabled) && Key_normal_game) {
647                         mprintf(("Debug key\n"));
648                         shift_status |= KEY_DEBUGGED1;
649                 }
650         }
651 #endif
652         LEAVE_CRITICAL_SECTION(&key_lock);              
653
654         return shift_status;
655 }
656
657 //      Returns amount of time key (specified by "code") has been down since last call.
658 //      Returns float, unlike key_down_time() which returns a fix.
659 float key_down_timef(uint scancode)     
660 {
661         uint time_down, time;
662         uint delta_time;
663
664         if ( !key_inited ) return 0.0f;
665
666         if ((scancode<0)|| (scancode>=NUM_KEYS)) return 0.0f;
667
668         ENTER_CRITICAL_SECTION(&key_lock);              
669
670         time = timer_get_milliseconds();
671         delta_time = time - key_data.TimeKeyDownChecked[scancode];
672         key_data.TimeKeyDownChecked[scancode] = time;
673
674         if ( delta_time <= 1 ) {
675                 key_data.TimeKeyWentDown[scancode] = time;
676                 if (keyd_pressed[scancode])     {
677                         LEAVE_CRITICAL_SECTION(&key_lock);              
678                         return 1.0f;
679                 } else  {
680                         LEAVE_CRITICAL_SECTION(&key_lock);              
681                         return 0.0f;
682                 }
683         }
684
685         if ( !keyd_pressed[scancode] )  {
686                 time_down = key_data.TimeKeyHeldDown[scancode];
687                 key_data.TimeKeyHeldDown[scancode] = 0;
688         } else  {
689                 time_down =  time - key_data.TimeKeyWentDown[scancode];
690                 key_data.TimeKeyWentDown[scancode] = time;
691         }
692
693         LEAVE_CRITICAL_SECTION(&key_lock);              
694
695         return i2fl(time_down) / i2fl(delta_time);
696 }
697
698 /*
699 //      Returns amount of time key (specified by "code") has been down since last call.
700 //      Returns float, unlike key_down_time() which returns a fix.
701 fix key_down_time( uint code )
702 {
703         uint time_down, time;
704         uint delta_time;
705
706         if ( !key_inited ) return 0.0f;
707
708         if ((scancode<0)|| (scancode>=NUM_KEYS)) return 0.0f;
709
710         EnterCriticalSection( &key_lock );
711
712         time = timer_get_milliseconds();
713         delta_time = time - TimeKeyDownChecked[scancode];
714         TimeKeyDownChecked[scancode] = time;
715
716         if ( delta_time <= 1 ) {
717                 LeaveCriticalSection( &key_lock );
718                 if (keyd_pressed[scancode])
719                         return F1_0;
720                 else
721                         return 0;
722         }
723
724         if ( !keyd_pressed[scancode] )  {
725                 time_down = key_data.TimeKeyHeldDown[scancode];
726                 key_data.TimeKeyHeldDown[scancode] = 0;
727         } else  {
728                 time_down =  time - key_data.TimeKeyWentDown[scancode];
729                 key_data.TimeKeyWentDown[scancode] = time;
730         }
731
732         LeaveCriticalSection( &key_lock );
733
734         return fixmuldiv( time_down, F1_0, delta_time );
735 }
736 */
737
738
739 // Returns number of times key has went from up to down since last call.
740 int key_down_count(int scancode)        
741 {
742         int n;
743
744         if ( !key_inited ) return 0;
745
746         if ((scancode<0)|| (scancode>=NUM_KEYS)) return 0;
747
748         ENTER_CRITICAL_SECTION(&key_lock);              
749
750         n = key_data.NumDowns[scancode];
751         key_data.NumDowns[scancode] = 0;
752
753         LEAVE_CRITICAL_SECTION(&key_lock);              
754
755         return n;
756 }
757
758
759 // Returns number of times key has went from down to up since last call.
760 int key_up_count(int scancode)  
761 {
762         int n;
763
764         if ( !key_inited ) return 0;
765         if ((scancode<0)|| (scancode>=NUM_KEYS)) return 0;
766
767         ENTER_CRITICAL_SECTION(&key_lock);              
768
769         n = key_data.NumUps[scancode];
770         key_data.NumUps[scancode] = 0;
771
772         LEAVE_CRITICAL_SECTION(&key_lock);              
773
774         return n;
775 }
776
777 int key_check(int key)
778 {
779         return key_data.down_check[key];
780 }
781
782 //      Add a key up or down code to the key buffer.  state=1 -> down, state=0 -> up
783 // latency => time difference in ms between when key was actually pressed and now
784 //void key_mark( uint code, int state )
785 void key_mark( uint code, int state, uint latency )
786 {
787         uint scancode, breakbit, temp, event_time;
788         ushort keycode; 
789
790         if ( !key_inited ) return;
791
792         ENTER_CRITICAL_SECTION(&key_lock);              
793
794         // If running in the UK, need to translate their wacky slash scancode to ours
795         if ( code == KEY_SLASH_UK ) {
796                 code = KEY_SLASH;
797         }
798
799         if(Lcl_fr){
800                 switch (code) {
801                 case KEY_A:
802                         code = KEY_Q;
803                         break;
804
805                 case KEY_M:
806                         code = KEY_COMMA;
807                         break;
808
809                 case KEY_Q:
810                         code = KEY_A;
811                         break;
812
813                 case KEY_W:
814                         code = KEY_Z;
815                         break;
816
817                 case KEY_Z:
818                         code = KEY_W;
819                         break;
820
821                 case KEY_SEMICOL:
822                         code = KEY_M;
823                         break;
824
825                 case KEY_COMMA:
826                         code = KEY_SEMICOL;
827                         break;
828                 }
829 #if !defined(PLAT_UNIX) || defined(__MACOSX__)
830         } else if(Lcl_gr){
831                 switch (code) {
832                 case KEY_Y:
833                         code = KEY_Z;
834                         break;
835
836                 case KEY_Z:
837                         code = KEY_Y;
838                         break;
839                 }
840 #endif
841         }
842
843         if ( (code == 0xc5) && !Key_running_NT ) {
844                 key_turn_off_numlock();
845         }
846
847         Assert( code < NUM_KEYS );      
848
849         event_time = timer_get_milliseconds() - latency;
850         // event_time = timeGetTime() - latency;
851
852         // Read in scancode
853         scancode = code & (NUM_KEYS-1);
854         breakbit = !state;
855         
856         if (breakbit)   {
857                 // Key going up
858                 keyd_last_released = scancode;
859                 keyd_pressed[scancode] = 0;
860                 key_data.NumUps[scancode]++;
861
862                 //      What is the point of this code?  "temp" is never used!
863                 temp = 0;
864                 temp |= keyd_pressed[KEY_LSHIFT] || keyd_pressed[KEY_RSHIFT];
865                 temp |= keyd_pressed[KEY_LALT] || keyd_pressed[KEY_RALT];
866                 temp |= keyd_pressed[KEY_LCTRL] || keyd_pressed[KEY_RCTRL];
867 //#ifndef NDEBUG
868                 temp |= keyd_pressed[KEY_DEBUG_KEY];
869 //#endif        
870                 if (event_time < key_data.TimeKeyWentDown[scancode])
871                         key_data.TimeKeyHeldDown[scancode] = 0;
872                 else
873                         key_data.TimeKeyHeldDown[scancode] += event_time - key_data.TimeKeyWentDown[scancode];
874         } else {
875                 // Key going down
876                 keyd_last_pressed = scancode;
877                 keyd_time_when_last_pressed = event_time;
878                 if (!keyd_pressed[scancode])    {
879                         // First time down
880                         key_data.TimeKeyWentDown[scancode] = event_time;
881                         keyd_pressed[scancode] = 1;
882                         key_data.NumDowns[scancode]++;
883                         key_data.down_check[scancode]++;
884
885 //                      mprintf(( "Scancode = %x\n", scancode ));
886
887 //                      if ( scancode == KEY_BREAK )
888 //                              Int3();
889
890
891                 } else if (!keyd_repeat) {
892                         // Don't buffer repeating key if repeat mode is off
893                         scancode = 0xAA;                
894                 } 
895
896                 if ( scancode!=0xAA ) {
897                         keycode = (unsigned short)scancode;
898
899                         if ( keyd_pressed[KEY_LSHIFT] || keyd_pressed[KEY_RSHIFT] )
900                                 keycode |= KEY_SHIFTED;
901
902                         if ( keyd_pressed[KEY_LALT] || keyd_pressed[KEY_RALT] )
903                                 keycode |= KEY_ALTED;
904
905                         if ( keyd_pressed[KEY_LCTRL] || keyd_pressed[KEY_RCTRL] )
906                                 keycode |= KEY_CTRLED;
907
908 #ifndef NDEBUG
909                         if ( keyd_pressed[KEY_DEBUG_KEY] )
910                                 keycode |= KEY_DEBUGGED;
911 //                      if ( keycode == (KEY_BACKSP + KEY_DEBUGGED) )   {
912 //                              keycode = 0;
913 //                              keyd_pressed[KEY_DEBUG_KEY] = 0;
914 //                              keyd_pressed[KEY_BACKSP] = 0;
915 //                              Int3();
916 //                      }
917 #else
918                         if ( keyd_pressed[KEY_DEBUG_KEY] ) {
919                                 mprintf(("Cheats_enabled = %i, Key_normal_game = %i\n", Cheats_enabled, Key_normal_game));
920                                 if (Cheats_enabled && Key_normal_game) {
921                                         keycode |= KEY_DEBUGGED1;
922                                 }
923                         }
924
925 #endif
926
927                         if ( keycode )  {
928                                 temp = key_data.keytail+1;
929                                 if ( temp >= KEY_BUFFER_SIZE ) temp=0;
930
931                                 if (temp!=key_data.keyhead)     {
932                                         int i, accept_key = 1;
933                                         // Num_filter_keys will only be non-zero when a key filter has
934                                         // been explicity set up via key_set_filter()
935                                         for ( i = 0; i < Num_filter_keys; i++ ) {
936                                                 accept_key = 0;
937                                                 if ( Key_filter[i] == keycode ) {
938                                                         accept_key = 1;
939                                                         break;
940                                                 }
941                                         }
942
943                                         if ( accept_key ) {
944                                                 key_data.keybuffer[key_data.keytail] = keycode;
945                                                 key_data.time_pressed[key_data.keytail] = keyd_time_when_last_pressed;
946                                                 key_data.keytail = temp;
947                                         }
948                                 }
949                         }
950                 }
951         }
952
953         LEAVE_CRITICAL_SECTION(&key_lock);              
954 }
955
956 #ifdef USE_DIRECTINPUT
957 void di_cleanup();
958 int di_init();
959 #endif
960
961
962 void key_close()
963 {
964         if ( !key_inited ) return;
965
966         #ifdef USE_DIRECTINPUT
967                 di_cleanup();
968         #endif
969
970         if ( Key_numlock_was_on ) {
971                 key_turn_on_numlock();
972                 Key_numlock_was_on = 0;
973         }
974
975         key_inited = 0;
976 #ifdef PLAT_UNIX
977 //      STUB_FUNCTION; /* don't need this? */
978 #else
979         DeleteCriticalSection( &key_lock );
980 #endif
981 }
982
983 void key_init()
984 {
985         // Initialize queue
986         if ( key_inited ) return;
987         key_inited = 1;
988
989 #ifdef PLAT_UNIX
990         FillSDLArray ();
991 //      STUB_FUNCTION; /* don't need this */
992 #else
993         InitializeCriticalSection( &key_lock );
994
995         ENTER_CRITICAL_SECTION(&key_lock);              
996 #endif
997
998         keyd_time_when_last_pressed = timer_get_milliseconds();
999         keyd_buffer_type = 1;
1000         keyd_repeat = 1;
1001
1002         // Clear the keyboard array
1003         key_flush();
1004
1005         // Clear key filter
1006         key_clear_filter();
1007
1008 #ifdef PLAT_UNIX
1009 //      STUB_FUNCTION; /* don't need this */
1010 #else
1011         LEAVE_CRITICAL_SECTION(&key_lock);              
1012
1013         #ifdef USE_DIRECTINPUT
1014                 di_init();
1015         #endif
1016
1017         OSVERSIONINFO ver;
1018         ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1019         GetVersionEx(&ver);
1020         if ( ver.dwPlatformId == VER_PLATFORM_WIN32_NT ) {
1021                 Key_running_NT = 1;
1022         } else {
1023                 Key_running_NT = 0;
1024                 if ( key_numlock_is_on() ) {
1025                         Key_numlock_was_on = 1;
1026                         key_turn_off_numlock();
1027                 }
1028         }
1029 #endif
1030
1031         atexit(key_close);
1032 }
1033
1034 void key_level_init()
1035 {
1036         int i;
1037
1038         for (i=0; i<NUM_KEYS; i++)
1039                 key_data.down_check[i] = 0;
1040 }
1041
1042 void key_lost_focus()
1043 {
1044         if ( !key_inited ) return;
1045
1046         key_flush();    
1047 }
1048
1049 void key_got_focus()
1050 {
1051         if ( !key_inited ) return;
1052         
1053         key_flush();    
1054 }
1055
1056 // Restricts the keys that are accepted from the keyboard
1057 //
1058 //      filter_array    =>              array of keys to act as a filter
1059 //      num                             =>              number of keys in filter_array
1060 //
1061 void key_set_filter(int *filter_array, int num)
1062 {
1063         int i;
1064
1065         if ( num >= MAX_FILTER_KEYS ) {
1066                 Int3();
1067                 num = MAX_FILTER_KEYS;
1068         }
1069
1070         Num_filter_keys = num;
1071
1072         for ( i = 0; i < num; i++ ) {
1073                 Key_filter[i] = filter_array[i];
1074         }
1075 }
1076
1077 // Clear the key filter, so all keypresses are accepted from keyboard 
1078 //
1079 void key_clear_filter()
1080 {
1081         int i;
1082
1083         Num_filter_keys = 0;
1084         for ( i = 0; i < MAX_FILTER_KEYS; i++ ) {
1085                 Key_filter[i] = -1;
1086         }
1087 }
1088
1089
1090 #ifdef USE_DIRECTINPUT
1091
1092 // JAS - April 18, 1998
1093 // Not using because DI has the following problems:  (Everything else works ok)
1094 // Under NT, Pause and Numlock report as identical keys.
1095 // Under 95, Pause is the same as pressing Ctrl then Numlock.  So the game fires each
1096 // time you hit it.
1097 // 
1098
1099 //============================================================================
1100 // Direct Input code
1101 // For the keyboard, this basically replaces our old functionallity of:
1102 // WM_KEYDOWN:
1103 //    key_mark(...);
1104 // WM_KEYUP:
1105 //    key_mark(...);
1106 //============================================================================
1107
1108
1109 #include "vdinput.h"
1110
1111 #define MAX_BUFFERED_KEYBOARD_EVENTS 10
1112
1113 static LPDIRECTINPUT                    Di_object = NULL;
1114 static LPDIRECTINPUTDEVICE      Di_keyboard = NULL;
1115 static HANDLE                                   Di_thread = NULL;
1116 static DWORD                                    Di_thread_id = NULL;
1117 static HANDLE                                   Di_event = NULL;
1118
1119 DWORD di_process(DWORD lparam)
1120 {
1121         while (1) {
1122                 if ( WaitForSingleObject( Di_event, INFINITE )==WAIT_OBJECT_0 ) {
1123
1124                         //mprintf(( "Got event!\n" ));
1125
1126                         HRESULT hr;
1127
1128                         DIDEVICEOBJECTDATA rgdod[10]; 
1129                         DWORD dwItems = MAX_BUFFERED_KEYBOARD_EVENTS; 
1130
1131 again:;
1132                         hr = Di_keyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), rgdod,  &dwItems, 0); 
1133
1134                         if (hr == DIERR_INPUTLOST) {
1135                                 /*
1136                                 *  DirectInput is telling us that the input stream has
1137                                 *  been interrupted.  We aren't tracking any state
1138                                 *  between polls, so we don't have any special reset
1139                                 *  that needs to be done.  We just re-acquire and
1140                                 *  try again.
1141                                 */
1142                                 Sleep(1000);            // Pause a second...
1143                                 hr = Di_keyboard->Acquire();
1144                                 if (SUCCEEDED(hr)) {
1145                                         goto again;
1146                                 }
1147                         }
1148
1149                         if (SUCCEEDED(hr)) { 
1150                                  // dwItems = number of elements read (could be zero)
1151                                  if (hr == DI_BUFFEROVERFLOW) { 
1152                                         // Buffer had overflowed. 
1153                                         mprintf(( "Buffer overflowed!\n" ));
1154                                  } 
1155                                         int i;
1156
1157                                         //mprintf(( "Got %d events\n", dwItems ));
1158
1159                                         for (i=0; i<(int)dwItems; i++ ) {
1160                                                 int key = rgdod[i].dwOfs;
1161                                                 int state = rgdod[i].dwData;
1162                                                 int stamp = rgdod[i].dwTimeStamp;
1163
1164                                                 int latency;
1165                                                 latency = timeGetTime() - stamp;
1166                                                 if ( latency < 0 )
1167                                                         latency=0;
1168
1169 //                                              if ( key == KEY_PRINT_SCRN )    {
1170 //                                                      key_mark( key, 1, latency );
1171 //                                              }
1172 //                                              key_mark( key, (state&0x80?1:0), latency );
1173                                                 mprintf(( "Key=%x, State=%x, Time=%d, Latency=%d\n", key, state, stamp, latency ));
1174                                         }
1175
1176                         } 
1177                 } 
1178
1179         }
1180
1181         return 0;
1182 }
1183
1184
1185 int di_init()
1186 {
1187     HRESULT hr;
1188
1189          return 0;
1190
1191
1192     /*
1193      *  Register with the DirectInput subsystem and get a pointer
1194      *  to a IDirectInput interface we can use.
1195      *
1196      *  Parameters:
1197      *
1198      *      g_hinst
1199      *
1200      *          Instance handle to our application or DLL.
1201      *
1202      *      DIRECTINPUT_VERSION
1203      *
1204      *          The version of DirectInput we were designed for.
1205      *          We take the value from the <dinput.h> header file.
1206      *
1207      *      &g_pdi
1208      *
1209      *          Receives pointer to the IDirectInput interface
1210      *          that was created.
1211      *
1212      *      NULL
1213      *
1214      *          We do not use OLE aggregation, so this parameter
1215      *          must be NULL.
1216      *
1217      */
1218     hr = DirectInputCreate(GetModuleHandle(NULL), 0x300, &Di_object, NULL);
1219
1220     if (FAILED(hr)) {
1221         mprintf(( "DirectInputCreate failed!\n" ));
1222         return FALSE;
1223     }
1224
1225     /*
1226      *  Obtain an interface to the system keyboard device.
1227      *
1228      *  Parameters:
1229      *
1230      *      GUID_SysKeyboard
1231      *
1232      *          The instance GUID for the device we wish to access.
1233      *          GUID_SysKeyboard is a predefined instance GUID that
1234      *          always refers to the system keyboard device.
1235      *
1236      *      &g_pKeyboard
1237      *
1238      *          Receives pointer to the IDirectInputDevice interface
1239      *          that was created.
1240      *
1241      *      NULL
1242      *
1243      *          We do not use OLE aggregation, so this parameter
1244      *          must be NULL.
1245      *
1246      */
1247     hr = Di_object->CreateDevice(GUID_SysKeyboard, &Di_keyboard, NULL);
1248
1249     if (FAILED(hr)) {
1250         mprintf(( "CreateDevice failed!\n" ));
1251         return FALSE;
1252     }
1253
1254     /*
1255      *  Set the data format to "keyboard format".
1256      *
1257      *  A data format specifies which controls on a device we
1258      *  are interested in, and how they should be reported.
1259      *
1260      *  This tells DirectInput that we will be passing an array
1261      *  of 256 bytes to IDirectInputDevice::GetDeviceState.
1262      *
1263      *  Parameters:
1264      *
1265      *      c_dfDIKeyboard
1266      *
1267      *          Predefined data format which describes
1268      *          an array of 256 bytes, one per scancode.
1269      */
1270     hr = Di_keyboard->SetDataFormat(&c_dfDIKeyboard);
1271
1272     if (FAILED(hr)) {
1273         mprintf(( "SetDataFormat failed!\n" ));
1274         return FALSE;
1275     }
1276
1277
1278     /*
1279      *  Set the cooperativity level to let DirectInput know how
1280      *  this device should interact with the system and with other
1281      *  DirectInput applications.
1282      *
1283      *  Parameters:
1284      *
1285      *      DISCL_NONEXCLUSIVE
1286      *
1287      *          Retrieve keyboard data when acquired, not interfering
1288      *          with any other applications which are reading keyboard
1289      *          data.
1290      *
1291      *      DISCL_FOREGROUND
1292      *
1293      *          If the user switches away from our application,
1294      *          automatically release the keyboard back to the system.
1295      *
1296      */
1297         hr = Di_keyboard->SetCooperativeLevel((HWND)os_get_window(), DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
1298
1299         if (FAILED(hr)) {
1300                 mprintf(( "SetCooperativeLevel failed!\n" ));
1301                 return FALSE;
1302         }
1303
1304         DIPROPDWORD hdr;
1305
1306         // Turn on buffering
1307         hdr.diph.dwSize = sizeof(DIPROPDWORD); 
1308         hdr.diph.dwHeaderSize = sizeof(DIPROPHEADER);
1309         hdr.diph.dwObj = 0;             
1310         hdr.diph.dwHow = DIPH_DEVICE;   // Apply to entire device
1311         hdr.dwData = 16;        //MAX_BUFFERED_KEYBOARD_EVENTS;
1312
1313         hr = Di_keyboard->SetProperty( DIPROP_BUFFERSIZE, &hdr.diph );
1314         if (FAILED(hr)) {
1315                 mprintf(( "SetProperty DIPROP_BUFFERSIZE failed\n" ));
1316                 return FALSE;
1317         }
1318
1319
1320         Di_event = CreateEvent( NULL, FALSE, FALSE, NULL );
1321         Assert(Di_event != NULL);
1322
1323         Di_thread = CreateThread(NULL, 1024, (LPTHREAD_START_ROUTINE)di_process, NULL, 0, &Di_thread_id);
1324         Assert( Di_thread != NULL );
1325
1326         SetThreadPriority(Di_thread, THREAD_PRIORITY_HIGHEST);
1327
1328         hr = Di_keyboard->SetEventNotification(Di_event);
1329         if (FAILED(hr)) {
1330                 mprintf(( "SetEventNotification failed\n" ));
1331                 return FALSE;
1332         }
1333
1334         Di_keyboard->Acquire();
1335
1336         return TRUE;
1337 }
1338
1339 void di_cleanup()
1340 {
1341     /*
1342      *  Destroy any lingering IDirectInputDevice object.
1343      */
1344     if (Di_keyboard) {
1345
1346         /*
1347          *  Cleanliness is next to godliness.  Unacquire the device
1348          *  one last time just in case we got really confused and tried
1349          *  to exit while the device is still acquired.
1350          */
1351         Di_keyboard->Unacquire();
1352
1353         Di_keyboard->Release();
1354         Di_keyboard = NULL;
1355     }
1356
1357     /*
1358      *  Destroy any lingering IDirectInput object.
1359      */
1360     if (Di_object) {
1361         Di_object->Release();
1362         Di_object = NULL;
1363     }
1364
1365         if ( Di_event ) {
1366                 CloseHandle(Di_event);
1367                 Di_event = NULL;
1368         }
1369
1370 }
1371
1372 #endif