Вернемся к функции WinMain() и рассмотрим следующий фрагмент кода:
while(msg.message != WM_QUIT) {
if(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else {
// Чтение из буфера клавиатуры
iResult = iReadKeyboard();
// Проверяем, сколько нажатий на клавиши возвращено
if(iResult) {
// Цикл обработки полученных данных
for(i = 0; i < iResult; i++) {
// Выход из программы, если нажата клавиша ESC
if(diks[DIK_ESCAPE][i]) {
PostQuitMessage(0);
}
else if (ascKeys[13][i]) {
PostQuitMessage(0);
}
}
}
}
}
Представленный код является стандартным циклом обработки сообщений Windows. Его ключевой особенностью является вызов функции iReadKeyboard(). Обращение к ней происходит каждый раз, когда в очереди нет системных сообщений для обработки. Функция возвращает количество зафиксированных изменений состояний клавиш и сохраняет их в глобальных массивах diks и ascKeys. Если функция возвратила какие-нибудь данные, программа в цикле перебирает полученные изменения состояний клавиш и проверяет не была ли нажата клавиша Esc. Если клавиша была нажата, выполнение программы завершается.
Функция iReadKeyboard()
Вместо того, чтобы одним махом показать вам весь код функции, я разделил его на небольшие кусочки. Вот первый фрагмент функции iReadKeyboard():
if(!pKeyboard || !pDI) {
return(INPUTERROR_NOKEYBOARD);
}
Этот маленький фрагмент кода проверяет существуют ли объекты клавиатуры и DirectInput. Если какого-нибудь из них нет, функция возвращает код ошибки. Пришло время следующего фрагмента:
hr = pKeyboard->GetDeviceData(
sizeof(DIDEVICEOBJECTDATA),
didKeyboardBuffer,
&dwItems,
0);
Вызов функции получения данных от устройства возвращает любые данные, находящиеся в буфере устройства ввода. В данном случае возвращается буфер клавиатуры. Переменная dwItems будет содержать количество возвращенных элементов, а сами они будут помещены в буфер didKeyboardBuffer. Переменная hr сохраняет код завершения, возвращаемый функцией получения данных от устройства. Логика проверки кода завершения выглядит следующим образом:
// Клавиатуа может быть потеряна, захватить устройство снова
if(FAILED(hr)) {
pKeyboard->Acquire();
return(INPUTERROR_SUCCESS);
}
Если переменная hr содержит код ошибки, это может быть вызвано тем, что клавиатура потеряна из-за сворачивания окна или каких-нибудь других действий. В этом случае нужно повторно захватить клавиатуру с помощью функции захвата устройства.
Если мы без ошибок прошли все предыдущие этапы, настало время в цикле получить данные от устройства и заполнить ими глобальный буфер клавиатуры. Соответствующий код представлен ниже:
// Если есть данные, обработаем их
if (dwItems) {
// Обработка данных
for(dwCurBuffer = 0; dwCurBuffer < dwItems; dwCurBuffer++) {
// Преобразование скан-кода в код ASCII
byteASCII = Scan2Ascii(
didKeyboardBuffer[dwCurBuffer].dwOfs);
// Указываем, что клавиша нажата
if(didKeyboardBuffer[dwCurBuffer].dwData & 0x80) {
ascKeys[byteASCII][dwCurBuffer] = 1;
diks[didKeyboardBuffer[dwCurBuffer].dwOfs]
[dwCurBuffer] = 1;
}
// Указываем, что клавиша отпущена
else {
ascKeys[byteASCII][dwCurBuffer] = 0;
diks[didKeyboardBuffer[dwCurBuffer].dwOfs]
[dwCurBuffer] = 0;
}
}
}
Код проверяет были ли возвращены какие-нибудь данные функцией получения данных от устройства. Если да, код в цикле перебирает возвращенные элементы в буфере и сохраняет результаты в глобальных массивах diks и ascKeys.
Определение состояния DIK
Массив didKeyboardBuffer хранит данные возвращенные DirectInput. Чтобы сделать их читаемыми, необходимо проверить значение каждого элемента массива. Если результат поразрядной логической операции И над возвращенным значением и константой 0x80 не равен нулю, значит клавиша была нажата; в ином случае клавиша была отпущена. Я знаю, это выглядит причудливо, но именно так работает DirectInput!
Преобразование кода DIK в код ASCII
Для преобразования кодов DIK в коды ASCII я написал следующую функцию:
BYTE Scan2Ascii(DWORD scancode)
{
UINT vk;
// Преобразование скан-кода в код ASCII
vk = MapVirtualKeyEx(scancode, 1, g_Layout);
// Возвращаем код ASCII
return(vk);
}
Функция получает код клавиши DirectInput и вызывает функцию MapVirtualKeyEx() для преобразования его в ASCII. Для работы функции отображения кодов необходимы данные о раскладке клавиатуры, которые мы получили на этапе инициализации.