}
break;
}
} else buffer_empty=TRUE;
}
if (curx<0) curx=0;
if (cury<0) cury=0;
if (curx>=screen_width-cursor_width) curx=screen_width-cursor_width-1;
if (cury>=screen_height-cursor_height) cury=screen_height-cursor_height-1;
if (curx==oldcurx && cury==oldcury) {
//----- обновление курсора не требуется ------
goto nevermind;
} else if (abs(curx-oldcurx) >= cursor_width || abs(cury-oldcury) >= cursor_height) {
//----- простой случай: прямоугольники нового
// и старого курсора не перекрываются -----
win->UpdateCursorSimpleCase(curx, cury, oldcurx, oldcury);
} else {
//----- сложный случай: прямоугольники нового
// и старого курсора перекрываются -----
win->UpdateCursorComplexCase(curx, cury, oldcurx, oldcury);
}
nevermind:;
critsection.Unlock();
}
TRACE('leaving mouse thread
');
return 0;
};
Функция MouseThread() имеет один параметр — значение, передаваемое функции AfxBeginThread() при создании потока (см. листинг 7.3). Мы передавали указатель this, поэтому сейчас сможем присвоить его значение указателю на класс CursorWin (переменная win). В функции MouseThread() указатель win будет использоваться для доступа к членам класса CursorWin.
Функция MouseThread() в цикле выполняет блокировку по двум событиям. Класс CMultiLock позволяет блокироваться как по событиям от мыши, так и по событию завершения потока. Фактическая блокировка выполняется функцией CMultiLock::Lock (). По умолчанию функция Lock() блокирует поток до установки всех (в данном случае - двух) заданных событий. Мы изменяем это поведение и передаем FALSE в качестве второго аргумента Lock(), показывая тем самым, что функция должна снимать блокировку при установке хотя бы одного из этих событий.
Когда любое из двух событий переходит в установленное состояние, функция Lock() завершается, и мы проверяем код возврата. Если выясняется, что было установлено событие завершения потока (обозначенное константой quit_event_index), мы выходим из функции MouseThread(), тем самым завершая поток. В противном случае активизация потока вызвана событием мыши, поэтому мы переходим к обработке новых данных.
Однако сначала необходимо захватить критическую секцию с помощью объекта critsection. Для получения данных нам придется обращаться к очереди событий от кнопок мыши и к первичной поверхности, поэтому выполнение этого кода следует синхронизировать с основным потоком.
Мы в цикле получаем данные от объекта DirectInputDevice, представляющего мышь, с помощью функции GetDeviceData (). Если получены данные о перемещении мыши, происходит обновление переменных curx и cury. Если получены данные о нажатии кнопок, они заносятся в очередь событий.
Когда цикл получения данных завершается (поскольку в буфере не остается элементов), мы проверяем переменные curx и cury и убеждаемся, что курсор не вышел за пределы экрана (вместо того чтобы писать код частичного отсечения курсора, мы выбираем простой путь и требуем, чтобы курсор всегда полностью оставался на экране).
Наконец, мы проверяем новое положение курсора. Если перемещение курсора не обнаружено, критическая секция освобождается, а объект CMultiLock снова используется для блокировки по обоим событиям. Если курсор переместился в другое положение, мы вызываем одну из двух функций обновления курсора в зависимости от того, перекрывается ли старая область курсора с новой. Если области перекрываются, вызывается функция UpdateCursorComplexCase(); в противном случае вызывается функция UpdateCursorSimpleCase().
Начнем с более простой функции UpdateCursorSimpleCase() (см. листинг 7.6).
Листинг 7.6. Функция UpdateCursorSimpleCase()
BOOL CursorWin::UpdateCursorSimpleCase(int curx, int cury, int oldcurx, int oldcury) {
RECT src;
HRESULT r;
//------ Блиттинг 1: стирание старого курсора ----------
r=primsurf->BltFast(oldcurx, oldcury, cursor_under, 0, DDBLTFAST_WAIT);
if (r!=DD_OK) {
TRACE('Blt 1 failed
');
CheckResult(r);
}
//------ Блиттинг 2: сохранение области под новым курсором ------
src.left=curx;
src.top=cury;
src.right=curx+cursor_width;
src.bottom=cury+cursor_height;
r=cursor_under->BltFast(0, 0, primsurf, &src, DDBLTFAST_WAIT);
if (r!=DD_OK) {
TRACE('Blt 2 failed
');
CheckResult(r);
}
//------ Блиттинг 3: рисование нового курсора ----------
r=primsurf->BltFast(curx, cury, cursor, 0, DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT);
if (r!=DD_OK) {
TRACE('Blt 3 failed
');
CheckResult(r);
}
return TRUE;
}
С помощью трех последовательных вызовов функции BltFast() интерфейса DirectDrawSurface, функция UpdateCursorSimpleCase() стирает существующий курсор, сохраняет область под новым курсором и рисует новый курсор.
В UpdateCursorComplexCase() функция BltFast() вызывается пять раз. Два дополнительных блиттинга предназначены для копирования обновляемой части первичной поверхности на вспомогательную поверхность (cursor_union) и обратно. Функция UpdateCursorComplexCase() приведена в листинге 7.7.
Листинг 7.7. Функция UpdateCursorComplexCase()
BOOL CursorWin::UpdateCursorComplexCase(int curx, int cury, int oldcurx, int oldcury) {
RECT src;
HRESULT r;
int unionx=min(curx, oldcurx);
int uniony=min(cury, oldcury);
int unionw=max(curx, oldcurx)-unionx+cursor_width;
int unionh=max(cury, oldcury)-uniony+cursor_height;
//----- Блиттинг 1: копирование объединяющего прямоугольника
// во вспомогательный буфер --------