Intro :
DirectInput est un composant de DirectX qui permet de gérer les entrées clavier (touches) et de la souris (position).
Bien qu’il soit tendance à n’être plus utilisé, il est toujours utile car il permet de capturer directement les entrées clavier à partir des drivers périphériques correspondants.
Prérequis :
– Savoir un peu lire du C++.
– Savoir initialiser DirectX 10.1.
– Savoir utiliser la classe Singleton.
Explications :
Voici le fichier InputManager.h :
#ifndef INPUT_MANAGER_H
#define INPUT_MANAGER_H
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>
#include <map>
#include <vector>
#include "Singleton.h"
#include "System.h"
class KeyEvent
{
public:
KeyEvent(int kc, wchar_t txt) : key(kc), text(txt) {}
virtual ~KeyEvent() {}
// L'ID de la touche
const int key;
// La caractère correspondant de la touche
const wchar_t text;
};
class KeyListener
{
public:
virtual ~KeyListener() {}
virtual void RepetitiveKeyPressed(const KeyEvent &arg) = 0;
virtual void RepetitiveKeyReleased(const KeyEvent &arg) = 0;
virtual void SoloKeyPressed(const KeyEvent &arg) = 0;
virtual void SoloKeyReleased(const KeyEvent &arg) = 0;
};
class InputManager : public Singleton<InputManager>
{
public:
enum Modifier
{
SHIFT = 0x0000001,
CTRL = 0x0000010,
ALT = 0x0000100
};
InputManager();
virtual ~InputManager();
bool Initialize(HINSTANCE hInstance, HWND hwnd, int iScreenWidth, int iScreenHeight);
void Frame();
void GetMouseLocation(int& mouseX, int& mouseY);
bool IsKeyDown(char key);
bool IsMouseButtonDown(unsigned int button);
bool IsModifierDown(Modifier modifier);
void SetBuffered(bool bBuffered);
// Enregistre le receveur de touches clavier
void SetKeyListener(KeyListener* pKeyListener);
// Renvoie la signification de la clé, ex : "F1" pour la touche F1
std::string GetKeyCodeAsString(char key);
// Renvoie la touche correspondante à la clé
wchar_t GetKeyCodeAsWChar(char key);
private:
bool ReadKeyboard();
bool ReadMouse();
void ProcessInput();
void UpdateBuffered();
void UpdateNonBuffered();
private:
IDirectInput8* m_pDI;
IDirectInputDevice8* m_pKeyboard;
IDirectInputDevice8* m_pMouse;
unsigned char m_keyboardState[256];
DIMOUSESTATE m_mouseState;
int m_iScreenWidth;
int m_iScreenHeight;
int m_iMouseX;
int m_iMouseY;
wchar_t m_deadkey;
bool m_bBufferedMode;
unsigned int m_iModifiers;
std::map<int, bool> m_pressedKeys;
/* Pourquoi ici un unique objet ?
Car on a besoin que d'un seul KeyListener car
c'est soit la console, soit la messagerie qui ont besoin des entrées clavier
à la fois */
KeyListener* m_pKeyListener;
// Variables conçues pour que l'appel à la callback KeyListener soit appelée moins de fois dans le temps
unsigned long m_iElapsedTime;
unsigned long m_iLastTime;
unsigned int m_iWaitTime;
// Variables conçues pour la touche appuyée soit repétée quand on reste longtemps appuyée dessus
unsigned long m_iElapsedTime2;
unsigned long m_iLastTime2;
unsigned int m_iWaitTime2;
bool m_bCanRepeatKey;
unsigned int m_iCurrentKeyCode;
};
#endif
Voici le fichier InputManager.cpp :
#include "Console.h"
#include "InputManager.h"
#include <sstream>
#include <DxErr.h>
template<> InputManager* Singleton<InputManager>::ms_instance = nullptr;
InputManager::InputManager() :
m_pDI(nullptr),
m_pKeyboard(nullptr),
m_pMouse(nullptr),
m_iMouseX(0),
m_iMouseY(0),
m_deadkey('\0'),
m_bBufferedMode(false),
m_iModifiers(0),
m_pKeyListener(nullptr),
m_bCanRepeatKey(false),
m_iWaitTime(INPUT_MANAGER_REPETITIVE_WAIT_TIME),
m_iLastTime(0),
m_iElapsedTime(0),
m_iWaitTime2(INPUT_MANAGER_REPETITIVE_WAIT_TIME_2),
m_iLastTime2(0),
m_iElapsedTime2(0),
m_iCurrentKeyCode(0)
{
SetBuffered(true);
}
InputManager::~InputManager()
{
if (m_pMouse)
{
m_pMouse->Unacquire();
SAFE_RELEASE(m_pMouse);
}
if (m_pKeyboard)
{
m_pKeyboard->Unacquire();
SAFE_RELEASE(m_pKeyboard);
}
if (m_pDI)
{
SAFE_RELEASE(m_pDI);
}
}
bool InputManager::Initialize(HINSTANCE hInstance, HWND hwnd, int iScreenWidth, int iScreenHeight)
{
HRESULT hr;
m_iScreenWidth = iScreenWidth;
m_iScreenHeight = iScreenHeight;
/****** Setup du clavier ******/
hr= DirectInput8Create(hInstance, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&m_pDI, nullptr);
if (FAILED(hr))
{
ShowMessageBoxDXError(hr);
return false;
}
hr = m_pDI->CreateDevice(GUID_SysKeyboard, &m_pKeyboard, nullptr);
if (FAILED(hr))
{
ShowMessageBoxDXError(hr);
return false;
}
hr = m_pKeyboard->SetDataFormat(&c_dfDIKeyboard);
if (FAILED(hr))
{
ShowMessageBoxDXError(hr);
return false;
}
hr = m_pKeyboard->SetCooperativeLevel(hwnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
if (FAILED(hr))
{
ShowMessageBoxDXError(hr);
return false;
}
/***********/
// On configure la taille du buffer qui sert à sauvegarder l'état
// des touches du clavier entrées
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = INPUT_MANAGER_BUFFER_SIZE;
hr = m_pKeyboard->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph);
if (FAILED(hr))
{
ShowMessageBoxDXError(hr);
return false;
}
/***********/
hr = m_pKeyboard->Acquire();
if (FAILED(hr))
{
ShowMessageBoxDXError(hr);
return false;
}
/****** Setup de la souris ******/
hr = m_pDI->CreateDevice(GUID_SysMouse, &m_pMouse, nullptr);
if (FAILED(hr))
{
ShowMessageBoxDXError(hr);
return false;
}
hr = m_pMouse->SetDataFormat(&c_dfDIMouse);
if (FAILED(hr))
{
ShowMessageBoxDXError(hr);
return false;
}
hr = m_pMouse->SetCooperativeLevel(hwnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
if (FAILED(hr))
{
ShowMessageBoxDXError(hr);
return false;
}
hr = m_pMouse->Acquire();
if (FAILED(hr))
{
ShowMessageBoxDXError(hr);
return false;
}
return true;
}
void InputManager::SetKeyListener(KeyListener* pKeyListener)
{
assert(pKeyListener);
m_pKeyListener = pKeyListener;
}
bool InputManager::IsModifierDown(Modifier modifier)
{
return m_iModifiers & MKF_MODIFIERS;
}
void InputManager::Frame()
{
ReadKeyboard();
ReadMouse();
ProcessInput();
}
void InputManager::GetMouseLocation(int& mouseX, int& mouseY)
{
mouseX = m_iMouseX;
mouseY = m_iMouseY;
}
bool InputManager::ReadKeyboard()
{
if (m_bBufferedMode)
{
UpdateBuffered();
}
else
{
UpdateNonBuffered();
}
return true;
}
/* setcapture & getcapture (essayer) */
void InputManager::UpdateBuffered()
{
DIDEVICEOBJECTDATA diBuff[INPUT_MANAGER_BUFFER_SIZE];
DWORD entries = INPUT_MANAGER_BUFFER_SIZE;
HRESULT hr;
hr = m_pKeyboard->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), diBuff, &entries, 0);
if (FAILED(hr))
{
if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED))
{
ZeroMemory(m_keyboardState, sizeof(m_keyboardState));
m_pKeyboard->Acquire();
}
}
// Pour la touche soit repétée quand on reste longtemps appuyée dessus
if (m_iCurrentKeyCode != 0)
{
m_iElapsedTime2 = GetTickCount() - m_iLastTime2;
if (m_iElapsedTime2 > m_iWaitTime2)
{
m_bCanRepeatKey = true;
m_iLastTime2 = GetTickCount();
}
}
else
{
// On reset le timer au-dessus
m_iLastTime2 = GetTickCount();
}
if (!SYSTEM->HasFocus())
{
m_bCanRepeatKey = false;
m_iElapsedTime2 = 0;
m_iCurrentKeyCode = 0;
}
// On parcourt les touches appuyées
for (unsigned int i = 0; i < entries; i++)
{
int iKeyCode = diBuff[i].dwOfs;
if (diBuff[i].dwData & 0x80)
{
m_pressedKeys[iKeyCode] = true;
m_iCurrentKeyCode = diBuff[i].dwData;
if (iKeyCode == DIK_LCONTROL || iKeyCode == DIK_RCONTROL)
{
m_iModifiers |= CTRL;
}
else if (iKeyCode == DIK_LSHIFT || iKeyCode == DIK_RSHIFT)
{
m_iModifiers |= SHIFT;
}
else if (iKeyCode == DIK_LMENU || iKeyCode == DIK_RMENU)
{
m_iModifiers |= ALT;
}
if (m_pKeyListener)
{
m_pKeyListener->SoloKeyPressed( KeyEvent(iKeyCode, GetKeyCodeAsWChar(iKeyCode)) );
}
}
else
{
m_pressedKeys[iKeyCode] = false;
m_bCanRepeatKey = false;
m_iCurrentKeyCode = 0;
if (iKeyCode == DIK_LCONTROL || iKeyCode == DIK_RCONTROL)
{
m_iModifiers &= ~CTRL;
}
else if (iKeyCode == DIK_LSHIFT || iKeyCode == DIK_RSHIFT)
{
m_iModifiers &= ~SHIFT;
}
else if (iKeyCode == DIK_LMENU || iKeyCode == DIK_RMENU)
{
m_iModifiers &= ~ALT;
}
if (m_pKeyListener)
{
m_pKeyListener->SoloKeyReleased( KeyEvent(iKeyCode, GetKeyCodeAsWChar(iKeyCode)) );
}
}
}
/********** Entrées répétées ***********/
// On cherche quelle touche du clavier a été appuyée
int iKeyCode = 0;
for (auto it = m_pressedKeys.begin(); it != m_pressedKeys.end(); it++)
{
bool bPressed = it->second;
if (bPressed)
{
iKeyCode = it->first;
}
}
if (m_pKeyListener && iKeyCode != 0)
{
m_iElapsedTime = GetTickCount() - m_iLastTime;
// On appel la callback a une certaine fréquence de temps
if (m_iElapsedTime > m_iWaitTime)
{
m_pKeyListener->RepetitiveKeyPressed( KeyEvent(iKeyCode, GetKeyCodeAsWChar(iKeyCode)) );
if (m_bCanRepeatKey)
{
m_pKeyListener->SoloKeyPressed( KeyEvent(iKeyCode, GetKeyCodeAsWChar(iKeyCode)) );
}
m_iLastTime = GetTickCount();
}
}
}
void InputManager::UpdateNonBuffered()
{
HRESULT hr;
hr = m_pKeyboard->GetDeviceState(sizeof(m_keyboardState), (LPVOID)&m_keyboardState);
if (FAILED(hr))
{
if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED))
{
ZeroMemory(m_keyboardState, sizeof(m_keyboardState));
m_pKeyboard->Acquire();
}
}
}
bool InputManager::ReadMouse()
{
HRESULT hr;
hr = m_pMouse->GetDeviceState(sizeof(DIMOUSESTATE), (LPVOID)&m_mouseState);
if (FAILED(hr))
{
if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED))
{
m_pMouse->Acquire();
}
else
{
return false;
}
}
return true;
}
void InputManager::ProcessInput()
{
m_iMouseX += m_mouseState.lX;
m_iMouseY += m_mouseState.lY;
if (m_iMouseX < 0)
{
m_iMouseX = 0;
}
if (m_iMouseY < 0)
{
m_iMouseY = 0;
}
if (m_iMouseX > m_iScreenWidth)
{
m_iMouseX = m_iScreenWidth;
}
if (m_iMouseY > m_iScreenHeight)
{
m_iMouseY = m_iScreenHeight;
}
return;
}
bool InputManager::IsKeyDown(char key)
{
return KEYDOWN(m_keyboardState, key);
}
bool InputManager::IsMouseButtonDown(unsigned int button)
{
return (m_mouseState.rgbButtons[button] & 0x80) != 0;
}
std::string InputManager::GetKeyCodeAsString(char key)
{
std::string sKeyCode;
char temp[256];
DIPROPSTRING prop;
prop.diph.dwSize = sizeof(DIPROPSTRING);
prop.diph.dwHeaderSize = sizeof(DIPROPHEADER);
prop.diph.dwObj = static_cast<DWORD>(key);
prop.diph.dwHow = DIPH_BYOFFSET;
if (SUCCEEDED(m_pKeyboard->GetProperty(DIPROP_KEYNAME, &prop.diph)))
{
if (WideCharToMultiByte(CP_ACP, 0, prop.wsz, -1, temp, sizeof(temp), nullptr, nullptr))
{
return sKeyCode.assign(temp);
}
}
std::stringstream ss;
ss << "Key_" << (int) key;
return sKeyCode.assign(ss.str());
}
wchar_t InputManager::GetKeyCodeAsWChar(char key)
{
BYTE keyState[256];
HKL layout = GetKeyboardLayout(0);
if (GetKeyboardState(keyState) == 0)
{
return 0;
}
unsigned int vk = MapVirtualKeyEx(key, 3, layout);
if (vk == 0)
{
return 0;
}
WCHAR buff[3] = {0};
int ascii = ToUnicodeEx(vk, key, keyState, buff, 3, 0, layout);
if(ascii == 1 && m_deadkey != '\0' )
{
WCHAR wcBuff[3] = {buff[0], m_deadkey, '\0'};
WCHAR out[3];
m_deadkey = '\0';
if (FoldStringW(MAP_PRECOMPOSED, (LPWSTR)wcBuff, 3, (LPWSTR)out, 3))
{
return out[0];
}
}
else if (ascii == 1)
{
m_deadkey = '\0';
return buff[0];
}
else if (ascii == 2)
{
switch (buff[0])
{
case 0x5E: // Circumflex accent: â
m_deadkey = 0x302;
break;
case 0x60: // Grave accent: à
m_deadkey = 0x300;
break;
case 0xA8: // Diaeresis: ü
m_deadkey = 0x308;
break;
case 0xB4: // Acute accent: é
m_deadkey = 0x301;
break;
case 0xB8: // Cedilla: ç
m_deadkey = 0x327;
break;
default:
m_deadkey = buff[0];
break;
}
}
return 0;
}
void InputManager::SetBuffered(bool bBuffered)
{
m_bBufferedMode = bBuffered;
}
Résumé :
Avec l’interface DirectInput, on peut élaborer un système d’entrée clavier souris tout simple.

