Книга: Графика для Windows средствами DirectDraw
Инициализация DirectInput
Разделы на этой странице:
Инициализация DirectInput
Инициализация DirectInput и DirectDraw выполняется в функции OnCreate(). DirectInput инициализируется версией OnCreate() класса QwertyWin, а DirectDraw — версией из DirectDrawWin. Функция QwertyWin::OnCreate() приведена в листинге 6.2.
Листинг 6.2. Функция QwertyWin::OnCreate()
int QwertyWin::OnCreate(LPCREATESTRUCT lpCreateStruct) {
HRESULT r=DirectInputCreate(AfxGetInstanceHandle(), DIRECTINPUT_VERSION, &dinput, 0);
if (r!=DI_OK) {
AfxMessageBox("DirectInputCreate() failed");
return -1;
}
r = dinput->CreateDevice(GUID_SysKeyboard, &keyboard, 0);
if (r!=DI_OK) {
AfxMessageBox("CreateDevice(keyboard) failed");
return -1;
}
r = keyboard->SetDataFormat(&c_dfDIKeyboard);
if (r!=DI_OK) {
AfxMessageBox("keyboard->SetDataFormat() failed");
return -1;
}
r=keyboard->SetCooperativeLevel(GetSafeHwnd(), DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
if (r!=DI_OK) {
AfxMessageBox("keyboard->SetCooperativeLevel() failed");
return -1;
}
if (DirectDrawWin::OnCreate(lpCreateStruct)==-1) return -1;
return 0;
}
Прежде всего обратите внимание — версия OnCreate() базового класса вызывается лишь в конце функции. Это сделано для того, чтобы при неудачной инициализации DirectInput программа выводила окно сообщения и прекращала работу без инициализации DirectDraw.
Сначала функция OnCreate() инициализирует указатель dinput с помощью функции DirectInputCreate(), которой необходимо передать четыре аргумента. Вызов этой функции выглядит так:
HRESULT r=DirectInputCreate(AfxGetInstanceHandle(), DIRECTINPUT_VERSION, &dinput, 0);
Первый аргумент - логический номер экземпляра приложения, получаемый функцией AfxGetInstanceHandle(). Второй аргумент — номер версии DirectInput. В нашем случае используется константа DIRECTINPUT_VERSION, она определяется DirectInput в зависимости от версии SDK, использованной для компиляции приложения. Различные версии DirectInput более подробно рассматриваются в этой главе ниже. Третий аргумент DirectInputCreate() — адрес инициализируемого указателя, а четвертый — показатель агрегирования COM, который обычно равен нулю (агрегированием называется разновидность наследования, используемая в COM). Если инициализация DirectInput проходит успешно (то есть если DirectInputCreate() возвращает DI_OK), указатель dinput может использоваться для работы с DirectInput.
Затем мы создаем экземпляр интерфейса DirectInputDevice, который представляет клавиатуру. Я снова приведу соответствующую строку листинга 6.2:
r = dinput->CreateDevice(GUID_SysKeyboard, &keyboard, 0);
Функция CreateDevice() интерфейса DirectInput применяется для инициализации устройств DirectInput. В нашем случае первым аргументом является стандартная константа GUID_SysKeyboard, показывающая, что мы собираемся работать с системной клавиатурой. Второй аргумент — адрес указателя keyboard, через который мы впоследствии будем обращаться к клавиатуре. Третий аргумент — показатель агрегирования COM, в нашем случае он должен быть равен нулю.
Следующий шаг — выбор формата данных устройства. Для клавиатуры он выполняется просто:
r = keyboard->SetDataFormat(&c_dfDIKeyboard);
Функции SetDataFormat() интерфейса DirectInputDevice передается единственный аргумент — константа стандартного формата c_dfDIKeyboard. Программа Qwerty работает лишь с одним устройством (клавиатурой), но, как мы убедимся в программе Smear, формат данных должен задаваться отдельно для каждого устройства, используемого программой.
Затем мы задаем уровень кооперации устройства с помощью функции SetCooperativeLevel() интерфейса DirectInputDevice. Соответствующий фрагмент листинга 6.2 выглядит так:
r=keyboard->SetCooperativeLevel(GetSafeHwnd(), DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
Функция SetCooperativeLevel() получает два аргумента: логический номер окна и набор флагов, определяющих уровень кооперации. Функция GetSafeHwnd() определяет логический номер окна, а флаги DISCL_FOREGROUND и DISCL_NONEXCLUSIVE задают нужный уровень кооперации. Флаг активного режима DISCL_FOREGROUND присутствует потому, что на время активности другого приложения нам не потребуется ввод от клавиатуры, а флаг DISCL_NONEXCLUSIVE — потому, что DirectInput не позволяет установить монопольный доступ к клавиатуре.
До получения данных с клавиатуры остался всего один шаг: мы должны захватить устройство функцией Acquire(). Эта задача решается функцией OnActivate(), которую мы рассмотрим ниже.
Функция QwertyWin::OnCreate() завершается вызовом функции DirectDrawWin::OnCreate(), инициализирующей DirectDraw. Эта функция обсуждалась в главе 3.
Захват клавиатуры
Итак, мы инициализировали DirectInput и подготовили клавиатуру к работе; теперь необходимо захватить ее. Для этой цели используется функция OnActivate(), потому что клавиатуру приходится захватывать при каждой активизации нашего приложения. Функция OnActivate() выглядит так:
void QwertyWin::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized) {
DirectDrawWin::OnActivate(nState, pWndOther, bMinimized);
if (nState!=WA_INACTIVE && keyboard) {
TRACE("keyboard->Acquire()n");
keyboard->Acquire();
}
}
После вызова версии OnActivate() базового класса мы проверяем, происходит ли активизация приложения (функция OnActivate() вызывается и в случае деактивизации, когда активным становится другое приложение). Если проверка дает положительный результат, мы вызываем функцию Acquire() интерфейса DirectInputDevice.
Перед вызовом Acquire() можно проверить, не была ли клавиатура захвачена ранее, но в этом нет необходимости. DirectInput игнорирует лишние вызовы функции Acquire().
Определение состояния клавиш
Теперь по указателю на интерфейс клавиатуры можно определить состояние отдельных клавиш. В нашей программе это происходит в функции DrawScene(), перед обновлением экрана. Функция DrawScene() приведена в листинге 6.3.
Листинг 6.3. Функция QwertyWin::DrawScene()
void QwertyWin::DrawScene() {
static char key[256];
keyboard->GetDeviceState(sizeof(key), &key);
//---------- Клавиши QWERTY --------
if (key[DIK_Q] & 0x80) BltSurface(backsurf, q_dn, 213, 70);
else BltSurface(backsurf, q_up, 213, 70);
if (key[DIK_W] & 0x80) BltSurface(backsurf, w_dn, 251, 70);
else BltSurface(backsurf, w_up, 251, 70);
if (key[DIK_E] & 0x80) BltSurface(backsurf, e_dn, 298, 70);
else BltSurface(backsurf, e_up, 298, 70);
if (key[DIK_R] & 0x80) BltSurface(backsurf, r_dn, 328, 70);
else BltSurface(backsurf, r_up, 328, 70);
if (key[DIK_T] & 0x80) BltSurface(backsurf, t_dn, 361, 70);
else BltSurface(backsurf, t_up, 361, 70);
if (key[DIK_Y] & 0x80) BltSurface(backsurf, y_dn, 393, 70);
else BltSurface(backsurf, y_up, 393, 70);
//---------------- LEFT CONTROL ---------------
if (key[DIK_LCONTROL] & 0x80) BltSurface(backsurf, lctrl_dn, 50, 180);
else BltSurface(backsurf, lctrl_up, 49, 180);
//---------------- RIGHT CONTROL ---------------
if (key[DIK_RCONTROL] & 0x80) BltSurface(backsurf, rctrl_dn, 490, 180);
else BltSurface(backsurf, rctrl_up, 490, 180);
//---------------- LEFT ALT ---------------
if (key[DIK_LMENU] & 0x80) BltSurface(backsurf, lalt_dn, 100, 260);
else BltSurface(backsurf, lalt_up, 100, 260);
//---------------- RIGHT ALT ---------------
if (key[DIK_RMENU] & 0x80) BltSurface(backsurf, ralt_dn, 440, 260);
else BltSurface(backsurf, ralt_up, 440, 260);
//---------------- SPACE -----------------
if (key[DIK_SPACE] & 0x80) BltSurface(backsurf, space_dn, 170, 340);
else BltSurface(backsurf, space_up, 170, 340);
//---------- ESCAPE -------------
if (key[DIK_ESCAPE] & 0x80) {
BltSurface(backsurf, esc_dn, 0, 0);
esc_pressed=TRUE;
} else {
BltSurface(backsurf, esc_up, 0, 0);
if (esc_pressed) PostMessage(WM_CLOSE);
}
primsurf->Flip(0, DDFLIP_WAIT);
}
Состояние устройства определяется функцией GetDeviceState() интерфейса DirectInputDevice. Тип и размер второго аргумента GetDeviceState() зависят от типа устройства, а также от формата данных, заданного функцией SetDataFormat(). Для клавиатуры функция должна получать массив из 256 байт, где каждый байт соответствует одной клавише. В DirectInput предусмотрен набор клавиатурных констант, которые используются как индексы массива и позволяют ссылаться на нужные клавиши. DirectInput обозначает нажатие клавиши установкой старшего бита того байта, который представляет данную клавишу. Объявление массива и вызов функции GetDeviceState() находятся в верхней части листинга 6.3, я снова привожу их:
static char key[256];
keyboard->GetDeviceState(sizeof(key), &key);
Адрес массива клавиш передается во втором аргументе GetDeviceState(). Первый аргумент определяет размер данных в байтах.
Все готово к проверке элементов массива. Сначала мы проверяем, была ли нажата клавиша Q:
if (key[DIK_Q] & 0x80) BltSurface(backsurf, q_dn, 213, 70);
else BltSurface(backsurf, q_up, 213, 70);
Константа DIK_Q определяет индекс клавиши Q в массиве. Мы проверяем значение старшего бита; если бит установлен, значит, клавиша Q нажата, и мы копируем поверхность, изображающую клавишу Q в нажатом состоянии (q_dn), функцией BltSurface(). Если клавиша не нажата, копируется поверхность q_up.
Обратите внимание: каждой клавише соответствует отдельный элемент массива, даже для функционально одинаковых клавиш. Например, две клавиши Alt обрабатываются по отдельности. Кроме того, DirectInput не отличает прописных букв от строчных. Чтобы учесть регистр буквы, придется дополнительно проверить состояние обеих клавиш Shift.
Оставшаяся часть функции состоит из аналогичных проверок состояния других клавиш. После того как все клавиши будут проверены, функция Flip() интерфейса DirectDrawSurface выводит новое изображение на экран.
Завершение приложения
Завершить работу DirectInput несложно — для этого достаточно освободить все интерфейсы DirectInput. В нашей программе это происходит в функции OnDestroy():
void QwertyWin::OnDestroy() {
DirectDrawWin::OnDestroy();
if (dinput) dinput->Release(), dinput=0;
if (keyboard) {
keyboard->Unacquire();
keyboard->Release(), keyboard=0;
}
}
- Глава 6. DirectInput
- DirectInput
- Initialising DirectInput
- Создание и инициализация семафоров
- Инициализация структур
- Установка размера файла, инициализация файла и разреженные файлы
- Инициализация: анализ бизнес-экосистем
- Инициализация Winsock
- Инициализация дескриптора безопасности
- Пример: инициализация атрибутов защиты
- Описание и инициализация указателя на структуру
- Инициализация DirectDraw