- 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
commit 7fea07dff7
9 changed files with 350 additions and 163 deletions

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;
}
}