File: key.cpp

package info (click to toggle)
freespace2 24.0.2%2Brepack-1
  • links: PTS, VCS
  • area: non-free
  • in suites: trixie
  • size: 43,188 kB
  • sloc: cpp: 583,107; ansic: 21,729; python: 1,174; sh: 464; makefile: 248; xml: 181
file content (697 lines) | stat: -rw-r--r-- 17,920 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
/*
 * Copyright (C) Volition, Inc. 1999.  All rights reserved.
 *
 * All source code herein is the property of Volition, Inc. You may not sell 
 * or otherwise commercially exploit the source or things you created based on the 
 * source.
 *
*/ 

#include "controlconfig/controlsconfig.h" //For textify scancode
#include "globalincs/pstypes.h"
#include "graphics/2d.h"
#include "io/key.h"
#include "math/fix.h"
#include "io/timer.h"
#include "localization/localize.h"
#include "scripting/global_hooks.h"
#include "scripting/scripting.h"
#include "cmdline/cmdline.h"

#define THREADED	// to use the proper set of macros
#include "osapi/osapi.h"


#define KEY_BUFFER_SIZE 16

//-------- Variable accessed by outside functions ---------
ubyte				keyd_buffer_type;		// 0=No buffer, 1=buffer ASCII, 2=buffer scans
ubyte				keyd_repeat;
uint				keyd_last_pressed;
uint				keyd_last_released;
ubyte				keyd_pressed[NUM_KEYS];
int				keyd_time_when_last_pressed;

typedef struct keyboard	{
	ushort			keybuffer[KEY_BUFFER_SIZE];
	uint				time_pressed[KEY_BUFFER_SIZE];
	uint				TimeKeyWentDown[NUM_KEYS];
	uint				TimeKeyHeldDown[NUM_KEYS];
	uint				TimeKeyDownChecked[NUM_KEYS];
	uint				NumDowns[NUM_KEYS];
	uint				NumUps[NUM_KEYS];
	int				down_check[NUM_KEYS];  // nonzero if has been pressed yet this mission
	uint				keyhead, keytail;
} keyboard;

keyboard key_data;

int key_inited = 0;

SDL_mutex* key_lock;

//int Backspace_debug=1;	// global flag that will enable/disable the backspace key from stopping execution
								// This flag was created since the backspace key is also used to correct mistakes
								// when typing in your pilots callsign.  This global flag is checked before execution
								// is stopped.

SCP_map<SDL_Scancode, int> SDLtoFS2;

int ascii_table[SIZE_OF_ASCII_TABLE] =
{ 255, 255, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=',255,255,
  'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', 255, 255,
  'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', 39, '`',
  255, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 255,'*',
  255, ' ', 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,255,255,
  255, 255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
  255,255,255,255,255,255,255,255 };

int shifted_ascii_table[SIZE_OF_ASCII_TABLE] =
{ 255, 255, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+',255,255,
  'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', 255, 255,
  'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', 
  255, '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', 255,255,
  255, ' ', 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,255,255,
  255, 255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
  255,255,255,255,255,255,255,255 };

static int Key_numlock_was_on = 0;	// Flag to indicate whether NumLock is on at start


SCP_string CheatUsed = "";
int Cheats_enabled = 0;
int Key_normal_game = 0;

namespace
{
	bool key_down_event_handler(const SDL_Event& e)
	{
		if (!os::events::isWindowEvent(e, os::getSDLMainWindow())) {
			return false;
		}

		if (SDLtoFS2[e.key.keysym.scancode]) {
			key_mark(SDLtoFS2[e.key.keysym.scancode], 1, 0);

			return true;
		}

		return false;
	}

	bool key_up_event_handler(const SDL_Event& e)
	{
		if (!os::events::isWindowEvent(e, os::getSDLMainWindow())) {
			return false;
		}

		if (SDLtoFS2[e.key.keysym.scancode]) {
			key_mark(SDLtoFS2[e.key.keysym.scancode], 0, 0);

			return true;
		}

		return false;
	}
}

void FillSDLArray ()
{
	SDLtoFS2[SDL_SCANCODE_0] = KEY_0;
	SDLtoFS2[SDL_SCANCODE_1] = KEY_1;
	SDLtoFS2[SDL_SCANCODE_2] = KEY_2;
	SDLtoFS2[SDL_SCANCODE_3] = KEY_3;
	SDLtoFS2[SDL_SCANCODE_4] = KEY_4;
	SDLtoFS2[SDL_SCANCODE_5] = KEY_5;
	SDLtoFS2[SDL_SCANCODE_6] = KEY_6;
	SDLtoFS2[SDL_SCANCODE_7] = KEY_7;
	SDLtoFS2[SDL_SCANCODE_8] = KEY_8;
	SDLtoFS2[SDL_SCANCODE_9] = KEY_9;

	SDLtoFS2[SDL_SCANCODE_A] = KEY_A;
	SDLtoFS2[SDL_SCANCODE_B] = KEY_B;
	SDLtoFS2[SDL_SCANCODE_C] = KEY_C;
	SDLtoFS2[SDL_SCANCODE_D] = KEY_D;
	SDLtoFS2[SDL_SCANCODE_E] = KEY_E;
	SDLtoFS2[SDL_SCANCODE_F] = KEY_F;
	SDLtoFS2[SDL_SCANCODE_G] = KEY_G;
	SDLtoFS2[SDL_SCANCODE_H] = KEY_H;
	SDLtoFS2[SDL_SCANCODE_I] = KEY_I;
	SDLtoFS2[SDL_SCANCODE_J] = KEY_J;
	SDLtoFS2[SDL_SCANCODE_K] = KEY_K;
	SDLtoFS2[SDL_SCANCODE_L] = KEY_L;
	SDLtoFS2[SDL_SCANCODE_M] = KEY_M;
	SDLtoFS2[SDL_SCANCODE_N] = KEY_N;
	SDLtoFS2[SDL_SCANCODE_O] = KEY_O;
	SDLtoFS2[SDL_SCANCODE_P] = KEY_P;
	SDLtoFS2[SDL_SCANCODE_Q] = KEY_Q;
	SDLtoFS2[SDL_SCANCODE_R] = KEY_R;
	SDLtoFS2[SDL_SCANCODE_S] = KEY_S;
	SDLtoFS2[SDL_SCANCODE_T] = KEY_T;
	SDLtoFS2[SDL_SCANCODE_U] = KEY_U;
	SDLtoFS2[SDL_SCANCODE_V] = KEY_V;
	SDLtoFS2[SDL_SCANCODE_W] = KEY_W;
	SDLtoFS2[SDL_SCANCODE_X] = KEY_X;
	SDLtoFS2[SDL_SCANCODE_Y] = KEY_Y;
	SDLtoFS2[SDL_SCANCODE_Z] = KEY_Z;


	SDLtoFS2[SDL_SCANCODE_MINUS] = KEY_MINUS;
	SDLtoFS2[SDL_SCANCODE_EQUALS] = KEY_EQUAL;
	SDLtoFS2[SDL_SCANCODE_SLASH] = KEY_DIVIDE; // No idea - DDOI
	SDLtoFS2[SDL_SCANCODE_BACKSLASH] = KEY_SLASH;
	SDLtoFS2[SDL_SCANCODE_NONUSBACKSLASH] = KEY_SLASH_UK;
	SDLtoFS2[SDL_SCANCODE_COMMA] = KEY_COMMA;
	SDLtoFS2[SDL_SCANCODE_PERIOD] = KEY_PERIOD;
	SDLtoFS2[SDL_SCANCODE_SEMICOLON] = KEY_SEMICOL;

	SDLtoFS2[SDL_SCANCODE_LEFTBRACKET] = KEY_LBRACKET;
	SDLtoFS2[SDL_SCANCODE_RIGHTBRACKET] = KEY_RBRACKET;

	SDLtoFS2[SDL_SCANCODE_GRAVE] = KEY_LAPOSTRO;
	SDLtoFS2[SDL_SCANCODE_APOSTROPHE] = KEY_RAPOSTRO;


	SDLtoFS2[SDL_SCANCODE_ESCAPE] = KEY_ESC;
	SDLtoFS2[SDL_SCANCODE_RETURN] = KEY_ENTER;
	SDLtoFS2[SDL_SCANCODE_BACKSPACE] = KEY_BACKSP;
	SDLtoFS2[SDL_SCANCODE_TAB] = KEY_TAB;
	SDLtoFS2[SDL_SCANCODE_SPACE] = KEY_SPACEBAR;

	SDLtoFS2[SDL_SCANCODE_NUMLOCKCLEAR] = KEY_NUMLOCK;
	SDLtoFS2[SDL_SCANCODE_SCROLLLOCK] = KEY_SCROLLOCK;
	SDLtoFS2[SDL_SCANCODE_CAPSLOCK] = KEY_CAPSLOCK;

	SDLtoFS2[SDL_SCANCODE_LSHIFT] = KEY_LSHIFT;
	SDLtoFS2[SDL_SCANCODE_RSHIFT] = KEY_RSHIFT;

	SDLtoFS2[SDL_SCANCODE_LALT] = KEY_LALT;
	SDLtoFS2[SDL_SCANCODE_RALT] = KEY_RALT;

	SDLtoFS2[SDL_SCANCODE_LCTRL] = KEY_LCTRL;
	SDLtoFS2[SDL_SCANCODE_RCTRL] = KEY_RCTRL;

	SDLtoFS2[SDL_SCANCODE_F1] = KEY_F1;
	SDLtoFS2[SDL_SCANCODE_F2] = KEY_F2;
	SDLtoFS2[SDL_SCANCODE_F3] = KEY_F3;
	SDLtoFS2[SDL_SCANCODE_F4] = KEY_F4;
	SDLtoFS2[SDL_SCANCODE_F5] = KEY_F5;
	SDLtoFS2[SDL_SCANCODE_F6] = KEY_F6;
	SDLtoFS2[SDL_SCANCODE_F7] = KEY_F7;
	SDLtoFS2[SDL_SCANCODE_F8] = KEY_F8;
	SDLtoFS2[SDL_SCANCODE_F9] = KEY_F9;
	SDLtoFS2[SDL_SCANCODE_F10] = KEY_F10;
	SDLtoFS2[SDL_SCANCODE_F11] = KEY_F11;
	SDLtoFS2[SDL_SCANCODE_F12] = KEY_F12;

	SDLtoFS2[SDL_SCANCODE_KP_0] = KEY_PAD0;
	SDLtoFS2[SDL_SCANCODE_KP_1] = KEY_PAD1;
	SDLtoFS2[SDL_SCANCODE_KP_2] = KEY_PAD2;
	SDLtoFS2[SDL_SCANCODE_KP_3] = KEY_PAD3;
	SDLtoFS2[SDL_SCANCODE_KP_4] = KEY_PAD4;
	SDLtoFS2[SDL_SCANCODE_KP_5] = KEY_PAD5;
	SDLtoFS2[SDL_SCANCODE_KP_6] = KEY_PAD6;
	SDLtoFS2[SDL_SCANCODE_KP_7] = KEY_PAD7;
	SDLtoFS2[SDL_SCANCODE_KP_8] = KEY_PAD8;
	SDLtoFS2[SDL_SCANCODE_KP_9] = KEY_PAD9;
	SDLtoFS2[SDL_SCANCODE_KP_MINUS] = KEY_PADMINUS;
	SDLtoFS2[SDL_SCANCODE_KP_PLUS] = KEY_PADPLUS;
	SDLtoFS2[SDL_SCANCODE_KP_PERIOD] = KEY_PADPERIOD;
	SDLtoFS2[SDL_SCANCODE_KP_DIVIDE] = KEY_PADDIVIDE;
	SDLtoFS2[SDL_SCANCODE_KP_MULTIPLY] = KEY_PADMULTIPLY;
	SDLtoFS2[SDL_SCANCODE_KP_ENTER] = KEY_PADENTER;

	SDLtoFS2[SDL_SCANCODE_INSERT] = KEY_INSERT;
	SDLtoFS2[SDL_SCANCODE_HOME] = KEY_HOME;
	SDLtoFS2[SDL_SCANCODE_PAGEUP] = KEY_PAGEUP;
	SDLtoFS2[SDL_SCANCODE_DELETE] = KEY_DELETE;
	SDLtoFS2[SDL_SCANCODE_END] = KEY_END;
	SDLtoFS2[SDL_SCANCODE_PAGEDOWN] = KEY_PAGEDOWN;
	SDLtoFS2[SDL_SCANCODE_UP] = KEY_UP;
	SDLtoFS2[SDL_SCANCODE_DOWN] = KEY_DOWN;
	SDLtoFS2[SDL_SCANCODE_LEFT] = KEY_LEFT;
	SDLtoFS2[SDL_SCANCODE_RIGHT] = KEY_RIGHT;

	SDLtoFS2[SDL_SCANCODE_PRINTSCREEN] = KEY_PRINT_SCRN;
	SDLtoFS2[SDL_SCANCODE_PAUSE] = KEY_PAUSE;
	//	SDLtoFS2[SDL_SCANCODE_BREAK] = KEY_BREAK;
}

SDL_Scancode fs2_to_sdl(int scancode) {
	for (const auto& code : SDLtoFS2) {
		if (code.second == scancode)
			return code.first;
	}

	return SDL_SCANCODE_UNKNOWN;
}

int key_numlock_is_on()
{
	const Uint8 *state = SDL_GetKeyboardState(NULL);

	return state[SDL_SCANCODE_NUMLOCKCLEAR];
}

void key_turn_off_numlock()
{

}

void key_turn_on_numlock()
{

}

//	Convert a BIOS scancode to ASCII.
//	If scancode >= 127, returns 255, meaning there is no corresponding ASCII code.
//	Uses ascii_table and shifted_ascii_table to translate scancode to ASCII.
int key_to_ascii(int keycode )
{
	int shifted;

	if ( !key_inited ) return 255;

	shifted = keycode & KEY_SHIFTED;
	keycode &= 0xFF;

	if ( keycode>=127 )
		return 255;

	if (shifted)
		return shifted_ascii_table[keycode];
	else
		return ascii_table[keycode];
}

//	Flush the keyboard buffer.
//	Clear the keyboard array (keyd_pressed).
void key_flush()
{
	int i;
	uint CurTime;

	if ( !key_inited ) return;

	SDL_LockMutex( key_lock );	

	key_data.keyhead = key_data.keytail = 0;

	//Clear the keyboard buffer
	for (i=0; i<KEY_BUFFER_SIZE; i++ )	{
		key_data.keybuffer[i] = 0;
		key_data.time_pressed[i] = 0;
	}
	
	//Clear the keyboard array

	CurTime = timer_get_milliseconds();


	for (i=0; i<NUM_KEYS; i++ )	{
		keyd_pressed[i] = 0;
		key_data.TimeKeyDownChecked[i] = CurTime;
		key_data.TimeKeyWentDown[i] = CurTime;
		key_data.TimeKeyHeldDown[i] = 0;
		key_data.NumDowns[i]=0;
		key_data.NumUps[i]=0;
	}

	SDL_UnlockMutex( key_lock );	
}

//	A nifty function which performs the function:
//		n = (n+1) % KEY_BUFFER_SIZE
//	(assuming positive values of n).
int add_one( int n )
{
	n++;
	if ( n >= KEY_BUFFER_SIZE ) n=0;
	return n;
}

// Returns 1 if character waiting... 0 otherwise
int key_checkch()
{
	int is_one_waiting = 0;

	if ( !key_inited ) return 0;

	SDL_LockMutex( key_lock );	

	if (key_data.keytail != key_data.keyhead){
		is_one_waiting = 1;
	}

	SDL_UnlockMutex( key_lock );		

	return is_one_waiting;
}

//	Return key scancode if a key has been pressed,
//	else return 0.
//	Reads keys out of the key buffer and updates keyhead.

//WMC - Added so scripting can get at keys.
int Current_key_down = 0;
int key_inkey()
{
	int key = 0;

	if ( !key_inited ) return 0;

	SDL_LockMutex( key_lock );	

	if (key_data.keytail!=key_data.keyhead)	{
		key = key_data.keybuffer[key_data.keyhead];
		key_data.keyhead = add_one(key_data.keyhead);
	}

	SDL_UnlockMutex( key_lock );	

	Current_key_down = key;

	return key;
}

// If not installed, uses BIOS and returns getch();
//	Else returns pending key (or waits for one if none waiting).
int key_getch()
{
	int in;

	if ( !key_inited ) return 0;
	
	while (!key_checkch()){
		os_poll();
	}
	in = key_inkey();

	return in;
}

//	Set global shift_status with modifier results (shift, ctrl, alt).
uint key_get_shift_status()
{
	unsigned int shift_status = 0;

	if ( !key_inited ) return 0;

	SDL_LockMutex( key_lock );		

	if ( keyd_pressed[KEY_LSHIFT] || keyd_pressed[KEY_RSHIFT] )
		shift_status |= KEY_SHIFTED;

	if ( keyd_pressed[KEY_LALT] || keyd_pressed[KEY_RALT] )
		shift_status |= KEY_ALTED;

	if ( keyd_pressed[KEY_LCTRL] || keyd_pressed[KEY_RCTRL] )
		shift_status |= KEY_CTRLED;

#ifndef NDEBUG
	if (keyd_pressed[KEY_DEBUG_KEY])
		shift_status |= KEY_DEBUGGED;
#else
	if (keyd_pressed[KEY_DEBUG_KEY]) {
		mprintf(("Cheats_enabled = %i, Key_normal_game = %i\n", Cheats_enabled, Key_normal_game));
		if ((Cheats_enabled) && Key_normal_game) {
			mprintf(("Debug key\n"));
			shift_status |= KEY_DEBUGGED1;
		}
	}
#endif
	SDL_UnlockMutex( key_lock );		

	return shift_status;
}

//	Returns amount of time key (specified by "code") has been down since last call.
//	Returns float, unlike key_down_time() which returns a fix.
float key_down_timef(uint scancode)	
{
	uint time_down, time;
	uint delta_time;

	if ( !key_inited ) {
		return 0.0f;
	}

	if (scancode >= NUM_KEYS) {
		return 0.0f;
	}

	SDL_LockMutex( key_lock );		

	time = timer_get_milliseconds();
	delta_time = time - key_data.TimeKeyDownChecked[scancode];
	key_data.TimeKeyDownChecked[scancode] = time;

	if ( delta_time <= 1 ) {
		key_data.TimeKeyWentDown[scancode] = time;
		if (keyd_pressed[scancode])	{
			SDL_UnlockMutex( key_lock );		
			return 1.0f;
		} else	{
			SDL_UnlockMutex( key_lock );		
			return 0.0f;
		}
	}

	if ( !keyd_pressed[scancode] )	{
		time_down = key_data.TimeKeyHeldDown[scancode];
		key_data.TimeKeyHeldDown[scancode] = 0;
	} else	{
		time_down =  time - key_data.TimeKeyWentDown[scancode];
		key_data.TimeKeyWentDown[scancode] = time;
	}

	SDL_UnlockMutex( key_lock );		

	return i2fl(time_down) / i2fl(delta_time);
}

// Returns number of times key has went from up to down since last call.
int key_down_count(int scancode)	
{
	int n;

	if ( !key_inited ) return 0;
	if ((scancode<0)|| (scancode>=NUM_KEYS)) return 0;

	SDL_LockMutex( key_lock );		

	n = key_data.NumDowns[scancode];
	key_data.NumDowns[scancode] = 0;

	SDL_UnlockMutex( key_lock );		

	return n;
}

//	Add a key up or down code to the key buffer.  state=1 -> down, state=0 -> up
// latency => time difference in ms between when key was actually pressed and now
//void key_mark( uint code, int state )
void key_mark( uint code, int state, uint latency )
{
	uint scancode, breakbit, temp, event_time;
	ushort keycode;	

	if ( !key_inited ) return;

	SDL_LockMutex( key_lock );		

	// If running in the UK, need to translate their wacky slash scancode to ours
	if ( code == KEY_SLASH_UK ) {
		code = KEY_SLASH;
	}

	Assert( code < NUM_KEYS );

	event_time = timer_get_milliseconds() - latency;
	// event_time = timeGetTime() - latency;

	// Read in scancode
	scancode = code & (NUM_KEYS-1);
	breakbit = !state;
	
	if (breakbit) {
		// Key going up
		keyd_last_released = scancode;
		keyd_pressed[scancode] = 0;
		key_data.NumUps[scancode]++;

		// What is the point of this code?  "temp" is never used!
		temp = 0;
		temp |= keyd_pressed[KEY_LSHIFT] || keyd_pressed[KEY_RSHIFT];
		temp |= keyd_pressed[KEY_LALT] || keyd_pressed[KEY_RALT];
		temp |= keyd_pressed[KEY_LCTRL] || keyd_pressed[KEY_RCTRL];
		temp |= keyd_pressed[KEY_DEBUG_KEY];
	
		if (event_time < key_data.TimeKeyWentDown[scancode]) {
			key_data.TimeKeyHeldDown[scancode] = 0;
		} else {
			key_data.TimeKeyHeldDown[scancode] += event_time - key_data.TimeKeyWentDown[scancode];
		}

		Current_key_down = scancode;
		if ( keyd_pressed[KEY_LSHIFT] || keyd_pressed[KEY_RSHIFT] ) {
			Current_key_down |= KEY_SHIFTED;
		}

		if ( keyd_pressed[KEY_LALT] || keyd_pressed[KEY_RALT] ) {
			Current_key_down |= KEY_ALTED;
		}

		if ( keyd_pressed[KEY_LCTRL] || keyd_pressed[KEY_RCTRL] ) {
			Current_key_down |= KEY_CTRLED;
		}

		if (scripting::hooks::OnKeyReleased->isActive()) {
			scripting::hooks::OnKeyReleased->run(scripting::hook_param_list(
				scripting::hook_param("Key", 's', textify_scancode_universal(Current_key_down))));
		}
	} else {
		// Key going down
		keyd_last_pressed = scancode;
		keyd_time_when_last_pressed = event_time;
		if (!keyd_pressed[scancode]) {
			// First time down
			key_data.TimeKeyWentDown[scancode] = event_time;
			keyd_pressed[scancode] = 1;
			key_data.NumDowns[scancode]++;
			key_data.down_check[scancode]++;

			//WMC - For scripting
			Current_key_down = scancode;
			if ( keyd_pressed[KEY_LSHIFT] || keyd_pressed[KEY_RSHIFT] ) {
				Current_key_down |= KEY_SHIFTED;
			}

			if ( keyd_pressed[KEY_LALT] || keyd_pressed[KEY_RALT] ) {
				Current_key_down |= KEY_ALTED;
			}

			if ( keyd_pressed[KEY_LCTRL] || keyd_pressed[KEY_RCTRL] ) {
				Current_key_down |= KEY_CTRLED;
			}

			if (scripting::hooks::OnKeyPressed->isActive()) {
				scripting::hooks::OnKeyPressed->run(scripting::hook_param_list(
					scripting::hook_param("Key", 's', textify_scancode_universal(Current_key_down))));
			}
		} else if (!keyd_repeat) {
			// Don't buffer repeating key if repeat mode is off
			scancode = 0xAA;		
		} 

		if ( scancode!=0xAA ) {
			keycode = (unsigned short)scancode;

			if ( keyd_pressed[KEY_LSHIFT] || keyd_pressed[KEY_RSHIFT] ) {
				keycode |= KEY_SHIFTED;
			}

			if ( keyd_pressed[KEY_LALT] || keyd_pressed[KEY_RALT] ) {
				keycode |= KEY_ALTED;
			}

			if ( keyd_pressed[KEY_LCTRL] || keyd_pressed[KEY_RCTRL] ) {
				keycode |= KEY_CTRLED;
			}

#ifndef NDEBUG
			if ( keyd_pressed[KEY_DEBUG_KEY] ) {
				keycode |= KEY_DEBUGGED;
			}
#else
			if ( keyd_pressed[KEY_DEBUG_KEY] ) {
				mprintf(("Cheats_enabled = %i, Key_normal_game = %i\n", Cheats_enabled, Key_normal_game));
				if (Cheats_enabled && Key_normal_game) {
					keycode |= KEY_DEBUGGED1;
				}
			}

#endif

			if ( keycode ) {
				temp = key_data.keytail+1;
				if ( temp >= KEY_BUFFER_SIZE ) temp=0;

				if (temp!=key_data.keyhead) {
					key_data.keybuffer[key_data.keytail] = keycode;
					key_data.time_pressed[key_data.keytail] = keyd_time_when_last_pressed;
					key_data.keytail = temp;
				}
			}
		}
	}

	SDL_UnlockMutex( key_lock );
}

void key_close()
{
	if ( !key_inited ) return;

	if ( Key_numlock_was_on ) {
		key_turn_on_numlock();
		Key_numlock_was_on = 0;
	}

	key_inited = 0;

	SDL_DestroyMutex( key_lock );
}

void key_init()
{
	// Initialize queue
	if ( key_inited ) return;
	key_inited = 1;

	key_lock = SDL_CreateMutex();

	SDL_LockMutex(key_lock);

	FillSDLArray();

	keyd_time_when_last_pressed = timer_get_milliseconds();
	keyd_buffer_type = 1;
	keyd_repeat = 1;

	// Clear the keyboard array
	key_flush();

	SDL_UnlockMutex( key_lock );

	os::events::addEventListener(SDL_KEYDOWN, os::events::DEFAULT_LISTENER_WEIGHT, key_down_event_handler);
	os::events::addEventListener(SDL_KEYUP, os::events::DEFAULT_LISTENER_WEIGHT, key_up_event_handler);

	atexit(key_close);
}

void key_level_init()
{
	int i;

	for (i=0; i<NUM_KEYS; i++)
		key_data.down_check[i] = 0;
}

void key_lost_focus()
{
	if ( !key_inited ) return;

	key_flush();	
}

void key_got_focus()
{
	if ( !key_inited ) return;
	
	key_flush();	
}