r=ddraw1->QueryInterface(IID_IDirectDraw2, (void**)&ddraw2);
if (r!=S_OK) {
AfxMessageBox('DirectDraw2 interface not supported');
return -1;
}
ddraw1->Release(), ddraw1=0;
ddraw2->SetCooperativeLevel(GetSafeHwnd(), DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX);
Сначала мы объявляем ddraw1, указатель на интерфейс DirectDraw. Это локальный и, следовательно, временный указатель. Класс DirectDrawWin объявляет ddraw2, указатель на интерфейс DirectDraw2, однако мы не сможем инициализировать его без интерфейса DirectDraw. Функция DirectDrawCreate() инициализирует указатель ddraw1. Первый аргумент является указателем на GUID выбранного драйвера. Адрес указателя ddraw1 передается в качестве второго аргумента. Последний аргумент DirectDrawCreate() должен быть равен 0.
Рис. 3.9. Диалоговое окно для выбора драйвера
После того как интерфейс DirectDraw будет инициализирован, им можно воспользоваться для получения указателя на интерфейс DirectDraw2. Для этого следует вызвать функцию QueryInterface() и передать ей GUID интерфейса DirectDraw2, определенный под именем IID_IDirectDraw2. Если вызов QueryInterface() заканчивается неудачно, программа выводит диалоговое окно и завершает работу. Фактически мы требуем присутствия библиотеки DirectX версии 2 и выше (потому что интерфейс DirectDraw2 впервые появился в DirectX 2). Если вызов QueryInterface() окажется успешным, указатель ddraw1 освобождается. Попеременный вызов функций интерфейсов DirectDraw и DirectDraw2 не рекомендуется, поэтому освобождение указателя на интерфейс DirectDraw гарантирует, что в оставшейся части кода будет использоваться только интерфейс DirectDraw2.
Затем мы вызываем функцию SetCooperativeLevel() и в качестве аргументов передаем ей логический номер нашего окна и три флага. По логическому номеру организуется взаимодействие окна с DirectDraw. При вызове SetCooperativeLevel() использованы три флага: DDSCL_EXCLUSIVE, DDSCL_FULLSCREEN и DDSCL_ALLOWMODEX. Флаги монопольного и полноэкранного режима обычно используются вместе для получения максимальных полномочий по управлению видеоустройством. Последний флаг означает, что все поддерживаемые видеорежимы Mode X должны быть доступны для выбора в программе. В Windows NT этот флаг игнорируется.
На следующем этапе необходимо определить все видеорежимы, поддерживаемые инициализированным драйвером DirectDraw. Для перечисления видеорежимов используется функция EnumDisplayModes(), аналогичная рассмотренной выше функции DirectDrawEnumerate (). В обоих случаях для перечисления используются косвенно вызываемые функции, а также предоставляются средства для передачи им данных, определяемых приложением. В нашем случае DisplayModeAvailable() является функцией косвенного вызова (callback function), а указатель this ссылается на произвольные данные. Функция DisplayModeAvailable() выглядит так:
HRESULT WINAPI DirectDrawWin::DisplayModeAvailable(LPDDSURFACEDESC desc, LPVOID p) {
DirectDrawWin* win=(DirectDrawWin*)p;
int& count=win->totaldisplaymodes;
if (count==MAXDISPLAYMODES) return DDENUMRET_CANCEL;
win->displaymode[count].width = desc->dwWidth;
win->displaymode[count].height = desc->dwHeight;
win->displaymode[count].depth = desc->ddpfPixelFormat.dwRGBBitCount;
count++;
return DDENUMRET_OK;
}
DirectDraw вызывает функцию DisplayModeAvailable() для каждого поддерживаемого видеорежима. Структура DDSURFACEDESC, передаваемая косвенно вызываемой функции, содержит описание обнаруженного видеорежима. Функция DisplayModeAvailable() сохраняет разрешение экрана и глубину пикселей в специальном массиве, называемом displaymode. В переменной total displaymodes хранится количество обнаруженных видеорежимов; если значение totaldisplaymodes достигает MAXDISPLAYMODES, перечисление завершается возвратом кода DDENUMRET_CANCEL.
Затем функция OnCreate() сортирует элементы displaymode так, чтобы режимы с низким разрешением находились в начале массива. Это делается с помощью функции Win32 qsort(), которой передается функция косвенного вызова для «сравнения» видеорежимов. В нашем случае используется функция CompareModes(), которая сравнивает видеорежимы сначала по разрешению, а затем по глубине пикселей. Я пропускаю дальнейшее обсуждение CompareModes(), потому что оно не имеет никакого отношения к DirectDraw.
На предыдущем этапе был подготовлен отсортированный список видеорежимов. Теперь мы выбираем один из этих режимов в качестве исходного. Класс DirectDrawWin заставляет производные классы принять это решение, объявляя чисто виртуальную функцию. Функция SelectInitialDisplayMode() из класса DirectDrawWin выглядит так:
virtual int SelectInitialDisplayMode() = 0;
В C++ чисто виртуальные функции обязательно должны переопределяться, в противном случае класс не будет компилироваться. Однако со стороны DirectDrawWin было бы нечестно требовать от производного класса выбора исходного видеорежима, не предоставляя ему средств для просмотра возможных вариантов (переменные класса, в которых хранятся сведения о видеорежимах, являются закрытыми (private)). Для этой цели в классе DirectDrawWin предусмотрены функции GetNumDisplayModes() и GetDisplayModeDimensions (). В версии SelectInitialDisplayMode() класса BounceWin эти функции используются для выбора исходного режима:
int BounceWin::SelectInitialDisplayMode() {
int i, nummodes=GetNumDisplayModes();
DWORD w,h,d;
for (i=0;i<nummodes;i++) {
GetDisplayModeDimensions(i, w, h, d);
if (w==desiredwidth && h==desiredheight && d==desireddepth) return i;
}
for (i=0;i>nummodes;i++) {
GetDisplayModeDimensions(i, w, h, d);
if (d==desireddepth) return i;
}
return 0;
}
Функция сначала определяет количество режимов функцией GetNumDisplayModes(), а затем в цикле пытается найти видеорежим с заданным разрешением и глубиной пикселей. Атрибуты каждого видеорежима извлекаются функцией GetDisplayModeDimensions(); если совпадение будет найдено, возвращается индекс видеорежима. В противном случае другой цикл ищет
На предпоследнем этапе происходит активизация выбранного режима. Для этого используется функция ActivateDisplayMode(), которая на самом деле выполняет и задачу последнего этапа (создание поверхностей приложения). Код этой функции приведен в листинге 3.2.
Листинг 3.2. Функция ActivateDisplayMode()
BOOL DirectDrawWin::ActivateDisplayMode(int mode) {
if (mode<0 || mode>=totaldisplaymodes) return FALSE;
DWORD width = displaymode[mode].width;
DWORD height = displaymode[mode].height;
DWORD depth = displaymode[mode].depth;
displayrect.left=0;
displayrect.top=0;
displayrect.right=width;
displayrect.bottom=height;
displaydepth=depth;