1 // HEADER FILES ------------------------------------------------------------
2 
3 #define WIN32_LEAN_AND_MEAN
4 #define DIRECTINPUT_VERSION 0x800
5 #define _WIN32_WINNT 0x0501
6 #include <windows.h>
7 #include <dinput.h>
8 
9 #define USE_WINDOWS_DWORD
10 #include "i_input.h"
11 #include "i_system.h"
12 #include "d_event.h"
13 #include "d_gui.h"
14 #include "c_cvars.h"
15 #include "doomdef.h"
16 #include "doomstat.h"
17 #include "win32iface.h"
18 #include "rawinput.h"
19 
20 // MACROS ------------------------------------------------------------------
21 
22 #define DINPUT_BUFFERSIZE	32
23 
24 // TYPES -------------------------------------------------------------------
25 
26 class FDInputKeyboard : public FKeyboard
27 {
28 public:
29 	FDInputKeyboard();
30 	~FDInputKeyboard();
31 
32 	bool GetDevice();
33 	void ProcessInput();
34 
35 protected:
36 	LPDIRECTINPUTDEVICE8 Device;
37 };
38 
39 class FRawKeyboard : public FKeyboard
40 {
41 public:
42 	FRawKeyboard();
43 	~FRawKeyboard();
44 
45 	bool GetDevice();
46 	bool ProcessRawInput(RAWINPUT *rawinput, int code);
47 
48 protected:
49 	USHORT E1Prefix;
50 };
51 
52 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
53 
54 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
55 
56 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
57 
58 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
59 
60 extern HWND Window;
61 extern LPDIRECTINPUT8 g_pdi;
62 extern LPDIRECTINPUT g_pdi3;
63 extern bool GUICapture;
64 
65 // PRIVATE DATA DEFINITIONS ------------------------------------------------
66 
67 // Convert DIK_* code to ASCII using Qwerty keymap
68 static const BYTE Convert[256] =
69 {
70   //  0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F
71 	  0,  27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=',   8,   9, // 0
72 	'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']',  13,   0, 'a', 's', // 1
73 	'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',  39, '`',   0,'\\', 'z', 'x', 'c', 'v', // 2
74 	'b', 'n', 'm', ',', '.', '/',   0, '*',   0, ' ',   0,   0,   0,   0,   0,   0, // 3
75 	  0,   0,   0,   0,   0,   0,   0, '7', '8', '9', '-', '4', '5', '6', '+', '1', // 4
76 	'2', '3', '0', '.',   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // 5
77 	  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // 6
78 	  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // 7
79 
80 	  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, '=',   0,   0, // 8
81 	  0, '@', ':', '_',   0,   0,   0,   0,   0,   0,   0,   0,  13,   0,   0,   0, // 9
82 	  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // A
83 	  0,   0,   0, ',',   0, '/',   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // B
84 	  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // C
85 	  0,   0,   0,   0,   0,   0,   0,   0
86 
87 };
88 
89 // PUBLIC DATA DEFINITIONS -------------------------------------------------
90 
91 FKeyboard *Keyboard;
92 
93 // Set this to false to make keypad-enter a usable separate key.
94 CVAR (Bool, k_mergekeys, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
95 
96 // CODE --------------------------------------------------------------------
97 
98 //==========================================================================
99 //
100 // FKeyboard - Constructor
101 //
102 //==========================================================================
103 
FKeyboard()104 FKeyboard::FKeyboard()
105 {
106 	memset(KeyStates, 0, sizeof(KeyStates));
107 }
108 
109 //==========================================================================
110 //
111 // FKeyboard - Destructor
112 //
113 //==========================================================================
114 
~FKeyboard()115 FKeyboard::~FKeyboard()
116 {
117 	AllKeysUp();
118 }
119 
120 //==========================================================================
121 //
122 // FKeyboard :: CheckAndSetKey
123 //
124 // Returns true if the key was already in the desired state, false if it
125 // wasn't.
126 //
127 //==========================================================================
128 
CheckAndSetKey(int keynum,INTBOOL down)129 bool FKeyboard::CheckAndSetKey(int keynum, INTBOOL down)
130 {
131 	BYTE *statebyte = &KeyStates[keynum >> 3];
132 	BYTE mask = 1 << (keynum & 7);
133 	if (down)
134 	{
135 		if (*statebyte & mask)
136 		{
137 			return true;
138 		}
139 		*statebyte |= mask;
140 		return false;
141 	}
142 	else
143 	{
144 		if (*statebyte & mask)
145 		{
146 			*statebyte &= ~mask;
147 			return false;
148 		}
149 		return true;
150 	}
151 }
152 
153 //==========================================================================
154 //
155 // FKeyboard :: AllKeysUp
156 //
157 // For every key currently marked as down, send a key up event and clear it.
158 //
159 //==========================================================================
160 
AllKeysUp()161 void FKeyboard::AllKeysUp()
162 {
163 	event_t ev = { 0 };
164 	ev.type = EV_KeyUp;
165 
166 	for (int i = 0; i < 256/8; ++i)
167 	{
168 		if (KeyStates[i] != 0)
169 		{
170 			BYTE states = KeyStates[i];
171 			int j = 0;
172 			KeyStates[i] = 0;
173 			do
174 			{
175 				if (states & 1)
176 				{
177 					ev.data1 = (i << 3) + j;
178 					ev.data2 = Convert[ev.data1];
179 					D_PostEvent(&ev);
180 				}
181 				states >>= 1;
182 				++j;
183 			}
184 			while (states != 0);
185 		}
186 	}
187 }
188 
189 //==========================================================================
190 //
191 // FKeyboard :: PostKeyEvent
192 //
193 // Posts a keyboard event, but only if the state is different from what we
194 // currently think it is. (For instance, raw keyboard input sends key
195 // down events every time the key automatically repeats, so we want to
196 // discard those.)
197 //
198 //==========================================================================
199 
PostKeyEvent(int key,INTBOOL down,bool foreground)200 void FKeyboard::PostKeyEvent(int key, INTBOOL down, bool foreground)
201 {
202 	event_t ev = { 0 };
203 
204 //	Printf("key=%02x down=%02x\n", key, down);
205 	// "Merge" multiple keys that are considered to be the same. If the
206 	// original unmerged key is down, it also needs to go up. (In case
207 	// somebody was holding the key down when they changed this setting.)
208 	if (k_mergekeys)
209 	{
210 		if (key == DIK_NUMPADENTER || key == DIK_RMENU || key == DIK_RCONTROL)
211 		{
212 			k_mergekeys = false;
213 			PostKeyEvent(key, false, foreground);
214 			k_mergekeys = true;
215 			key &= 0x7F;
216 		}
217 		else if (key == DIK_RSHIFT)
218 		{
219 			k_mergekeys = false;
220 			PostKeyEvent(key, false, foreground);
221 			k_mergekeys = true;
222 			key = DIK_LSHIFT;
223 		}
224 	}
225 	if (key == 0x59)
226 	{ // Turn kp= on a Mac keyboard into kp= on a PC98 keyboard.
227 		key = DIK_NUMPADEQUALS;
228 	}
229 
230 	// Generate the event, if appropriate.
231 	if (down)
232 	{
233 		if (!foreground || GUICapture)
234 		{ // Do not generate key down events if we are in the background
235 		  // or in "GUI Capture" mode.
236 			return;
237 		}
238 		ev.type = EV_KeyDown;
239 	}
240 	else
241 	{
242 		ev.type = EV_KeyUp;
243 	}
244 	if (CheckAndSetKey(key, down))
245 	{ // Key is already down or up.
246 		return;
247 	}
248 	ev.data1 = key;
249 	ev.data2 = Convert[key];
250 	D_PostEvent(&ev);
251 }
252 
253 //==========================================================================
254 //
255 // FDInputKeyboard - Constructor
256 //
257 //==========================================================================
258 
FDInputKeyboard()259 FDInputKeyboard::FDInputKeyboard()
260 {
261 	Device = NULL;
262 }
263 
264 //==========================================================================
265 //
266 // FDInputKeyboard - Destructor
267 //
268 //==========================================================================
269 
~FDInputKeyboard()270 FDInputKeyboard::~FDInputKeyboard()
271 {
272 	if (Device != NULL)
273 	{
274 		Device->Release();
275 		Device = NULL;
276 	}
277 }
278 
279 //==========================================================================
280 //
281 // FDInputKeyboard :: GetDevice
282 //
283 // Create the device interface and initialize it.
284 //
285 //==========================================================================
286 
GetDevice()287 bool FDInputKeyboard::GetDevice()
288 {
289 	HRESULT hr;
290 
291 	if (g_pdi3 != NULL)
292 	{ // DirectInput3 interface
293 		hr = g_pdi3->CreateDevice(GUID_SysKeyboard, (LPDIRECTINPUTDEVICE*)&Device, NULL);
294 	}
295 	else if (g_pdi != NULL)
296 	{ // DirectInput8 interface
297 		hr = g_pdi->CreateDevice(GUID_SysKeyboard, &Device, NULL);
298 	}
299 	else
300 	{
301 		hr = -1;
302 	}
303 	if (FAILED(hr))
304 	{
305 		return false;
306 	}
307 
308 	// Yes, this is a keyboard.
309 	hr = Device->SetDataFormat(&c_dfDIKeyboard);
310 	if (FAILED(hr))
311 	{
312 ufailit:
313 		Device->Release();
314 		Device = NULL;
315 		return false;
316 	}
317 	// Set cooperative level.
318 	hr = Device->SetCooperativeLevel(Window, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
319 	if (FAILED(hr))
320 	{
321 		goto ufailit;
322 	}
323 	// Set buffer size so we can use buffered input.
324 	DIPROPDWORD prop;
325 	prop.diph.dwSize = sizeof(prop);
326 	prop.diph.dwHeaderSize = sizeof(prop.diph);
327 	prop.diph.dwObj = 0;
328 	prop.diph.dwHow = DIPH_DEVICE;
329 	prop.dwData = DINPUT_BUFFERSIZE;
330 	hr = Device->SetProperty(DIPROP_BUFFERSIZE, &prop.diph);
331 	if (FAILED(hr))
332 	{
333 		goto ufailit;
334 	}
335 	Device->Acquire();
336 	return true;
337 }
338 
339 //==========================================================================
340 //
341 // FDInputKeyboard :: ProcessInput
342 //
343 //==========================================================================
344 
ProcessInput()345 void FDInputKeyboard::ProcessInput()
346 {
347 	DIDEVICEOBJECTDATA od;
348 	DWORD dwElements;
349 	HRESULT hr;
350 	bool foreground = (GetForegroundWindow() == Window);
351 
352 	for (;;)
353 	{
354 		DWORD cbObjectData = g_pdi3 ? sizeof(DIDEVICEOBJECTDATA_DX3) : sizeof(DIDEVICEOBJECTDATA);
355 		dwElements = 1;
356 		hr = Device->GetDeviceData(cbObjectData, &od, &dwElements, 0);
357 		if (hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED)
358 		{
359 			Device->Acquire();
360 			hr = Device->GetDeviceData(cbObjectData, &od, &dwElements, 0);
361 		}
362 		if (FAILED(hr) || !dwElements)
363 		{
364 			break;
365 		}
366 
367 		if (od.dwOfs >= 1 && od.dwOfs <= 255)
368 		{
369 			PostKeyEvent(od.dwOfs, od.dwData & 0x80, foreground);
370 		}
371 	}
372 }
373 
374 /**************************************************************************/
375 /**************************************************************************/
376 
377 //==========================================================================
378 //
379 // FRawKeyboard - Constructor
380 //
381 //==========================================================================
382 
FRawKeyboard()383 FRawKeyboard::FRawKeyboard()
384 {
385 	E1Prefix = 0;
386 }
387 
388 //==========================================================================
389 //
390 // FRawKeyboard - Destructor
391 //
392 //==========================================================================
393 
~FRawKeyboard()394 FRawKeyboard::~FRawKeyboard()
395 {
396 	if (MyRegisterRawInputDevices != NULL)
397 	{
398 		RAWINPUTDEVICE rid;
399 		rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE;
400 		rid.usUsage = HID_GDP_KEYBOARD;
401 		rid.dwFlags = RIDEV_REMOVE;
402 		rid.hwndTarget = NULL;
403 		MyRegisterRawInputDevices(&rid, 1, sizeof(rid));
404 	}
405 }
406 
407 //==========================================================================
408 //
409 // FRawKeyboard :: GetDevice
410 //
411 // Ensure the API is present and we can listen for keyboard input.
412 //
413 //==========================================================================
414 
GetDevice()415 bool FRawKeyboard::GetDevice()
416 {
417 	RAWINPUTDEVICE rid;
418 
419 	if (MyRegisterRawInputDevices == NULL)
420 	{
421 		return false;
422 	}
423 	rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE;
424 	rid.usUsage = HID_GDP_KEYBOARD;
425 	rid.dwFlags = RIDEV_INPUTSINK;
426 	rid.hwndTarget = Window;
427 	if (!MyRegisterRawInputDevices(&rid, 1, sizeof(rid)))
428 	{
429 		return false;
430 	}
431 	return true;
432 }
433 
434 //==========================================================================
435 //
436 // FRawKeyboard :: ProcessRawInput
437 //
438 // Convert scan codes to DirectInput key codes. For the most part, this is
439 // straight forward: Scan codes without any prefix are passed unmodified.
440 // Scan codes with an 0xE0 prefix byte are generally passed by ORing them
441 // with 0x80. And scan codes with an 0xE1 prefix are the annoying Pause key
442 // which will generate another scan code that looks like Num Lock.
443 //
444 // This is a bit complicated only because the state of PC key codes is a bit
445 // of a mess. Keyboards may use simpler codes internally, but for the sake
446 // of compatibility, programs are presented with XT-compatible codes. This
447 // means that keys which were originally a shifted form of another key and
448 // were split off into a separate key all their own, or which were formerly
449 // a separate key and are now part of another key (most notable PrtScn and
450 // SysRq), will still generate code sequences that XT-era software will
451 // still perceive as the original sequences to use those keys.
452 //
453 //==========================================================================
454 
ProcessRawInput(RAWINPUT * raw,int code)455 bool FRawKeyboard::ProcessRawInput(RAWINPUT *raw, int code)
456 {
457 	if (raw->header.dwType != RIM_TYPEKEYBOARD)
458 	{
459 		return false;
460 	}
461 	int keycode = raw->data.keyboard.MakeCode;
462 	if (keycode == 0 && (raw->data.keyboard.Flags & RI_KEY_E0))
463 	{ // Even if the make code is 0, we might still be able to extract a
464 	  // useful key from the message.
465 		if (raw->data.keyboard.VKey >= VK_BROWSER_BACK && raw->data.keyboard.VKey <= VK_LAUNCH_APP2)
466 		{
467 			static const BYTE MediaKeys[VK_LAUNCH_APP2 - VK_BROWSER_BACK + 1] =
468 			{
469 				DIK_WEBBACK, DIK_WEBFORWARD, DIK_WEBREFRESH, DIK_WEBSTOP,
470 				DIK_WEBSEARCH, DIK_WEBFAVORITES, DIK_WEBHOME,
471 
472 				DIK_MUTE, DIK_VOLUMEDOWN, DIK_VOLUMEUP,
473 				DIK_NEXTTRACK, DIK_PREVTRACK, DIK_MEDIASTOP, DIK_PLAYPAUSE,
474 
475 				DIK_MAIL, DIK_MEDIASELECT, DIK_MYCOMPUTER, DIK_CALCULATOR
476 			};
477 			keycode = MediaKeys[raw->data.keyboard.VKey - VK_BROWSER_BACK];
478 		}
479 	}
480 	if (keycode < 1 || keycode > 0xFF)
481 	{
482 		return false;
483 	}
484 	if (raw->data.keyboard.Flags & RI_KEY_E1)
485 	{
486 		E1Prefix = raw->data.keyboard.MakeCode;
487 		return false;
488 	}
489 	if (raw->data.keyboard.Flags & RI_KEY_E0)
490 	{
491 		if (keycode == DIK_LSHIFT || keycode == DIK_RSHIFT)
492 		{ // Ignore fake shifts.
493 			return false;
494 		}
495 		keycode |= 0x80;
496 	}
497 	// The sequence for an unshifted pause is E1 1D 45 (E1 Prefix +
498 	// Control + Num Lock).
499 	if (E1Prefix)
500 	{
501 		if (E1Prefix == 0x1D && keycode == DIK_NUMLOCK)
502 		{
503 			keycode = DIK_PAUSE;
504 			E1Prefix = 0;
505 		}
506 		else
507 		{
508 			E1Prefix = 0;
509 			return false;
510 		}
511 	}
512 	// If you press Ctrl+Pause, the keyboard sends the Break make code
513 	// E0 46 instead of the Pause make code.
514 	if (keycode == 0xC6)
515 	{
516 		keycode = DIK_PAUSE;
517 	}
518 	// If you press Ctrl+PrtScn (to get SysRq), the keyboard sends
519 	// the make code E0 37. If you press PrtScn without any modifiers,
520 	// it sends E0 2A E0 37. And if you press Alt+PrtScn, it sends 54
521 	// (which is undefined in the charts I can find.)
522 	if (keycode == 0x54)
523 	{
524 		keycode = DIK_SYSRQ;
525 	}
526 	// If you press any keys in the island between the main keyboard
527 	// and the numeric keypad with Num Lock turned on, they generate
528 	// a fake shift before their actual codes. They do not generate this
529 	// fake shift if Num Lock is off. We unconditionally discard fake
530 	// shifts above, so we don't need to do anything special for these,
531 	// since they are also prefixed by E0 so we can tell them apart from
532 	// their keypad counterparts.
533 
534 	// Okay, we're done translating the keycode. Post it (or ignore it.)
535 	PostKeyEvent(keycode, !(raw->data.keyboard.Flags & RI_KEY_BREAK), code == RIM_INPUT);
536 	return true;
537 }
538 
539 //==========================================================================
540 //
541 // I_StartupKeyboard
542 //
543 //==========================================================================
544 
I_StartupKeyboard()545 void I_StartupKeyboard()
546 {
547 	Keyboard = new FRawKeyboard;
548 	if (Keyboard->GetDevice())
549 	{
550 		return;
551 	}
552 	delete Keyboard;
553 	Keyboard = new FDInputKeyboard;
554 	if (!Keyboard->GetDevice())
555 	{
556 		delete Keyboard;
557 	}
558 }
559 
560