Наше знакомство с программой Switch начинается с конструктора switchWin, внутри которого происходит первоначальная инициализация переменных класса. Не следует путать эту инициализацию с той, что выполняется функцией CreateCustomSurfaces(), потому что в отличие конструктора CreateCustomSurfaces() вызывается при каждой смене видеорежима. Конструктор выглядит так:
| SwitchWin::SwitchWin() { bmpsurf=0; x=y=0; xinc=8; yinc=1; menusurf=0; fpssurf=0;
vlargefont = CreateFont( 28, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, VARIABLE_PITCH, "Arial" ); smallfont = CreateFont( 14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, VARIABLE_PITCH, "Arial" ); } |
После того как объект SwitchWin будет создан, DirectDrawWin вызывает функции SelectDriver() и SelectInitialDisplayMode(). Поскольку в программе Switch обе функции ведут себя стандартным образом (как описано в главе 3), мы не будем их рассматривать.
Затем класс DirectDrawWin вызывает функцию SwitchWin::CreateCustomSurfaces(), в которой подготавливает три поверхности, используемые программой Switch:
| BOOL SwitchWin::CreateCustomSurfaces() { int displaydepth=GetCurDisplayDepth(); CString filename; if (displaydepth==8) filename="tri08.bmp"; else filename="tri24.bmp";
bmpsurf=CreateSurface( filename, TRUE ); if (bmpsurf==0) { TRACE("surface creation failed\n"); return FALSE; } selectmode=GetCurDisplayMode(); CreateMenuSurface(); UpdateMenuSurface(); CreateFPSSurface(); return TRUE; } |
Как упоминалось выше, перед инициализацией DirectDraw программа SuperSwitch выводит в функции SuperSwitchWin::OnCreate() диалоговое окно. После вывода диалогового окна функция вызывает версию OnCreate() класса DirectDrawWin. Код функции SuperSwitchWin::OnCreate() выглядит так:
| int SuperSwitchWin::OnCreate(LPCREATESTRUCT lpCreateStruct) { IntroDialog introdialog; if (introdialog.DoModal()!=IDOK) return -1;
include_refresh=introdialog.include_refresh; if (DirectDrawWin::OnCreate(lpCreateStruct)==-1) return -1; if (include_refresh) ddraw2->EnumDisplayModes( DDEDM_REFRESHRATES, 0, this, StoreModeInfo ); return 0; } |
Теперь мы вызываем версию OnCreate() класса DirectDrawWin, где и происходит инициализация DirectDraw. Функция составляет список видеорежимов, активизирует исходный режим и создает поверхности приложения. Если вызов функции OnCreate() завершается неудачей, мы завершаем приложение, возвращая код –1.
Следующий шаг - повторное составление списка видеорежимов. На этот раз при вызове функции EnumDisplayModes() в первом аргументе передается флаг DDEDM_REFRESHRATES, согласно которому каждый видеорежим должен быть включен в список по одному разу для каждой поддерживаемой частоты. В результате мы сможем построить список частот для каждого видеорежима. Четвертый аргумент EnumDisplayModes() — функция косвенного вызова StoreModeInfo(), которая выглядит так:
| HRESULT WINAPI SuperSwitchWin::StoreModeInfo( LPDDSURFACEDESC desc, LPVOID p) { DWORD w=desc->dwWidth; DWORD h=desc->dwHeight; DWORD d=desc->ddpfPixelFormat.dwRGBBitCount; DWORD r=desc->dwRefreshRate;
SuperSwitchWin* win=(SuperSwitchWin*)p; int index=win->GetDisplayModeIndex( w, h, d ); win->refresh_rates[index].Add(r); return DDENUMRET_OK; } |
После выхода из функции OnCreate() класс DirectDrawWin вызывает функцию CreateCustomSurfaces(). По сравнению с программой Switch эта функция не изменилась; она по-прежнему создает три поверхности, потому что новая поверхность (ratemenusurface) создается только в случае необходимости.
Перед тем как инициализировать DirectDraw, класс DirectDrawWin вызывает функцию SelectDriver(), чтобы производные классы могли выбрать драйвер DirectDraw при наличии нескольких вариантов. В программе BmpView мы отказываемся от этой возможности и позволяем выбрать первичный драйвер по умолчанию. Это сделано потому, что для вывода диалоговых окон используется механизм GDI, а GDI может выводить только на первичное видеоустройство (которому соответствует первичный драйвер DirectDraw).
Следующим этапом инициализации приложения является вызов функции SelectInitialDisplayMode(), которую мы обязаны переопределить. Наша версия SelectInitialDisplayMode() выбирает видеорежим с параметрами 640x480x16. Исходный видеорежим не так уж важен, потому что он, скорее всего, будет переопределен пользователем при выборе BMP-файла. Однако функция SelectInitialDisplayMode() (см. листинг 5.6) выполняет две дополнительные задачи.
Листинг 5.6. Функция BmpViewWin::SelectInitialDisplayMode()
| int BmpViewWin::SelectInitialDisplayMode() { DisplayModeDescription desc; int i, nummodes=GetNumDisplayModes(); DWORD w,h,d;
for (i=0;i<nummodes;i++) { GetDisplayModeDimensions( i, w, h, d ); desc.w=w; desc.h=h; desc.d=d; desc.desc.Format("%dx%dx%d", w, h, d ); if ( d==8 ) palettemode.Add( desc ); else nonpalettemode.Add( desc ); } DWORD curdepth=GetDisplayDepth(); for (i=0;i<nummodes;i++) { GetDisplayModeDimensions( i, w, h, d ); if (w==640 && h==480 && d==curdepth) return i; } for (i=0;i<nummodes;i++) { GetDisplayModeDimensions( i, w, h, d ); if (d==curdepth) return i; } for (i=0;i<nummodes;i++) { GetDisplayModeDimensions( i, w, h, d ); if (w==640 && h==480) return i; } GetSystemPalette(); return 0; } |
Наше знакомство с программой Cursor начинается с функции OnCreate(), которая отвечает за инициализацию DirectDraw, DirectInput и потока ввода. Функция OnCreate() приведена в листинге 7.2.
Листинг 7.2. Функция CursorWin::OnCreate()
| int CursorWin::OnCreate(LPCREATESTRUCT lpCreateStruct) { HRESULT r=DirectInputCreate( AfxGetInstanceHandle(), DIRECTINPUT_VERSION, &dinput, 0 ); if (r!=DI_OK) { AfxMessageBox("DirectInputCreate() failed"); return -1; }
if (InitMouse()==FALSE) return -1; if (InitKeyboard()==FALSE) return -1; if (DirectDrawWin::OnCreate(lpCreateStruct) == -1) return -1; mousethread->ResumeThread(); return 0; } |
Давайте рассмотрим функцию InitMouse(), чтобы получить общее представление об инициализации мыши и создании потока ввода. Функция InitMouse() приведена в листинге 7.3.
Листинг 7.3. Функция InitMouse()
| BOOL CursorWin::InitMouse() { HRESULT r; r = dinput->CreateDevice( GUID_SysMouse, &mouse, 0 ); if (r!=DI_OK) { TRACE("CreateDevice(mouse) failed\n"); return FALSE; }
r = mouse->SetDataFormat( &c_dfDIMouse ); if (r!=DI_OK) { TRACE("mouse->SetDataFormat() failed\n"); return FALSE; } r = mouse->SetCooperativeLevel( GetSafeHwnd(), DISCL_NONEXCLUSIVE | DISCL_FOREGROUND ); if (r!=DI_OK) { TRACE("mouse->SetCooperativeLevel() failed\n"); return FALSE; } DIPROPDWORD property; property.diph.dwSize=sizeof(DIPROPDWORD); property.diph.dwHeaderSize=sizeof(DIPROPHEADER); property.diph.dwObj=0; property.diph.dwHow=DIPH_DEVICE; property.dwData=64; r = mouse->SetProperty( DIPROP_BUFFERSIZE, &property.diph ); if (r!=DI_OK) { TRACE("mouse->SetProperty() failed (buffersize)\n"); return FALSE; } mouse_event[mouse_event_index]=new CEvent; mouse_event[quit_event_index]=new CEvent; r = mouse->SetEventNotification( *mouse_event[mouse_event_index] ); if (r!=DI_OK) { TRACE("mouse->SetEventNotification() failed\n"); return FALSE; } mousethread=AfxBeginThread( (AFX_THREADPROC)MouseThread, this, THREAD_PRIORITY_TIME_CRITICAL, 0, CREATE_SUSPENDED ); return TRUE; } |
При запуске программы Bumper прежде всего вызывается функция SelectDriver(). Чтобы добиться максимальной гибкости, при наличии нескольких драйверов DirectDraw программа Bumper выводит меню. Функция SelectDriver() выглядит так:
| int BumperWin::SelectDriver() { int numdrivers=GetNumDrivers(); if (numdrivers==1) return 0; CArray<CString, CString> drivers; for (int i=0;i<numdrivers;i++) { LPSTR desc, name; GetDriverInfo( i, 0, &desc, &name ); drivers.Add(desc); }
DriverDialog dialog; dialog.SetContents( &drivers ); if (dialog.DoModal()!=IDOK) return -1; return dialog.GetSelection(); } |
программа выводит меню со списком драйверов и использует драйвер, выбранный пользователем. Наши функции проверки столкновений предназначены только для 8-битных поверхностей, поэтому драйверы, не поддерживающие 8-битных видеорежимов (скажем, драйверы 3Dfx), в этой программе не работают. Следовательно, функция SelectInitialDisplayMode() должна правильно реагировать на выбор такого драйвера.
Функция SelectInitialDisplayMode() вызывается после функции SelectDriver(), но перед созданием поверхностей. Функция выглядит так:
| int BumperWin::SelectInitialDisplayMode() { DWORD curdepth=GetDisplayDepth(); int i, nummodes=GetNumDisplayModes(); DWORD w,h,d; if (curdepth!=desireddepth) ddraw2->SetDisplayMode( 640, 480, curdepth, 0, 0 );
for (i=0;i<nummodes;i++) { GetDisplayModeDimensions( i, w, h, d ); if (w==desiredwidth && h==desiredheight && d==desireddepth) return i; } ddraw2->RestoreDisplayMode(); ddraw2->Release(), ddraw2=0; AfxMessageBox("Can't find 8-bit mode on this device"); return -1; } |
ищет конкретный видеорежим 640x480x8. Если этот режим не найден, она выводит сообщение и возвращает –1, говоря тем самым классу DirectDrawWin о том, что приложение следует завершить. Если режим будет найден, функция возвращает его индекс. По этому индексу класс DirectDrawWin узнает о том, какой видеорежим следует активизировать.