mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-26 23:55:40 +00:00
1136 lines
35 KiB
C++
1136 lines
35 KiB
C++
|
#include "DirectInputSystem.h"
|
||
|
#include "Supermodel.h"
|
||
|
|
||
|
#include <SDL.h>
|
||
|
#include <SDL_syswm.h>
|
||
|
|
||
|
// TODO - need to double check these all correct and see if can fill in any missing codes (although most just don't exist)
|
||
|
DIKeyMapStruct CDirectInputSystem::s_keyMap[] =
|
||
|
{
|
||
|
// General keys
|
||
|
{ "BACKSPACE", DIK_BACK },
|
||
|
{ "TAB", DIK_TAB },
|
||
|
//{ "CLEAR", ?? },
|
||
|
{ "RETURN", DIK_RETURN },
|
||
|
{ "PAUSE", DIK_PAUSE },
|
||
|
{ "ESCAPE", DIK_ESCAPE },
|
||
|
{ "SPACE", DIK_SPACE },
|
||
|
//{ "EXCLAIM", ?? },
|
||
|
//{ "DBLQUOTE", ?? },
|
||
|
//{ "HASH", ?? },
|
||
|
//{ "DOLLAR", ?? },
|
||
|
//{ "AMPERSAND", ?? },
|
||
|
{ "QUOTE", DIK_APOSTROPHE },
|
||
|
{ "LEFTPAREN", DIK_LBRACKET },
|
||
|
{ "RIGHTPAREN", DIK_RBRACKET },
|
||
|
//{ "ASTERISK", ?? },
|
||
|
//{ "PLUS", ?? },
|
||
|
{ "COMMA", DIK_COMMA },
|
||
|
{ "MINUS", DIK_MINUS },
|
||
|
{ "PERIOD", DIK_PERIOD },
|
||
|
{ "SLASH", DIK_SLASH },
|
||
|
{ "0", DIK_0 },
|
||
|
{ "1", DIK_1 },
|
||
|
{ "2", DIK_2 },
|
||
|
{ "3", DIK_3 },
|
||
|
{ "4", DIK_4 },
|
||
|
{ "5", DIK_5 },
|
||
|
{ "6", DIK_6 },
|
||
|
{ "7", DIK_7 },
|
||
|
{ "8", DIK_8 },
|
||
|
{ "9", DIK_9 },
|
||
|
//{ "COLON", ?? },
|
||
|
{ "SEMICOLON", DIK_SEMICOLON },
|
||
|
{ "LESS", DIK_OEM_102 },
|
||
|
{ "EQUALS", DIK_EQUALS },
|
||
|
//{ "GREATER", ?? },
|
||
|
//{ "QUESTION", ?? },
|
||
|
//{ "AT", ?? },
|
||
|
//{ "LEFTBRACKET", ?? },
|
||
|
//{ "BACKSLASH", ?? },
|
||
|
//{ "RIGHTBRACKET", ?? },
|
||
|
//{ "CARET", ?? },
|
||
|
//{ "UNDERSCORE", ?? },
|
||
|
{ "BACKQUOTE", DIK_GRAVE },
|
||
|
{ "A", DIK_A },
|
||
|
{ "B", DIK_B },
|
||
|
{ "C", DIK_C },
|
||
|
{ "D", DIK_D },
|
||
|
{ "E", DIK_E },
|
||
|
{ "F", DIK_F },
|
||
|
{ "G", DIK_G },
|
||
|
{ "H", DIK_H },
|
||
|
{ "I", DIK_I },
|
||
|
{ "J", DIK_J },
|
||
|
{ "K", DIK_K },
|
||
|
{ "L", DIK_L },
|
||
|
{ "M", DIK_M },
|
||
|
{ "N", DIK_N },
|
||
|
{ "O", DIK_O },
|
||
|
{ "P", DIK_P },
|
||
|
{ "Q", DIK_Q },
|
||
|
{ "R", DIK_R },
|
||
|
{ "S", DIK_S },
|
||
|
{ "T", DIK_T },
|
||
|
{ "U", DIK_U },
|
||
|
{ "V", DIK_V },
|
||
|
{ "W", DIK_W },
|
||
|
{ "X", DIK_X },
|
||
|
{ "Y", DIK_Y },
|
||
|
{ "Z", DIK_Z },
|
||
|
{ "DEL", DIK_DELETE },
|
||
|
|
||
|
// Keypad
|
||
|
{ "KEYPAD0", DIK_NUMPAD0 },
|
||
|
{ "KEYPAD1", DIK_NUMPAD1 },
|
||
|
{ "KEYPAD2", DIK_NUMPAD2 },
|
||
|
{ "KEYPAD3", DIK_NUMPAD3 },
|
||
|
{ "KEYPAD4", DIK_NUMPAD4 },
|
||
|
{ "KEYPAD5", DIK_NUMPAD5 },
|
||
|
{ "KEYPAD6", DIK_NUMPAD6 },
|
||
|
{ "KEYPAD7", DIK_NUMPAD7 },
|
||
|
{ "KEYPAD8", DIK_NUMPAD8 },
|
||
|
{ "KEYPAD9", DIK_NUMPAD9 },
|
||
|
{ "KEYPADPERIOD", DIK_DECIMAL },
|
||
|
{ "KEYPADDIVIDE", DIK_DIVIDE },
|
||
|
{ "KEYPADMULTIPLY", DIK_MULTIPLY },
|
||
|
{ "KEYPADMINUS", DIK_SUBTRACT },
|
||
|
{ "KEYPADPLUS", DIK_ADD },
|
||
|
{ "KEYPADENTER", DIK_NUMPADENTER },
|
||
|
{ "KEYPADEQUALS", DIK_NUMPADEQUALS },
|
||
|
|
||
|
// Arrows + Home/End Pad
|
||
|
{ "UP", DIK_UP },
|
||
|
{ "DOWN", DIK_DOWN },
|
||
|
{ "RIGHT", DIK_RIGHT },
|
||
|
{ "LEFT", DIK_LEFT },
|
||
|
{ "INSERT", DIK_INSERT },
|
||
|
{ "HOME", DIK_HOME },
|
||
|
{ "END", DIK_END },
|
||
|
{ "PGUP", DIK_PRIOR },
|
||
|
{ "PGDN", DIK_NEXT },
|
||
|
|
||
|
// Function Key
|
||
|
{ "F1", DIK_F1 },
|
||
|
{ "F2", DIK_F2 },
|
||
|
{ "F3", DIK_F3 },
|
||
|
{ "F4", DIK_F4 },
|
||
|
{ "F5", DIK_F5 },
|
||
|
{ "F6", DIK_F6 },
|
||
|
{ "F7", DIK_F7 },
|
||
|
{ "F8", DIK_F8 },
|
||
|
{ "F9", DIK_F9 },
|
||
|
{ "F10", DIK_F10 },
|
||
|
{ "F11", DIK_F11 },
|
||
|
{ "F12", DIK_F12 },
|
||
|
{ "F13", DIK_F13 },
|
||
|
{ "F14", DIK_F14 },
|
||
|
{ "F15", DIK_F15 },
|
||
|
|
||
|
// Modifier Keys
|
||
|
{ "NUMLOCK", DIK_NUMLOCK },
|
||
|
{ "CAPSLOCK", DIK_CAPITAL },
|
||
|
{ "SCROLLLOCK", DIK_SCROLL },
|
||
|
{ "RIGHTSHIFT", DIK_RSHIFT },
|
||
|
{ "LEFTSHIFT", DIK_LSHIFT },
|
||
|
{ "RIGHTCTRL", DIK_RCONTROL },
|
||
|
{ "LEFTCTRL", DIK_LCONTROL },
|
||
|
{ "RIGHTALT", DIK_RMENU },
|
||
|
{ "LEFTALT", DIK_LMENU },
|
||
|
//{ "RIGHTMETA", ?? },
|
||
|
//{ "LEFTMETA", ?? },
|
||
|
{ "RIGHTWINDOWS", DIK_RWIN },
|
||
|
{ "LEFTWINDOWS", DIK_LWIN },
|
||
|
//{ "ALTGR", ?? },
|
||
|
//{ "COMPOSE", ?? },
|
||
|
|
||
|
// Other
|
||
|
//{ "HELP", ?? },
|
||
|
{ "PRINT", DIK_SYSRQ },
|
||
|
//{ "SYSREQ", ?? },
|
||
|
//{ "BREAK", ?? },
|
||
|
//{ "MENU", ?? },
|
||
|
//{ "POWER", ?? },
|
||
|
//{ "EURO", ?? },
|
||
|
//{ "UNDO", ?? },
|
||
|
};
|
||
|
|
||
|
BOOL CALLBACK DI8EnumDevicesCallback(LPCDIDEVICEINSTANCE instance, LPVOID context)
|
||
|
{
|
||
|
// Keep track of all joystick device GUIDs
|
||
|
vector<GUID> *guids = (vector<GUID>*)context;
|
||
|
guids->push_back(instance->guidInstance);
|
||
|
return DIENUM_CONTINUE;
|
||
|
}
|
||
|
|
||
|
BOOL CALLBACK DI8EnumAxesCallback(LPCDIDEVICEOBJECTINSTANCE instance, LPVOID context)
|
||
|
{
|
||
|
// Find out which joystick axes are present
|
||
|
// TODO - is usage/usage page best method or is better to use something like:
|
||
|
// int axisNum = (instance->dwOfs - offsetof(DIJOYSTATE2, lX)) / sizeof(LONG)?
|
||
|
JoyDetails *joyDetails = (JoyDetails*)context;
|
||
|
if (MATCHES_USAGE(instance->wUsage, instance->wUsagePage, X_AXIS_USAGE)) joyDetails->hasXAxis = true;
|
||
|
else if (MATCHES_USAGE(instance->wUsage, instance->wUsagePage, Y_AXIS_USAGE)) joyDetails->hasYAxis = true;
|
||
|
else if (MATCHES_USAGE(instance->wUsage, instance->wUsagePage, Z_AXIS_USAGE)) joyDetails->hasZAxis = true;
|
||
|
else if (MATCHES_USAGE(instance->wUsage, instance->wUsagePage, RX_AXIS_USAGE)) joyDetails->hasRXAxis = true;
|
||
|
else if (MATCHES_USAGE(instance->wUsage, instance->wUsagePage, RY_AXIS_USAGE)) joyDetails->hasRYAxis = true;
|
||
|
else if (MATCHES_USAGE(instance->wUsage, instance->wUsagePage, RZ_AXIS_USAGE)) joyDetails->hasRZAxis = true;
|
||
|
return DIENUM_CONTINUE;
|
||
|
}
|
||
|
|
||
|
CDirectInputSystem::CDirectInputSystem(bool useRawInput) : CInputSystem("DirectInput/RawInput"), m_useRawInput(useRawInput),
|
||
|
m_activated(false), m_hwnd(NULL), m_getRIDevListPtr(NULL), m_getRIDevInfoPtr(NULL), m_regRIDevsPtr(NULL), m_getRIDataPtr(NULL),
|
||
|
m_di8(NULL), m_di8Keyboard(NULL), m_di8Mouse(NULL)
|
||
|
{
|
||
|
// Reset initial states
|
||
|
memset(&m_combRawMseState, 0, sizeof(RawMseState));
|
||
|
memset(&m_diKeyState, 0, sizeof(LPDIRECTINPUTDEVICE8));
|
||
|
memset(&m_diMseState, 0, sizeof(LPDIRECTINPUTDEVICE8));
|
||
|
}
|
||
|
|
||
|
CDirectInputSystem::~CDirectInputSystem()
|
||
|
{
|
||
|
CloseKeyboardsAndMice();
|
||
|
CloseJoysticks();
|
||
|
|
||
|
if (m_di8 != NULL)
|
||
|
{
|
||
|
m_di8->Release();
|
||
|
m_di8 = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CDirectInputSystem::OpenKeyboardsAndMice()
|
||
|
{
|
||
|
if (m_useRawInput)
|
||
|
{
|
||
|
// If RawInput enabled, get list of available devices
|
||
|
UINT nDevices;
|
||
|
if (m_getRIDevListPtr(NULL, &nDevices, sizeof(RAWINPUTDEVICELIST)) == 0 && nDevices > 0)
|
||
|
{
|
||
|
PRAWINPUTDEVICELIST pDeviceList = new RAWINPUTDEVICELIST[nDevices];
|
||
|
if (pDeviceList != NULL && m_getRIDevListPtr(pDeviceList, &nDevices, sizeof(RAWINPUTDEVICELIST)) != (UINT)-1)
|
||
|
{
|
||
|
// Loop through devices backwards (since new devices are usually added at beginning)
|
||
|
for (int devNum = nDevices - 1; devNum >= 0; devNum--)
|
||
|
{
|
||
|
RAWINPUTDEVICELIST device = pDeviceList[devNum];
|
||
|
|
||
|
// Get device name
|
||
|
UINT nLength;
|
||
|
if (m_getRIDevInfoPtr(device.hDevice, RIDI_DEVICENAME, NULL, &nLength) != 0)
|
||
|
continue;
|
||
|
char *name = new char[nLength];
|
||
|
if (m_getRIDevInfoPtr(device.hDevice, RIDI_DEVICENAME, name, &nLength) == -1)
|
||
|
continue;
|
||
|
|
||
|
// Ignore any RDP devices
|
||
|
if (strstr(name, "Root#RDP_") != NULL)
|
||
|
continue;
|
||
|
|
||
|
// Store device handles for attached keyboards and mice
|
||
|
if (device.dwType == RIM_TYPEKEYBOARD)
|
||
|
{
|
||
|
m_rawKeyboards.push_back(device.hDevice);
|
||
|
|
||
|
BOOL *keyState = new BOOL[255];
|
||
|
memset(keyState, 0, sizeof(BOOL) * 255);
|
||
|
m_rawKeyStates.push_back(keyState);
|
||
|
}
|
||
|
else if (device.dwType == RIM_TYPEMOUSE)
|
||
|
{
|
||
|
m_rawMice.push_back(device.hDevice);
|
||
|
|
||
|
RawMseState *mseState = new RawMseState();
|
||
|
memset(mseState, 0, sizeof(RawMseState));
|
||
|
m_rawMseStates.push_back(mseState);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DebugLog("RawInput - found %d keyboards and %d mice", m_rawKeyboards.size(), m_rawMice.size());
|
||
|
|
||
|
// Check some devices were actually found
|
||
|
m_useRawInput = m_rawKeyboards.size() > 0 && m_rawMice.size() > 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ErrorLog("Unable to query RawInput API for attached devices (error %d) - switching to DirectInput.\n", GetLastError());
|
||
|
|
||
|
m_useRawInput = false;
|
||
|
}
|
||
|
|
||
|
if (pDeviceList != NULL)
|
||
|
delete[] pDeviceList;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ErrorLog("Unable to query RawInput API for attached devices (error %d) - switching to DirectInput.\n", GetLastError());
|
||
|
|
||
|
m_useRawInput = false;
|
||
|
}
|
||
|
|
||
|
if (m_useRawInput)
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// If get here then either RawInput disabled or getting its devices failed so default to DirectInput.
|
||
|
// Open DirectInput system keyboard and set its data format
|
||
|
HRESULT hr;
|
||
|
if (FAILED(hr = m_di8->CreateDevice(GUID_SysKeyboard, &m_di8Keyboard, NULL)))
|
||
|
{
|
||
|
ErrorLog("Unable to create DirectInput keyboard device (error %d) - key input will be unavailable.\n", hr);
|
||
|
|
||
|
m_di8Keyboard = NULL;
|
||
|
}
|
||
|
else if (FAILED(hr = m_di8Keyboard->SetDataFormat(&c_dfDIKeyboard)))
|
||
|
{
|
||
|
ErrorLog("Unable to set data format for DirectInput keyboard (error %d) - key input will be unavailable.\n", hr);
|
||
|
|
||
|
m_di8Keyboard->Release();
|
||
|
m_di8Keyboard = NULL;
|
||
|
}
|
||
|
|
||
|
// Open DirectInput system mouse and set its data format
|
||
|
if (FAILED(hr = m_di8->CreateDevice(GUID_SysMouse, &m_di8Mouse, NULL)))
|
||
|
{
|
||
|
ErrorLog("Unable to create DirectInput mouse device (error %d) - mouse input will be unavailable.\n", hr);
|
||
|
|
||
|
m_di8Mouse = NULL;
|
||
|
return;
|
||
|
}
|
||
|
if (FAILED(hr = m_di8Mouse->SetDataFormat(&c_dfDIMouse2)))
|
||
|
{
|
||
|
ErrorLog("Unable to set data format for DirectInput mouse (error %d) - mouse input will be unavailable.\n", hr);
|
||
|
|
||
|
m_di8Mouse->Release();
|
||
|
m_di8Mouse = NULL;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Set mouse axis mode to relative
|
||
|
DIPROPDWORD dipdw;
|
||
|
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
|
||
|
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
||
|
dipdw.diph.dwHow = DIPH_DEVICE;
|
||
|
dipdw.diph.dwObj = 0;
|
||
|
dipdw.dwData = DIPROPAXISMODE_REL;
|
||
|
if (FAILED(hr = m_di8Mouse->SetProperty(DIPROP_AXISMODE, &dipdw.diph)))
|
||
|
{
|
||
|
ErrorLog("Unable to set axis mode for DirectInput mouse (error %d) - mouse input will be unavailable.\n", hr);
|
||
|
|
||
|
m_di8Mouse->Release();
|
||
|
m_di8Mouse = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CDirectInputSystem::ActivateKeyboardsAndMice()
|
||
|
{
|
||
|
// Sync up all mice with current cursor position
|
||
|
ResetMice();
|
||
|
|
||
|
if (m_useRawInput)
|
||
|
{
|
||
|
// Register for RawInput
|
||
|
RAWINPUTDEVICE rid[2];
|
||
|
|
||
|
// Register for keyboard input
|
||
|
rid[0].usUsagePage = 0x01;
|
||
|
rid[0].usUsage = 0x06;
|
||
|
rid[0].dwFlags = (m_isConfiguring ? RIDEV_INPUTSINK : RIDEV_CAPTUREMOUSE) | RIDEV_NOLEGACY;
|
||
|
rid[0].hwndTarget = m_hwnd;
|
||
|
|
||
|
// Register for mouse input
|
||
|
rid[1].usUsagePage = 0x01;
|
||
|
rid[1].usUsage = 0x02;
|
||
|
rid[1].dwFlags = (m_isConfiguring ? RIDEV_INPUTSINK : RIDEV_CAPTUREMOUSE) | RIDEV_NOLEGACY;
|
||
|
rid[1].hwndTarget = m_hwnd;
|
||
|
|
||
|
if (!m_regRIDevsPtr(rid, 2, sizeof(RAWINPUTDEVICE)))
|
||
|
ErrorLog("Unable to register for keyboard and mouse input with RawInput API (error %d) - keyboard and mouse input will be unavailable.\n", GetLastError());
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Set DirectInput cooperative level of keyboard and mouse
|
||
|
if (m_di8Keyboard != NULL)
|
||
|
m_di8Keyboard->SetCooperativeLevel(m_hwnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
|
||
|
if (m_di8Mouse != NULL)
|
||
|
m_di8Mouse->SetCooperativeLevel(m_hwnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
|
||
|
}
|
||
|
|
||
|
void CDirectInputSystem::PollKeyboardsAndMice()
|
||
|
{
|
||
|
if (m_useRawInput)
|
||
|
{
|
||
|
// For RawInput, only thing to do is update wheelDir from wheelData for each mouse state. Everything else is updated via WM events.
|
||
|
for (vector<RawMseState*>::iterator it = m_rawMseStates.begin(); it < m_rawMseStates.end(); it++)
|
||
|
{
|
||
|
if ((*it)->wheelDelta != 0)
|
||
|
{
|
||
|
(*it)->wheelDir = ((*it)->wheelDelta > 0 ? 1 : -1);
|
||
|
(*it)->wheelDelta = 0;
|
||
|
}
|
||
|
else
|
||
|
(*it)->wheelDir = 0;
|
||
|
}
|
||
|
if (m_combRawMseState.wheelDelta != 0)
|
||
|
{
|
||
|
m_combRawMseState.wheelDir = (m_combRawMseState.wheelDelta > 0 ? 1 : -1);
|
||
|
m_combRawMseState.wheelDelta = 0;
|
||
|
}
|
||
|
else
|
||
|
m_combRawMseState.wheelDir = 0;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Get current keyboard state from DirectInput
|
||
|
HRESULT hr;
|
||
|
if (m_di8Keyboard != NULL)
|
||
|
{
|
||
|
if (FAILED(hr = m_di8Keyboard->Poll()))
|
||
|
{
|
||
|
hr = m_di8Keyboard->Acquire();
|
||
|
while (hr == DIERR_INPUTLOST)
|
||
|
hr = m_di8Keyboard->Acquire();
|
||
|
|
||
|
if (hr == DIERR_OTHERAPPHASPRIO || hr == DIERR_INVALIDPARAM || hr == DIERR_NOTINITIALIZED)
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Keep track of keyboard state
|
||
|
m_di8Keyboard->GetDeviceState(sizeof(m_diKeyState), m_diKeyState);
|
||
|
}
|
||
|
|
||
|
// Get current mouse state from DirectInput
|
||
|
if (m_di8Mouse != NULL)
|
||
|
{
|
||
|
if (FAILED(hr = m_di8Mouse->Poll()))
|
||
|
{
|
||
|
hr = m_di8Mouse->Acquire();
|
||
|
while (hr == DIERR_INPUTLOST)
|
||
|
hr = m_di8Mouse->Acquire();
|
||
|
|
||
|
if (hr == DIERR_OTHERAPPHASPRIO || hr == DIERR_INVALIDPARAM || hr == DIERR_NOTINITIALIZED)
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Keep track of mouse absolute axis values, clamping them at display edges, aswell as wheel direction and buttons
|
||
|
DIMOUSESTATE2 mseState;
|
||
|
m_di8Mouse->GetDeviceState(sizeof(mseState), &mseState);
|
||
|
m_diMseState.x = CInputSource::Clamp(m_diMseState.x + mseState.lX, m_dispX, m_dispX + m_dispW);
|
||
|
m_diMseState.y = CInputSource::Clamp(m_diMseState.y + mseState.lY, m_dispY, m_dispY + m_dispH);
|
||
|
if (mseState.lZ != 0)
|
||
|
{
|
||
|
// Z-axis is clamped to range -100 to 100 (DirectInput returns +120 & -120 for wheel delta which are scaled to +5 & -5)
|
||
|
LONG wheelDelta = 5 * mseState.lZ / 120;
|
||
|
m_diMseState.z = CInputSource::Clamp(m_diMseState.z + wheelDelta, -100, 100);
|
||
|
m_diMseState.wheelDir = (wheelDelta > 0 ? 1 : -1);
|
||
|
}
|
||
|
else
|
||
|
m_diMseState.wheelDir = 0;
|
||
|
memcpy(&m_diMseState.buttons, mseState.rgbButtons, sizeof(m_diMseState.buttons));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CDirectInputSystem::CloseKeyboardsAndMice()
|
||
|
{
|
||
|
if (m_useRawInput)
|
||
|
{
|
||
|
if (m_activated)
|
||
|
{
|
||
|
// If RawInput was registered, then unregister now
|
||
|
RAWINPUTDEVICE rid[2];
|
||
|
|
||
|
// Unregister from keyboard input
|
||
|
rid[0].usUsagePage = 0x01;
|
||
|
rid[0].usUsage = 0x06;
|
||
|
rid[0].dwFlags = RIDEV_REMOVE;
|
||
|
rid[0].hwndTarget = m_hwnd;
|
||
|
|
||
|
// Unregister from mouse input
|
||
|
rid[1].usUsagePage = 0x01;
|
||
|
rid[1].usUsage = 0x02;
|
||
|
rid[1].dwFlags = RIDEV_REMOVE;
|
||
|
rid[1].hwndTarget = m_hwnd;
|
||
|
|
||
|
m_regRIDevsPtr(rid, 2, sizeof(RAWINPUTDEVICE));
|
||
|
}
|
||
|
|
||
|
for (vector<BOOL*>::iterator it = m_rawKeyStates.begin(); it < m_rawKeyStates.end(); it++)
|
||
|
delete[] *it;
|
||
|
for (vector<RawMseState*>::iterator it = m_rawMseStates.begin(); it < m_rawMseStates.end(); it++)
|
||
|
delete *it;
|
||
|
}
|
||
|
|
||
|
// If DirectInput keyboard and mouse were created, then release them now
|
||
|
if (m_di8Keyboard != NULL)
|
||
|
{
|
||
|
m_di8Keyboard->Unacquire();
|
||
|
m_di8Keyboard->Release();
|
||
|
m_di8Keyboard = NULL;
|
||
|
}
|
||
|
if (m_di8Mouse != NULL)
|
||
|
{
|
||
|
m_di8Mouse->Unacquire();
|
||
|
m_di8Mouse->Release();
|
||
|
m_di8Mouse = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CDirectInputSystem::ResetMice()
|
||
|
{
|
||
|
// Get current mouse cursor position in window
|
||
|
POINT p;
|
||
|
if (!GetCursorPos(&p) || !ScreenToClient(m_hwnd, &p))
|
||
|
return;
|
||
|
|
||
|
// Set all mice coords to current cursor position
|
||
|
if (m_useRawInput)
|
||
|
{
|
||
|
m_combRawMseState.x = p.x;
|
||
|
m_combRawMseState.y = p.y;
|
||
|
m_combRawMseState.z = 0;
|
||
|
for (vector<RawMseState*>::iterator it = m_rawMseStates.begin(); it < m_rawMseStates.end(); it++)
|
||
|
{
|
||
|
(*it)->x = p.x;
|
||
|
(*it)->y = p.y;
|
||
|
(*it)->z = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_diMseState.x = p.x;
|
||
|
m_diMseState.y = p.y;
|
||
|
m_diMseState.z = 0;
|
||
|
}
|
||
|
|
||
|
void CDirectInputSystem::ProcessRawInput(HRAWINPUT hInput)
|
||
|
{
|
||
|
// RawInput data event
|
||
|
BYTE buffer[4096];
|
||
|
LPBYTE pBuf = buffer;
|
||
|
|
||
|
// Get size of data structure to receive
|
||
|
UINT dwSize;
|
||
|
if (m_getRIDataPtr(hInput, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER)) != 0)
|
||
|
return;
|
||
|
if (dwSize > sizeof(buffer))
|
||
|
{
|
||
|
pBuf = new BYTE[dwSize];
|
||
|
if (pBuf == NULL)
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Get data
|
||
|
if (m_getRIDataPtr(hInput, RID_INPUT, pBuf, &dwSize, sizeof(RAWINPUTHEADER)) == dwSize)
|
||
|
{
|
||
|
RAWINPUT *pData = (RAWINPUT*)pBuf;
|
||
|
if (pData->header.dwType == RIM_TYPEKEYBOARD)
|
||
|
{
|
||
|
// Keyboard event, so identify which keyboard produced event
|
||
|
BOOL *keyState = NULL;
|
||
|
size_t kbdNum;
|
||
|
for (kbdNum = 0; kbdNum < m_rawKeyboards.size(); kbdNum++)
|
||
|
{
|
||
|
if (m_rawKeyboards[kbdNum] == pData->header.hDevice)
|
||
|
{
|
||
|
keyState = m_rawKeyStates[kbdNum];
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check is a valid keyboard
|
||
|
if (keyState != NULL)
|
||
|
{
|
||
|
// Get scancode of key and whether key was pressed or released
|
||
|
BOOL isRight = pData->data.keyboard.Flags & RI_KEY_E0;
|
||
|
UINT8 scanCode = (pData->data.keyboard.MakeCode & 0x7f) | (isRight ? 0x80 : 0x00);
|
||
|
BOOL pressed = !(pData->data.keyboard.Flags & RI_KEY_BREAK);
|
||
|
|
||
|
// Store current state for key
|
||
|
if (scanCode != 0xAA)
|
||
|
keyState[scanCode] = pressed;
|
||
|
}
|
||
|
}
|
||
|
else if (pData->header.dwType == RIM_TYPEMOUSE)
|
||
|
{
|
||
|
// Mouse event, so identify which mouse produced event
|
||
|
RawMseState *mseState = NULL;
|
||
|
size_t mseNum;
|
||
|
for (mseNum = 0; mseNum < m_rawMice.size(); mseNum++)
|
||
|
{
|
||
|
if (m_rawMice[mseNum] == pData->header.hDevice)
|
||
|
{
|
||
|
mseState = m_rawMseStates[mseNum];
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check is a valid mouse
|
||
|
if (mseState != NULL)
|
||
|
{
|
||
|
// Get X- & Y-axis data
|
||
|
LONG lx = pData->data.mouse.lLastX;
|
||
|
LONG ly = pData->data.mouse.lLastY;
|
||
|
if (pData->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE)
|
||
|
{
|
||
|
// If data is absolute, then scale source values (0 to 65535) to display dimensions
|
||
|
mseState->x = CInputSource::Scale(lx, 0, 0xFFFF, m_dispX, m_dispX + m_dispW);
|
||
|
mseState->y = CInputSource::Scale(ly, 0, 0xFFFF, m_dispY, m_dispY + m_dispH);
|
||
|
|
||
|
// Also update combined state
|
||
|
m_combRawMseState.x = mseState->x;
|
||
|
m_combRawMseState.y = mseState->y;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// If data is relative, then keep track of absolute position, clamping it at display edges
|
||
|
mseState->x = CInputSource::Clamp(mseState->x + lx, m_dispX, m_dispX + m_dispW);
|
||
|
mseState->y = CInputSource::Clamp(mseState->y + ly, m_dispY, m_dispY + m_dispH);
|
||
|
|
||
|
// Also update combined state
|
||
|
m_combRawMseState.x = CInputSource::Clamp(m_combRawMseState.x + lx, m_dispX, m_dispX + m_dispW);
|
||
|
m_combRawMseState.y = CInputSource::Clamp(m_combRawMseState.y + ly, m_dispY, m_dispY + m_dispH);
|
||
|
}
|
||
|
|
||
|
// Get button flags and wheel delta (RawInput returns +120 & -120 for the latter which are scaled to +5 & -5)
|
||
|
USHORT butFlags = pData->data.mouse.usButtonFlags;
|
||
|
LONG wheelDelta = 5 * (SHORT)pData->data.mouse.usButtonData / 120;
|
||
|
|
||
|
// Update Z-axis (wheel) value
|
||
|
if (butFlags & RI_MOUSE_WHEEL)
|
||
|
{
|
||
|
// Z-axis is clamped to range -100 to 100
|
||
|
mseState->z = CInputSource::Clamp(mseState->z + wheelDelta, -100, 100);
|
||
|
mseState->wheelDelta += wheelDelta;
|
||
|
}
|
||
|
|
||
|
// Keep track of buttons pressed/released
|
||
|
if (butFlags & RI_MOUSE_LEFT_BUTTON_DOWN) mseState->buttons |= 1;
|
||
|
else if (butFlags & RI_MOUSE_LEFT_BUTTON_UP) mseState->buttons ^= 1;
|
||
|
if (butFlags & RI_MOUSE_MIDDLE_BUTTON_DOWN) mseState->buttons |= 2;
|
||
|
else if (butFlags & RI_MOUSE_MIDDLE_BUTTON_UP) mseState->buttons ^= 2;
|
||
|
if (butFlags & RI_MOUSE_RIGHT_BUTTON_DOWN) mseState->buttons |= 4;
|
||
|
else if (butFlags & RI_MOUSE_RIGHT_BUTTON_UP) mseState->buttons ^= 4;
|
||
|
if (butFlags & RI_MOUSE_BUTTON_4_DOWN) mseState->buttons |= 8;
|
||
|
else if (butFlags & RI_MOUSE_BUTTON_4_UP) mseState->buttons ^= 8;
|
||
|
if (butFlags & RI_MOUSE_BUTTON_5_DOWN) mseState->buttons |= 16;
|
||
|
else if (butFlags & RI_MOUSE_BUTTON_5_UP) mseState->buttons ^= 16;
|
||
|
|
||
|
// Also update combined state for wheel axis and buttons
|
||
|
if (butFlags & RI_MOUSE_WHEEL)
|
||
|
{
|
||
|
// Z-axis is clamped to range -100 to 100
|
||
|
m_combRawMseState.z = CInputSource::Clamp(m_combRawMseState.z + wheelDelta, -100, 100);
|
||
|
m_combRawMseState.wheelDelta += wheelDelta;
|
||
|
}
|
||
|
|
||
|
m_combRawMseState.buttons = 0;
|
||
|
for (vector<RawMseState*>::iterator it = m_rawMseStates.begin(); it < m_rawMseStates.end(); it++)
|
||
|
m_combRawMseState.buttons |= (*it)->buttons;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pBuf != buffer)
|
||
|
delete[] pBuf;
|
||
|
}
|
||
|
|
||
|
void CDirectInputSystem::OpenJoysticks()
|
||
|
{
|
||
|
// Get the GUIDs of all attached joystick devices
|
||
|
vector<GUID> guids;
|
||
|
HRESULT hr;
|
||
|
if (FAILED(hr = m_di8->EnumDevices(DI8DEVCLASS_GAMECTRL, DI8EnumDevicesCallback, &guids, DIEDFL_ATTACHEDONLY)))
|
||
|
return;
|
||
|
|
||
|
// Loop through those found
|
||
|
int joyNum = 0;
|
||
|
for (vector<GUID>::iterator it = guids.begin(); it < guids.end(); it++)
|
||
|
{
|
||
|
joyNum++;
|
||
|
|
||
|
// Open joystick with DirectInput for given GUID and set its data format
|
||
|
LPDIRECTINPUTDEVICE8 di8Joystick;
|
||
|
if (FAILED(hr = m_di8->CreateDevice((*it), &di8Joystick, NULL)))
|
||
|
{
|
||
|
ErrorLog("Unable to create DirectInput joystick device %d (error %d) - skipping joystick.\n", joyNum, hr);
|
||
|
continue;
|
||
|
}
|
||
|
if (FAILED(hr = di8Joystick->SetDataFormat(&c_dfDIJoystick2)))
|
||
|
{
|
||
|
ErrorLog("Unable to set data format for DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr);
|
||
|
|
||
|
di8Joystick->Release();
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Get joystick's capabilities
|
||
|
DIDEVCAPS devCaps;
|
||
|
devCaps.dwSize = sizeof(DIDEVCAPS);
|
||
|
if (FAILED(hr = di8Joystick->GetCapabilities(&devCaps)))
|
||
|
{
|
||
|
ErrorLog("Unable to query capabilities of DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr);
|
||
|
|
||
|
di8Joystick->Release();
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Gather joystick details (num POVs & buttons and which axes are available)
|
||
|
JoyDetails *joyDetails = new JoyDetails();
|
||
|
joyDetails->numPOVs = devCaps.dwPOVs;
|
||
|
joyDetails->numButtons = devCaps.dwButtons;
|
||
|
if (FAILED(hr = di8Joystick->EnumObjects(DI8EnumAxesCallback, joyDetails, DIDFT_AXIS)))
|
||
|
{
|
||
|
ErrorLog("Unable to enumerate axes of DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr);
|
||
|
|
||
|
di8Joystick->Release();
|
||
|
continue;
|
||
|
}
|
||
|
joyDetails->numAxes =
|
||
|
joyDetails->hasXAxis + joyDetails->hasYAxis + joyDetails->hasZAxis +
|
||
|
joyDetails->hasRXAxis + joyDetails->hasRYAxis + joyDetails->hasRZAxis;
|
||
|
|
||
|
// Configure axes, if any
|
||
|
if (joyDetails->numAxes > 0)
|
||
|
{
|
||
|
// Set axis range to be from -32768 to 32767
|
||
|
DIPROPRANGE didpr;
|
||
|
didpr.diph.dwSize = sizeof(DIPROPRANGE);
|
||
|
didpr.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
||
|
didpr.diph.dwHow = DIPH_DEVICE;
|
||
|
didpr.diph.dwObj = 0;
|
||
|
didpr.lMin = -32768;
|
||
|
didpr.lMax = 32767;
|
||
|
if (FAILED(hr = di8Joystick->SetProperty(DIPROP_RANGE, &didpr.diph)))
|
||
|
{
|
||
|
ErrorLog("Unable to set axis range of DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr);
|
||
|
|
||
|
di8Joystick->Release();
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Set axis mode to absolute
|
||
|
DIPROPDWORD dipdw;
|
||
|
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
|
||
|
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
||
|
dipdw.diph.dwHow = DIPH_DEVICE;
|
||
|
dipdw.diph.dwObj = 0;
|
||
|
dipdw.dwData = DIPROPAXISMODE_ABS;
|
||
|
if (FAILED(hr = di8Joystick->SetProperty(DIPROP_AXISMODE, &dipdw.diph)))
|
||
|
{
|
||
|
ErrorLog("Unable to set axis mode of DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr);
|
||
|
|
||
|
di8Joystick->Release();
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Turn off deadzone as handled by this class
|
||
|
dipdw.dwData = 0;
|
||
|
if (FAILED(hr = di8Joystick->SetProperty(DIPROP_DEADZONE, &dipdw.diph)))
|
||
|
{
|
||
|
ErrorLog("Unable to set deadzone of DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr);
|
||
|
|
||
|
di8Joystick->Release();
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Turn off saturation as handle by this class
|
||
|
dipdw.dwData = 10000;
|
||
|
if (FAILED(hr = di8Joystick->SetProperty(DIPROP_SATURATION, &dipdw.diph)))
|
||
|
{
|
||
|
ErrorLog("Unable to set saturation of DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr);
|
||
|
|
||
|
di8Joystick->Release();
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Create initial blank joystick state
|
||
|
LPDIJOYSTATE2 joyState = new DIJOYSTATE2();
|
||
|
memset(joyState, 0, sizeof(DIJOYSTATE2));
|
||
|
for (int povNum = 0; povNum < 4; povNum++)
|
||
|
joyState->rgdwPOV[povNum] = -1;
|
||
|
|
||
|
m_di8Joysticks.push_back(di8Joystick);
|
||
|
m_joyDetails.push_back(joyDetails);
|
||
|
m_diJoyStates.push_back(joyState);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CDirectInputSystem::ActivateJoysticks()
|
||
|
{
|
||
|
// Set DirectInput cooperative level of joysticks
|
||
|
for (vector<LPDIRECTINPUTDEVICE8>::iterator it = m_di8Joysticks.begin(); it < m_di8Joysticks.end(); it++)
|
||
|
(*it)->SetCooperativeLevel(m_hwnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
|
||
|
}
|
||
|
|
||
|
void CDirectInputSystem::PollJoysticks()
|
||
|
{
|
||
|
// Get current joystick states from DirectInput
|
||
|
for (size_t i = 0; i < m_di8Joysticks.size(); i++)
|
||
|
{
|
||
|
LPDIRECTINPUTDEVICE8 joystick = m_di8Joysticks[i];
|
||
|
LPDIJOYSTATE2 state = m_diJoyStates[i];
|
||
|
|
||
|
HRESULT hr;
|
||
|
if (FAILED(hr = joystick->Poll()))
|
||
|
{
|
||
|
hr = joystick->Acquire();
|
||
|
while (hr == DIERR_INPUTLOST)
|
||
|
hr = joystick->Acquire();
|
||
|
|
||
|
if (hr == DIERR_OTHERAPPHASPRIO || hr == DIERR_INVALIDPARAM || hr == DIERR_NOTINITIALIZED)
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Keep tack of each joystick state
|
||
|
joystick->GetDeviceState(sizeof(DIJOYSTATE2), state);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CDirectInputSystem::CloseJoysticks()
|
||
|
{
|
||
|
// Release each DirectInput joystick
|
||
|
for (size_t joyNum = 0; joyNum < m_di8Joysticks.size(); joyNum++)
|
||
|
{
|
||
|
LPDIRECTINPUTDEVICE8 joystick = m_di8Joysticks[joyNum];
|
||
|
JoyDetails *joyDetails = m_joyDetails[joyNum];
|
||
|
LPDIJOYSTATE2 diJoyState = m_diJoyStates[joyNum];
|
||
|
|
||
|
joystick->Unacquire();
|
||
|
joystick->Release();
|
||
|
|
||
|
delete joyDetails;
|
||
|
delete diJoyState;
|
||
|
}
|
||
|
|
||
|
m_di8Joysticks.clear();
|
||
|
m_joyDetails.clear();
|
||
|
m_diJoyStates.clear();
|
||
|
}
|
||
|
|
||
|
bool CDirectInputSystem::InitializeSystem()
|
||
|
{
|
||
|
if (m_useRawInput)
|
||
|
{
|
||
|
// Dynamically load RawInput API
|
||
|
HMODULE user32 = LoadLibrary(TEXT("user32.dll"));
|
||
|
if (user32 != NULL)
|
||
|
{
|
||
|
m_getRIDevListPtr = (GetRawInputDeviceListPtr)GetProcAddress(user32, "GetRawInputDeviceList");
|
||
|
m_getRIDevInfoPtr = (GetRawInputDeviceInfoPtr)GetProcAddress(user32, "GetRawInputDeviceInfoA");
|
||
|
m_regRIDevsPtr = (RegisterRawInputDevicesPtr)GetProcAddress(user32, "RegisterRawInputDevices");
|
||
|
m_getRIDataPtr = (GetRawInputDataPtr)GetProcAddress(user32, "GetRawInputData");
|
||
|
m_useRawInput = m_getRIDevListPtr != NULL && m_getRIDevInfoPtr != NULL && m_regRIDevsPtr != NULL && m_getRIDataPtr != NULL;
|
||
|
}
|
||
|
else
|
||
|
m_useRawInput = false;
|
||
|
|
||
|
if (!m_useRawInput)
|
||
|
ErrorLog("Unable to initialize RawInput API (library hooks are not available) - switching to DirectInput.\n");
|
||
|
}
|
||
|
|
||
|
// Dynamically create DirectInput8 via COM, rather than statically linking to dinput8.dll
|
||
|
// TODO - if fails, try older versions of DirectInput
|
||
|
HRESULT hr;
|
||
|
if (FAILED(hr = CoInitialize(NULL)))
|
||
|
{
|
||
|
ErrorLog("Unable to initialize COM (error %d).\n", hr);
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
if (FAILED(hr = CoCreateInstance(CLSID_DirectInput8, NULL, CLSCTX_INPROC_SERVER, IID_IDirectInput8A, (LPVOID*)&m_di8)))
|
||
|
{
|
||
|
ErrorLog("Unable to initialize DirectInput API (error %d) - is DirectX 8 or later installed?\n", hr);
|
||
|
|
||
|
CoUninitialize();
|
||
|
return false;
|
||
|
}
|
||
|
if (FAILED(hr = m_di8->Initialize(GetModuleHandle(NULL), DIRECTINPUT_VERSION)))
|
||
|
{
|
||
|
ErrorLog("Unable to initialize DirectInput API (error %d) - is DirectX 8 or later installed?\n", hr);
|
||
|
|
||
|
m_di8->Release();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Open all devices
|
||
|
OpenKeyboardsAndMice();
|
||
|
OpenJoysticks();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
int CDirectInputSystem::GetNumKeyboards()
|
||
|
{
|
||
|
// If RawInput enabled, then return number of keyboards found. Otherwise, return ANY_KEYBOARD as DirectInput cannot handle multiple keyboards
|
||
|
return (m_useRawInput ? m_rawKeyboards.size() : ANY_KEYBOARD);
|
||
|
}
|
||
|
|
||
|
int CDirectInputSystem::GetNumMice()
|
||
|
{
|
||
|
// If RawInput enabled, then return number of mice found. Otherwise, return ANY_MOUSE as DirectInput cannot handle multiple keyboards
|
||
|
return (m_useRawInput ? m_rawMice.size() : ANY_MOUSE);
|
||
|
}
|
||
|
|
||
|
int CDirectInputSystem::GetNumJoysticks()
|
||
|
{
|
||
|
// Return number of joysticks found
|
||
|
return m_di8Joysticks.size();
|
||
|
}
|
||
|
|
||
|
int CDirectInputSystem::GetKeyIndex(const char *keyName)
|
||
|
{
|
||
|
for (int i = 0; i < NUM_DI_KEYS; i++)
|
||
|
{
|
||
|
if (stricmp(keyName, s_keyMap[i].keyName) == 0)
|
||
|
return i;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
const char *CDirectInputSystem::GetKeyName(int keyIndex)
|
||
|
{
|
||
|
if (keyIndex < 0 || keyIndex >= NUM_DI_KEYS)
|
||
|
return NULL;
|
||
|
return s_keyMap[keyIndex].keyName;
|
||
|
}
|
||
|
|
||
|
JoyDetails *CDirectInputSystem::GetJoyDetails(int joyNum)
|
||
|
{
|
||
|
return m_joyDetails[joyNum];
|
||
|
}
|
||
|
|
||
|
bool CDirectInputSystem::IsKeyPressed(int kbdNum, int keyIndex)
|
||
|
{
|
||
|
// Get DI key code (scancode) for given key index
|
||
|
int diKey = s_keyMap[keyIndex].diKey;
|
||
|
|
||
|
if (m_useRawInput)
|
||
|
{
|
||
|
// For RawInput, check if key is currently pressed for given keyboard number
|
||
|
BOOL *keyState = m_rawKeyStates[kbdNum];
|
||
|
return !!keyState[diKey];
|
||
|
}
|
||
|
|
||
|
// For DirectInput, just check common keyboard state
|
||
|
return !!(m_diKeyState[diKey] & 0x80);
|
||
|
}
|
||
|
|
||
|
int CDirectInputSystem::GetMouseAxisValue(int mseNum, int axisNum)
|
||
|
{
|
||
|
if (m_useRawInput)
|
||
|
{
|
||
|
// For RawInput, get combined or individual mouse state and return value for given axis
|
||
|
// The cursor is always hidden when using RawInput, so it does not matter if these values don't match with the cursor (with multiple
|
||
|
// mice the cursor is irrelevant anyway)
|
||
|
RawMseState *mseState = (mseNum == ANY_MOUSE ? &m_combRawMseState : m_rawMseStates[mseNum]);
|
||
|
switch (axisNum)
|
||
|
{
|
||
|
case AXIS_X: return mseState->x;
|
||
|
case AXIS_Y: return mseState->y;
|
||
|
case AXIS_Z: return mseState->z;
|
||
|
default: return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// For DirectInput, for X- and Y-axes just use cursor position within window if available (so that mouse movements sync with the cursor)
|
||
|
if (axisNum == AXIS_X || axisNum == AXIS_Y)
|
||
|
{
|
||
|
POINT p;
|
||
|
if (GetCursorPos(&p) && ScreenToClient(m_hwnd, &p))
|
||
|
return (axisNum == AXIS_X ? p.x : p.y);
|
||
|
}
|
||
|
|
||
|
// Otherwise, return the raw DirectInput axis values
|
||
|
switch (axisNum)
|
||
|
{
|
||
|
case AXIS_X: return m_diMseState.x;
|
||
|
case AXIS_Y: return m_diMseState.y;
|
||
|
case AXIS_Z: return m_diMseState.z;
|
||
|
default: return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int CDirectInputSystem::GetMouseWheelDir(int mseNum)
|
||
|
{
|
||
|
if (m_useRawInput)
|
||
|
{
|
||
|
// For RawInput, get combined or individual mouse state and return the wheel value
|
||
|
RawMseState *mseState = (mseNum == ANY_MOUSE ? &m_combRawMseState : m_rawMseStates[mseNum]);
|
||
|
return mseState->wheelDir;
|
||
|
}
|
||
|
|
||
|
// For DirectInput just return the common wheel value
|
||
|
return m_diMseState.wheelDir;
|
||
|
}
|
||
|
|
||
|
bool CDirectInputSystem::IsMouseButPressed(int mseNum, int butNum)
|
||
|
{
|
||
|
if (m_useRawInput)
|
||
|
{
|
||
|
// For RawInput, get combined or individual mouse state and return the button state
|
||
|
RawMseState *mseState = (mseNum == ANY_MOUSE ? &m_combRawMseState : m_rawMseStates[mseNum]);
|
||
|
return !!(mseState->buttons & (1<<butNum));
|
||
|
}
|
||
|
|
||
|
// For DirectInput just return the common button state (taking care with the middle and right mouse buttons
|
||
|
// which DirectInput numbers 2 and 1 respectively, rather than 1 and 2)
|
||
|
if (butNum == 1) butNum = 2;
|
||
|
else if (butNum == 2) butNum = 1;
|
||
|
return (butNum < 5 ? !!(m_diMseState.buttons[butNum] & 0x80) : false);
|
||
|
}
|
||
|
|
||
|
int CDirectInputSystem::GetJoyAxisValue(int joyNum, int axisNum)
|
||
|
{
|
||
|
// Get joystick state for given joystick and return raw joystick axis value (values range from -32768 to 32767)
|
||
|
LPDIJOYSTATE2 diJoyState = m_diJoyStates[joyNum];
|
||
|
switch (axisNum)
|
||
|
{
|
||
|
case AXIS_X: return (int)diJoyState->lX;
|
||
|
case AXIS_Y: return (int)diJoyState->lY;
|
||
|
case AXIS_Z: return (int)diJoyState->lZ;
|
||
|
case AXIS_RX: return (int)diJoyState->lRx;
|
||
|
case AXIS_RY: return (int)diJoyState->lRy;
|
||
|
case AXIS_RZ: return (int)diJoyState->lRz;
|
||
|
default: return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool CDirectInputSystem::IsJoyPOVInDir(int joyNum, int povNum, int povDir)
|
||
|
{
|
||
|
// Get joystick state for given joystick and check if POV-hat value for given POV number is pointing in required direction
|
||
|
LPDIJOYSTATE2 diJoyState = m_diJoyStates[joyNum];
|
||
|
int povVal = diJoyState->rgdwPOV[povNum] / 100; // DirectInput value is angle of POV-hat in 100ths of a degree
|
||
|
switch (povDir)
|
||
|
{
|
||
|
case POV_UP: return povVal == 315 || povVal == 0 || povVal == 45;
|
||
|
case POV_DOWN: return povVal == 135 || povVal == 180 || povVal == 225;
|
||
|
case POV_RIGHT: return povVal == 45 || povVal == 90 || povVal == 135;
|
||
|
case POV_LEFT: return povVal == 225 || povVal == 270 || povVal == 315;
|
||
|
default: return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool CDirectInputSystem::IsJoyButPressed(int joyNum, int butNum)
|
||
|
{
|
||
|
// Get joystick state for given joystick and return current button value for given button number
|
||
|
LPDIJOYSTATE2 diJoyState = m_diJoyStates[joyNum];
|
||
|
return !!diJoyState->rgbButtons[butNum];
|
||
|
}
|
||
|
|
||
|
void CDirectInputSystem::Wait(int ms)
|
||
|
{
|
||
|
Sleep(ms);
|
||
|
}
|
||
|
|
||
|
bool CDirectInputSystem::ConfigMouseCentered()
|
||
|
{
|
||
|
// When checking if mouse centered, use system cursor rather than raw values (otherwise user's mouse movements won't match up
|
||
|
// with onscreen cursor during configuration)
|
||
|
POINT p;
|
||
|
if (!GetCursorPos(&p) || !ScreenToClient(m_hwnd, &p))
|
||
|
return false;
|
||
|
|
||
|
// See if mouse in center of display
|
||
|
unsigned lx = m_dispX + m_dispW / 4;
|
||
|
unsigned ly = m_dispY + m_dispH / 4;
|
||
|
if (p.x < lx || p.x > lx + m_dispW / 2 || p.y < ly || p.y > ly + m_dispH / 2)
|
||
|
return false;
|
||
|
|
||
|
// Once mouse has been centered, sync up mice raw values with current cursor position so that movements are detected correctly
|
||
|
ResetMice();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
CInputSource *CDirectInputSystem::CreateAnyMouseSource(EMousePart msePart)
|
||
|
{
|
||
|
// If using RawInput, create a mouse source that uses the combined mouse state m_combRawState, rather than combining all the individual mouse
|
||
|
// sources in the default manner
|
||
|
if (m_useRawInput)
|
||
|
return CreateMouseSource(ANY_MOUSE, msePart);
|
||
|
|
||
|
return CInputSystem::CreateAnyMouseSource(msePart);
|
||
|
}
|
||
|
|
||
|
bool CDirectInputSystem::Poll()
|
||
|
{
|
||
|
// See if keyboard, mice and joysticks have been activated yet
|
||
|
if (!m_activated)
|
||
|
{
|
||
|
// If not, then get Window handle of SDL window
|
||
|
SDL_SysWMinfo info;
|
||
|
SDL_VERSION(&info.version);
|
||
|
if (SDL_GetWMInfo(&info))
|
||
|
m_hwnd = info.window;
|
||
|
|
||
|
// Tell SDL to pass on all Windows events
|
||
|
// Removed - see below
|
||
|
//SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
|
||
|
|
||
|
// Activate the devices now that a Window handle is available
|
||
|
ActivateKeyboardsAndMice();
|
||
|
ActivateJoysticks();
|
||
|
|
||
|
m_activated = true;
|
||
|
}
|
||
|
|
||
|
// Wait or poll for event from SDL
|
||
|
// Removed - see below
|
||
|
/*
|
||
|
SDL_Event e;
|
||
|
while (SDL_PollEvent(&e))
|
||
|
{
|
||
|
if (e.type == SDL_QUIT)
|
||
|
return false;
|
||
|
else if (e.type == SDL_SYSWMEVENT)
|
||
|
{
|
||
|
SDL_SysWMmsg *wmMsg = e.syswm.msg;
|
||
|
ProcessWMEvent(wmMsg->hwnd, wmMsg->msg, wmMsg->wParam, wmMsg->lParam);
|
||
|
}
|
||
|
}*/
|
||
|
|
||
|
// Wait or poll for event on Windows message queue (done this way instead of using SDL_PollEvent as above because
|
||
|
// for some reason this causes RawInput HRAWINPUT handles to arrive stale. Not sure what SDL_PollEvent is doing to cause this
|
||
|
// but the following code can replace it without any problems as it is effectively what SDL_PollEvent does anyway)
|
||
|
MSG msg;
|
||
|
while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
|
||
|
{
|
||
|
int ret = GetMessage(&msg, NULL, 0, 0);
|
||
|
if (ret == 0)
|
||
|
return false;
|
||
|
else if (ret > 0)
|
||
|
{
|
||
|
TranslateMessage(&msg);
|
||
|
|
||
|
// Handle RawInput messages here
|
||
|
if (m_useRawInput && msg.message == WM_INPUT)
|
||
|
ProcessRawInput((HRAWINPUT)msg.lParam);
|
||
|
|
||
|
// Propagate all messages to default (SDL) handlers
|
||
|
DispatchMessage(&msg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Poll keyboards, mice and joysticks
|
||
|
PollKeyboardsAndMice();
|
||
|
PollJoysticks();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void CDirectInputSystem::SetMouseVisibility(bool visible)
|
||
|
{
|
||
|
if (m_useRawInput)
|
||
|
ShowCursor(m_isConfiguring && visible ? TRUE : FALSE);
|
||
|
else
|
||
|
ShowCursor(visible ? TRUE : FALSE);
|
||
|
}
|
||
|
|
||
|
void CDirectInputSystem::ConfigEnd()
|
||
|
{
|
||
|
CInputSystem::ConfigEnd();
|
||
|
|
||
|
// When configuration has taken place, make sure devices get re-activated
|
||
|
m_activated = false;
|
||
|
}
|