- Split DirectInput keyboard handling into a separate file and class. I also

switched it to buffered input, and the pause key seems to be properly
  cooked, so I don't need to look for it with WM_KEYDOWN/UP. Tab doesn't
  need to be special-cased either, because buffered input never passes on
  the Tab key when you press Alt+Tab. I have no idea why I special-cased
  Num Lock, but it seems to be working fine. By setting the exclusive mode
  to background, I can also avoid special code for releasing all keys when
  the window loses focus, because I'll still receive those events while the
  window is in the background.


SVN r1613 (trunk)
This commit is contained in:
Randy Heit 2009-05-27 03:16:46 +00:00
parent 0a310b9832
commit 7fea07dff7
9 changed files with 350 additions and 163 deletions

View file

@ -1,4 +1,13 @@
May 26, 2009
- Split DirectInput keyboard handling into a separate file and class. I also
switched it to buffered input, and the pause key seems to be properly
cooked, so I don't need to look for it with WM_KEYDOWN/UP. Tab doesn't
need to be special-cased either, because buffered input never passes on
the Tab key when you press Alt+Tab. I have no idea why I special-cased
Num Lock, but it seems to be working fine. By setting the exclusive mode
to background, I can also avoid special code for releasing all keys when
the window loses focus, because I'll still receive those events while the
window is in the background.
- Fixed: The Heretic "take weapons" cheat did not remove all the weapons at
once. This is because destroying one weapon has a potential to destroy a
sister weapon as well. If the sister weapon is the next one in line (as it

View file

@ -398,6 +398,7 @@ if( WIN32 )
win32/i_cd.cpp
win32/i_crash.cpp
win32/i_input.cpp
win32/i_keyboard.cpp
win32/i_mouse.cpp
win32/i_main.cpp
win32/i_movie.cpp

View file

@ -191,14 +191,14 @@ const char *KeyNames[NUM_KEYS] =
NULL, NULL, NULL, NULL, NULL, NULL, "voldown", NULL, //A8
"volup", NULL, "webhome", "kp,", NULL, "kp/", NULL, "sysrq", //B0
"ralt", NULL, NULL, NULL, NULL, NULL, NULL, NULL, //B8
NULL, NULL, NULL, NULL, NULL, NULL, NULL, "home", //C0
NULL, NULL, NULL, NULL, NULL, "pause", NULL, "home", //C0
"uparrow", "pgup", NULL, "leftarrow",NULL, "rightarrow",NULL, "end", //C8
"downarrow","pgdn", "ins", "del", NULL, NULL, NULL, NULL, //D0
NULL, NULL, NULL, "lwin", "rwin", "apps", "power", "sleep", //D8
NULL, NULL, NULL, "wake", NULL, "search", "favorites","refresh", //E0
"webstop", "webforward","webback", "mycomputer","mail", "mediaselect",NULL, NULL, //E8
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, //F0
NULL, NULL, NULL, NULL, NULL, NULL, NULL, "pause", //F8
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, //F8
// non-keyboard buttons that can be bound
"mouse1", "mouse2", "mouse3", "mouse4", // 8 mouse buttons

View file

@ -104,6 +104,7 @@ enum ESkillLevels
// DOOM keyboard definition. Everything below 0x100 matches
// a DirectInput key code.
//
#define KEY_PAUSE 0xc5 // DIK_PAUSE
#define KEY_RIGHTARROW 0xcd // DIK_RIGHT
#define KEY_LEFTARROW 0xcb // DIK_LEFT
#define KEY_UPARROW 0xc8 // DIK_UP
@ -126,7 +127,6 @@ enum ESkillLevels
#define KEY_F12 0x58 // DIK_F12
#define KEY_BACKSPACE 0x0e // DIK_BACK
#define KEY_PAUSE 0xff
#define KEY_EQUALS 0x0d // DIK_EQUALS
#define KEY_MINUS 0x0c // DIK_MINUS

View file

@ -124,12 +124,11 @@ extern bool SpawnEAXWindow;
static HMODULE DInputDLL;
static void KeyRead ();
static BOOL I_StartupKeyboard ();
static HRESULT InitJoystick ();
bool GUICapture;
extern FMouse *Mouse;
extern FInputDevice *Keyboard;
bool VidResizing;
@ -294,8 +293,6 @@ CVAR (Float, joy_forwardspeed, -1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Float, joy_sidespeed, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Float, joy_upspeed, -1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
// set this to false to make keypad-enter a usable separate key!
CVAR (Bool, k_mergekeys, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, k_allowfullscreentoggle, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
static FBaseCVar * const JoyConfigVars[] =
@ -307,31 +304,9 @@ static FBaseCVar * const JoyConfigVars[] =
};
static BYTE KeyState[256];
static BYTE DIKState[2][NUM_KEYS];
static int KeysReadCount;
/*static BYTE DIKState[2][NUM_KEYS];
static int ActiveDIKState;
// Convert DIK_* code to ASCII using Qwerty keymap
static const BYTE Convert [256] =
{
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 8, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', 13, 0, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', 39, '`', 0,'\\', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '/', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, // 3
0, 0, 0, 0, 0, 0, 0, '7', '8', '9', '-', '4', '5', '6', '+', '1', // 4
'2', '3', '0', '.', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '=', 0, 0, // 8
0, '@', ':', '_', 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, // 9
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A
0, 0, 0, ',', 0, '/', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C
0, 0, 0, 0, 0, 0, 0, 0
};
static void FlushDIKState (int low=0, int high=NUM_KEYS-1)
{
@ -352,7 +327,7 @@ static void FlushDIKState (int low=0, int high=NUM_KEYS-1)
}
}
}
*/
extern int chatmodeon;
static void I_CheckGUICapture ()
@ -373,21 +348,32 @@ static void I_CheckGUICapture ()
GUICapture = wantCapt;
if (wantCapt)
{
FlushDIKState ();
// FlushDIKState ();
}
}
}
bool CallHook(FInputDevice *device, HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result)
{
if (device == NULL)
{
return false;
}
*result = 0;
return device->WndProcHook(hWnd, message, wParam, lParam, result);
}
LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (Mouse != NULL)
{
LRESULT result = 0;
LRESULT result;
if (Mouse->WndProcHook(hWnd, message, wParam, lParam, &result))
if (CallHook(Keyboard, hWnd, message, wParam, lParam, &result))
{
return result;
}
if (CallHook(Mouse, hWnd, message, wParam, lParam, &result))
{
return result;
}
event_t event;
@ -424,7 +410,7 @@ LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
case WM_KILLFOCUS:
if (g_pKey) g_pKey->Unacquire ();
FlushDIKState ();
// FlushDIKState ();
HaveFocus = false;
I_CheckNativeMouse (true); // Make sure mouse gets released right away
break;
@ -510,6 +496,7 @@ LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
}
else
{
#if 0
if (message == WM_KEYUP)
{
event.type = EV_KeyUp;
@ -544,6 +531,7 @@ LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
DIKState[ActiveDIKState][event.data1] = (event.type == EV_KeyDown);
D_PostEvent (&event);
}
#endif
}
break;
@ -1248,11 +1236,9 @@ bool I_InitInput (void *hwnd)
// Free all input resources
void I_ShutdownInput ()
{
if (g_pKey)
if (Keyboard != NULL)
{
g_pKey->Unacquire ();
g_pKey->Release ();
g_pKey = NULL;
delete Keyboard;
}
if (Mouse != NULL)
{
@ -1281,120 +1267,6 @@ void I_ShutdownInput ()
}
}
// Initialize the keyboard
static BOOL I_StartupKeyboard (void)
{
HRESULT hr;
// Obtain an interface to the system key device.
if (g_pdi3)
{
hr = g_pdi3->CreateDevice (GUID_SysKeyboard, (LPDIRECTINPUTDEVICE*)&g_pKey, NULL);
}
else
{
hr = g_pdi->CreateDevice (GUID_SysKeyboard, &g_pKey, NULL);
}
if (FAILED(hr))
{
I_FatalError ("Could not create keyboard device");
}
// Set the data format to "keyboard format".
hr = g_pKey->SetDataFormat (&c_dfDIKeyboard);
if (FAILED(hr))
{
I_FatalError ("Could not set keyboard data format");
}
// Set the cooperative level.
hr = g_pKey->SetCooperativeLevel (Window, DISCL_FOREGROUND|DISCL_NONEXCLUSIVE);
if (FAILED(hr))
{
I_FatalError("Could not set keyboard cooperative level");
}
g_pKey->Acquire ();
return TRUE;
}
static void KeyRead ()
{
HRESULT hr;
event_t event;
BYTE *fromState, *toState;
int i;
if (g_pKey == NULL)
{
return;
}
memset (&event, 0, sizeof(event));
fromState = DIKState[ActiveDIKState];
toState = DIKState[ActiveDIKState ^ 1];
hr = g_pKey->GetDeviceState (256, toState);
if (hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED)
{
hr = g_pKey->Acquire ();
if (hr != DI_OK)
{
return;
}
hr = g_pKey->GetDeviceState (256, toState);
}
if (hr != DI_OK)
{
return;
}
// Successfully got the buffer
KeysReadCount++;
ActiveDIKState ^= 1;
// Copy key states not handled here from the old to the new buffer
memcpy (toState + 256, fromState + 256, NUM_KEYS - 256);
toState[DIK_TAB] = fromState[DIK_TAB];
toState[DIK_NUMLOCK] = fromState[DIK_NUMLOCK];
if (k_mergekeys)
{
// "Merge" multiple keys that are considered to be the same.
// Also clear out the alternate versions after merging.
toState[DIK_RETURN] |= toState[DIK_NUMPADENTER];
toState[DIK_LMENU] |= toState[DIK_RMENU];
toState[DIK_LCONTROL] |= toState[DIK_RCONTROL];
toState[DIK_LSHIFT] |= toState[DIK_RSHIFT];
toState[DIK_NUMPADENTER] = 0;
toState[DIK_RMENU] = 0;
toState[DIK_RCONTROL] = 0;
toState[DIK_RSHIFT] = 0;
}
// Now generate events for any keys that differ between the states
if (!GUICapture)
{
for (i = 1; i < 256; i++)
{
if (toState[i] != fromState[i])
{
event.type = toState[i] ? EV_KeyDown : EV_KeyUp;
event.data1 = i;
event.data2 = Convert[i];
event.data3 = (toState[DIK_LSHIFT] ? GKM_SHIFT : 0) |
(toState[DIK_LCONTROL] ? GKM_CTRL : 0) |
(toState[DIK_LMENU] ? GKM_ALT : 0);
D_PostEvent (&event);
}
}
}
}
void I_GetEvent ()
{
MSG mess;
@ -1414,8 +1286,10 @@ void I_GetEvent ()
}
}
KeyRead ();
if (Keyboard != NULL)
{
Keyboard->ProcessInput();
}
if (Mouse != NULL)
{
Mouse->ProcessInput();

View file

@ -86,6 +86,7 @@ protected:
void I_StartupMouse();
void I_CheckNativeMouse(bool prefer_native);
void I_StartupKeyboard();
// USB HID usage page numbers
#define HID_GENERIC_DESKTOP_PAGE 0x01

301
src/win32/i_keyboard.cpp Normal file
View file

@ -0,0 +1,301 @@
// HEADER FILES ------------------------------------------------------------
#define WIN32_LEAN_AND_MEAN
#define DIRECTINPUT_VERSION 0x800
#define _WIN32_WINNT 0x0501
#include <windows.h>
#include <dinput.h>
#define USE_WINDOWS_DWORD
#include "i_input.h"
#include "i_system.h"
#include "d_event.h"
#include "d_gui.h"
#include "c_cvars.h"
#include "doomdef.h"
#include "doomstat.h"
#include "win32iface.h"
#include "rawinput.h"
// MACROS ------------------------------------------------------------------
#define DINPUT_BUFFERSIZE 32
// TYPES -------------------------------------------------------------------
class FDInputKeyboard : public FInputDevice
{
public:
FDInputKeyboard();
~FDInputKeyboard();
bool GetDevice();
void ProcessInput();
bool WndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result);
protected:
LPDIRECTINPUTDEVICE8 Device;
BYTE KeyStates[256/8];
int CheckKey(int keynum) const
{
return KeyStates[keynum >> 3] & (1 << (keynum & 7));
}
void SetKey(int keynum, bool down)
{
if (down)
{
KeyStates[keynum >> 3] |= 1 << (keynum & 7);
}
else
{
KeyStates[keynum >> 3] &= ~(1 << (keynum & 7));
}
}
};
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
extern HWND Window;
extern LPDIRECTINPUT8 g_pdi;
extern LPDIRECTINPUT g_pdi3;
extern bool GUICapture;
extern bool HaveFocus;
// PRIVATE DATA DEFINITIONS ------------------------------------------------
// Convert DIK_* code to ASCII using Qwerty keymap
static const BYTE Convert [256] =
{
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 8, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', 13, 0, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', 39, '`', 0,'\\', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '/', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, // 3
0, 0, 0, 0, 0, 0, 0, '7', '8', '9', '-', '4', '5', '6', '+', '1', // 4
'2', '3', '0', '.', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '=', 0, 0, // 8
0, '@', ':', '_', 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, // 9
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A
0, 0, 0, ',', 0, '/', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C
0, 0, 0, 0, 0, 0, 0, 0
};
// PUBLIC DATA DEFINITIONS -------------------------------------------------
FInputDevice *Keyboard;
// Set this to false to make keypad-enter a usable separate key.
CVAR (Bool, k_mergekeys, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
// CODE --------------------------------------------------------------------
//==========================================================================
//
// FDInputKeyboard - Constructor
//
//==========================================================================
FDInputKeyboard::FDInputKeyboard()
{
Device = NULL;
memset(KeyStates, 0, sizeof(KeyStates));
}
//==========================================================================
//
// FDInputKeyboard - Destructor
//
//==========================================================================
FDInputKeyboard::~FDInputKeyboard()
{
if (Device != NULL)
{
Device->Release();
Device = NULL;
}
}
//==========================================================================
//
// FDInputKeyboard :: GetDevice
//
// Create the device interface and initialize it.
//
//==========================================================================
bool FDInputKeyboard::GetDevice()
{
HRESULT hr;
if (g_pdi3 != NULL)
{ // DirectInput3 interface
hr = g_pdi3->CreateDevice(GUID_SysKeyboard, (LPDIRECTINPUTDEVICE*)&Device, NULL);
}
else if (g_pdi != NULL)
{ // DirectInput8 interface
hr = g_pdi->CreateDevice(GUID_SysKeyboard, &Device, NULL);
}
else
{
hr = -1;
}
if (FAILED(hr))
{
return false;
}
// Yes, this is a keyboard.
hr = Device->SetDataFormat(&c_dfDIKeyboard);
if (FAILED(hr))
{
ufailit:
Device->Release();
Device = NULL;
return false;
}
// Set cooperative level.
hr = Device->SetCooperativeLevel(Window, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
if (FAILED(hr))
{
goto ufailit;
}
// Set buffer size so we can use buffered input.
DIPROPDWORD prop;
prop.diph.dwSize = sizeof(prop);
prop.diph.dwHeaderSize = sizeof(prop.diph);
prop.diph.dwObj = 0;
prop.diph.dwHow = DIPH_DEVICE;
prop.dwData = DINPUT_BUFFERSIZE;
hr = Device->SetProperty(DIPROP_BUFFERSIZE, &prop.diph);
if (FAILED(hr))
{
goto ufailit;
}
Device->Acquire();
return true;
}
//==========================================================================
//
// FDInputKeyboard :: ProcessInput
//
//==========================================================================
void FDInputKeyboard::ProcessInput()
{
DIDEVICEOBJECTDATA od;
DWORD dwElements;
HRESULT hr;
int key;
bool foreground = (GetForegroundWindow() == Window);
event_t ev = { 0 };
for (;;)
{
DWORD cbObjectData = g_pdi3 ? sizeof(DIDEVICEOBJECTDATA_DX3) : sizeof(DIDEVICEOBJECTDATA);
dwElements = 1;
hr = Device->GetDeviceData(cbObjectData, &od, &dwElements, 0);
if (hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED)
{
Device->Acquire();
hr = Device->GetDeviceData(cbObjectData, &od, &dwElements, 0);
}
if (FAILED(hr) || !dwElements)
{
break;
}
key = od.dwOfs;
// Printf("buffer ofs=%02x data=%02x\n", od.dwOfs, od.dwData);
if (key >= 1 && key <= 255)
{
if (k_mergekeys)
{
// "Merge" multiple keys that are considered to be the same.
if (key == DIK_NUMPADENTER)
{
key = DIK_RETURN;
}
else if (key == DIK_RMENU)
{
key = DIK_LMENU;
}
else if (key == DIK_RCONTROL)
{
key = DIK_LCONTROL;
}
else if (key == DIK_RSHIFT)
{
key = DIK_LSHIFT;
}
}
// Generate an event, but only if it isn't a repeat of the existing state.
if (od.dwData & 0x80)
{
if (!foreground || GUICapture)
{ // Do not generate key down events if we are in the background
// or in "GUI Capture" mode.
continue;
}
if (CheckKey(key))
{ // Key is already down.
continue;
}
SetKey(key, true);
ev.type = EV_KeyDown;
}
else
{
if (!CheckKey(key))
{ // Key is already up.
continue;
}
SetKey(key, false);
ev.type = EV_KeyUp;
}
ev.data1 = key;
ev.data2 = Convert[key];
D_PostEvent(&ev);
}
}
}
//==========================================================================
//
// FDInputKeyboard :: WndProcHook
//
//==========================================================================
bool FDInputKeyboard::WndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result)
{
return false;
}
//==========================================================================
//
// I_StartupKeyboard
//
//==========================================================================
void I_StartupKeyboard()
{
Keyboard = new FDInputKeyboard;
if (!Keyboard->GetDevice())
{
delete Keyboard;
}
}

View file

@ -762,7 +762,6 @@ void FDInputMouse::ProcessInput()
DIDEVICEOBJECTDATA od;
DWORD dwElements;
HRESULT hr;
int count = 0;
int dx, dy;
dx = 0;
@ -787,8 +786,6 @@ void FDInputMouse::ProcessInput()
if (FAILED(hr) || !dwElements)
break;
count++;
/* Look at the element to see what happened */
// GCC does not like putting the DIMOFS_ macros in case statements,
// so use ifs instead.

View file

@ -1976,6 +1976,10 @@
RelativePath=".\src\win32\i_input.h"
>
</File>
<File
RelativePath=".\src\win32\i_keyboard.cpp"
>
</File>
<File
RelativePath=".\src\win32\i_main.cpp"
>