1 //-----------------------------------------------------------------------------
4 // Desc: Class enumerate through the DirectDraw drivers, Direct3D devices,
5 // and the display modes available to each device.
8 // Copyright (c) 1997-1998 Microsoft Corporation. All rights reserved
9 //-----------------------------------------------------------------------------
17 DECLARE_HANDLE(HMONITOR);
26 //-----------------------------------------------------------------------------
27 // Constants and function prototypes for the user select driver dialog
28 //-----------------------------------------------------------------------------
29 DLGTEMPLATE* _BuildDriverSelectTemplate();
30 BOOL CALLBACK _DriverSelectProc( HWND, UINT, WPARAM, LPARAM );
35 //-----------------------------------------------------------------------------
36 // Global data for the enumerator functions
37 //-----------------------------------------------------------------------------
38 static LPDIRECTDRAW4 g_pDD = NULL; // Used for callbacks
39 static BOOL g_bRefRastEnumerated = FALSE; // For the reference rast
40 static BOOL g_bDevicesEnumerated = FALSE; // Used during enumeration
42 D3DEnum_DriverInfo* g_pFirstDriver = NULL; // List of DD drivers
43 D3DEnum_DriverInfo* g_pDefaultDriver = NULL; // Default driver
44 D3DEnum_DriverInfo* g_pCurrentDriver = NULL; // The selected DD driver
46 static HRESULT (*g_fnAppConfirmFn)(DDCAPS*, D3DDEVICEDESC*) = NULL;
51 //-----------------------------------------------------------------------------
52 // Local callback functions used during enumeration
53 //-----------------------------------------------------------------------------
56 //-----------------------------------------------------------------------------
57 // Name: EnumDisplayModesCallback()
58 // Desc: Callback function called for each display mode. Each available
59 // display mode is added to a list for further choosing from the app.
60 //-----------------------------------------------------------------------------
61 static HRESULT WINAPI EnumDisplayModesCallback( DDSURFACEDESC2* pddsd,
65 if( NULL==pddsd || NULL==pvContext )
66 return DDENUMRET_CANCEL;
68 D3DEnum_DeviceInfo* pDevice = (D3DEnum_DeviceInfo*)pvContext;
69 D3DEnum_ModeInfo* pNewMode;
70 DWORD dwBitDepth = pDevice->ddDesc.dwDeviceRenderBitDepth;
71 DWORD dwModeDepth = pddsd->ddpfPixelFormat.dwRGBBitCount;
73 // Check mode for compatability with device. Skip 8-bit modes.
74 if( (32==dwModeDepth) && (0==(dwBitDepth&DDBD_32)) ) return DDENUMRET_OK;
75 if( (24==dwModeDepth) && (0==(dwBitDepth&DDBD_24)) ) return DDENUMRET_OK;
76 if( (16==dwModeDepth) && (0==(dwBitDepth&DDBD_16)) ) return DDENUMRET_OK;
77 if( ( 8==dwModeDepth) ) return DDENUMRET_OK;
79 // Create a new mode structure
80 if( NULL == ( pNewMode = new D3DEnum_ModeInfo ) )
81 return DDENUMRET_CANCEL;
83 // Initialize the new mode structure
84 ZeroMemory( pNewMode, sizeof(D3DEnum_ModeInfo) );
85 memcpy( &pNewMode->ddsd, pddsd, sizeof(DDSURFACEDESC2) );
86 sprintf( pNewMode->strDesc, TEXT("%ld x %ld x %ld"), pddsd->dwWidth,
87 pddsd->dwHeight, dwModeDepth );
89 // Link the new mode struct in the list of other display modes
90 D3DEnum_ModeInfo** pMode = &pDevice->pFirstMode;
92 pMode = &((*pMode)->pNext);
95 // If this is a 640x480x16 mode, save it as the default mode
96 if( ( 640 == pddsd->dwWidth ) && ( 480 == pddsd->dwHeight ) &&
97 ( 16 == pddsd->ddpfPixelFormat.dwRGBBitCount ) )
98 pDevice->pCurrentMode = pNewMode;
100 if( NULL == pDevice->pCurrentMode )
101 pDevice->pCurrentMode = pNewMode;
109 //-----------------------------------------------------------------------------
110 // Name: Enum3DDevicesCallback()
111 // Desc: Callback function called for each DirectX 3D device. The driver's
112 // attributes are added to the list of available drivers.
113 //-----------------------------------------------------------------------------
114 static HRESULT WINAPI Enum3DDevicesCallback( GUID* pGUID, LPSTR strDesc,
115 LPSTR strName, LPD3DDEVICEDESC pHALDesc,
116 LPD3DDEVICEDESC pHELDesc, LPVOID pvContext )
118 D3DEnum_DriverInfo* pDriver = (D3DEnum_DriverInfo*)pvContext;
119 D3DEnum_DeviceInfo* pNewDevice;
122 if( NULL==pGUID || NULL==pHALDesc || NULL==pHELDesc || NULL==pDriver )
123 return D3DENUMRET_CANCEL;
125 // Handle specific device GUIDs. NullDevice renders nothing
126 if( IsEqualGUID( *pGUID, IID_IDirect3DNullDevice ) )
127 return D3DENUMRET_OK;
129 // Set a flag so we know enumeration is working. This is just a feature to
130 // help return more informative return codes later on.
131 g_bDevicesEnumerated = TRUE;
133 // Get info about this device.
134 BOOL bIsHardware = ( 0 != pHALDesc->dwFlags );
135 D3DDEVICEDESC* pDesc = bIsHardware ? pHALDesc : pHELDesc;
137 // Only enumerate software rasterizers for the primary device (which has
138 // a NULL GUID). This is to avoid duplicates
139 if( pDriver->pGUID != NULL )
140 if( FALSE == bIsHardware )
141 return D3DENUMRET_OK;
144 // Give the app a chance to accept or reject this device, based on
145 // what feature set it supports
146 if( g_fnAppConfirmFn )
147 if( FAILED( g_fnAppConfirmFn( &pDriver->ddDriverCaps, pDesc ) ) )
148 return D3DENUMRET_OK;
150 // Create a new D3D Driver struct
151 if( NULL == ( pNewDevice = new D3DEnum_DeviceInfo ) )
152 return D3DENUMRET_CANCEL;
153 ZeroMemory( pNewDevice, sizeof(D3DEnum_DeviceInfo) );
155 // Copy remaining device attributes
156 memcpy( &pNewDevice->guid, pGUID, sizeof(GUID) );
157 pNewDevice->pGUID = &pNewDevice->guid;
158 strncpy( pNewDevice->strName, strName, 39 );
159 memcpy( &pNewDevice->ddDesc, pDesc, sizeof(D3DDEVICEDESC) );
160 pNewDevice->bIsHardware = bIsHardware;
162 if( pNewDevice->bIsHardware )
163 pDriver->pCurrentDevice = pNewDevice;
166 if( NULL == pDriver->pCurrentDevice )
167 if( D3DCOLOR_RGB & pHELDesc->dcmColorModel )
168 pDriver->pCurrentDevice = pNewDevice;
171 // Enumerate the display modes
172 g_pDD->EnumDisplayModes( 0, NULL, pNewDevice, EnumDisplayModesCallback );
174 // Get the display mode's depth
176 ddsd.dwSize = sizeof(DDSURFACEDESC2);
177 g_pDD->GetDisplayMode( &ddsd );
178 DWORD dwDisplayBPP = ddsd.ddpfPixelFormat.dwRGBBitCount;
180 // Set the initial bWindowed flag if the device can render in a window and
181 // supports the current display mode
182 if( pDriver->ddDriverCaps.dwCaps2 & DDCAPS2_CANRENDERWINDOWED )
184 for( D3DEnum_ModeInfo* pMode=pNewDevice->pFirstMode; pMode;
185 pMode = pMode->pNext )
187 if( pMode->ddsd.ddpfPixelFormat.dwRGBBitCount == dwDisplayBPP )
189 pNewDevice->bCompatbileWithDesktop = TRUE;
190 if( NULL == pDriver->pGUID )
191 pNewDevice->bWindowed = TRUE;
196 if( pNewDevice->pFirstMode )
198 // Link it with the other D3D drivers in the DD Driver struct
199 D3DEnum_DeviceInfo** pDevice = &pDriver->pFirstDevice;
201 pDevice = &((*pDevice)->pNext);
202 (*pDevice) = pNewDevice;
205 // Device has no modes, so delete it
208 if( IsEqualGUID( *pGUID, IID_IDirect3DRefDevice ) )
209 g_bRefRastEnumerated = TRUE;
211 return D3DENUMRET_OK;
217 //-----------------------------------------------------------------------------
218 // Name: DirectDrawEnumCallbackEx()
219 // Desc: Callback function called for each DirectDraw driver. Unless we have
220 // multimon or a type of card which uses a separate 2D card in
221 // conjunction with the 3D card, this is only done once.
222 //-----------------------------------------------------------------------------
223 static BOOL WINAPI DirectDrawEnumCallbackEx( GUID FAR* pGUID, LPSTR strDesc,
224 LPSTR strName, VOID*,
227 // Use the GUID to create the DirectDraw object, so that information
228 // can be extracted from it.
230 if( FAILED( DirectDrawCreate( pGUID, &pDD, 0L ) ) )
232 DEBUG_MSG( TEXT("Can't create DDraw during enumeration!") );
233 return D3DENUMRET_OK;
236 // Query the DirectDraw driver for access to Direct3D.
237 if( FAILED( pDD->QueryInterface( IID_IDirectDraw4, (VOID**)&g_pDD ) ) )
239 DEBUG_MSG( TEXT("Can't query IDirectDraw4 during enumeration!") );
241 return D3DENUMRET_OK;
245 // Query the DirectDraw driver for access to Direct3D.
247 if( FAILED( g_pDD->QueryInterface( IID_IDirect3D3, (VOID**)&pD3D ) ) )
249 DEBUG_MSG( TEXT("Can't query IDirect3D3 during enumeration!") );
251 return D3DENUMRET_OK;
254 // Copy the DDDriver info into a new DriverInfo struct
255 D3DEnum_DriverInfo* pNewDriver = new D3DEnum_DriverInfo;
256 if( NULL == pNewDriver )
257 return D3DENUMRET_CANCEL;
259 ZeroMemory( pNewDriver, sizeof(D3DEnum_DriverInfo) );
261 // Copy the GUID (if specified) and the driver name
264 memcpy( &pNewDriver->guid, pGUID, sizeof(GUID) );
265 pNewDriver->pGUID = &pNewDriver->guid;
267 strncpy( pNewDriver->strDesc, strDesc, 39 );
268 strncpy( pNewDriver->strName, strName, 39 );
269 pNewDriver->hMonitor = hMonitor;
271 // Get the caps bits for the driver
272 pNewDriver->ddDriverCaps.dwSize = sizeof(DDCAPS);
273 pNewDriver->ddHELCaps.dwSize = sizeof(DDCAPS);
274 g_pDD->GetCaps( &pNewDriver->ddDriverCaps, &pNewDriver->ddHELCaps );
276 // Now, enumerate all the 3D devices
277 pD3D->EnumDevices( Enum3DDevicesCallback, pNewDriver );
279 if( pNewDriver->pFirstDevice )
281 // Link the new DDDriver with the global list
282 D3DEnum_DriverInfo** pDriver = &g_pFirstDriver;
284 pDriver = &((*pDriver)->pNext);
285 (*pDriver) = pNewDriver;
287 // Decide if this is a good default driver
289 g_pCurrentDriver = pNewDriver;
292 // Driver has no devices, so delete it
303 //-----------------------------------------------------------------------------
304 // Name: DirectDrawEnumCallback()
305 // Desc: Non-mulitmon version of the ddraw enumeration callback
306 //-----------------------------------------------------------------------------
307 static BOOL WINAPI DirectDrawEnumCallback( GUID FAR* pGUID, LPSTR strDesc,
308 LPSTR strName, VOID* )
310 return DirectDrawEnumCallbackEx( pGUID, strDesc, strName, NULL, NULL );
316 //-----------------------------------------------------------------------------
317 // Name: D3DEnum_FreeResources()
318 // Desc: Frees all resources used for driver enumeration
319 //-----------------------------------------------------------------------------
320 VOID D3DEnum_FreeResources()
322 // Loop through each driver, and delete everything
323 while( g_pFirstDriver )
325 D3DEnum_DriverInfo* pDriverVictim = g_pFirstDriver;
326 g_pFirstDriver = g_pFirstDriver->pNext;
328 while( pDriverVictim->pFirstDevice )
330 D3DEnum_DeviceInfo* pDeviceVictim = pDriverVictim->pFirstDevice;
331 pDriverVictim->pFirstDevice = pDeviceVictim->pNext;
333 while( pDeviceVictim->pFirstMode )
335 D3DEnum_ModeInfo* pModeVictim = pDeviceVictim->pFirstMode;
336 pDeviceVictim->pFirstMode = pModeVictim->pNext;
339 delete pDeviceVictim;
341 delete pDriverVictim;
348 //-----------------------------------------------------------------------------
349 // Name: RefreshListForDesktopCompatibility()
350 // Desc: Loops through list of devices, and marks a flag for whether the device
351 // is compatible with the desktop bit depth.
352 //-----------------------------------------------------------------------------
353 static VOID RefreshListForDesktopCompatibility()
355 // Get the currect display mode description
358 if( FAILED( DirectDrawCreate( NULL, &pDD, NULL ) ) )
360 ddsd.dwSize = sizeof(DDSURFACEDESC);
361 pDD->GetDisplayMode( &ddsd );
364 // Get the display mode's depth
365 DWORD dwDisplayBPP = ddsd.ddpfPixelFormat.dwRGBBitCount;
367 // Loop through the devices, and check if any modes works with the current
369 for( D3DEnum_DriverInfo* pDriver = g_pFirstDriver; pDriver;
370 pDriver = pDriver->pNext )
372 for( D3DEnum_DeviceInfo* pDevice=pDriver->pFirstDevice; pDevice;
373 pDevice = pDevice->pNext )
375 pDevice->bCompatbileWithDesktop = FALSE;
377 for( D3DEnum_ModeInfo* pMode=pDevice->pFirstMode; pMode;
378 pMode = pMode->pNext )
380 if( pMode->ddsd.ddpfPixelFormat.dwRGBBitCount == dwDisplayBPP )
381 pDevice->bCompatbileWithDesktop = TRUE;
389 //-----------------------------------------------------------------------------
390 // Name: D3DEnum_EnumerateDevices()
391 // Desc: Enumerates all drivers, devices, and modes. The optional app-supplied
392 // callback is called for each enumerated device, to confirm that the
393 // device supports the feature set required by the app.
394 //-----------------------------------------------------------------------------
395 HRESULT D3DEnum_EnumerateDevices(
396 HRESULT (*AppConfirmFn)(DDCAPS*, D3DDEVICEDESC*) )
398 g_fnAppConfirmFn = AppConfirmFn;
399 g_bRefRastEnumerated = FALSE;
401 // We need to manually get the procedure address for the DDrawEnumEx()
403 HMODULE hDDrawDLL = GetModuleHandle("DDRAW.DLL");
404 if( NULL == hDDrawDLL )
406 DEBUG_MSG( TEXT("Can't load DDRAW.DLL!") );
407 return D3DENUMERR_NODIRECTDRAW;
410 // Find the DDraw enumeration function, and call it
411 LPDIRECTDRAWENUMERATEEX pDDrawEnumFn = (LPDIRECTDRAWENUMERATEEX)
412 GetProcAddress( hDDrawDLL, "DirectDrawEnumerateExA" );
415 pDDrawEnumFn( DirectDrawEnumCallbackEx, NULL,
416 DDENUM_ATTACHEDSECONDARYDEVICES |
417 DDENUM_DETACHEDSECONDARYDEVICES |
418 DDENUM_NONDISPLAYDEVICES );
420 DirectDrawEnumerate( DirectDrawEnumCallback, NULL );
422 // Select a driver. Ask for a hardware device that renders in a window
423 return D3DEnum_SelectDefaultDriver( NULL );
429 //-----------------------------------------------------------------------------
430 // Name: D3DEnum_SelectDefaultDriver()
431 // Desc: Picks a default driver according to the passed in flags.
432 //-----------------------------------------------------------------------------
433 HRESULT D3DEnum_SelectDefaultDriver( DWORD dwFlags )
435 // Refresh the list of devices to mark which devices (if any) are
436 // compatible with the current desktop (ie. can render in a window).
437 RefreshListForDesktopCompatibility();
439 // If a specific driver was requested, perform that search here
440 if( dwFlags & 0x0000003c )
442 for( D3DEnum_DriverInfo* pDriver = g_pFirstDriver; pDriver;
443 pDriver = pDriver->pNext )
445 for( D3DEnum_DeviceInfo* pDevice = pDriver->pFirstDevice; pDevice;
446 pDevice = pDevice->pNext )
450 if( IsEqualGUID( *pDevice->pGUID, IID_IDirect3DRGBDevice ) )
452 if( dwFlags & D3DENUM_RGBEMULATION )
455 else if( IsEqualGUID( *pDevice->pGUID, IID_IDirect3DRefDevice ) )
457 if( dwFlags & D3DENUM_REFERENCERAST )
462 if( dwFlags & D3DENUM_PRIMARYHAL )
463 if( pDriver == g_pFirstDriver )
465 if( dwFlags & D3DENUM_SECONDARYHAL )
466 if( pDriver != g_pFirstDriver )
472 g_pCurrentDriver = pDriver;
473 g_pCurrentDriver->pCurrentDevice = pDevice;
478 return D3DENUMERR_NOTFOUND;
481 // Do 4 passes, looping through drivers, devices and modes. The 1st pass
482 // searches for hardware. The 2nd pass looks for software devices. The
483 // final two passes allow fullscreen modes.
484 for( WORD pass=0; pass<4; pass++ )
486 BOOL bSeekHardware = ( pass & 0x1 ) ? FALSE : TRUE;
487 BOOL bSeekWindowed = ( pass & 0x2 ) ? FALSE : TRUE;
489 // Skip the passes we aren't allowing
490 if( (TRUE==bSeekHardware) && (dwFlags&D3DENUM_SOFTWAREONLY) )
492 if( (TRUE==bSeekWindowed) && (dwFlags&D3DENUM_FULLSCREENONLY) )
495 for( D3DEnum_DriverInfo* pDriver = g_pFirstDriver; pDriver;
496 pDriver = pDriver->pNext )
498 DDCAPS* pCaps = &pDriver->ddDriverCaps;
501 if( 0 == ( pCaps->dwCaps2 & DDCAPS2_CANRENDERWINDOWED ) )
504 for( D3DEnum_DeviceInfo* pDevice = pDriver->pFirstDevice; pDevice;
505 pDevice = pDevice->pNext )
507 if( bSeekHardware != pDevice->bIsHardware )
509 if( bSeekWindowed && FALSE == pDevice->bCompatbileWithDesktop )
512 pDevice->bWindowed = bSeekWindowed;
513 g_pCurrentDriver = pDriver;
514 g_pCurrentDriver->pCurrentDevice = pDevice;
521 // No compatible devices were found. Return an error code
522 if( FALSE == g_bDevicesEnumerated )
523 return D3DENUMERR_ENUMERATIONFAILED; // Enumeration really did fail
524 if( FALSE == g_bRefRastEnumerated )
525 return D3DENUMERR_SUGGESTREFRAST; // Suggest enabling the RefRast
527 return D3DENUMERR_NOCOMPATIBLEDEVICES;
533 //-----------------------------------------------------------------------------
534 // Name: D3DEnum_UserDlgSelectDriver()
535 // Desc: Displays a dialog box for the user to select a driver/device/mode.
536 // The return values are akin to the Windows DialogBox() function.
537 //-----------------------------------------------------------------------------
538 INT D3DEnum_UserDlgSelectDriver( HWND hwndParent, BOOL bCurrentlyWindowed )
542 // Check in case drivers weren't properly enumerated beforehand.
543 if( NULL == g_pCurrentDriver )
546 // Refresh the list of devices to mark which devices (if any) are
547 // compatible with the current desktop (ie. can render in a window).
548 RefreshListForDesktopCompatibility();
550 // Match the current windowed-vs-fullscreen state
551 g_pCurrentDriver->pCurrentDevice->bWindowed = bCurrentlyWindowed;
553 // Pop up a dialog box for the user's choice of driver/device/mode
554 HINSTANCE hInstance = (HINSTANCE)GetWindowLong( hwndParent,
557 // Create dynamic dialog template
558 DLGTEMPLATE* pDlgSelect = _BuildDriverSelectTemplate();
561 // Create dialog box from template
562 nResult = DialogBoxIndirectParam( hInstance, pDlgSelect, hwndParent,
563 (DLGPROC)_DriverSelectProc, 0L );
573 //-----------------------------------------------------------------------------
574 // Name: D3DEnum_GetSelectedDriver()
575 // Desc: Returns the currently selected driver, device, and display mode
576 //-----------------------------------------------------------------------------
577 HRESULT D3DEnum_GetSelectedDriver( LPGUID* ppDriverGUID, LPGUID* ppDeviceGUID,
578 LPDDSURFACEDESC2* ppddsd, BOOL* pbWindowed,
583 if( (!ppDriverGUID) || (!ppDeviceGUID) )
586 // Abort if things weren't setup correctly
587 if( NULL == g_pCurrentDriver )
588 return D3DENUMERR_ENUMERATIONFAILED;
590 // Copy the driver and device GUID ptrs
591 (*ppDriverGUID) = g_pCurrentDriver->pGUID;
592 (*ppDeviceGUID) = g_pCurrentDriver->pCurrentDevice->pGUID;
595 (*ppddsd) = &g_pCurrentDriver->pCurrentDevice->pCurrentMode->ddsd;
597 (*pbWindowed) = g_pCurrentDriver->pCurrentDevice->bWindowed;
599 (*pbIsHardware) = g_pCurrentDriver->pCurrentDevice->bIsHardware;
607 //-----------------------------------------------------------------------------
608 // Name: D3DEnum_GetSelectedDriver()
609 // Desc: Returns the currently selected driver, device, and display mode
610 //-----------------------------------------------------------------------------
611 HRESULT D3DEnum_GetSelectedDriver( D3DEnum_DriverInfo** ppDriverInfo,
612 D3DEnum_DeviceInfo** ppDeviceInfo )
615 // Abort if things weren't setup correctly
616 if( NULL == g_pCurrentDriver )
617 return D3DENUMERR_ENUMERATIONFAILED;
619 // Copy the driver and device info ptrs
620 if( ppDriverInfo ) *ppDriverInfo = g_pCurrentDriver;
621 if( ppDeviceInfo ) *ppDeviceInfo = g_pCurrentDriver->pCurrentDevice;
629 //-----------------------------------------------------------------------------
630 // Name: D3DEnum_GetFirstDriver()
631 // Desc: Returns a ptr to the first DriverInfo structure in the list.
632 //-----------------------------------------------------------------------------
633 D3DEnum_DriverInfo* D3DEnum_GetFirstDriver()
635 return g_pFirstDriver;