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