todo : remplacer les scancodes par les virtual codes
Intro :
A l’inverse de l’utilisation du composant DirectInput de DirectX, nous pouvons directement utiliser les réponses de la boucle de message WndProc en tant qu’entrées clavier-souris.
Cet article fournira la classe InputManager utilisée dans d’autres articles.
Explications :
Voici le code pour le fichier InputManager.h
#ifndef INPUT_MANAGER_H
#define INPUT_MANAGER_H
#include <windows.h>
#include <map>
#include <vector>
#include <sstream>
#include <iostream>
#define BIT(x) 1 << x
#include "Singleton.h"
// Objet d'entrée clavier qui sera envoyé au KeyListener enregistré
// dans la classe InputManager
class KeyEvent
{
public:
KeyEvent(int kc, wchar_t txt) : keyCode(kc), text(txt) {}
virtual ~KeyEvent() {}
// L'ID de la touche
const int keyCode;
// La caractère correspondant de la touche
const wchar_t text;
};
// Classe KeyListener qui permet à n'importe quel objet héritant de
// cette classe de recevoir les entrées clavier
class KeyListener
{
public:
virtual ~KeyListener() {}
virtual void OnKeyPressed(const KeyEvent &arg) = 0;
virtual void OnKeyReleased(const KeyEvent &arg) = 0;
};
class InputManager : public Singleton<InputManager>
{
public:
// Touches spéciales
enum KeyModifier
{
SHIFT = BIT(0),
CTRL = BIT(1),
ALT = BIT(2)
};
// Bouttons de la souris
enum MouseButton
{
MouseButtonLeft,
MouseButtonRight,
MouseButtonMiddle,
MouseButtoNone
};
InputManager();
virtual ~InputManager();
// Méthodes d'enregistrements des entrées clavier de la boucle
// de message Win32 WndProc
void InjectKeyDown(unsigned int iVirtualKeyCode, unsigned int iScanCode);
void InjectKeyUp(unsigned int iVirtualKeyCode, unsigned int iScanCode);
void InjectKeyModifierDown(KeyModifier modifier);
void InjectKeyModifierUp(KeyModifier modifier);
// Méthodes d'enregistrements des entrées souris de la boucle
// de message Win32 WndProc
void InjectMousePosition(int iMouseX, int iMouseY);
void InjectMouseButtonDown(MouseButton button);
void InjectMouseButtonUp();
// Méthodes de récupérations de la position de la souris
void GetMouseAbsoluteLocation(int& iMouseX, int& iMouseY);
void GetMouseRelativePosition(float& fX, float& fY);
// Méthodes de récupérations des entrées clavier
bool IsKeyDown(unsigned int key);
bool IsKeyUp(unsigned int key);
bool IsMouseButtonDown(unsigned int button);
bool IsKeyModifierDown(KeyModifier modifier);
// Enregistre le receveur de touches clavier
void SetKeyListener(KeyListener* pKeyListener);
// Renvoie le caractère wchar_t correspondant à la clé spécifié
wchar_t GetKeyCodeAsWChar(char key);
// Renvoie une chaîne de caractère correspondant à la clé spécifié
std::string GetKeyCodeAsString(char key);
// Met le curseur au centre de la fenêtre
void CenterMouseCursor();
// Je place cette fonction utilitaire au sein même
// de la classe, mais sa place serait plus juste ailleurs
std::string Formater(const std::wstring& src)
{
char outString[512];
CharToOemW(src.c_str(), outString);
return std::string(outString);
}
private:
// Coordonnées de la souris
int m_iMouseX;
int m_iMouseY;
// Variable représentant les combinaison de touches
// de modification
unsigned int m_iModifiers;
// Touches préssées
std::map<unsigned int, bool> m_pressedKeys;
// L'écouteur d'entrée clavier
KeyListener* m_pKeyListener;
// Le boutton de la souris^^
MouseButton m_iMouseButton;
// variable ppour gérer les accents comme :
// û, ì, ò, ô, etc...
wchar_t m_deadKey;
};
#endif
Voici le code pour le fichier InputManager.cpp :
#include "InputManager.h"
#include "System.h"
#include <sstream>
template<> InputManager* Singleton<InputManager>::ms_instance = nullptr;
InputManager::InputManager() :
m_iMouseX(0),
m_iMouseY(0),
m_iModifiers(0),
m_pKeyListener(nullptr),
m_iMouseButton(MouseButton::MouseButtoNone),
m_deadKey('\0')
{
}
InputManager::~InputManager()
{
}
void InputManager::SetKeyListener(KeyListener* pKeyListener)
{
assert(pKeyListener);
m_pKeyListener = pKeyListener;
}
bool InputManager::IsKeyModifierDown(KeyModifier modifier)
{
return m_iModifiers & modifier;
}
void InputManager::InjectKeyModifierDown(KeyModifier modifier)
{
m_iModifiers |= modifier;
}
void InputManager::InjectKeyModifierUp(KeyModifier modifier)
{
m_iModifiers &= ~(modifier);
}
void InputManager::InjectKeyDown(unsigned int iVirtualKeyCode, unsigned int iScanCode)
{
m_pressedKeys[iVirtualKeyCode] = true;
if (m_pKeyListener != nullptr)
{
wchar_t c = GetKeyCodeAsWChar(iScanCode);
KeyEvent keyEvent(iScanCode, c);
m_pKeyListener->OnKeyPressed(keyEvent);
}
}
void InputManager::InjectKeyUp(unsigned int iVirtualKeyCode, unsigned int iScanCode)
{
m_pressedKeys[iVirtualKeyCode] = false;
if (m_pKeyListener != nullptr)
{
wchar_t c = GetKeyCodeAsWChar(iScanCode);
KeyEvent keyEvent(iScanCode, c);
m_pKeyListener->OnKeyReleased(keyEvent);
}
}
void InputManager::InjectMousePosition(int iMouseX, int iMouseY)
{
m_iMouseX = iMouseX;
m_iMouseY = iMouseY;
}
void InputManager::GetMouseAbsoluteLocation(int& mouseX, int& mouseY)
{
mouseX = m_iMouseX;
mouseY = m_iMouseY;
}
// todo : essayer avec les m_iMouseX et m_iMouseY
void InputManager::GetMouseRelativePosition(float& fX, float& fY)
{
POINT p;
if (!GetCursorPos(&p))
{
DWORD xy = GetMessagePos();
p.x = LOWORD(xy);
p.y = HIWORD(xy);
}
int borderX = GetSystemMetrics(SM_CXSIZEFRAME);
int borderY = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYSIZEFRAME);
RECT rect;
GetWindowRect(SYSTEM->GetHwnd(), &rect);
float x = p.x - rect.left - borderX;
float y = p.y - rect.top - borderY;
fX = ((float) x / (float) SYSTEM->GetWindowWidth()) - 0.5f;
fY = ((float) y / (float) SYSTEM->GetWindowHeight()) - 0.5f;
}
bool InputManager::IsKeyDown(unsigned int key)
{
return m_pressedKeys[key] == true;
}
bool InputManager::IsKeyUp(unsigned int key)
{
return m_pressedKeys[key] == false;
}
void InputManager::InjectMouseButtonDown(MouseButton button)
{
m_iMouseButton = button;
}
void InputManager::InjectMouseButtonUp()
{
m_iMouseButton = MouseButtoNone;
}
bool InputManager::IsMouseButtonDown(unsigned int button)
{
return m_iMouseButton == button;
}
std::string InputManager::GetKeyCodeAsString(char key)
{
wchar_t w = GetKeyCodeAsWChar(key);
std::wstring wStr;
wStr.push_back( w );
std::string str = Formater(wStr);
return 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];
if (FoldStringW(MAP_PRECOMPOSED, (LPWSTR)wcBuff, 3, (LPWSTR)out, 3))
{
return out[0];
}
}
else if (ascii == -1)
{
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;
}
}
else if (ascii == 1)
{
m_deadKey = '\0';
return buff[0];
}
return 0;
}
void InputManager::CenterMouseCursor()
{
int borderX = GetSystemMetrics(SM_CXSIZEFRAME);
int borderY = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYSIZEFRAME);
RECT rect;
HWND hwnd = SYSTEM->GetHwnd();
GetWindowRect(hwnd, &rect);
unsigned int iWindowWidth = SYSTEM->GetWindowWidth();
unsigned int iWindowHeight = SYSTEM->GetWindowHeight();
SetCursorPos(0.5f * iWindowWidth + rect.left + borderX, 0.5f * iWindowHeight + rect.top + borderY);
}
Voici la fonction WndProc utilisée pour obtenir les entrées afin de les passer à la classe InputManager :
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (!InputManager::IsCreated())
{
return DefWindowProc(hWnd, message, wParam, lParam);
}
switch (message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_KEYDOWN:
{
INPUT_MANAGER->InjectKeyDown(wParam, HIWORD(lParam));
if (wParam == VK_SHIFT)
{
INPUT_MANAGER->InjectKeyModifierDown(InputManager::KeyModifier::SHIFT);
}
if (wParam == VK_CONTROL)
{
INPUT_MANAGER->InjectKeyModifierDown(InputManager::KeyModifier::CTRL);
}
if (wParam == VK_MENU)
{
INPUT_MANAGER->InjectKeyModifierDown(InputManager::KeyModifier::ALT);
}
}
break;
case WM_KEYUP:
{
INPUT_MANAGER->InjectKeyUp(wParam, HIWORD(lParam));
if (wParam == VK_SHIFT)
{
INPUT_MANAGER->InjectKeyModifierUp(InputManager::KeyModifier::SHIFT);
}
if (wParam == VK_CONTROL)
{
INPUT_MANAGER->InjectKeyModifierUp(InputManager::KeyModifier::CTRL);
}
if (wParam == VK_MENU)
{
INPUT_MANAGER->InjectKeyModifierUp(InputManager::KeyModifier::ALT);
}
break;
}
case WM_MOUSEMOVE:
{
int iMouseX = (int) LOWORD(lParam);
int iMouseY = (int) HIWORD(lParam);
INPUT_MANAGER->InjectMousePosition(iMouseX, iMouseY);
break;
}
case WM_LBUTTONDOWN:
{
INPUT_MANAGER->InjectMouseButtonDown(InputManager::MouseButton::MouseButtonLeft);
break;
}
case WM_RBUTTONDOWN:
{
INPUT_MANAGER->InjectMouseButtonDown(InputManager::MouseButton::MouseButtonRight);
break;
}
case WM_MBUTTONDOWN:
{
INPUT_MANAGER->InjectMouseButtonDown(InputManager::MouseButton::MouseButtonMiddle);
break;
}
case WM_MBUTTONUP:
case WM_LBUTTONUP:
case WM_RBUTTONUP:
{
INPUT_MANAGER->InjectMouseButtonUp();
break;
}
default:
{
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
return 0;
}
Résumé :
Nous avons présenter une manière de récupérer les entrées clavier de votre jeu ou application.
Voici l’archive du code complet pour cet RawInputManager.zip
Références :

