683 lines
19 KiB
C++
683 lines
19 KiB
C++
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
|
// Copyright (C) 2010 Winch Gate Property Limited
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as
|
|
// published by the Free Software Foundation, either version 3 of the
|
|
// License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
#include "stdmisc.h"
|
|
#include "di_keyboard_device.h"
|
|
|
|
#ifdef NL_OS_WINDOWS
|
|
|
|
#include "nel/misc/win_event_emitter.h"
|
|
#include <memory>
|
|
#include <dinput.h>
|
|
#include <Winuser.h>
|
|
|
|
#include "Mmsystem.h"
|
|
|
|
namespace NLMISC
|
|
{
|
|
|
|
// used to do a conversion from DX key code to Nel keys enums
|
|
struct CKeyConv
|
|
{
|
|
uint DIKey;
|
|
TKey NelKey;
|
|
const char *KeyName;
|
|
bool Repeatable;
|
|
};
|
|
|
|
// this is used to build a conversion table
|
|
static const CKeyConv DIToNel[] =
|
|
{
|
|
//
|
|
{DIK_F1, KeyF1, "F1", true},
|
|
{DIK_F2, KeyF2, "F2", true},
|
|
{DIK_F3, KeyF3, "F3", true},
|
|
{DIK_F4, KeyF4, "F4", true},
|
|
{DIK_F5, KeyF5, "F5", true},
|
|
{DIK_F6, KeyF6, "F6", true},
|
|
{DIK_F7, KeyF7, "F7", true},
|
|
{DIK_F8, KeyF8, "F8", true},
|
|
{DIK_F9, KeyF9, "F9", true},
|
|
{DIK_F10, KeyF10, "F10", true},
|
|
{DIK_F11, KeyF11, "F11", true},
|
|
{DIK_F12, KeyF12, "F12", true},
|
|
{DIK_F13, KeyF13, "F13", true},
|
|
{DIK_F14, KeyF14, "F14", true},
|
|
{DIK_F15, KeyF15, "F15", true},
|
|
//
|
|
{DIK_NUMPAD0, KeyNUMPAD0, "NUMPAD0", true},
|
|
{DIK_NUMPAD1, KeyNUMPAD1, "NUMPAD1", true},
|
|
{DIK_NUMPAD2, KeyNUMPAD2, "NUMPAD2", true},
|
|
{DIK_NUMPAD3, KeyNUMPAD3, "NUMPAD3", true},
|
|
{DIK_NUMPAD4, KeyNUMPAD4, "NUMPAD4", true},
|
|
{DIK_NUMPAD5, KeyNUMPAD5, "NUMPAD5", true},
|
|
{DIK_NUMPAD6, KeyNUMPAD6, "NUMPAD6", true},
|
|
{DIK_NUMPAD7, KeyNUMPAD7, "NUMPAD7", true},
|
|
{DIK_NUMPAD8, KeyNUMPAD8, "NUMPAD8", true},
|
|
{DIK_NUMPAD9, KeyNUMPAD9, "NUMPAD9", true},
|
|
//
|
|
{DIK_DIVIDE, KeyDIVIDE, "/", true},
|
|
{DIK_DECIMAL, KeyDECIMAL, "NUMPAD .", true},
|
|
//
|
|
{DIK_LSHIFT, KeyLSHIFT, "LEFT SHIFT", false},
|
|
{DIK_RSHIFT, KeyRSHIFT, "RIGHT SHIFT", false},
|
|
//
|
|
{DIK_LCONTROL, KeyLCONTROL, "LEFT CONTROL", false},
|
|
{DIK_RCONTROL, KeyRCONTROL, "RIGHT CONTROL", false},
|
|
//
|
|
{DIK_LMENU, KeyLMENU, "ALT", false},
|
|
{DIK_RMENU, KeyRMENU, "ALT GR", false},
|
|
//
|
|
{DIK_UP, KeyUP, "UP", true},
|
|
{DIK_PRIOR, KeyPRIOR, "PRIOR", true},
|
|
{DIK_LEFT, KeyLEFT, "LEFT", true},
|
|
{DIK_RIGHT, KeyRIGHT, "RIGHT", true},
|
|
{DIK_END, KeyEND, "END", true},
|
|
{DIK_DOWN, KeyDOWN, "DOWN", true},
|
|
{DIK_NEXT, KeyNEXT, "NEXT", true},
|
|
{DIK_INSERT, KeyINSERT, "INSERT", true},
|
|
{DIK_DELETE, KeyDELETE, "DELETE", true},
|
|
{DIK_HOME, KeyHOME, "HOME", true},
|
|
{DIK_LWIN, KeyLWIN, "LEFT WIN", false},
|
|
{DIK_RWIN, KeyRWIN, "RIGHT WIN", false},
|
|
{DIK_APPS, KeyAPPS, "APPS", false},
|
|
{DIK_BACK, KeyBACK, "BACK", true},
|
|
//
|
|
{DIK_SYSRQ, KeySNAPSHOT, "SNAPSHOT", false},
|
|
{DIK_SCROLL, KeySCROLL, "SCROLL", false},
|
|
{DIK_PAUSE, KeyPAUSE, "PAUSE", false},
|
|
//
|
|
{DIK_NUMLOCK, KeyNUMLOCK, "NUMLOCK", false},
|
|
//
|
|
{DIK_NUMPADENTER, KeyRETURN, "RETURN", true},
|
|
//{DIK_NUMPADENTER, KeyRETURN, "ENTER", true},
|
|
//
|
|
{DIK_CONVERT, KeyCONVERT, "CONVERT", false},
|
|
{DIK_NOCONVERT, KeyNONCONVERT, "NOCONVERT", true},
|
|
//
|
|
{DIK_KANA, KeyKANA, false},
|
|
{DIK_KANJI, KeyKANJI, false},
|
|
};
|
|
|
|
|
|
///========================================================================
|
|
const CKeyConv *CDIKeyboard::DIKeyToNelKeyTab[CDIKeyboard::NumKeys];
|
|
|
|
///========================================================================
|
|
CDIKeyboard::CDIKeyboard(CWinEventEmitter *we, HWND hwnd)
|
|
: _Keyboard(NULL),
|
|
_WE(we),
|
|
ShiftPressed(false),
|
|
CtrlPressed(false),
|
|
AltPressed(false),
|
|
_CapsLockToggle(true),
|
|
_hWnd(hwnd),
|
|
_RepeatDelay(250),
|
|
_RepeatPeriod(200),
|
|
_FirstPressDate(-1),
|
|
_LastDIKeyPressed(0)
|
|
{
|
|
if (::GetKeyboardState((PBYTE) _VKKeyState) == FALSE)
|
|
{
|
|
std::fill(_VKKeyState, _VKKeyState + NumKeys, 0);
|
|
}
|
|
// test whether the user toggle its keyboard with shift or not..
|
|
HKEY hKey;
|
|
if (::RegOpenKeyEx(HKEY_CURRENT_USER, "Keyboard Layout", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
|
|
{
|
|
DWORD type = REG_DWORD;
|
|
DWORD value;
|
|
DWORD size = sizeof(DWORD);
|
|
if (::RegQueryValueEx(hKey, "Attributes", NULL, &type, (LPBYTE) &value, &size) == ERROR_SUCCESS)
|
|
{
|
|
_CapsLockToggle = (value & (1 << 16)) == 0;
|
|
}
|
|
::RegCloseKey(hKey);
|
|
}
|
|
// get repeat delay and period
|
|
int keybDelay;
|
|
if (::SystemParametersInfo(SPI_GETKEYBOARDDELAY, 0, &keybDelay, 0) != 0)
|
|
{
|
|
_RepeatDelay = 250 + 250 * keybDelay;
|
|
}
|
|
DWORD keybSpeed;
|
|
if (::SystemParametersInfo(SPI_GETKEYBOARDSPEED, 0, &keybSpeed, 0) != 0)
|
|
{
|
|
_RepeatPeriod = (uint) (1000.f / (keybSpeed * (27.5f / 31.f) + 2.5f));
|
|
}
|
|
// get keyboard layout
|
|
_KBLayout = ::GetKeyboardLayout(NULL);
|
|
|
|
_RepetitionDisabled.resize(NumKeys);
|
|
_RepetitionDisabled.clearAll();
|
|
}
|
|
|
|
///========================================================================
|
|
void CDIKeyboard::updateVKKeyState(uint diKey, bool pressed, TKey &keyValue, TKey &charValue)
|
|
{
|
|
bool extKey;
|
|
bool repeatable;
|
|
keyValue = DIKeyToNelKey(diKey, extKey, repeatable);
|
|
//
|
|
if (keyValue == 0)
|
|
{
|
|
charValue = keyValue;
|
|
return;
|
|
}
|
|
//
|
|
if (pressed)
|
|
{
|
|
// check for toggle key
|
|
switch (keyValue)
|
|
{
|
|
case KeyPAUSE:
|
|
case KeyKANA:
|
|
case KeyKANJI:
|
|
_VKKeyState[keyValue] ^= 0x01; // toggle first bit
|
|
break;
|
|
case KeyCAPITAL:
|
|
if (_CapsLockToggle)
|
|
{
|
|
_VKKeyState[keyValue] ^= 0x01;
|
|
//toggleCapsLock(false);
|
|
}
|
|
else
|
|
{
|
|
if ((_VKKeyState[keyValue] & 0x01) == 0)
|
|
{
|
|
_VKKeyState[keyValue] |= 0x01;
|
|
//toggleCapsLock(false);
|
|
}
|
|
}
|
|
break;
|
|
case KeyNUMLOCK:
|
|
_VKKeyState[keyValue] ^= 0x01;
|
|
//setNumLock((_VKKeyState[keyValue] & 0x01) != 0);
|
|
break;
|
|
case KeySCROLL:
|
|
_VKKeyState[keyValue] ^= 0x01;
|
|
//toggleScrollLock();
|
|
break;
|
|
|
|
}
|
|
|
|
_VKKeyState[keyValue] |= 0x80;
|
|
}
|
|
else
|
|
{
|
|
_VKKeyState[keyValue] &= ~0x80;
|
|
}
|
|
//
|
|
switch (keyValue)
|
|
{
|
|
case KeyLSHIFT: charValue = KeySHIFT; break;
|
|
case KeyRSHIFT: charValue = KeySHIFT; break;
|
|
case KeyLCONTROL: charValue = KeyCONTROL; break;
|
|
case KeyRCONTROL: charValue = KeyCONTROL; break;
|
|
case KeyLMENU: charValue = KeyMENU; break;
|
|
case KeyRMENU: charValue = KeyMENU; break;
|
|
default: charValue = keyValue; break;
|
|
}
|
|
//
|
|
if (charValue == KeySHIFT && !_CapsLockToggle)
|
|
{
|
|
if (_VKKeyState[KeyCAPITAL] & 0x01)
|
|
{
|
|
_VKKeyState[KeyCAPITAL] &= ~0x01;
|
|
//toggleCapsLock(true);
|
|
}
|
|
}
|
|
//
|
|
if (charValue != keyValue)
|
|
{
|
|
_VKKeyState[charValue] = _VKKeyState[keyValue];
|
|
}
|
|
//
|
|
updateCtrlAltShiftValues();
|
|
}
|
|
|
|
///========================================================================
|
|
void CDIKeyboard::updateCtrlAltShiftValues()
|
|
{
|
|
ShiftPressed = (_VKKeyState[KeySHIFT] & 0x80) != 0;
|
|
CtrlPressed = (_VKKeyState[KeyCONTROL] & 0x80) != 0;
|
|
AltPressed = (_VKKeyState[KeyMENU] & 0x80) != 0;
|
|
}
|
|
|
|
///========================================================================
|
|
CDIKeyboard::~CDIKeyboard()
|
|
{
|
|
if (_Keyboard)
|
|
{
|
|
_Keyboard->Unacquire();
|
|
_Keyboard->Release();
|
|
}
|
|
}
|
|
|
|
///========================================================================
|
|
CDIKeyboard *CDIKeyboard::createKeyboardDevice(IDirectInput8 *di8,
|
|
HWND hwnd,
|
|
CDIEventEmitter *diEventEmitter,
|
|
CWinEventEmitter *we
|
|
) throw(EDirectInput)
|
|
{
|
|
std::auto_ptr<CDIKeyboard> kb(new CDIKeyboard(we, hwnd));
|
|
kb->_DIEventEmitter = diEventEmitter;
|
|
HRESULT result = di8->CreateDevice(GUID_SysKeyboard, &kb->_Keyboard, NULL);
|
|
if (result != DI_OK) throw EDirectInputNoKeyboard();
|
|
result = kb->_Keyboard->SetCooperativeLevel(hwnd, DISCL_FOREGROUND | DISCL_EXCLUSIVE);
|
|
if (result != DI_OK) throw EDirectInputCooperativeLevelFailed();
|
|
result = kb->_Keyboard->SetDataFormat(&c_dfDIKeyboard);
|
|
kb->setBufferSize(16);
|
|
kb->_Keyboard->Acquire();
|
|
|
|
// Enable win32 keyboard messages only if hardware mouse in normal mode
|
|
if (kb->_WE)
|
|
kb->_WE->enableKeyboardEvents(false);
|
|
|
|
return kb.release();
|
|
}
|
|
|
|
///========================================================================
|
|
void CDIKeyboard::poll(CInputDeviceServer *dev)
|
|
{
|
|
nlassert(_Keyboard);
|
|
nlassert(_KeyboardBufferSize > 0);
|
|
static std::vector<DIDEVICEOBJECTDATA> datas;
|
|
datas.resize(_KeyboardBufferSize);
|
|
DWORD numElements = _KeyboardBufferSize;
|
|
HRESULT result = _Keyboard->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), &datas[0], &numElements, 0);
|
|
if (result == DIERR_NOTACQUIRED || result == DIERR_INPUTLOST)
|
|
{
|
|
result = _Keyboard->Acquire();
|
|
if (result != DI_OK) return;
|
|
// get device state
|
|
::GetKeyboardState((unsigned char *) _VKKeyState);
|
|
_LastDIKeyPressed = 0;
|
|
updateCtrlAltShiftValues();
|
|
result = _Keyboard->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), &datas[0], &numElements, 0);
|
|
if (result != DI_OK) return;
|
|
}
|
|
else if (result != DI_OK)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_PollTime = (uint32) CTime::getLocalTime();
|
|
|
|
|
|
// process each message in the list
|
|
for (uint k = 0; k < numElements; ++k)
|
|
{
|
|
CDIEvent *die = new CDIEvent;
|
|
die->Emitter = this;
|
|
die->Datas = datas[k];
|
|
dev->submitEvent(die);
|
|
}
|
|
}
|
|
|
|
///========================================================================
|
|
void CDIKeyboard::transitionOccured(CEventServer *server, const IInputDeviceEvent *nextMessage)
|
|
{
|
|
repeatKey(buildDateFromEvent(nextMessage), server);
|
|
}
|
|
|
|
///========================================================================
|
|
TKeyButton CDIKeyboard::buildKeyButtonsFlags() const
|
|
{
|
|
return (TKeyButton) ( (ShiftPressed ? shiftKeyButton : 0)
|
|
| (CtrlPressed ? ctrlKeyButton : 0)
|
|
| (AltPressed ? altKeyButton : 0)
|
|
);
|
|
}
|
|
|
|
///========================================================================
|
|
void CDIKeyboard::keyTriggered(bool pressed, uint dikey, CEventServer *server, uint32 date)
|
|
{
|
|
#if 0
|
|
const uint numPairs = sizeof(DIToNel) / sizeof(CKeyConv);
|
|
for (uint k = 0; k < numPairs; ++k)
|
|
{
|
|
if (DIToNel[k].DIKey == key)
|
|
{
|
|
nlinfo(DIToNel[k].KeyName);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
TKey keyValue, charValue;
|
|
updateVKKeyState(dikey, pressed, keyValue, charValue);
|
|
if (keyValue == 0) return;
|
|
|
|
CEventKey *ek;
|
|
if (pressed )
|
|
{
|
|
ek = new CEventKeyDown(keyValue, buildKeyButtonsFlags(), true, _DIEventEmitter);
|
|
}
|
|
else
|
|
{
|
|
ek = new CEventKeyUp(keyValue, buildKeyButtonsFlags(), _DIEventEmitter);
|
|
}
|
|
server->postEvent(ek);
|
|
|
|
if (pressed)
|
|
{
|
|
if (_RepetitionDisabled[(uint) keyValue] == false)
|
|
{
|
|
_LastEmitDate = _FirstPressDate = date;
|
|
_LastDIKeyPressed = dikey;
|
|
}
|
|
else // not a repeatable key
|
|
{
|
|
_LastDIKeyPressed = 0;
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// key released ?
|
|
if (dikey == _LastDIKeyPressed)
|
|
{
|
|
_LastDIKeyPressed = 0;
|
|
}
|
|
|
|
if (_RepetitionDisabled[(uint) keyValue] == true)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
// first char event (if repetition not disabled)
|
|
if (keyValue >= KeyNUMPAD0 && keyValue <= KeyNUMPAD9 || keyValue == KeyDECIMAL)
|
|
{
|
|
if ((_VKKeyState[KeyNUMLOCK] & 0x01) != 0)
|
|
{
|
|
sendUnicode(charValue, dikey, server, pressed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sendUnicode(charValue, dikey, server, pressed);
|
|
}
|
|
|
|
_FirstPressDate = (uint32) NLMISC::CTime::getLocalTime(); // can't use the time stamp, because we can't not sure it matches the local time.
|
|
// time stamp is used for evenrts sorting only
|
|
}
|
|
|
|
///========================================================================
|
|
void CDIKeyboard::submit(IInputDeviceEvent *deviceEvent, CEventServer *server)
|
|
{
|
|
CDIEvent *die = safe_cast<CDIEvent *>(deviceEvent);
|
|
bool pressed = (die->Datas.dwData & 0x80) != 0;
|
|
keyTriggered(pressed, (uint) die->Datas.dwOfs, server, die->Datas.dwTimeStamp);
|
|
}
|
|
|
|
///========================================================================
|
|
TMouseButton CDIKeyboard::buildKeyboardButtonFlags() const
|
|
{
|
|
nlassert(_Keyboard);
|
|
return _DIEventEmitter->buildKeyboardButtonFlags();
|
|
}
|
|
|
|
///========================================================================
|
|
bool CDIKeyboard::setBufferSize(uint size)
|
|
{
|
|
nlassert(size > 0);
|
|
nlassert(_Keyboard);
|
|
_Keyboard->Unacquire();
|
|
DIPROPDWORD dipdw;
|
|
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
|
|
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
|
dipdw.diph.dwObj = 0;
|
|
dipdw.diph.dwHow = DIPH_DEVICE;
|
|
dipdw.dwData = size;
|
|
HRESULT r = _Keyboard->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph );
|
|
if (r != DI_OK) return false;
|
|
_KeyboardBufferSize = size;
|
|
return true;
|
|
}
|
|
|
|
///========================================================================
|
|
uint CDIKeyboard::getBufferSize() const
|
|
{
|
|
return _KeyboardBufferSize;
|
|
}
|
|
|
|
///========================================================================
|
|
TKey CDIKeyboard::DIKeyToNelKey(uint diKey, bool &extKey, bool &repeatable)
|
|
{
|
|
// some key are not handled by MapVirtualKeyEx so we need to convert them ourselves
|
|
static bool tableBuilt = false;
|
|
|
|
if (!tableBuilt)
|
|
{
|
|
uint k;
|
|
for (k = 0; k < NumKeys; ++k)
|
|
{
|
|
DIKeyToNelKeyTab[k] = NULL; // set as not a valid key by default
|
|
}
|
|
const uint numPairs = sizeof(DIToNel) / sizeof(CKeyConv);
|
|
for (k = 0; k < numPairs; ++k)
|
|
{
|
|
DIKeyToNelKeyTab[DIToNel[k].DIKey] = &DIToNel[k];
|
|
}
|
|
tableBuilt = true;
|
|
}
|
|
|
|
|
|
//
|
|
if (DIKeyToNelKeyTab[diKey] != NULL)
|
|
{
|
|
const CKeyConv &keyConv = *DIKeyToNelKeyTab[diKey];
|
|
extKey = true;
|
|
repeatable = keyConv.Repeatable;
|
|
return keyConv.NelKey;
|
|
}
|
|
|
|
|
|
|
|
// try doing the conversion using MapVirtualKey
|
|
TKey key = (TKey) ::MapVirtualKeyEx(diKey, 1, _KBLayout);
|
|
extKey = false;
|
|
return key;
|
|
}
|
|
|
|
///========================================================================
|
|
void CDIKeyboard::sendUnicode(TKey vkey, uint dikey, CEventServer *server, bool pressed)
|
|
{
|
|
uint8 oldShift = _VKKeyState[KeySHIFT];
|
|
/// If caps lock is off when pressing shift, we must disable shift, to get no minuscule letters when it is pressed and capslocks is on.
|
|
if (!_CapsLockToggle && _VKKeyState[KeyCAPITAL] & 0x01)
|
|
{
|
|
_VKKeyState[KeySHIFT] = 0;
|
|
}
|
|
// 'ToUnicode??' is supported since NT4.0 only
|
|
// Check if there's support
|
|
|
|
|
|
static bool init = false;
|
|
static bool toUnicodeSupported = false;
|
|
if (!init)
|
|
{
|
|
init = true;
|
|
OSVERSIONINFO osvi;
|
|
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
if (::GetVersionEx (&osvi))
|
|
{
|
|
if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
|
|
{
|
|
if (osvi.dwMajorVersion >= 4)
|
|
{
|
|
toUnicodeSupported = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (toUnicodeSupported)
|
|
{
|
|
const uint maxNumKeys = 8;
|
|
WCHAR keyUnicodes[maxNumKeys];
|
|
int res = ::ToUnicodeEx(vkey, dikey | (pressed ? 0 : (1 << 15)), (unsigned char *) _VKKeyState, keyUnicodes, maxNumKeys, 0, _KBLayout);
|
|
//
|
|
_VKKeyState[KeySHIFT] = oldShift;
|
|
//
|
|
for (sint k = 0; k < res; ++k)
|
|
{
|
|
CEventChar *evc = new CEventChar((ucchar) keyUnicodes[k], buildKeyButtonsFlags(), _DIEventEmitter);
|
|
server->postEvent(evc);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
unsigned char buf[2];
|
|
int res = ::ToAsciiEx(vkey, dikey | (pressed ? 0 : (1 << 15)), (unsigned char *) _VKKeyState, (LPWORD) buf, 0, _KBLayout);
|
|
for (sint k = 0; k < res; ++k)
|
|
{
|
|
CEventChar *evc = new CEventChar((ucchar) buf[k], buildKeyButtonsFlags(), _DIEventEmitter);
|
|
server->postEvent(evc);
|
|
}
|
|
}
|
|
}
|
|
|
|
///========================================================================
|
|
void CDIKeyboard::repeatKey(uint32 currentDate, CEventServer *server)
|
|
{
|
|
if (_LastDIKeyPressed == 0 || _LastDIKeyPressed == DIK_INSERT) return;
|
|
bool extKey;
|
|
bool repeatable;
|
|
TKey vkey = DIKeyToNelKey(_LastDIKeyPressed, extKey, repeatable);
|
|
if (vkey == 0) return;
|
|
if (currentDate - _FirstPressDate < _RepeatDelay) return;
|
|
|
|
sint32 firstDate = _LastEmitDate - (_FirstPressDate + _RepeatDelay);
|
|
sint32 lastDate = currentDate - (_FirstPressDate + _RepeatDelay);
|
|
if (firstDate < 0) firstDate = 0;
|
|
|
|
if (lastDate < firstDate) return;
|
|
|
|
uint numRep = (uint) ((lastDate + _RepeatPeriod - 1) / _RepeatPeriod - (firstDate + _RepeatPeriod - 1) / _RepeatPeriod);
|
|
//numRep = std::min(16u, numRep); // too much repetitions don't make sense...
|
|
if ((sint) numRep < 0) return; // 50 days loop..
|
|
numRep = 1; // fix : for now it seems better to limit the number of repetition to 1 per frame (it can be greater than 1 only if framerate is slow, but its not very useable)
|
|
|
|
|
|
// numpad case
|
|
if (vkey >= KeyNUMPAD0 && vkey <= KeyNUMPAD9 || vkey == KeyDECIMAL)
|
|
{
|
|
// check whether numlock is activated
|
|
if ((_VKKeyState[KeyNUMLOCK] & 0x01) != 0)
|
|
{
|
|
for (uint k = 0; k < numRep; ++k)
|
|
{
|
|
sendUnicode(vkey, _LastDIKeyPressed, server, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// arrow, home, end.. events
|
|
for (uint k = 0; k < numRep; ++k)
|
|
{
|
|
CEventKey *ek = new CEventKeyDown(vkey, buildKeyButtonsFlags(), false, _DIEventEmitter);
|
|
server->postEvent(ek);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (uint k = 0; k < numRep; ++k)
|
|
{
|
|
// if it is an extended key, repetition won't be managed by sendUnicode
|
|
if (extKey && repeatable)
|
|
{
|
|
CEventKey *ek = new CEventKeyDown(vkey, buildKeyButtonsFlags(), false, _DIEventEmitter);
|
|
server->postEvent(ek);
|
|
}
|
|
else
|
|
{
|
|
sendUnicode(vkey, _LastDIKeyPressed, server, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
_LastEmitDate = currentDate;
|
|
}
|
|
|
|
///========================================================================
|
|
uint32 CDIKeyboard::buildDateFromEvent(const IInputDeviceEvent *deviceEvent)
|
|
{
|
|
if (deviceEvent)
|
|
{
|
|
const CDIEvent *die = safe_cast<const CDIEvent *>(deviceEvent);
|
|
return (uint32) die->Datas.dwData;
|
|
}
|
|
else
|
|
{
|
|
return _PollTime;
|
|
}
|
|
}
|
|
|
|
///========================================================================
|
|
void CDIKeyboard::disableRepetition(const TKey *keyTab, uint numKey)
|
|
{
|
|
_RepetitionDisabled.clearAll();
|
|
for (uint k = 0; k < numKey; ++k)
|
|
{
|
|
_RepetitionDisabled.set((sint) keyTab[k]);
|
|
}
|
|
|
|
if (_LastDIKeyPressed != 0)
|
|
{
|
|
bool extKey;
|
|
bool repeatable;
|
|
TKey key = DIKeyToNelKey(_LastDIKeyPressed, extKey, repeatable);
|
|
if (_RepetitionDisabled[(uint) key])
|
|
{
|
|
// disable this key repetition
|
|
_LastDIKeyPressed = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
///========================================================================
|
|
uint CDIKeyboard::getNumDisabledRepetition() const
|
|
{
|
|
uint numKey = 0;
|
|
for (uint k = 0; k < NumKeys; ++k)
|
|
{
|
|
if (_RepetitionDisabled[k]) ++numKey;
|
|
}
|
|
return numKey;
|
|
}
|
|
|
|
///========================================================================
|
|
void CDIKeyboard::getDisabledRepetitions(TKey *destTab) const
|
|
{
|
|
for (uint k = 0; k < NumKeys; ++k)
|
|
{
|
|
if (_RepetitionDisabled[k]) *destTab++ = (TKey) k;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
} // NLMISC
|
|
|
|
#endif // NL_OS_WINDOWS
|