- Added Raw Input keyboard handling.

SVN r1614 (trunk)
This commit is contained in:
Randy Heit 2009-05-27 20:44:54 +00:00
parent 7fea07dff7
commit a11073341c
4 changed files with 397 additions and 152 deletions

View file

@ -1,4 +1,7 @@
May 26, 2009 May 27, 2009
- Added Raw Input keyboard handling.
May 26, 2009
- Split DirectInput keyboard handling into a separate file and class. I also - 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 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 cooked, so I don't need to look for it with WM_KEYDOWN/UP. Tab doesn't

View file

@ -128,7 +128,7 @@ static HRESULT InitJoystick ();
bool GUICapture; bool GUICapture;
extern FMouse *Mouse; extern FMouse *Mouse;
extern FInputDevice *Keyboard; extern FKeyboard *Keyboard;
bool VidResizing; bool VidResizing;
@ -153,13 +153,6 @@ LPDIRECTINPUT g_pdi3;
static LPDIRECTINPUTDEVICE8 g_pJoy; static LPDIRECTINPUTDEVICE8 g_pJoy;
// These can also be earlier IDirectInputDevice interfaces.
// Since IDirectInputDevice8 just added new methods to it
// without rearranging the old ones, I just maintain one
// pointer for each device instead of two.
static LPDIRECTINPUTDEVICE8 g_pKey;
TArray<GUIDName> JoystickNames; TArray<GUIDName> JoystickNames;
static DIDEVCAPS JoystickCaps; static DIDEVCAPS JoystickCaps;
@ -304,30 +297,7 @@ static FBaseCVar * const JoyConfigVars[] =
}; };
static BYTE KeyState[256]; static BYTE KeyState[256];
/*static BYTE DIKState[2][NUM_KEYS];
static int ActiveDIKState;
static void FlushDIKState (int low=0, int high=NUM_KEYS-1)
{
int i;
event_t event;
BYTE *state = DIKState[ActiveDIKState];
memset (&event, 0, sizeof(event));
event.type = EV_KeyUp;
for (i = low; i <= high; ++i)
{
if (state[i])
{
state[i] = 0;
event.data1 = i;
event.data2 = i < 256 ? Convert[i] : 0;
D_PostEvent (&event);
}
}
}
*/
extern int chatmodeon; extern int chatmodeon;
static void I_CheckGUICapture () static void I_CheckGUICapture ()
@ -346,9 +316,9 @@ static void I_CheckGUICapture ()
if (wantCapt != GUICapture) if (wantCapt != GUICapture)
{ {
GUICapture = wantCapt; GUICapture = wantCapt;
if (wantCapt) if (wantCapt && Keyboard != NULL)
{ {
// FlushDIKState (); Keyboard->AllKeysUp();
} }
} }
} }
@ -408,18 +378,11 @@ LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
return 0; return 0;
case WM_KILLFOCUS: case WM_KILLFOCUS:
if (g_pKey) g_pKey->Unacquire ();
// FlushDIKState ();
HaveFocus = false; HaveFocus = false;
I_CheckNativeMouse (true); // Make sure mouse gets released right away I_CheckNativeMouse (true); // Make sure mouse gets released right away
break; break;
case WM_SETFOCUS: case WM_SETFOCUS:
if (g_pKey)
{
g_pKey->Acquire();
}
HaveFocus = true; HaveFocus = true;
I_CheckNativeMouse (false); I_CheckNativeMouse (false);
break; break;
@ -494,45 +457,6 @@ LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
} }
} }
} }
else
{
#if 0
if (message == WM_KEYUP)
{
event.type = EV_KeyUp;
}
else
{
if (lParam & 0x40000000)
{
return 0;
}
else
{
event.type = EV_KeyDown;
}
}
switch (wParam)
{
case VK_PAUSE:
event.data1 = KEY_PAUSE;
break;
case VK_TAB:
event.data1 = DIK_TAB;
event.data2 = '\t';
break;
case VK_NUMLOCK:
event.data1 = DIK_NUMLOCK;
break;
}
if (event.data1)
{
DIKState[ActiveDIKState][event.data1] = (event.type == EV_KeyDown);
D_PostEvent (&event);
}
#endif
}
break; break;
case WM_CHAR: case WM_CHAR:

View file

@ -84,6 +84,36 @@ protected:
WORD ButtonState; // bit mask of current button states (1=down, 0=up) WORD ButtonState; // bit mask of current button states (1=down, 0=up)
}; };
class FKeyboard : public FInputDevice
{
public:
FKeyboard();
~FKeyboard();
void AllKeysUp();
protected:
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));
}
}
bool CheckAndSetKey(int keynum, INTBOOL down);
void PostKeyEvent(int keynum, INTBOOL down, bool foreground);
};
void I_StartupMouse(); void I_StartupMouse();
void I_CheckNativeMouse(bool prefer_native); void I_CheckNativeMouse(bool prefer_native);
void I_StartupKeyboard(); void I_StartupKeyboard();

View file

@ -23,7 +23,7 @@
// TYPES ------------------------------------------------------------------- // TYPES -------------------------------------------------------------------
class FDInputKeyboard : public FInputDevice class FDInputKeyboard : public FKeyboard
{ {
public: public:
FDInputKeyboard(); FDInputKeyboard();
@ -35,23 +35,20 @@ public:
protected: protected:
LPDIRECTINPUTDEVICE8 Device; LPDIRECTINPUTDEVICE8 Device;
BYTE KeyStates[256/8]; };
int CheckKey(int keynum) const class FRawKeyboard : public FKeyboard
{ {
return KeyStates[keynum >> 3] & (1 << (keynum & 7)); public:
} FRawKeyboard();
void SetKey(int keynum, bool down) ~FRawKeyboard();
{
if (down) bool GetDevice();
{ void ProcessInput();
KeyStates[keynum >> 3] |= 1 << (keynum & 7); bool WndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result);
}
else protected:
{ USHORT E1Prefix;
KeyStates[keynum >> 3] &= ~(1 << (keynum & 7));
}
}
}; };
// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
@ -71,7 +68,7 @@ extern bool HaveFocus;
// PRIVATE DATA DEFINITIONS ------------------------------------------------ // PRIVATE DATA DEFINITIONS ------------------------------------------------
// Convert DIK_* code to ASCII using Qwerty keymap // Convert DIK_* code to ASCII using Qwerty keymap
static const BYTE Convert [256] = static const BYTE Convert[256] =
{ {
// 0 1 2 3 4 5 6 7 8 9 A B C D E F // 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 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 8, 9, // 0
@ -94,13 +91,163 @@ static const BYTE Convert [256] =
// PUBLIC DATA DEFINITIONS ------------------------------------------------- // PUBLIC DATA DEFINITIONS -------------------------------------------------
FInputDevice *Keyboard; FKeyboard *Keyboard;
// Set this to false to make keypad-enter a usable separate key. // Set this to false to make keypad-enter a usable separate key.
CVAR (Bool, k_mergekeys, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Bool, k_mergekeys, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
// CODE -------------------------------------------------------------------- // CODE --------------------------------------------------------------------
//==========================================================================
//
// FKeyboard - Constructor
//
//==========================================================================
FKeyboard::FKeyboard()
{
memset(KeyStates, 0, sizeof(KeyStates));
}
//==========================================================================
//
// FKeyboard - Destructor
//
//==========================================================================
FKeyboard::~FKeyboard()
{
AllKeysUp();
}
//==========================================================================
//
// FKeyboard :: CheckAndSetKey
//
// Returns true if the key was already in the desired, false if it wasn't.
//
//==========================================================================
bool FKeyboard::CheckAndSetKey(int keynum, INTBOOL down)
{
BYTE *statebyte = &KeyStates[keynum >> 3];
BYTE mask = 1 << (keynum & 7);
if (down)
{
if (*statebyte & mask)
{
return true;
}
*statebyte |= mask;
return false;
}
else
{
if (*statebyte & mask)
{
*statebyte &= ~mask;
return false;
}
return true;
}
}
//==========================================================================
//
// FKeyboard :: AllKeysUp
//
// For every key currently marked as down, send a key up event and clear it.
//
//==========================================================================
void FKeyboard::AllKeysUp()
{
event_t ev = { 0 };
ev.type = EV_KeyUp;
for (int i = 0; i < 256/8; ++i)
{
if (KeyStates[i] != 0)
{
BYTE states = KeyStates[i];
int j = 0;
KeyStates[i] = 0;
do
{
if (states & 1)
{
ev.data1 = (i << 3) + j;
ev.data2 = Convert[ev.data1];
D_PostEvent(&ev);
}
states >>= 1;
++j;
}
while (states != 0);
}
}
}
//==========================================================================
//
// FKeyboard :: PostKeyEvent
//
// Posts a keyboard event, but only if the state is different from what we
// currently think it is. (For instance, raw keyboard input sends key
// down events every time the key automatically repeats, so we want to
// discard those.)
//
//==========================================================================
void FKeyboard::PostKeyEvent(int key, INTBOOL down, bool foreground)
{
event_t ev = { 0 };
// Printf("key=%02x down=%02x\n", key, down);
// "Merge" multiple keys that are considered to be the same. If the
// original unmerged key is down, it also needs to go up. (In case
// somebody was holding the key down when they changed this setting.)
if (k_mergekeys)
{
if (key == DIK_NUMPADENTER || key == DIK_RMENU || key == DIK_RCONTROL)
{
k_mergekeys = false;
PostKeyEvent(key, false, foreground);
k_mergekeys = true;
key &= 0x7F;
}
else if (key == DIK_RSHIFT)
{
k_mergekeys = false;
PostKeyEvent(key, false, foreground);
k_mergekeys = true;
key = DIK_LSHIFT;
}
}
// Generate the event, if appropriate.
if (down)
{
if (!foreground || GUICapture)
{ // Do not generate key down events if we are in the background
// or in "GUI Capture" mode.
return;
}
ev.type = EV_KeyDown;
}
else
{
ev.type = EV_KeyUp;
}
if (CheckAndSetKey(key, down))
{ // Key is already down or up.
return;
}
ev.data1 = key;
ev.data2 = Convert[key];
D_PostEvent(&ev);
}
//========================================================================== //==========================================================================
// //
// FDInputKeyboard - Constructor // FDInputKeyboard - Constructor
@ -110,7 +257,6 @@ CVAR (Bool, k_mergekeys, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
FDInputKeyboard::FDInputKeyboard() FDInputKeyboard::FDInputKeyboard()
{ {
Device = NULL; Device = NULL;
memset(KeyStates, 0, sizeof(KeyStates));
} }
//========================================================================== //==========================================================================
@ -199,10 +345,8 @@ void FDInputKeyboard::ProcessInput()
DIDEVICEOBJECTDATA od; DIDEVICEOBJECTDATA od;
DWORD dwElements; DWORD dwElements;
HRESULT hr; HRESULT hr;
int key;
bool foreground = (GetForegroundWindow() == Window); bool foreground = (GetForegroundWindow() == Window);
event_t ev = { 0 };
for (;;) for (;;)
{ {
DWORD cbObjectData = g_pdi3 ? sizeof(DIDEVICEOBJECTDATA_DX3) : sizeof(DIDEVICEOBJECTDATA); DWORD cbObjectData = g_pdi3 ? sizeof(DIDEVICEOBJECTDATA_DX3) : sizeof(DIDEVICEOBJECTDATA);
@ -218,57 +362,9 @@ void FDInputKeyboard::ProcessInput()
break; break;
} }
key = od.dwOfs; if (od.dwOfs >= 1 && od.dwOfs <= 255)
// Printf("buffer ofs=%02x data=%02x\n", od.dwOfs, od.dwData);
if (key >= 1 && key <= 255)
{ {
if (k_mergekeys) PostKeyEvent(od.dwOfs, od.dwData & 0x80, foreground);
{
// "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);
} }
} }
} }
@ -284,6 +380,192 @@ bool FDInputKeyboard::WndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM
return false; return false;
} }
//==========================================================================
//
// FRawKeyboard - Constructor
//
//==========================================================================
FRawKeyboard::FRawKeyboard()
{
E1Prefix = 0;
}
//==========================================================================
//
// FRawKeyboard - Destructor
//
//==========================================================================
FRawKeyboard::~FRawKeyboard()
{
if (MyRegisterRawInputDevices != NULL)
{
RAWINPUTDEVICE rid;
rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE;
rid.usUsage = HID_GDP_KEYBOARD;
rid.dwFlags = RIDEV_REMOVE;
rid.hwndTarget = NULL;
MyRegisterRawInputDevices(&rid, 1, sizeof(rid));
}
}
//==========================================================================
//
// FRawKeyboard :: GetDevice
//
// Ensure the API is present and we can listen for keyboard input.
//
//==========================================================================
bool FRawKeyboard::GetDevice()
{
RAWINPUTDEVICE rid;
if (MyRegisterRawInputDevices == NULL)
{
return false;
}
rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE;
rid.usUsage = HID_GDP_KEYBOARD;
rid.dwFlags = RIDEV_INPUTSINK;
rid.hwndTarget = Window;
if (!MyRegisterRawInputDevices(&rid, 1, sizeof(rid)))
{
return false;
}
return true;
}
//==========================================================================
//
// FRawKeyboard :: ProcessInput
//
//==========================================================================
void FRawKeyboard::ProcessInput()
{
}
//==========================================================================
//
// FRawKeyboard :: WndProcHook
//
// Convert scan codes to DirectInput key codes. For the most part, this is
// straight forward: Scan codes without any prefix are passed unmodified.
// Scan codes with an 0xE0 prefix byte are generally passed by ORing them
// with 0x80. And scan codes with an 0xE1 prefix are the annowing Pause key
// which will generate another scan code that looks like Num Lock.
//
// This is a bit complicated only because the state of PC key codes is a bit
// of a mess. Keyboards may use simpler codes internally, but for the sake
// of compatibility, programs are presented with XT-compatible codes. This
// means that keys which were originally a shifted form of another key and
// were split off into a separate key all their own, or which were formerly
// a separate key and are now part of another key (most notable PrtScn and
// SysRq), will still generate code sequences that XT-era software will
// still perceive as the original sequences to use those keys.
//
//==========================================================================
bool FRawKeyboard::WndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result)
{
if (message != WM_INPUT)
{
return false;
}
BYTE buffer[40];
UINT size = sizeof(buffer);
int keycode;
if (MyGetRawInputData((HRAWINPUT)lParam, RID_INPUT, buffer, &size, sizeof(RAWINPUTHEADER)) > 0)
{
RAWINPUT *raw = (RAWINPUT *)buffer;
if (raw->header.dwType != RIM_TYPEKEYBOARD)
{
return false;
}
keycode = raw->data.keyboard.MakeCode;
if (keycode == 0 && (raw->data.keyboard.Flags & RI_KEY_E0))
{ // Even if the make code is 0, we might still be able to extract a
// useful key from the message.
if (raw->data.keyboard.VKey >= VK_BROWSER_BACK && raw->data.keyboard.VKey <= VK_LAUNCH_APP2)
{
static const BYTE MediaKeys[VK_LAUNCH_APP2 - VK_BROWSER_BACK + 1] =
{
DIK_WEBBACK, DIK_WEBFORWARD, DIK_WEBREFRESH, DIK_WEBSTOP,
DIK_WEBSEARCH, DIK_WEBFAVORITES, DIK_WEBHOME,
DIK_MUTE, DIK_VOLUMEDOWN, DIK_VOLUMEUP,
DIK_NEXTTRACK, DIK_PREVTRACK, DIK_MEDIASTOP, DIK_PLAYPAUSE,
DIK_MAIL, DIK_MEDIASELECT, DIK_MYCOMPUTER, DIK_CALCULATOR
};
keycode = MediaKeys[raw->data.keyboard.VKey - VK_BROWSER_BACK];
}
}
if (keycode < 1 || keycode > 0xFF)
{
return false;
}
if (raw->data.keyboard.Flags & RI_KEY_E1)
{
E1Prefix = raw->data.keyboard.MakeCode;
return false;
}
if (raw->data.keyboard.Flags & RI_KEY_E0)
{
if (keycode == DIK_LSHIFT || keycode == DIK_RSHIFT)
{ // Ignore fake shifts.
return false;
}
keycode |= 0x80;
}
// The sequence for an unshifted pause is E1 1D 45 (E1 Prefix +
// Control + Num Lock).
if (E1Prefix)
{
if (E1Prefix == 0x1D && keycode == DIK_NUMLOCK)
{
keycode = DIK_PAUSE;
E1Prefix = 0;
}
else
{
E1Prefix = 0;
return false;
}
}
// If you press Ctrl+Pause, the keyboard sends the Break make code
// E0 46 instead of the Pause make code.
if (keycode == 0xC6)
{
keycode = DIK_PAUSE;
}
// If you press Ctrl+PrtScn (to get SysRq), the keyboard sends
// the make code E0 37. If you press PrtScn without any modifiers,
// it sends E0 2A E0 37. And if you press Alt+PrtScn, it sends 54
// (which is undefined in the charts I can find.)
if (keycode == 0x54)
{
keycode = DIK_SYSRQ;
}
// If you press any keys in the island between the main keyboard
// and the numeric keypad with Num Lock turned on, they generate
// a fake shift before their actual codes. They do not generate this
// fake shift if Num Lock is off. We unconditionally discard fake
// shifts above, so we don't need to do anything special for these,
// since they are also prefixed by E0 so we can tell them apart from
// their keypad counterparts.
// Okay, we're done translating the keycode. Post it (or ignore it.)
PostKeyEvent(keycode, !(raw->data.keyboard.Flags & RI_KEY_BREAK),
GET_RAWINPUT_CODE_WPARAM(wParam) == RIM_INPUT);
}
return false;
}
//========================================================================== //==========================================================================
// //
// I_StartupKeyboard // I_StartupKeyboard
@ -292,6 +574,12 @@ bool FDInputKeyboard::WndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM
void I_StartupKeyboard() void I_StartupKeyboard()
{ {
Keyboard = new FRawKeyboard;
if (Keyboard->GetDevice())
{
return;
}
delete Keyboard;
Keyboard = new FDInputKeyboard; Keyboard = new FDInputKeyboard;
if (!Keyboard->GetDevice()) if (!Keyboard->GetDevice())
{ {