Шрифт:
Интервал:
Закладка:
Листинг 6.1. Класс QwertyWin
class QwertyWin : public DirectDrawWin {
public:
QwertyWin();
protected:
//{{AFX_MSG(QwertyWin)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnDestroy();
afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
private:
int SelectDriver();
int SelectInitialDisplayMode();
BOOL CreateCustomSurfaces();
void DrawScene();
void RestoreSurfaces();
private:
LPDIRECTINPUT dinput;
LPDIRECTINPUTDEVICE keyboard;
BOOL esc_pressed;
LPDIRECTDRAWSURFACE esc_up, esc_dn;
LPDIRECTDRAWSURFACE space_up, space_dn;
LPDIRECTDRAWSURFACE q_up, q_dn;
LPDIRECTDRAWSURFACE w_up, w_dn;
LPDIRECTDRAWSURFACE e_up, e_dn;
LPDIRECTDRAWSURFACE r_up, r_dn;
LPDIRECTDRAWSURFACE t_up, t_dn;
LPDIRECTDRAWSURFACE y_up, y_dn;
LPDIRECTDRAWSURFACE rctrl_up, rctrl_dn;
LPDIRECTDRAWSURFACE lctrl_up, lctrl_dn;
LPDIRECTDRAWSURFACE lalt_up, lalt_dn;
LPDIRECTDRAWSURFACE ralt_up, ralt_dn;
};
Прежде чем двигаться дальше, обратите внимание на отсутствие обработчика OnKeyDown(). Во всех программах, рассмотренных нами ранее, функция OnKeyDown() обрабатывала сообщения от клавиатуры. В программе Qwerty мы пользуемся услугами DirectInput и потому не нуждаемся в OnKeyDown().
В самом начале объявляются три обработчика сообщений:
• OnCreate()
• OnDestroy()
• OnActivate()
Функция OnCreate() инициализирует и настраивает DirectInput, а функция OnDestroy() освобождает объекты DirectInput. Функция OnActivate(), вызываемая MFC при получении или потере фокуса, будет использована для повторного захвата клавиатуры.
Две следующие функции, SelectDriver() и SelectInitialDisplayMode(), присутствуют почти во всех наших программах. Они остались в том виде, в котором их создал AppWizard, и потому не требуют обсуждения.
Функции CreateCustomSurfaces() и RestoreSurfaces() делают то же, что и раньше, так что они тоже не рассматриваются. Достаточно сказать, что эти функции инициализируют и восстанавливают поверхности, указатели на которые объявляются в нижней части листинга 6.1.
Функция DrawScene() с помощью DirectInput определяет, какие клавиши были нажаты, и обеспечивает соответствующий вывод. Вскоре мы рассмотрим эту функцию.
После функций следуют переменные класса. Сначала объявляется указатель на интерфейс DirectInput(dinput), через него выполняется инициализация и осуществляются обращения к DirectInput. Переменная key — указатель на интерфейс DirectInputDevice, используемый для обращений к клавиатуре. Логическая переменная esc_pressed сигнализирует о завершении приложения.
Оставшаяся часть определения класса состоит из указателей на интерфейсы DirectDrawSurface. Для каждой клавиши, поддерживаемой приложением, создаются две поверхности (для нажатого и отпущенного состояния).
Инициализация 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.
- Microsoft Visual C++ и MFC. Программирование для Windows 95 и Windows NT. Часть 2 - Александр Фролов - Программирование
- C# для профессионалов. Том II - Симон Робинсон - Программирование
- QT 4: программирование GUI на С++ - Жасмин Бланшет - Программирование
- C# 4.0: полное руководство - Герберт Шилдт - Программирование
- Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен - Программирование
- Сделай видеоигру один и не свихнись - Слава Грис - Программирование / Руководства
- Творческий отбор. Как создавались лучшие продукты Apple во времена Стива Джобса - Кен Косиенда - Прочая околокомпьтерная литература / Интернет / Программирование
- От «Энигмы» до ChatGPT - Рустам Агамалиев - Программирование / Экономика
- Программирование игр и головоломок - Жак Арсак - Программирование
- Разработка ядра Linux - Роберт Лав - Программирование