Шрифт:
Интервал:
Закладка:
Перед тем как получать данные от клавиатуры или мыши, необходимо предварительно захватить их. Кроме того, устройство приходится захватывать заново, если приложение некоторое время было неактивным. И исходный, и все последующие захваты устройства выполняются в обработчике OnActivate():
void SmearWin::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized) {
DirectDrawWin::OnActivate(nState, pWndOther, bMinimized);
if (nState!=WA_INACTIVE) {
if (keyboard) {
TRACE("keyboard->Acquire()n");
keyboard->Acquire();
}
if (mouse) {
TRACE("mouse->Acquire()n");
mouse->Acquire();
}
}
}
Функция Acquire() вызывается для каждого устройства независимо от того, уступалось ли оно. DirectInput игнорирует лишние вызовы Acquire().
Получение данных от мышиХлопоты с инициализацией мыши и клавиатуры закончены, теперь можно получать от них данные. Функция DrawScene() (см. листинг 6.6) через указатели mouse и keyboard обращается к обоим устройствам и получает от них данные.
Листинг 6.6. Функция SmearWin::DrawScene()
void SmearWin::DrawScene() {
static char key[256];
keyboard->GetDeviceState(sizeof(key), &key);
if (key[DIK_ESCAPE] & 0x80) PostMessage(WM_CLOSE);
BOOL done=FALSE;
while (!done) {
DIDEVICEOBJECTDATA data;
DWORD elements=1;
HRESULT r=mouse->GetDeviceData(sizeof(data), &data, &elements, 0);
if (r==DI_OK && elements==1) {
switch(data.dwOfs) {
case DIMOFS_X:
x+=data.dwData;
break;
case DIMOFS_Y:
y+=data.dwData;
break;
}
} else if (elements==0) done=TRUE;
}
BltSurface(primsurf, sphere, x, y, TRUE);
}
Функция DrawScene() сначала проверяет состояние клавиатуры функцией GetDeviceState() интерфейса DirectInputDevice. Если была нажата клавиша Escape, она посылает сообщение WM_CLOSE, что приводит к завершению приложения. О функции GetDeviceState() и проверке состояния клавиш рассказано в программе Qwerty, поэтому сейчас мы займемся кодом, относящимся к мыши. DrawScene() в цикле извлекает элементы буфера мыши. Для получения данных, а также для проверки отсутствия элементов при пустом буфере используется функция GetDeviceData() интерфейса DirectInputDevice.
Каждый элемент буфера представлен структурой DIDEVICEOBJECTDATA. Эта структура используется независимо от типа устройства, поэтому ее поля были сделаны универсальными. DirectInput определяет структуру DIDEVICEOBJECTDATA следующим образом:
typedef struct {
DWORD dwOfs;
DWORD dwData;
DWORD dwTimeStamp;
DWORD dwSequence;
} DIDEVICEOBJECTDATA, *LPDIDEVICEOBJECTDATA;
Для мыши поле dwOfs определяет тип события. В DirectInput определены следующие константы, описывающие ввод от мыши:
• DIMOFS_X
• DIMOFS_Y
• DIMOFS_Z
• DIMOFS_BUTTON0
• DIMOFS_BUTTON1
• DIMOFS_BUTTON2
• DIMOFS_BUTTON3
Программа Smear реагирует только на перемещение мыши по осям x и y, поэтому после вызова функции GetDeviceData() поле dwOfs сравнивается с константами DIMOFS_X и DIMOFS_Y.
Поле dwData определяет новые значения осевых координат и кнопок. Поскольку мы используем относительные значения, содержимое этого поля равно смещению по данной оси с момента получения последних данных. Следовательно, оно может быть и отрицательным. Поле dwData используется для обновления переменных x и y.
Поля dwTimeStamp и dwSequence содержат информацию о том, когда произошло данное событие. Поле dwTimeStamp определяет время в миллисекундах (о том, как интерпретируется эта величина, можно подробно узнать в описании функции Win32 GetTickCount()). Поле dwSequence определяет порядок наступления событий. События с меньшими номерами наступили раньше, однако несколько событий могут иметь одинаковые порядковые номера. Например, если мышь или рукоять джойстика смещается по диагонали, события для координат x и y будут иметь одинаковые номера.
Вернемся к функции DrawScene(). Цикл ввода извлекает элементы буфера до тех пор, пока буфер не опустеет. Этот цикл выглядит так:
while (!done) {
DIDEVICEOBJECTDATA data;
DWORD elements=1;
HRESULT r=mouse->GetDeviceData(sizeof(data), &data, &elements, 0);
if (r==DI_OK && elements==1) {
switch (data.dwOfs) {
case DIMOFS_X:
x+=data.dwData;
break;
case DIMOFS_Y:
y+=data.dwData;
break;
}
} else if (elements==0) done=TRUE;
}
Третий аргумент GetDeviceData() используется двояко. Значение, передаваемое функции, определяет количество элементов, извлекаемых из буфера. В нашем случае используется всего одна структура DIDEVICEOBJECTDATA, поэтому передается число 1. При возврате из функции это значение показывает количество полученных элементов.
Если вызов функции прошел успешно и значение elements осталось равным 1, значит, элемент буфера был прочитан, а поля dwOfs и dwData определяют тип события. Нулевое значение elements говорит о том, что буфер пуст и цикл завершается.
После извлечения всех элементов буфера остается лишь вывести поверхность в позиции, определяемой переменными x и y. Для этого используется функция BltSurface():
BltSurface(primsurf, sphere, x, y, TRUE);
Обратите внимание: поверхность изображения копируется непосредственно на первичную поверхность, как говорилось при обсуждении структуры программы Smear.
Завершение приложенияПеред завершением приложения MFC вызывает функцию OnDestroy(); мы воспользуемся ею для освобождения объектов DirectInput. Функция OnDestroy() выглядит так:
void SmearWin::OnDestroy() {
DirectDrawWin::OnDestroy();
if (dinput) dinput->Release(), dinput=0;
if (keyboard) {
keyboard->Unacquire();
keyboard->Release(), keyboard=0;
}
if (mouse) {
mouse->Unacquire();
mouse->Release(), mouse=0;
}
}
Функция OnDestroy() просто освобождает каждый объект DirectInput (и вызывает одноименную функцию базового класса).
ЗаключениеВ этой главе мы узнали, как организовать в своих приложениях поддержку DirectInput и обойти традиционные механизмы ввода Windows. Такое решение повышает быстродействие и дает больше возможностей для обработки пользовательского ввода. По этим причинам DirectInput будет использоваться во всех оставшихся программах этой книги.
В следующей главе мы займемся одной неприятной проблемой, связанной с курсором мыши. Как вы вскоре убедитесь, DirectInput станет неотъемлемой частью ее решения.
Глава 7. Проблема курсора
Если вы пытались работать с мышью в полноэкранном приложении DirectDraw, скорее всего, проблема с курсором вам уже знакома. Ввод от мыши нетрудно получить и использовать в программе, пока не приходится отображать курсор на экране. Но попробуйте-ка вывести стандартный курсор Windows — и проблема заявит о себе.
Первая трудность связана с переключением страниц. Если курсор мыши не был отключен, Windows не подозревает о том, что видеоустройства находятся под управлением DirectDraw, и выводит курсор мыши на поверхности GDI. Если поверхность GDI скрыта, а вместо нее на экране отображается другая поверхность, курсор исчезает. Эта переменчивость приводит к тому, что курсор мыши мерцает и выглядит полупрозрачным.
Более того, курсор может оказаться искаженным или вообще отсутствовать. Например, в видеорежимах Mode X используется нелинейная организация пикселей. Windows пытается вывести курсор мыши, но поскольку режимы Mode X не поддерживаются Windows GDI, изображение курсора портится. Кроме того, Windows не умеет выводить курсор мыши на вторичных видеоустройствах (например, на видеокартах с чипами 3Dfx). Вывод продолжает поступать на первичное видеоустройство независимо от того, какое устройство активно в данный момент, поэтому курсор мыши пропадает.
Значит, если полноэкранное приложение захочет вывести курсор мыши, ему придется делать это самостоятельно. На первый взгляд все просто: нужно создать небольшую поверхность с изображением курсора и перемещать ее в соответствии с вводом от мыши. Такое решение работает, но у него есть свои недостатки.
Раз курсор мыши отображается самим приложением, а не Windows, частота его прорисовки зависит от быстродействия приложения. Если приложение постоянно работает на 75 FPS, это будет приемлемо, но что если частота вывода кадров упадет до 30 FPS и ниже? С падением частоты курсор будет все медленнее реагировать на действия пользователя.
- Microsoft Visual C++ и MFC. Программирование для Windows 95 и Windows NT. Часть 2 - Александр Фролов - Программирование
- C# для профессионалов. Том II - Симон Робинсон - Программирование
- QT 4: программирование GUI на С++ - Жасмин Бланшет - Программирование
- C# 4.0: полное руководство - Герберт Шилдт - Программирование
- Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен - Программирование
- Сделай видеоигру один и не свихнись - Слава Грис - Программирование / Руководства
- Творческий отбор. Как создавались лучшие продукты Apple во времена Стива Джобса - Кен Косиенда - Прочая околокомпьтерная литература / Интернет / Программирование
- От «Энигмы» до ChatGPT - Рустам Агамалиев - Программирование / Экономика
- Программирование игр и головоломок - Жак Арсак - Программирование
- Разработка ядра Linux - Роберт Лав - Программирование