//============================================================================= // // Copyright (C) 1998 Microsoft Corporation. All rights reserved. // // File: ddmodent.c // Content: DirectDraw display mode code for NT // // Date By Reason // ---------- -------- ----------------------------------------------------- // 02/20/1998 johnstep Initialial implementation, replaces ddmode.c on NT // 05/29/1998 jeffno ModeX emulation // //============================================================================= #include "ddrawpr.h" #include "ddrawgdi.h" #define MODEX_WIDTH 320 #define MODEX_HEIGHT1 200 #define MODEX_HEIGHT2 240 #define MODEX_BPP 8 //============================================================================= // // Function: GetNumberOfMonitorAttachedToDesktop // // Count number of monitors attached to current desktop. // //============================================================================= DWORD GetNumberOfMonitorAttachedToDesktop() { DWORD dwNumberOfMonitor = 0; DWORD iDevNum = 0; DISPLAY_DEVICE DisplayDevice; ZeroMemory(&DisplayDevice,sizeof(DISPLAY_DEVICE)); DisplayDevice.cb = sizeof(DISPLAY_DEVICE); while (EnumDisplayDevices(NULL,iDevNum,&DisplayDevice,0)) { if (DisplayDevice.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) { dwNumberOfMonitor++; } ZeroMemory(&DisplayDevice,sizeof(DISPLAY_DEVICE)); DisplayDevice.cb = sizeof(DISPLAY_DEVICE); iDevNum++; } return dwNumberOfMonitor; } //============================================================================= // // Function: resetAllDirectDrawObjects // // On NT we have to reenable all the DirectDraw objects on any mode change // because a mode change disables all the kernel mode DirectDraw objects due // to desktop changes, etc. // //============================================================================= void resetAllDirectDrawObjects() { LPDDRAWI_DIRECTDRAW_LCL pdd_lcl; LPDDRAWI_DIRECTDRAW_GBL pdd_gbl; BOOL bRestoreGamma; HDC hdc; WORD wMonitorsAttachedToDesktop = (WORD) GetNumberOfMonitorAttachedToDesktop(); // First mark all DirectDraw global objects as having not changed. for (pdd_lcl = lpDriverLocalList; pdd_lcl;) { if (pdd_lcl->lpGbl) { pdd_lcl->lpGbl->dwFlags |= DDRAWI_DDRAWDATANOTFETCHED; } pdd_lcl = pdd_lcl->lpLink; } // Now reset all drivers unmarking them as we go. We may need to create // temporary kernel mode DirectDraw objects in order to pass down a valid // handle to the kernel. for (pdd_lcl = lpDriverLocalList; pdd_lcl;) { pdd_gbl = pdd_lcl->lpGbl; if (pdd_gbl && (pdd_gbl->dwFlags & DDRAWI_DDRAWDATANOTFETCHED)) { // Determine if the gamma ramp needs to be restored bRestoreGamma = ( pdd_lcl->lpPrimary != NULL ) && ( pdd_lcl->lpPrimary->lpLcl->lpSurfMore->lpGammaRamp != NULL ) && ( pdd_lcl->lpPrimary->lpLcl->dwFlags & DDRAWISURF_SETGAMMA ); pdd_gbl->dwFlags &= ~DDRAWI_DDRAWDATANOTFETCHED; if (!(pdd_gbl->dwFlags & DDRAWI_MODEX)) { // If we find a local for this process/driver pair, we will use // its hDD to pass to the kernel. If not, we must create a // temproary kernel mode DirectDraw object, and delete it after // resetting the driver. FetchDirectDrawData(pdd_gbl, TRUE, 0, NULL, NULL, 0, pdd_lcl); } else { DDHALMODEINFO mi = { MODEX_WIDTH, // width (in pixels) of mode MODEX_HEIGHT1, // height (in pixels) of mode MODEX_WIDTH, // pitch (in bytes) of mode MODEX_BPP, // bits per pixel (WORD)(DDMODEINFO_PALETTIZED | DDMODEINFO_MODEX), // flags 0, // refresh rate 0, // red bit mask 0, // green bit mask 0, // blue bit mask 0 // alpha bit mask }; //fixup the height to the actual height: mi.dwHeight = pdd_lcl->dmiPreferred.wHeight; fetchModeXData( pdd_gbl, &mi, INVALID_HANDLE_VALUE ); } pdd_gbl->dmiCurrent.wMonitorsAttachedToDesktop = (BYTE)wMonitorsAttachedToDesktop; hdc = DD_CreateDC(pdd_gbl->cDriverName); if ( pdd_gbl->dwFlags & DDRAWI_NOHARDWARE ) { // The HEL will wipe out our hard-earned modex data otherwise if (0 == (pdd_gbl->dwFlags & DDRAWI_MODEX) ) { extern void UpdateDirectDrawMode(LPDDRAWI_DIRECTDRAW_GBL); UpdateDirectDrawMode(pdd_gbl); } } else { if( bRestoreGamma ) { SetGamma( pdd_lcl->lpPrimary->lpLcl, pdd_lcl ); } InitDIB(hdc, pdd_gbl->gpbmiSrc); InitDIB(hdc, pdd_gbl->gpbmiDest); } DD_DoneDC(hdc); } pdd_lcl = pdd_lcl->lpLink; } CheckAliasedLocksOnModeChange(); } //============================================================================= // // Function: ModeChangedOnENTERDDRAW // //============================================================================= void ModeChangedOnENTERDDRAW(void) { resetAllDirectDrawObjects(); } //============================================================================= // // Function: FillBitMasks // //============================================================================= void FillBitMasks(LPDDPIXELFORMAT pddpf, HDC hdc) { if (hdc) { HBITMAP hbm; BITMAPINFO *pbmi; DWORD *pdwColors; if (pbmi = LocalAlloc(LPTR, 3 * sizeof (RGBQUAD) + sizeof (BITMAPINFO))) { if (hbm = CreateCompatibleBitmap(hdc, 1, 1)) { pbmi->bmiHeader.biSize = sizeof (BITMAPINFOHEADER); if (GetDIBits(hdc, hbm, 0, 0, NULL, pbmi, DIB_RGB_COLORS)) { if (pbmi->bmiHeader.biCompression == BI_BITFIELDS) { GetDIBits(hdc, hbm, 0, pbmi->bmiHeader.biHeight, NULL, pbmi, DIB_RGB_COLORS); pdwColors = (DWORD *) &pbmi->bmiColors[0]; pddpf->dwRBitMask = pdwColors[0]; pddpf->dwGBitMask = pdwColors[1]; pddpf->dwBBitMask = pdwColors[2]; pddpf->dwRGBAlphaBitMask = 0; } } DeleteObject(hbm); } LocalFree(pbmi); } } else { switch (pddpf->dwRGBBitCount) { case 15: pddpf->dwRBitMask = 0x7C00; pddpf->dwGBitMask = 0x03E0; pddpf->dwBBitMask = 0x001F; pddpf->dwRGBAlphaBitMask = 0; break; case 16: pddpf->dwRBitMask = 0xF800; pddpf->dwGBitMask = 0x07E0; pddpf->dwBBitMask = 0x001F; pddpf->dwRGBAlphaBitMask = 0; break; case 32: pddpf->dwRBitMask = 0x00FF0000; pddpf->dwGBitMask = 0x0000FF00; pddpf->dwBBitMask = 0x000000FF; pddpf->dwRGBAlphaBitMask = 0x00000000; break; default: pddpf->dwRBitMask = 0; pddpf->dwGBitMask = 0; pddpf->dwBBitMask = 0; pddpf->dwRGBAlphaBitMask = 0; } } } //============================================================================= // // Function: setPixelFormat // //============================================================================= static void setPixelFormat(LPDDPIXELFORMAT pddpf, HDC hdc, DWORD bpp) { pddpf->dwSize = sizeof (DDPIXELFORMAT); pddpf->dwFlags = DDPF_RGB; pddpf->dwRGBBitCount = hdc ? GetDeviceCaps(hdc, BITSPIXEL) : bpp; switch (pddpf->dwRGBBitCount) { case 8: pddpf->dwFlags |= DDPF_PALETTEINDEXED8; pddpf->dwRBitMask = 0; pddpf->dwGBitMask = 0; pddpf->dwBBitMask = 0; pddpf->dwRGBAlphaBitMask = 0; break; case 24: pddpf->dwRBitMask = 0x00FF0000; pddpf->dwGBitMask = 0x0000FF00; pddpf->dwBBitMask = 0x000000FF; pddpf->dwRGBAlphaBitMask = 0x00000000; break; default: FillBitMasks(pddpf, hdc); break; } } //============================================================================= // // Function: DD_GetDisplayMode // //============================================================================= HRESULT DDAPI DD_GetDisplayMode(LPDIRECTDRAW pdd, LPDDSURFACEDESC pddsd) { LPDDRAWI_DIRECTDRAW_INT pdd_int; LPDDRAWI_DIRECTDRAW_LCL pdd_lcl; LPDDRAWI_DIRECTDRAW_GBL pdd_gbl; HDC hdc; ENTER_DDRAW(); DPF(2,A,"ENTERAPI: DD_GetDisplayMode"); TRY { pdd_int = (LPDDRAWI_DIRECTDRAW_INT) pdd; if (!VALID_DIRECTDRAW_PTR(pdd_int)) { DPF(0, "Invalid object"); LEAVE_DDRAW(); return DDERR_INVALIDOBJECT; } pdd_lcl = pdd_int->lpLcl; pdd_gbl = pdd_lcl->lpGbl; if (!VALIDEX_DDSURFACEDESC2_PTR(pddsd) && !VALIDEX_DDSURFACEDESC_PTR(pddsd)) { DPF(0, "Invalid params"); LEAVE_DDRAW(); return DDERR_INVALIDPARAMS; } } EXCEPT(EXCEPTION_EXECUTE_HANDLER) { DPF_ERR("DD_GetDisplayMode: Exception encountered validating parameters"); LEAVE_DDRAW(); return DDERR_INVALIDPARAMS; } ZeroMemory(pddsd, pddsd->dwSize); if (LOWERTHANDDRAW4(pdd_int)) { pddsd->dwSize = sizeof (DDSURFACEDESC); } else { pddsd->dwSize = sizeof (DDSURFACEDESC2); } pddsd->dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PITCH | DDSD_PIXELFORMAT | DDSD_REFRESHRATE; hdc = DD_CreateDC(pdd_gbl->cDriverName); pddsd->dwWidth = pdd_gbl->lpModeInfo->dwWidth; pddsd->dwHeight = pdd_gbl->lpModeInfo->dwHeight; pddsd->dwRefreshRate = pdd_gbl->lpModeInfo->wRefreshRate; setPixelFormat(&(pddsd->ddpfPixelFormat), hdc, 0); pddsd->lPitch = (pddsd->dwWidth * pddsd->ddpfPixelFormat.dwRGBBitCount) >> 3; // hack // set stereo surface caps bits if driver marks mode as stereo mode if (GetDDStereoMode(pdd_gbl, pddsd->dwWidth, pddsd->dwHeight, pddsd->ddpfPixelFormat.dwRGBBitCount, pddsd->dwRefreshRate) && !LOWERTHANDDRAW7(pdd_int) && VALIDEX_DDSURFACEDESC2_PTR(pddsd)) { LPDDSURFACEDESC2 pddsd2 = (LPDDSURFACEDESC2)pddsd; pddsd2->ddsCaps.dwCaps2 |= DDSCAPS2_STEREOSURFACELEFT; } DD_DoneDC(hdc); LEAVE_DDRAW(); return DD_OK; } //============================================================================= // // Function: SetDisplayMode // //============================================================================= /* * IsRefreshRateSupported */ BOOL IsRefreshRateSupported(LPDDRAWI_DIRECTDRAW_GBL pdrv, DWORD Width, DWORD Height, DWORD BitsPerPixel, DWORD RefreshRate) { DEVMODE dm; LPSTR pDeviceName; int i; pDeviceName = (_stricmp(pdrv->cDriverName, "display") == 0) ? g_szPrimaryDisplay : pdrv->cDriverName; for (i = 0;; i++) { ZeroMemory(&dm, sizeof dm); dm.dmSize = sizeof dm; if (EnumDisplaySettings(pDeviceName, i, &dm)) { if ((dm.dmPelsWidth == Width) && (dm.dmPelsHeight == Height) && (dm.dmBitsPerPel == BitsPerPixel) && (dm.dmDisplayFrequency == RefreshRate)) { return TRUE; } } else { break; } } return FALSE; } /* * PickRefreshRate * * On NT, we want to pick a high reffresh rate, but we don't want to pick one * too high. In theory, mode pruning would be 100% safe and we can always pick * a high one, but we don't trust it 100%. */ DWORD PickRefreshRate(LPDDRAWI_DIRECTDRAW_GBL pdrv, DWORD Width, DWORD Height, DWORD RefreshRate, DWORD BitsPerPixel) { DEVMODE dm; LPSTR pDeviceName; pDeviceName = (_stricmp(pdrv->cDriverName, "display") == 0) ? g_szPrimaryDisplay : pdrv->cDriverName; if (dwRegFlags & DDRAW_REGFLAGS_FORCEREFRESHRATE) { if (IsRefreshRateSupported(pdrv, Width, Height, BitsPerPixel, dwForceRefreshRate)) { return dwForceRefreshRate; } } // If the app specified the refresh rate, we will use it; otherwise, we'll // pick one ourselves. if (RefreshRate == 0) { // If the mode requires no more bandwidth than the desktop mode from which // the app was launched, we will go ahead and try that mode. ZeroMemory(&dm, sizeof dm); dm.dmSize = sizeof dm; EnumDisplaySettings(pDeviceName, ENUM_REGISTRY_SETTINGS, &dm); if ((Width <= dm.dmPelsWidth) && (Height <= dm.dmPelsHeight)) { if (IsRefreshRateSupported(pdrv, Width, Height, BitsPerPixel, dm.dmDisplayFrequency)) { RefreshRate = dm.dmDisplayFrequency; } } // If we still don't have a refresh rate, try 75hz if (RefreshRate == 0) { if (IsRefreshRateSupported(pdrv, Width, Height, BitsPerPixel, 75)) { RefreshRate = 75; } } // If we still don't have a refresh rate, use 60hz if (RefreshRate == 0) { if (IsRefreshRateSupported(pdrv, Width, Height, BitsPerPixel, 60)) { RefreshRate = 60; } } } return RefreshRate; } HRESULT SetDisplayMode( LPDDRAWI_DIRECTDRAW_LCL pdd_lcl, DWORD index, BOOL force, BOOL useRefreshRate) { LPDDRAWI_DIRECTDRAW_GBL pdd_gbl; DEVMODE dm; LONG result; BOOL bNewMode; DDHALINFO ddhi; LPCTSTR pszDevice; DWORD refreshRate; BOOL forceRefresh; pdd_lcl->dwLocalFlags |= DDRAWILCL_MODEHASBEENCHANGED; pdd_gbl = pdd_lcl->lpGbl; // // If not forcing, do not change mode with surface locks. // if (!force) { if (pdd_gbl->dwSurfaceLockCount > 0) { LPDDRAWI_DDRAWSURFACE_INT pTemp; // When we enabled vidmem vertex buffers in DX8, we found that some // apps do not unlock them before the mode change, but we don't want // to break them now, so we will hack around this by allowing the // mode switch to occur if all that's locked are vidmem VBs. pTemp = pdd_gbl->dsList; while (pTemp != NULL) { if (pTemp->lpLcl->lpGbl->dwUsageCount > 0) { if ((pTemp->lpLcl->ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY) && !(pTemp->lpLcl->ddsCaps.dwCaps & DDSCAPS_EXECUTEBUFFER)) { break; } } pTemp = pTemp->lpLink; } if (pTemp != NULL) { return DDERR_SURFACEBUSY; } } } // // Add code here to not set mode if it didn't change? // ZeroMemory(&dm, sizeof dm); dm.dmSize = sizeof dm; dm.dmBitsPerPel = pdd_lcl->dmiPreferred.wBPP; dm.dmPelsWidth = pdd_lcl->dmiPreferred.wWidth; dm.dmPelsHeight = pdd_lcl->dmiPreferred.wHeight; if (dm.dmBitsPerPel == 16) { if (pdd_gbl->lpModeInfo->wFlags & DDMODEINFO_555MODE) { dm.dmBitsPerPel = 15; } } dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; if (useRefreshRate) { dm.dmDisplayFrequency = PickRefreshRate(pdd_lcl->lpGbl, dm.dmPelsWidth, dm.dmPelsHeight, pdd_lcl->dmiPreferred.wRefreshRate, dm.dmBitsPerPel); dm.dmFields |= DM_DISPLAYFREQUENCY; } else { dm.dmDisplayFrequency = PickRefreshRate(pdd_lcl->lpGbl, dm.dmPelsWidth, dm.dmPelsHeight, 0, dm.dmBitsPerPel); if (dm.dmDisplayFrequency > 0) { dm.dmFields |= DM_DISPLAYFREQUENCY; pdd_lcl->dmiPreferred.wRefreshRate = (WORD) dm.dmDisplayFrequency; } } if (_stricmp(pdd_gbl->cDriverName, "DISPLAY")) { pszDevice = pdd_gbl->cDriverName; } else { pszDevice = NULL; } // clean up any previous modex stuff: pdd_gbl->dwFlags &= ~DDRAWI_MODEX; NotifyDriverToDeferFrees(); pdd_gbl->dwFlags |= DDRAWI_CHANGINGMODE; result = ChangeDisplaySettingsEx(pszDevice, &dm, NULL, CDS_FULLSCREEN, 0); pdd_gbl->dwFlags &= ~DDRAWI_CHANGINGMODE; DPF(5, "ChangeDisplaySettings: %d", result); if (result != DISP_CHANGE_SUCCESSFUL) { // // Check if it's a potentially emulated ModeX mode // if (pdd_lcl->dwLocalFlags & DDRAWILCL_ALLOWMODEX) { if (pdd_lcl->dmiPreferred.wBPP == MODEX_BPP && pdd_lcl->dmiPreferred.wWidth == MODEX_WIDTH) { if (pdd_lcl->dmiPreferred.wHeight == MODEX_HEIGHT2 || pdd_lcl->dmiPreferred.wHeight == MODEX_HEIGHT1) { // Set 640x480x8 for consistency with win9x and reliable mouse pos messages. dm.dmFields &= ~DM_DISPLAYFREQUENCY; dm.dmPelsWidth = 640; dm.dmPelsHeight = 480; pdd_gbl->dwFlags |= DDRAWI_CHANGINGMODE; result = ChangeDisplaySettingsEx(pszDevice, &dm, NULL, CDS_FULLSCREEN, 0); pdd_gbl->dwFlags &= ~DDRAWI_CHANGINGMODE; } } } if (result == DISP_CHANGE_SUCCESSFUL) { //now we are in 640x480, we need to mark the ddraw local that it's in emulated modex pdd_gbl->dwFlags |= DDRAWI_MODEX; } else { //failed to set 640x480 NotifyDriverOfFreeAliasedLocks(); return DDERR_UNSUPPORTED; } } uDisplaySettingsUnique = DdQueryDisplaySettingsUniqueness(); resetAllDirectDrawObjects(); pdd_lcl->dwLocalFlags |= DDRAWILCL_MODEHASBEENCHANGED | DDRAWILCL_DIRTYDC; return DD_OK; } //============================================================================= // // Function: DD_SetDisplayMode // //============================================================================= HRESULT DDAPI DD_SetDisplayMode( LPDIRECTDRAW pdd, DWORD dwWidth, DWORD dwHeight, DWORD dwBPP) { DPF(2,A,"ENTERAPI: DD_SetDisplayMode"); return DD_SetDisplayMode2(pdd, dwWidth, dwHeight, dwBPP, 0, 0); } //============================================================================= // // Function: DD_SetDisplayMode2 // //============================================================================= HRESULT DDAPI DD_SetDisplayMode2( LPDIRECTDRAW pdd, DWORD dwWidth, DWORD dwHeight, DWORD dwBPP, DWORD dwRefreshRate, DWORD dwFlags) { LPDDRAWI_DIRECTDRAW_INT pdd_int; LPDDRAWI_DIRECTDRAW_LCL pdd_lcl; LPDDRAWI_DIRECTDRAW_GBL pdd_gbl; HRESULT hr; DISPLAYMODEINFO dmiSave; BOOL excl_exists,has_excl; ENTER_DDRAW(); DPF(2,A,"ENTERAPI: DD_SetDisplayMode2"); TRY { pdd_int = (LPDDRAWI_DIRECTDRAW_INT) pdd; if (!VALID_DIRECTDRAW_PTR(pdd_int)) { DPF(0, "Invalid object"); LEAVE_DDRAW(); return DDERR_INVALIDOBJECT; } if (dwFlags & ~DDSDM_VALID) { DPF_ERR("Invalid flags"); LEAVE_DDRAW(); return DDERR_INVALIDPARAMS; } pdd_lcl = pdd_int->lpLcl; pdd_gbl = pdd_lcl->lpGbl; if (pdd_gbl->dwSurfaceLockCount > 0) { LPDDRAWI_DDRAWSURFACE_INT pTemp; // When we enabled vidmem vertex buffers in DX8, we found that some // apps do not unlock them before the mode change, but we don't want // to break them now, so we will hack around this by allowing the // mode switch to occur if all that's locked are vidmem VBs. pTemp = pdd_gbl->dsList; while (pTemp != NULL) { if (pTemp->lpLcl->lpGbl->dwUsageCount > 0) { if ((pTemp->lpLcl->ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY) && !(pTemp->lpLcl->ddsCaps.dwCaps & DDSCAPS_EXECUTEBUFFER)) { break; } } pTemp = pTemp->lpLink; } if (pTemp != NULL) { DPF_ERR("Surfaces are locked, can't switch the mode"); LEAVE_DDRAW(); return DDERR_SURFACEBUSY; } } CheckExclusiveMode(pdd_lcl, &excl_exists, &has_excl, FALSE, NULL, FALSE); if (excl_exists && (!has_excl)) { DPF_ERR("Can't change mode; exclusive mode not owned"); LEAVE_DDRAW(); return DDERR_NOEXCLUSIVEMODE; } dmiSave = pdd_lcl->dmiPreferred; pdd_lcl->dmiPreferred.wWidth = (WORD) dwWidth; pdd_lcl->dmiPreferred.wHeight = (WORD) dwHeight; pdd_lcl->dmiPreferred.wBPP = (BYTE) dwBPP; pdd_lcl->dmiPreferred.wRefreshRate = (WORD) dwRefreshRate; } EXCEPT(EXCEPTION_EXECUTE_HANDLER) { DPF_ERR("DD_SetDisplayMode2: Exception encountered validating parameters"); LEAVE_DDRAW(); return DDERR_INVALIDPARAMS; } hr = SetDisplayMode(pdd_lcl, 0, FALSE, dwRefreshRate ? TRUE : FALSE); if (FAILED(hr)) { pdd_lcl->dmiPreferred = dmiSave; } else { pdd_lcl->dmiPreferred = pdd_gbl->dmiCurrent; } LEAVE_DDRAW(); return hr; } //============================================================================= // // Function: RestoreDisplayMode // //============================================================================= HRESULT RestoreDisplayMode(LPDDRAWI_DIRECTDRAW_LCL pdd_lcl, BOOL force) { LPDDRAWI_DIRECTDRAW_GBL pdd_gbl; LPCTSTR pszDevice; LONG result; pdd_gbl = pdd_lcl->lpGbl; pdd_gbl->dwFlags &= ~DDRAWI_MODEX; if (!(pdd_lcl->dwLocalFlags & DDRAWILCL_MODEHASBEENCHANGED)) { DPF(2, "Mode was never changed by this app"); return DD_OK; } if (!force) { if (pdd_gbl->dwSurfaceLockCount > 0) { LPDDRAWI_DDRAWSURFACE_INT pTemp; // When we enabled vidmem vertex buffers in DX8, we found that some // apps do not unlock them before the mode change, but we don't want // to break them now, so we will hack around this by allowing the // mode switch to occur if all that's locked are vidmem VBs. pTemp = pdd_gbl->dsList; while (pTemp != NULL) { if (pTemp->lpLcl->lpGbl->dwUsageCount > 0) { if ((pTemp->lpLcl->ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY) && !(pTemp->lpLcl->ddsCaps.dwCaps & DDSCAPS_EXECUTEBUFFER)) { break; } } pTemp = pTemp->lpLink; } if (pTemp != NULL) { return DDERR_SURFACEBUSY; } } } if (_stricmp(pdd_gbl->cDriverName, "DISPLAY")) { pszDevice = pdd_gbl->cDriverName; } else { pszDevice = NULL; } NotifyDriverToDeferFrees(); pdd_gbl->dwFlags |= DDRAWI_CHANGINGMODE; result = ChangeDisplaySettingsEx(pszDevice, NULL, NULL, CDS_FULLSCREEN, 0); pdd_gbl->dwFlags &= ~DDRAWI_CHANGINGMODE; if (result != DISP_CHANGE_SUCCESSFUL) { NotifyDriverOfFreeAliasedLocks(); return DDERR_UNSUPPORTED; } // // FetchDirectDrawData here, which will update the global object with // the new mode information. // uDisplaySettingsUnique = DdQueryDisplaySettingsUniqueness(); resetAllDirectDrawObjects(); pdd_lcl->dwLocalFlags &= ~DDRAWILCL_MODEHASBEENCHANGED; pdd_lcl->dwLocalFlags |= DDRAWILCL_DIRTYDC; RedrawWindow(NULL, NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN); return DD_OK; } //============================================================================= // // Function: DD_RestoreDisplayMode // //============================================================================= HRESULT DDAPI DD_RestoreDisplayMode(LPDIRECTDRAW pdd) { LPDDRAWI_DIRECTDRAW_INT pdd_int; LPDDRAWI_DIRECTDRAW_LCL pdd_lcl; LPDDRAWI_DIRECTDRAW_GBL pdd_gbl; BOOL excl_exists,has_excl; HRESULT hr; ENTER_DDRAW(); DPF(2,A,"ENTERAPI: DD_RestoreDisplayMode"); TRY { pdd_int = (LPDDRAWI_DIRECTDRAW_INT) pdd; if (!VALID_DIRECTDRAW_PTR(pdd_int)) { DPF(0, "Invalid object"); LEAVE_DDRAW(); return DDERR_INVALIDOBJECT; } pdd_lcl = pdd_int->lpLcl; pdd_gbl = pdd_lcl->lpGbl; CheckExclusiveMode(pdd_lcl, &excl_exists, &has_excl, FALSE, NULL, FALSE); if (excl_exists && (!has_excl)) { DPF_ERR("Can't change mode; exclusive mode owned"); LEAVE_DDRAW(); return DDERR_NOEXCLUSIVEMODE; } if (pdd_gbl->dwSurfaceLockCount > 0) { LPDDRAWI_DDRAWSURFACE_INT pTemp; // When we enabled vidmem vertex buffers in DX8, we found that some // apps do not unlock them before the mode change, but we don't want // to break them now, so we will hack around this by allowing the // mode switch to occur if all that's locked are vidmem VBs. pTemp = pdd_gbl->dsList; while (pTemp != NULL) { if (pTemp->lpLcl->lpGbl->dwUsageCount > 0) { if ((pTemp->lpLcl->ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY) && !(pTemp->lpLcl->ddsCaps.dwCaps & DDSCAPS_EXECUTEBUFFER)) { break; } } pTemp = pTemp->lpLink; } if (pTemp != NULL) { DPF_ERR("Surfaces are locked, can't switch the mode"); LEAVE_DDRAW(); return DDERR_SURFACEBUSY; } } } EXCEPT(EXCEPTION_EXECUTE_HANDLER) { DPF_ERR("DD_RestoreDisplayMode: Exception encountered validating parameters"); LEAVE_DDRAW(); return DDERR_INVALIDPARAMS; } hr = RestoreDisplayMode(pdd_lcl, TRUE); LEAVE_DDRAW(); return hr; } //============================================================================= // // Function: DD_EnumDisplayModes // //============================================================================= HRESULT DDAPI DD_EnumDisplayModes( LPDIRECTDRAW pdd, DWORD dwFlags, LPDDSURFACEDESC pddsd, LPVOID pContext, LPDDENUMMODESCALLBACK pEnumCallback) { DPF(2,A,"ENTERAPI: DD_EnumDisplayModes"); if (pddsd) { DDSURFACEDESC2 ddsd2; TRY { if(!VALID_DIRECTDRAW_PTR(((LPDDRAWI_DIRECTDRAW_INT) pdd))) { return DDERR_INVALIDOBJECT; } if(!VALID_DDSURFACEDESC_PTR(pddsd)) { DPF_ERR("Invalid surface description. Did you set the dwSize member?"); DPF_APIRETURNS(DDERR_INVALIDPARAMS); return DDERR_INVALIDPARAMS; } CopyMemory(&ddsd2, pddsd, sizeof *pddsd); } EXCEPT(EXCEPTION_EXECUTE_HANDLER) { DPF_ERR("Exception encountered validating parameters: Bad LPDDSURFACEDESC"); DPF_APIRETURNS(DDERR_INVALIDPARAMS); return DDERR_INVALIDPARAMS; } ddsd2.dwSize = sizeof ddsd2; ZeroMemory(((LPBYTE)&ddsd2 + sizeof *pddsd), (sizeof ddsd2) - (sizeof *pddsd)); return DD_EnumDisplayModes4(pdd, dwFlags, &ddsd2, pContext, (LPDDENUMMODESCALLBACK2) pEnumCallback); } return DD_EnumDisplayModes4(pdd, dwFlags, NULL, pContext, (LPDDENUMMODESCALLBACK2) pEnumCallback); } BOOL EnumerateMode( LPDDRAWI_DIRECTDRAW_INT pdd_int, LPDDENUMMODESCALLBACK2 pEnumCallback, LPVOID pContext, WORD wWidth, WORD wHeight, WORD wBPP, WORD wRefreshRate, DWORD dwFlags, BOOL bIsEmulatedModex ) { DDSURFACEDESC2 ddsd; ZeroMemory(&ddsd, sizeof ddsd); if (LOWERTHANDDRAW4(pdd_int)) { ddsd.dwSize = sizeof (DDSURFACEDESC); } else { ddsd.dwSize = sizeof (DDSURFACEDESC2); } ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_PITCH | DDSD_REFRESHRATE; ddsd.dwWidth = wWidth; ddsd.dwHeight = wHeight; ddsd.lPitch = (ddsd.dwWidth * wBPP) >> 3; // hack setPixelFormat(&(ddsd.ddpfPixelFormat), NULL, wBPP); if (dwFlags & DDEDM_REFRESHRATES) { ddsd.dwRefreshRate = wRefreshRate; } else { ddsd.dwRefreshRate = 0; } if ( bIsEmulatedModex ) { ddsd.ddsCaps.dwCaps |= DDSCAPS_MODEX; } else { // call driver here if this is a stereo mode!!! if (!LOWERTHANDDRAW7(pdd_int) && GetDDStereoMode(pdd_int->lpLcl->lpGbl, wWidth, wHeight, wBPP, ddsd.dwRefreshRate)) { ddsd.ddsCaps.dwCaps2 |= DDSCAPS2_STEREOSURFACELEFT; } } return pEnumCallback(&ddsd, pContext); } //============================================================================= // // Function: DD_EnumDisplayModes4 // //============================================================================= HRESULT DDAPI DD_EnumDisplayModes4( LPDIRECTDRAW pdd, DWORD dwFlags, LPDDSURFACEDESC2 pddsd, LPVOID pContext, LPDDENUMMODESCALLBACK2 pEnumCallback) { LPDDRAWI_DIRECTDRAW_INT pdd_int; LPDDRAWI_DIRECTDRAW_LCL pdd_lcl; LPDDRAWI_DIRECTDRAW_GBL pdd_gbl; HRESULT hr; DEVMODE dm; int i, j; DWORD dwResult; DISPLAYMODEINFO *pdmi; DISPLAYMODEINFO *pdmiTemp; int numModes; int maxModes; LPCTSTR pszDevice; BOOL bFound320x240x8 = FALSE; BOOL bFound320x200x8 = FALSE; BOOL bFound640x480x8 = FALSE; ENTER_DDRAW(); DPF(2,A,"ENTERAPI: DD_EnumDisplayModes4"); TRY { pdd_int = (LPDDRAWI_DIRECTDRAW_INT) pdd; if (!VALID_DIRECTDRAW_PTR(pdd_int)) { DPF(0, "Invalid object"); LEAVE_DDRAW(); return DDERR_INVALIDOBJECT; } pdd_lcl = pdd_int->lpLcl; pdd_gbl = pdd_lcl->lpGbl; if (pddsd && !VALID_DDSURFACEDESC2_PTR(pddsd)) { DPF_ERR("Invalid surface description"); LEAVE_DDRAW(); return DDERR_INVALIDPARAMS; } if (dwFlags & ~DDEDM_VALID) { DPF_ERR("Invalid flags"); LEAVE_DDRAW(); return DDERR_INVALIDPARAMS; } if (!VALIDEX_CODE_PTR(pEnumCallback)) { DPF_ERR("Invalid enumerate callback pointer"); LEAVE_DDRAW(); return DDERR_INVALIDPARAMS; } } EXCEPT(EXCEPTION_EXECUTE_HANDLER) { DPF_ERR("Exception encountered validating parameters: Bad LPDDSURFACEDESC"); LEAVE_DDRAW(); return DDERR_INVALIDPARAMS; } maxModes = 256; // enough to handle most drivers pdmi = LocalAlloc(LMEM_FIXED, maxModes * sizeof (DISPLAYMODEINFO)); if (!pdmi) { DPF_ERR("Out of memory building mode list"); LEAVE_DDRAW(); return DDERR_GENERIC; } if (_stricmp(pdd_gbl->cDriverName, "DISPLAY")) { pszDevice = pdd_gbl->cDriverName; } else { pszDevice = NULL; } dm.dmSize = sizeof(dm); for (numModes = 0, j = 0; EnumDisplaySettings(pszDevice, j, &dm); ++j) { //Filter MODEX driver modes if ( (_stricmp(dm.dmDeviceName,"MODEX") == 0) || (_stricmp(dm.dmDeviceName,"VGA") == 0) ) { DPF(5,"Filtered mode %dx%dx%d from %s",dm.dmPelsWidth,dm.dmPelsHeight,dm.dmBitsPerPel,dm.dmDeviceName); continue; } if (dm.dmBitsPerPel == MODEX_BPP) { if (dm.dmPelsWidth == MODEX_WIDTH) { if (dm.dmPelsHeight == MODEX_HEIGHT1) bFound320x200x8 = TRUE; if (dm.dmPelsHeight == MODEX_HEIGHT2) bFound320x240x8 = TRUE; } if (dm.dmPelsWidth == 640 && dm.dmPelsHeight == 480) bFound640x480x8 = TRUE; } //Filter less than 256 color modes if (dm.dmBitsPerPel < 8) { continue; } // // NOTE: If the driver supports 15 bpp but not 16, then // EnumDisplaySettings will return 16 for compatibility reasons. The // bitmasks we fill in will be for 16 bpp (since we can't determine // which mode it really is), so they may be incorrect. // // There should never be a case where we got only 15 bpp. If a driver // only supports 555, it should be reported as 16 bpp. // if (dm.dmBitsPerPel == 15) { dm.dmBitsPerPel = 16; } // // If the caller supplied a DDSURFACEDESC, check for width, // height, bpp, and refresh rate for a match. // if (pddsd && (((pddsd->dwFlags & DDSD_WIDTH) && (dm.dmPelsWidth != pddsd->dwWidth)) || ((pddsd->dwFlags & DDSD_HEIGHT) && (dm.dmPelsHeight != pddsd->dwHeight)) || ((pddsd->dwFlags & DDSD_PIXELFORMAT) && (dm.dmBitsPerPel != pddsd->ddpfPixelFormat.dwRGBBitCount)) || ((pddsd->dwFlags & DDSD_REFRESHRATE) && (dm.dmDisplayFrequency != pddsd->dwRefreshRate)))) { continue; // current mode does not match criteria } // // Check to see if mode is already in the list. The flag which // affects this is DDEDM_REFRESHRATES. // for (i = 0; i < numModes; ++i) { if ((dm.dmPelsWidth == pdmi[i].wWidth) && (dm.dmPelsHeight == pdmi[i].wHeight) && (dm.dmBitsPerPel == pdmi[i].wBPP)) { if (dwFlags & DDEDM_REFRESHRATES) { if (dm.dmDisplayFrequency == pdmi[i].wRefreshRate) { break; // found a match } } else { break; // found a match } } } if (i < numModes) { continue; // mode already in list } pdmi[numModes].wWidth = (WORD) dm.dmPelsWidth; pdmi[numModes].wHeight = (WORD) dm.dmPelsHeight; pdmi[numModes].wBPP = (BYTE) dm.dmBitsPerPel; pdmi[numModes].wRefreshRate = (dwFlags & DDEDM_REFRESHRATES) ? (WORD) dm.dmDisplayFrequency : 0; if (++numModes >= maxModes) { if (maxModes < 8192) { maxModes <<= 1; pdmiTemp = LocalAlloc(LMEM_FIXED, maxModes * sizeof (DISPLAYMODEINFO)); if (pdmiTemp) { CopyMemory(pdmiTemp, pdmi, numModes * sizeof (DISPLAYMODEINFO)); LocalFree(pdmi); pdmi = pdmiTemp; } else { LocalFree(pdmi); DPF_ERR("Out of memory expanding mode list"); LEAVE_DDRAW(); return DDERR_GENERIC; } } else { LocalFree(pdmi); DPF_ERR("Too many display modes"); LEAVE_DDRAW(); return DDERR_GENERIC; } } } // // Should we sort modes here? Probably not. // for (i = 0; i < numModes; ++i) { if (!EnumerateMode( pdd_int, pEnumCallback, pContext, pdmi[i].wWidth, pdmi[i].wHeight, pdmi[i].wBPP, pdmi[i].wRefreshRate, dwFlags, FALSE )) //not a modex mode { break; } } // // Enumerate emulated modex modes if required // while (1) { if (pdd_lcl->dwLocalFlags & DDRAWILCL_ALLOWMODEX) { //640x480 is necessary to turn on emulation if ( bFound640x480x8 ) { if ( !bFound320x200x8 ) { if (!EnumerateMode( pdd_int, pEnumCallback, pContext, MODEX_WIDTH,MODEX_HEIGHT1,MODEX_BPP,60, dwFlags, TRUE )) //not a modex mode { break; } } if ( !bFound320x240x8 ) { if (!EnumerateMode( pdd_int, pEnumCallback, pContext, MODEX_WIDTH,MODEX_HEIGHT2,MODEX_BPP,60, dwFlags, TRUE )) //not a modex mode { break; } } } } break; } LocalFree(pdmi); LEAVE_DDRAW(); return DD_OK; }