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