• Выбирать исходный видеорежим. При запуске приложения класс DirectDrawWin опознает все возможные видеорежимы. Производный класс должен выбрать видеорежим, который изначально устанавливается в приложении.
• Создавать и инициализировать все поверхности, необходимые для работы приложения, кроме первичной поверхности и вторичного буфера.
• Конструировать кадры, выполняя блиттинг содержимого поверхностей во вторичный буфер приложения, и отображать их посредством переключения страниц.
• Восстанавливать потерянные поверхности. Класс DirectDrawWin автоматически обнаруживает потерю поверхности и в должный момент предоставляет производным классам возможность ее восстановления. Первичная поверхность вместе со вторичным буфером восстанавливается классом DirectDrawWin.
Давайте перейдем к программному коду. Мы будем рассматривать его в хронологическом порядке, начиная с инициализации приложения. После этого мы займемся выводом и восстановлением потерянных поверхностей, а заодно изучим вспомогательные функции DirectDrawWin. Наконец, наше знакомство с приложением Bounce закончится анализом кода, завершающего работу приложения.
Перед тем как начинать работу, необходимо создать экземпляры всех основных классов приложения. В нашем случае это будут классы BounceWin и BounceApp. Объект приложения создается способом, традиционным для MFC, то есть объявлением глобального экземпляра:
BounceApp theapp;
Класс BounceApp наследует свои функциональные возможности от DirectDrawApp, и больше ему почти ничего не требуется. Есть всего одно исключение: класс BounceApp отвечает за создание объекта BounceWin. Это происходит в функции InitInstance(), вызываемой MFC при запуске приложения. Функция InitInstance() выглядит так:
BOOL BounceApp::InitInstance() {
BounceWin* win=new BounceWin;
if (!win->Create('High Performance Bounce Demo', IDI_ICON)) {
AfxMessageBox('Failed to create window');
return FALSE;
}
m_pMainWnd=win;
return DirectDrawApp::InitInstance();
}
Функция InitInstance() создает экземпляр класса BounceWin и вызывает функцию BounceWin::Create (). При вызове Create() необходимо передать два аргумента: строку с названием окна и идентификатор ресурса значка. Хотя название окна не отображается во время работы приложения (потому что приложение занимает весь экран и не имеет строки заголовка), оно будет выводиться в списке задач, а также на панели задач при сворачивании приложения. Если вызов Create() закончится неудачей, то функция InitInstance() возвращает FALSE. По этому признаку MFC узнает о том, что приложение следует аварийно завершить.
Затем переменная m_pMainWnd инициализируется указателем на созданный объект окна. Эта переменная принадлежит классу CWinApp; инициализируя ее, вы сообщаете классу CWinApp о том, каким объектом окна он будет управлять. Если m_pMainWnd не будет присвоен указатель на окно, MFC завершает приложение с ошибкой.
Наконец, мы вызываем функцию DirectDrawApp:InitInstance() и используем полученное от нее значение в качестве результата функции BounceApp::InitInstance(). Функция InitInstance() класса DirectDrawApp выглядит так:
BOOL DirectDrawApp::InitInstance() {
ASSERT(m_pMainWnd);
m_pMainWnd->ShowWindow(SW_SHOWNORMAL);
m_pMainWnd->UpdateWindow();
ShowCursor(FALSE);
return TRUE;
}
Я уже упоминал о том, что MFC требует задать значение переменной m_pMainWnd, но поскольку значение m_pMainWnd используется в этой функции, проверку можно выполнить и самостоятельно. Макрос MFC ASSERT() проверяет значение переменной m_pMainWnd. Если указатель равен нулю, приложение завершается с ошибкой. Если он отличен от нуля, мы вызываем две функции созданного окна: ShowWindow() и UpdateWindow(). Эти функции отображают окно на экране. Наконец, функция ShowCursor() отключает курсор мыши.
Создание и отображение окна завершает процесс инициализации классов DirectDrawApp и BounceApp. Теперь давайте посмотрим, как этот процесс отражается на классах DirectDrawWin и BounceWin.
Как мы уже знаем, функция Create() вызывается из функции BounceApp:: InitInstance(). Она не реализуется классом BounceWin, а наследуется от DirectDrawWin. Функция Create() выглядит так:
BOOL DirectDrawWin::Create(const CString& title,int icon) {
CString sClassName;
sClassName = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW, LoadCursor(0, IDC_ARROW), (HBRUSH)(COLOR_WINDOW + 1), LoadIcon (AfxGetInstanceHandle(), MAKEINTRESOURCE(icon)));
return CWnd::CreateEx(WS_EX_TOPMOST, sClassName, title, WS_POPUP, 0, 0, 100, 100, 0, 0);
}
Сначала функция Create() регистрирует класс окна с помощью функции AfxRegisterWndClass(). Затем она вызывает функцию CreateEx(), в которой и происходит фактическое создание окна.
Обратите внимание на то, что создаваемое окно имеет размеры 100x100 (седьмой и восьмой аргументы CreateEx()). Такой размер выбран произвольно. DirectDraw при подключении окна автоматически изменяет его размер так, чтобы оно занимало весь экран. Также обратите внимание на флаг WS_EX_TOPMOST: окно полноэкранного приложения DirectDraw должно выводиться поверх остальных окон.
Атрибут верхнего окна, а также занятие им всего экрана необходимы для того, чтобы механизм GDI не смог ничего вывести на экран. GDI ничего не знает о DirectDraw, поэтому наше окно «обманывает» GDI на то время, пока весь экран находится под управлением DirectDraw. Вообще говоря, вывод средствами GDI может происходить и в полноэкранном режиме, но обычно это не рекомендуется, потому что вывод GDI может попасть на невидимую поверхность. Эта тема более подробно рассматривается в главе 5.
Инициализация DirectDraw
Фактическое создание окна (вызов функции CreateEx()) заставляет Windows послать нашему приложению сообщение WM_CREATE. Класс DirectDrawWin перехватывает это сообщение в обработчике OnCreate(), созданном ClassWizard (см. листинг 3.1).
Листинг 3.1. Функция DirectDrawWin::OnCreate()
int DirectDrawWin::OnCreate(LPCREATESTRUCT) {
DirectDrawEnumerate(DriverAvailable, this);
if (totaldrivers==0) {
AfxMessageBox('No DirectDraw drivers detected');
return -1;
}
int driverindex=SelectDriver();
if (driverindex<0) {
TRACE('No DirectDraw driver selected
');
return -1;
} else if (driverindex>totaldrivers-1) {
AfxMessageBox('Invalid DirectDraw driver selected
');
return -1;
}
LPDIRECTDRAW ddraw1;
DirectDrawCreate(driver[driverindex].guid, &ddraw1, 0);
HRESULT r;
r=ddraw1->QueryInterface(IID_IDirectDraw2, (void**)&ddraw2);
if (r!=S_OK) {
AfxMessageBox('DirectDraw2 interface not supported');